From 498ae01164f39cd164a68a1afffa54e6a245e32a Mon Sep 17 00:00:00 2001 From: patacongo Date: Sun, 15 Nov 2009 19:48:08 +0000 Subject: [PATCH] Add logic to handle card insertion/removal events git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2259 42af7a65-404d-4744-a932-0658087f49c3 --- arch/arm/src/stm32/stm32_sdio.c | 27 +++++ drivers/mmcsd/mmcsd_sdio.c | 190 ++++++++++++++++++++++++++++++-- include/nuttx/sdio.h | 26 +++++ include/nuttx/spi.h | 4 +- 4 files changed, 236 insertions(+), 11 deletions(-) diff --git a/arch/arm/src/stm32/stm32_sdio.c b/arch/arm/src/stm32/stm32_sdio.c index a7fe28c7d4..85db660ea4 100644 --- a/arch/arm/src/stm32/stm32_sdio.c +++ b/arch/arm/src/stm32/stm32_sdio.c @@ -159,6 +159,8 @@ static void stm32_eventenable(FAR struct sdio_dev_s *dev, sdio_event_t eventset boolean enable); static ubyte stm32_eventwait(FAR struct sdio_dev_s *dev, uint32 timeout); static ubyte stm32_events(FAR struct sdio_dev_s *dev); +static int stm32_registercallback(FAR struct sdio_dev_s *dev, + sdio_mediachange_t callback, void *arg) /* DMA */ @@ -1068,6 +1070,31 @@ static ubyte stm32_events(FAR struct sdio_dev_s *dev) return 0; } +/**************************************************************************** + * Name: stm32_registercallback + * + * Description: + * Register a callback that that will be invoked on any media status + * change. Callbacks should not be made from interrupt handlers, rather + * interrupt level events should be handled by calling back on the work + * thread. + * + * Input Parameters: + * dev - Device-specific state data + * callback - The funtion to call on the media change + * arg - A caller provided value to return with the callback + * + * Returned Value: + * 0 on success; negated errno on failure. + * + ****************************************************************************/ + +static int stm32_registercallback(FAR struct sdio_dev_s *dev, + sdio_mediachange_t callback, void *arg) +{ + return -ENOSYS; +} + /**************************************************************************** * Name: stm32_dmasupported * diff --git a/drivers/mmcsd/mmcsd_sdio.c b/drivers/mmcsd/mmcsd_sdio.c index 65abe645f5..c7f168b408 100644 --- a/drivers/mmcsd/mmcsd_sdio.c +++ b/drivers/mmcsd/mmcsd_sdio.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -98,6 +99,7 @@ struct mmcsd_state_s { struct sdio_dev_s *dev; /* The SDIO device bound to this instance */ ubyte crefs; /* Open references on the driver */ + sem_t sem; /* Assures mutually exclusive access to the slot */ /* Status flags */ @@ -134,6 +136,11 @@ struct mmcsd_state_s * Private Function Prototypes ****************************************************************************/ +/* Misc Helpers *************************************************************/ + +static void mmcsd_takesem(struct mmcsd_state_s *priv); +#define mmcsd_givesem(p) sem_post(&priv->sem); + /* Command/response helpers *************************************************/ static int mmcsd_sendcmdpoll(struct mmcsd_state_s *priv, uint32 cmd, @@ -176,6 +183,7 @@ static int mmcsd_ioctl(FAR struct inode *inode, int cmd, /* Initialization/uninitialization/reset ************************************/ +static void mmcsd_mediachange(FAR void *arg); static int mmcsd_widebus(struct mmcsd_state_s *priv); static int mmcsd_mmcinitialize(struct mmcsd_state_s *priv); static int mmcsd_sdinitialize(struct mmcsd_state_s *priv); @@ -207,6 +215,24 @@ static const struct block_operations g_bops = * Private Functions ****************************************************************************/ +/**************************************************************************** + * Misc Helpers + ****************************************************************************/ + +static void mmcsd_takesem(struct mmcsd_state_s *priv) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&priv->sem) != 0) + { + /* The only case that an error should occr here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + /**************************************************************************** * Command/Response Helpers ****************************************************************************/ @@ -844,7 +870,9 @@ static int mmcsd_open(FAR struct inode *inode) /* Just increment the reference count on the driver */ DEBUGASSERT(priv->crefs < MAX_CREFS); + mmcsd_takesem(priv); priv->crefs++; + mmcsd_givesem(priv); return OK; } @@ -866,7 +894,9 @@ static int mmcsd_close(FAR struct inode *inode) /* Decrement the reference count on the block driver */ DEBUGASSERT(priv->crefs > 0); + mmcsd_takesem(priv); priv->crefs--; + mmcsd_givesem(priv); return OK; } @@ -883,16 +913,20 @@ static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer, size_t start_sector, unsigned int nsectors) { struct mmcsd_state_s *priv; + int ret; fvdbg("sector: %d nsectors: %d sectorsize: %d\n"); DEBUGASSERT(inode && inode->i_private); priv = (struct mmcsd_state_s *)inode->i_private; + mmcsd_takesem(priv); #ifdef CONFIG_FS_READAHEAD - return rwb_read(&priv->rwbuffer, start_sector, nsectors, buffer); + ret = rwb_read(&priv->rwbuffer, start_sector, nsectors, buffer); #else - return mmcsd_doread(priv, buffer, start_sector, nsectors); + ret = mmcsd_doread(priv, buffer, start_sector, nsectors); #endif + mmcsd_givesem(priv); + return ret; } /**************************************************************************** @@ -909,16 +943,20 @@ static ssize_t mmcsd_write(FAR struct inode *inode, const unsigned char *buffer, size_t start_sector, unsigned int nsectors) { struct mmcsd_state_s *priv; + int ret; fvdbg("sector: %d nsectors: %d sectorsize: %d\n"); DEBUGASSERT(inode && inode->i_private); priv = (struct mmcsd_state_s *)inode->i_private; + mmcsd_takesem(priv); #ifdef CONFIG_FS_WRITEBUFFER - return rwb_write(&priv->rwbuffer, start_sector, nsectors, buffer); + ret = rwb_write(&priv->rwbuffer, start_sector, nsectors, buffer); #else - return mmcsd_dowrite(priv, buffer, start_sector, nsectors); + ret = mmcsd_dowrite(priv, buffer, start_sector, nsectors); #endif + mmcsd_givesem(priv); + return ret; } #endif @@ -936,6 +974,8 @@ static int mmcsd_geometry(FAR struct inode *inode, struct geometry *geometry) fvdbg("Entry\n"); DEBUGASSERT(inode && inode->i_private); + + mmcsd_takesem(priv); if (geometry) { /* Is there a (supported) card inserted in the slot? */ @@ -972,6 +1012,8 @@ static int mmcsd_geometry(FAR struct inode *inode, struct geometry *geometry) ret = OK; } } + + mmcsd_givesem(priv); return ret; } @@ -993,6 +1035,7 @@ static int mmcsd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) /* Process the IOCTL by command */ + mmcsd_takesem(priv); switch (cmd) { case BIOC_PROBE: /* Check for media in the slot */ @@ -1028,6 +1071,7 @@ static int mmcsd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) break; } + mmcsd_givesem(priv); return ret; } @@ -1035,6 +1079,48 @@ static int mmcsd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg) * Initialization/uninitialization/reset ****************************************************************************/ +/**************************************************************************** + * Name: mmcsd_mediachange + * + * Description: + * This is a callback function from the SDIO driver that indicates that + * there has been a change in the slot... either a card has been inserted + * or a card has been removed. + * + * Assumptions: + * This callback is NOT supposd to run in the context of an interrupt + * handler; it is probably running in the context of work thread. + * + ****************************************************************************/ + +static void mmcsd_mediachange(FAR void *arg) +{ + struct mmcsd_state_s *priv = (struct mmcsd_state_s *)arg; + + DEBUGASSERT(priv); + + /* Is there a card present in the slot? */ + + mmcsd_takesem(priv); + if (SDIO_PRESENT(priv->dev)) + { + /* No... process the card insertion. This could cause chaos if we think + * that a card is already present and there are mounted filesystms! + */ + + (void)mmcsd_probe(priv); + } + else + { + /* No... process the card removal. This could have very bad implications + * for any mounted file systems! + */ + + (void)mmcsd_removed(priv); + } + mmcsd_givesem(priv); +} + /**************************************************************************** * Name: mmcsd_widebus * @@ -1274,7 +1360,18 @@ static int mmcsd_sdinitialize(struct mmcsd_state_s *priv) return ret; } - priv->rca = (uint16)rca; + /* R6 Published RCA Response (48-bit, SD card only) + * 47 0 Start bit + * 46 0 Transmission bit (0=from card) + * 45:40 bit5 - bit0 Command index (0-63) + * 39:8 bit31 - bit0 32-bit Argument Field, consisting of: + * [31:16] New published RCA of card + * [15:0] Card status bits {23,22,19,12:0} + * 7:1 bit6 - bit0 CRC7 + * 0 1 End bit + */ + + priv->rca = (uint16)(rca >> 16); fvdbg("RCA: %04x\n", priv->rca); /* This should have caused a transition to standby state. However, this will @@ -1754,10 +1851,84 @@ static int mmcsd_removed(struct mmcsd_state_s *priv) static int mmcsd_hwinitialize(struct mmcsd_state_s *priv) { -#ifdef CONFIG_CPP_HAVE_WARNING -# warning "Not implemented" + int ret; + + mmcsd_takesem(priv); + +#ifdef CONFIG_SDIO_DMA + /* Does this architecture support DMA with the MMC/SD device? */ + + priv->dma = SDIO_DMASUPPORTED(priv->dev); + fvdbg("DMA supported: %d\n", priv->dma); #endif - return -ENODEV; + + /* Attach and prepare MMC/SD interrupts */ + + if (SDIO_ATTACH(priv->dev)) + { + fdbg("ERROR: Unable to attach MMC/SD interrupts\n"); + mmcsd_givesem(priv); + return -EBUSY; + } + fvdbg("Successfully attached MMC/SD interrupts\n"); + + /* Register a callback so that we get informed if media is inserted or + * removed from the slot. + */ + + SDIO_REGISTERCALLBACK(priv->dev, mmcsd_mediachange, (FAR void *)priv); + + /* Is there a card in the slot now? For an MMC/SD card, there are three + * possible card detect mechanisms: + * + * 1. Mechanical insertion that can be detected using the WP switch + * that is closed when a card is inserted into then SD slot (SD + * "hot insertion capable" card conector only) + * 2. Electrical insertion that can be sensed using the pull-up resistor + * on CD/DAT3 (both SD/MMC), + * 3. Or by periodic attempts to initialize the card from software. + * + * The behavior of SDIO_PRESENT() is to use whatever information is available + * on the particular platform. If no card insertion information is available + * (polling only), then SDIO_PRESENT() will always return true and we will + * try to initialize the card. + */ + + if (SDIO_PRESENT(priv->dev)) + { + /* Yes... probe for a card in the slot */ + + ret = mmcsd_probe(priv); + if (ret != OK) + { + fvdbg("Slot not empty, but initialization failed: %d\n", ret); + + /* NOTE: The failure to initialize a card does not mean that + * initialization has failed! A card could be installed in the slot + * at a later time. ENODEV is return in this case, but should not be + * interpreted as an error. + */ + + ret = -ENODEV; + } + } + else + { + /* No... Setup to receive the media inserted event */ + + SDIO_EVENTENABLE(priv->dev, SDIOEVENT_INSERTED); + + /* ENODEV is returned to indicate that no card is inserted in the slot. */ + + ret = -ENODEV; + } + + /* OK is returned only if the slot initialized correctly AND the card in + * the slot was successfully configured. + */ + + mmcsd_givesem(priv); + return ret; } /**************************************************************************** @@ -1824,6 +1995,7 @@ int mmcsd_slotinitialize(int minor, int slotno, FAR struct sdio_dev_s *dev) /* Initialize the MMC/SD state structure */ memset(priv, 0, sizeof(struct mmcsd_state_s)); + sem_init(&priv->sem, 0, 1); /* Bind the MMCSD driver to the MMCSD state structure */ @@ -1884,8 +2056,8 @@ int mmcsd_slotinitialize(int minor, int slotno, FAR struct sdio_dev_s *dev) errout_with_buffers: #if defined(CONFIG_FS_WRITEBUFFER) || defined(CONFIG_FS_READAHEAD) rwb_uninitialize(&priv->rwbuffer); -#endif errout_with_hwinit: +#endif mmcsd_hwuninitialize(priv); errout_with_alloc: free(priv); diff --git a/include/nuttx/sdio.h b/include/nuttx/sdio.h index ebecc5923e..576526a0ec 100755 --- a/include/nuttx/sdio.h +++ b/include/nuttx/sdio.h @@ -571,6 +571,27 @@ #define SDIO_EVENTS(dev) ((dev)->events(dev)) +/**************************************************************************** + * Name: SDIO_REGISTERCALLBACK + * + * Description: + * Register a callback that that will be invoked on any media status + * change. Callbacks should not be made from interrupt handlers, rather + * interrupt level events should be handled by calling back on the work + * thread. + * + * Input Parameters: + * dev - Device-specific state data + * callback - The funtion to call on the media change + * arg - A caller provided value to return with the callback + * + * Returned Value: + * 0 on success; negated errno on failure. + * + ****************************************************************************/ + +#define SDIO_REGISTERCALLBACK(d,c,a) ((d)->registercallback(d,c,a)) + /**************************************************************************** * Name: SDIO_DMASUPPORTED * @@ -727,6 +748,10 @@ * Public Types ****************************************************************************/ +/* The type of the media change callback function */ + +typedef void (*sdio_mediachange_t)(FAR void *arg); + /* Various clocking used by the MMC/SD driver */ enum sdio_clock_e @@ -788,6 +813,7 @@ struct sdio_dev_s void (*eventenable)(FAR struct sdio_dev_s *dev, sdio_eventset_t eventset); ubyte (*eventwait)(FAR struct sdio_dev_s *dev, uint32 timeout); ubyte (*events)(FAR struct sdio_dev_s *dev); + int (*registercallback)(FAR struct sdio_dev_s *dev, sdio_mediachange_t callback, void *arg); /* DMA */ diff --git a/include/nuttx/spi.h b/include/nuttx/spi.h index c5bfcdd92e..0093491ae2 100644 --- a/include/nuttx/spi.h +++ b/include/nuttx/spi.h @@ -290,7 +290,7 @@ /* The type of the media change callback function */ -typedef void (*mediachange_t)(void *arg); +typedef void (*spi_mediachange_t)(FAR void *arg); /* If the board supports multiple SPI devices, this enumeration identifies * which is selected or de-seleted. @@ -332,7 +332,7 @@ struct spi_ops_s void (*sndblock)(FAR struct spi_dev_s *dev, FAR const void *buffer, size_t nwords); void (*recvblock)(FAR struct spi_dev_s *dev, FAR void *buffer, size_t nwords); #endif - int (*registercallback)(FAR struct spi_dev_s *dev, mediachange_t callback, void *arg); + int (*registercallback)(FAR struct spi_dev_s *dev, spi_mediachange_t callback, void *arg); }; /* SPI private data. This structure only defines the initial fields of the