I started ESP8266 project, the first step was to support SDMMC over SPI. I have implemented SDMMC SPI interface years ago. The main challenge was to adapt it to new MCU core. After many attempts, it was up and running. Below are lessons learned.

All in all, the main aim was to work-around the way, ESP HSPI treats MOSI level. It is 0 when inactive, i.e. all the time we reading SPI packets, slave receives 00s. No SDMMCs I know start communicating under these conditions. They expect high MOSI, as shown on picture below, taken from the same SDMMC code working on STM32 platform.

SDMMC over SPI. Platform - STM32F103xx
SDMMC over SPI. Platform - STM32F103xx

While, if used as-is, ESP8266 HSPI demonstrates the following MOSI behavior:

SDMMC over SPI. Platform - ESP8266. Default behavior (MOSI = 0 when reading, or idle, not working with SDMMC)
SDMMC over SPI. Platform -  ESP8266. Default behavior (MOSI = 0 when reading, or idle, not working with SDMMC)

What we have to do to work-around this, is to unconfigure MOSI function to GPIO13 output, while reading SPI, and set it to 1.
While, if we got to write to SPI, we heve to re-configure GPIO13 back to MOSI, and proceed as usual. After that, SDMMC starts answering, and initialization finally completes OK.

SDMMC over SPI, ESP8266, working HSPI work-arond, MOSI = 1 when reading.
SDMMC over SPI, ESP8266, working HSPI work-arond, MOSI = 1 when reading.

Below are excerpt files from the working SDMMC project.
After initialization code is successful (SDMMC is "open") we may use our storage elsewhere, for instance in famous ELM FAT FS library calls.

SDMMC over SPI C source open-close sample

// ESP includes
#include <esp_common.h>
#include <gpio.h>
#include <spi_interface.h>

#include "retarget.h"
// Our ESFWXE tuneups
#include "sdmmc_spiIntf.h"
// ESFWXE Drivers
#include <esfwxe/drivers/flash/sdmmc_spi.h>

// Application-specific SDMMC driver pieces implementation
static SpiAttr s_spi;
static SdmmcInfo s_sdmmc;

spiHANDLE sdmmcSpiBusGet(void)
{
  return &s_spi;  
}

SdmmcInfo* sdmmcInfoGet(void)
{
  return &s_sdmmc;  
}

static void sdmmcInfoReset(void)
{
	memset( 
    &s_sdmmc, 
    0, 
    SdmmcInfo_SZE
  );  
}

esBL sdmmcOpen(void)
{
	ES_DEBUG_TRACE("sdmmcOpen...\n");

  // reconfigure spi to low speed and open it
  spiConfig( 
    &s_spi, 
    sdmmcInitFreq 
  );
  
  sdmmcPowerOn(
    &s_spi, 
    &s_sdmmc, 
    TRUE
  );
  
  if( 
    sdmmcInit(
      &s_spi, 
      &s_sdmmc, 
      3300, //< SDMMC expected VCC voltage
      TRUE // Use SDMMC CRC
    ) &&
    sdmmcOk == (s_sdmmc.flags & sdmmcOk) &&
    sdmmcVoltageMismatch != (s_sdmmc.flags & sdmmcVoltageMismatch)
  )
  {
    // Set full-speed spi
    spiConfig(
      &s_spi,
      20000000
    );
    
    // Recalculate sdmmc timings for general 500ms timeout
    sdmmcCalcIoTimings(
      &s_spi, 
      &s_sdmmc, 
      500
    );

    ES_DEBUG_TRACE("...OK\n");
    return TRUE;
  }

  sdmmcPowerOn(
    &s_spi, 
    &s_sdmmc, 
    FALSE
  );
  
	ES_DEBUG_TRACE("...NOK\n");
  return FALSE;
}

void sdmmcClose(void)
{
	ES_DEBUG_TRACE("sdmmcClose...\n");
  
  sdmmcPowerOn(
    &s_spi, 
    &s_sdmmc, 
    FALSE
  );  
  
  sdmmcInfoReset();
  
	ES_DEBUG_TRACE("...OK\n");  
}

void sdmmcHwInit(void)
{
	ES_DEBUG_TRACE("sdmmcHwInit...\n");
  
  sdmmcInfoReset();
  sdmmcSpiInit(&s_spi);
  
	ES_DEBUG_TRACE("...OK\n");
}

SDMMC over SPI generic API C source

#include <esfwxe/target.h>
#pragma hdrstop

#include <string.h>
#include <esfwxe/utils.h>
#include <esfwxe/crc.h>
#ifndef USE_CUSTOM_SPI
# include <esfwxe/core/spi.h>
#endif
#include <esfwxe/drivers/flash/sdmmc_spi.h>

// SDMMC over SPI driver implementation
//

// Definitions for MMC/SDC command
#define CMD0									(0x40+0)	// GO_IDLE_STATE
#define CMD1									(0x40+1)	// SEND_OP_COND (MMC)
#define ACMD41								(0xC0+41)	// SEND_OP_COND (SDC)
#define CMD8									(0x40+8)	// SEND_IF_COND
#define CMD9									(0x40+9)	// SEND_CSD
#define CMD10									(0x40+10)	// SEND_CID
#define CMD12									(0x40+12)	// STOP_TRANSMISSION
#define CMD13									(0x40+13)	// SD_STATUS
#define ACMD13								(0xC0+13)	// SD_STATUS (SDC)
#define CMD16									(0x40+16)	// SET_BLOCKLEN
#define CMD17									(0x40+17)	// READ_SINGLE_BLOCK
#define CMD18									(0x40+18)	// READ_MULTIPLE_BLOCK
#define CMD23									(0x40+23)	// SET_BLOCK_COUNT (MMC)
#define ACMD23								(0xC0+23)	// SET_WR_BLK_ERASE_COUNT (SDC)
#define CMD24									(0x40+24)	// WRITE_BLOCK
#define CMD25									(0x40+25)	// WRITE_MULTIPLE_BLOCK
#define CMD28									(0x40+28)	// SET_WRITE_PROT
#define CMD29									(0x40+29)	// CLR_WRITE_PROT
#define CMD32									(0x40+32)	// ERASE_WR_BLK_START_ADDR
#define CMD33									(0x40+33)	// ERASE_WR_BLK_END_ADDR
#define CMD38									(0x40+38)	// ERASE
#define CMD55									(0x40+55)	// APP_CMD
#define CMD58									(0x40+58)	// READ_OCR
#define CMD59									(0x40+59)	// CRC_ON_OFF

// command response masks
//
// R1, R2, R3 LSB masks
//
#define RM_IN_IDLE 						0x01 		// In Idle State
#define	RM_ERASE_RST					0x02		// Erase Reset
#define RM_ILLEGAL_CMD 				0x04 		// Illegal Command
#define RM_CRC_ERROR					0x08		// CRC Error
#define RM_ERASE_SEQ_ERR			0x10		// Erase Sequence Error
#define RM_ADDR_ERR						0x20		// Address Error
#define RM_PARAM_ERR					0x40		// Parameter Error

// R2, R3 MSB masks
//
#define RM_CARD_LOCKED				0x01 	// Card Locked
#define RM_WRPROT_ERASE_SKIP 	0x02	// Write Protect Erase Skip
#define RM_LOCK_ULOCK_FAILED	RM_WRPROT_ERASE_SKIP // Lock/Unlock Failed
#define RM_UNSPECIFIED_ERROR	0x04	// Unspecified Error
#define RM_CARD_CTLR_ERROR  	0x08	// Card Controller Error
#define RM_CARD_ECC__FAILED		0x10 	// Card ECC Failed
#define RM_WRPROT_VIOLATION		0x20	// Write Protect Violation
#define	RM_ERASE_PARAM				0x40	// Erase Parameter
#define RM_OUT_OF_RANGE				0x80	// Out of Range
#define RM_CSD_OVERWRITE			RM_OUT_OF_RANGE // CSD Overwrite

// read data error token masks
//
#define RET_UNSPECIFIED_ERROR	0x01	// Unspecified Error
#define RET_CARD_CTLR_ERROR		0x02  // Card Controller Error
#define RET_CARD_ECC_FAILED		0x04	// Card ECC Failed
#define RET_OUT_OF_RANGE			0x08	// Out of Range
#define RET_CARD_LOCKED				0x10	// Card Locked

// block data start|stop tokens
#define DT_RBLOCK_START 			0xFE
#define DT_RBLOCK_MULTI_START DT_RBLOCK_START
#define DT_WBLOCK_START				DT_RBLOCK_START
#define DT_WBLOCK_MULTI_START	0xFC
#define DT_WBLOCK_MULTI_STOP	0xFD

// data write result token analysis
//
#define DWRT_EXTRACT(r) 			((r) & 0x1F) // extract data write token value from byte response

// write token values
//
#define DWRT_AOK							0x05 	// data write accepted
#define DWRT_CRC_ERROR				0x0B	// data write was rejected due to CRC error
#define DWRT_WRITE_ERROR			0x0D	// data write was rejected due to write error

// length of command packet
#define CMD_PACKET_LEN				6

#if defined( ES_USE_SDMMC_DEBUG_TRACE ) && defined( ES_DEBUG_TRACE )
# define ES_SDMMC_TRACE       ES_DEBUG_TRACE
#else
# define ES_SDMMC_TRACE(...)  ((void)0)
#endif

// response types
//
typedef enum {
  sdmmcR1,
  sdmmcR1b,
  sdmmcR2,
  sdmmcR3,
  sdmmcR7,
  // special const - responses count, must go last
  sdmmcRcnt

} sdmmcResponse;

// response data sizes (additional bytes after the first response byte)
static const esU8 c_sdmmcResponseSize[sdmmcRcnt] = {
  0,	// r1
  0, 	// r1b
  1,	// r2
  4,	// r3
  4		// r7
};

// internal sdmmc io buffer
static esU8 s_sdmmcBuff[16];

// sdmmc internal helper functions
//
// convert response masks to abstract sdmmc status mask
static __inline void sdmmcConvertR1toStatus(esU8 r1, SdmmcInfo* info)
{
  info->stat = r1;
}

static __inline void sdmmcConvertR2toStatus(esU8 r1, esU8 r2, SdmmcInfo* info)
{
  sdmmcConvertR1toStatus(r1, info);
  info->stat |= ((esU16)r2) << 7;
}

// wait until bus becomes ready (DO idles to high state)
esBL sdmmcWaitReady(spiHANDLE h, SdmmcInfo* info)
{
  esU32 retries = 0;
  do
  {
    spiGetBytes(h, s_sdmmcBuff, 1);

  } while( 
      s_sdmmcBuff[0] != 0xFF && 
      ++retries < info->ioRetries );
  
  if( retries < info->ioRetries )
	{
    return TRUE;
  }
	else
  {
    info->stat |= sdmmcWaitReadyExpired;
		
    ES_SDMMC_TRACE("...Failed to wait until SDMMC is ready\n");
		
    return FALSE;
  }
}

// receive command response block
static esBL sdmmcGetResponse(spiHANDLE h, SdmmcInfo* info, sdmmcResponse r )
{
  // wait for response's first byte
  // get command response
  esU32 retries = 0;
  do
  {
    spiGetBytes(h, s_sdmmcBuff, 1);
  
  }	while( (s_sdmmcBuff[0] & 0x80) && 
            ++retries < info->ioRetries );
  // retries not expired and additional bytes needed
  if( retries < info->ioRetries )
  {
    // get the rest of response packet
    if( c_sdmmcResponseSize[r] )
      spiGetBytes(h, s_sdmmcBuff+1, c_sdmmcResponseSize[r]);
    
    // convert responses to universal status field
    if( sdmmcR2 == r )
      sdmmcConvertR2toStatus(s_sdmmcBuff[0], s_sdmmcBuff[1], info);
    else
      sdmmcConvertR1toStatus(s_sdmmcBuff[0], info);

    return TRUE;
  }

  return FALSE;
}

// return response type for specified command value
static __inline sdmmcResponse sdmmcGetResponseTypeForCmd(esU8 cmd)
{
  switch(cmd)
  { 
  case CMD8:
    return sdmmcR7;
  case CMD12:
  case CMD28:
  case CMD29:
  case CMD38:
    return sdmmcR1b;
  case CMD58:
    return sdmmcR3;
  case CMD13:
    return sdmmcR2;
  }

  return sdmmcR1;
}

// send single command packet to card and get response to it
static esBL sdmmcSendCmdInternal(spiHANDLE h, SdmmcInfo* info, esU8 cmd, esU32 arg)
{
  const esU8* argpos = (const esU8*)&arg + 3;

  ES_SDMMC_TRACE("...sending CMD%d", (int)cmd-0x40);
 
  // wait until card becomes ready
  if( 
		sdmmcWaitReady(
			h, 
			info
		) 
	)
  {
    // pack command + argument + crc
    s_sdmmcBuff[0] = cmd;
    s_sdmmcBuff[1] = *argpos--;
    s_sdmmcBuff[2] = *argpos--;
    s_sdmmcBuff[3] = *argpos--;
    s_sdmmcBuff[4] = *argpos--;
    // default to single stop bit if no crc support is active
    s_sdmmcBuff[5] = 1;
    // finalize packet with left-aligned crc7 + stop bit
    // use precalculated crcs for CMD0 and CMD8 commands with known contents
    if( CMD0 == cmd )
      s_sdmmcBuff[5] = 0x95;
    else if( CMD8 == cmd ) // for 0x1AA argument
      s_sdmmcBuff[5] = 0x87;
    else if( info->flags & sdmmcUseCrc )
      s_sdmmcBuff[5] = (crc7(0, s_sdmmcBuff, 5) << 1) + 1;
      
    // send command packet	
    spiPutBytes(
			h, 
			s_sdmmcBuff, 
			CMD_PACKET_LEN
		);

    // if stop reading command issued, skip one dummy byte
    if(CMD12 == cmd) 
      spiGetBytes(
				h, 
				s_sdmmcBuff, 
				1
			);

#ifdef DEBUG
    if( 
			!sdmmcGetResponse(
				h, 
				info, 
				sdmmcGetResponseTypeForCmd(cmd)
			) 
		)
    {
      ES_SDMMC_TRACE("...Failed to get response to command\n");
      
      return FALSE;
    }
				
    ES_SDMMC_TRACE("...OK\n");
		
		return TRUE;
#else
    return sdmmcGetResponse(
			h, 
			info, 
			sdmmcGetResponseTypeForCmd(cmd)
		);
#endif
  }

  return FALSE;
}

static __inline esBL sdmmcSendCmd(spiHANDLE h, SdmmcInfo* info, esU8 cmd, esU32 arg)
{
  esBL result;

  sdmmcSELECT;

  if( cmd & 0x80 ) // handle ACMDs
    result = sdmmcSendCmdInternal(h, info, CMD55, 0) &&	
      sdmmcSendCmdInternal(h, info, cmd & 0x7F, arg);
  else
    result = sdmmcSendCmdInternal(h, info, cmd, arg);

  sdmmcDESELECT;

  return result;
}

static esBL sdmmcEnterIdle(spiHANDLE h, SdmmcInfo* info, esBL useCrc)
{
  sdmmcDESELECT;

  // wait for >= 74 spi bus clocks with CS and DI set to high state
  memset(
    s_sdmmcBuff, 
    0xFFFFFFFF, 
    sizeof(s_sdmmcBuff)
  );
  spiPutBytes(h, s_sdmmcBuff, 12);

  // issue CMD0 && check response
  if( sdmmcSendCmd(h, info, CMD0, 0) &&
      sdmmcStatIdle == info->stat )
  {
    // set primary initialized flag
    info->flags = sdmmcOk;

    // activate CRC support, if required
    if( useCrc &&
        sdmmcSendCmd(h, info, CMD59, 1) &&
        sdmmcStatIdle == info->stat 
    )
      info->flags |= sdmmcUseCrc;

    return TRUE;	
  }

  return FALSE;
}

// read OCR and check voltage mask
static esBL sdmmcCheckVoltageMask(spiHANDLE h, SdmmcInfo* info, esU32 mask)
{
  // read OCR 
  if( sdmmcSendCmd(h, info, CMD58, 0) &&
      (	sdmmcStatAOK == info->stat ||
        sdmmcStatIdle == info->stat ) )
  {
    if( (s_sdmmcBuff[2] & ((mask >> 16) & 0xFF)) ||
        (s_sdmmcBuff[3] & ((mask >> 8) & 0xFF)) )
      return TRUE;			
    else
      info->flags |= sdmmcVoltageMismatch;
  }

  return FALSE;
}

static esBL sdmmcCheckSd1(spiHANDLE h, SdmmcInfo* info, esU32 mask)
{
  esU32 retries = info->ioRetries;
  while( retries-- )
  {
    if( !sdmmcSendCmd(h, info, ACMD41, 0) )
      break;

    if( sdmmcStatAOK == info->stat )
    {
      info->type = sdmmcSd1;
      return sdmmcCheckVoltageMask(h, info, mask);
    }
    else if( sdmmcStatIdle != info->stat )
      break;
  }

  return FALSE;
}

static esBL sdmmcCheckMmc3(spiHANDLE h, SdmmcInfo* info, esU32 mask)
{
  esU32 tries = info->ioRetries;
  while( tries-- )
  {
    if( !sdmmcSendCmd(h, info, CMD1, 0) )
      break;

    if( sdmmcStatAOK == info->stat )
    {
      info->type = sdmmcMmc3;
      return sdmmcCheckVoltageMask(h, info, mask);
    }
    else if( sdmmcStatIdle != info->stat )
      break;
  }

  return FALSE;
}

static esBL sdmmcCheckSd2(spiHANDLE h, SdmmcInfo* info, esU32 mask)
{
  esBL result = FALSE;
  esU32 tries = info->ioRetries;
  // wait until exit card idle state, continuously sending ACMD41 with high capacity bit set
  while( tries-- )
  {
    if( !sdmmcSendCmd(h, info, ACMD41, 0x40000000) )
      break;

    if( sdmmcStatAOK == info->stat )
    {
      info->type = sdmmcSd2;
      result = sdmmcCheckVoltageMask(h, info, mask);
      // check high capacity bit
      if( result && (s_sdmmcBuff[1] & 0x40) )
        info->flags |= sdmmcHighCapacity;
      break;
    }
    else if( sdmmcStatIdle != info->stat )
      break;
  }

  return result;
}

// LV range voltages are currently not supported
// voltage is in millivolts
static __inline esU32 sdmmcMakeVoltageMask(esU16 v)
{
  esU32 result = 0;

  if( v >= 2700 && 
      v <= 2800 )
    result |= (1 << 15);
  if( v >= 2800 && 
      v <= 2900 )
    result |= (1 << 16);
  if( v >= 2900 && 
      v <= 3000 )
    result |= (1 << 17);
  if( v >= 3000 && 
      v <= 3100 )
    result |= (1 << 18);
  if( v >= 3100 && 
      v <= 3200 )
    result |= (1 << 19);
  if( v >= 3200 && 
      v <= 3300 )
    result |= (1 << 20);
  if( v >= 3300 && 
      v <= 3400 )
    result |= (1 << 21);
  if( v >= 3400 && 
      v <= 3500 )
    result |= (1 << 22);
  if( v >= 3500 && 
      v <= 3600 )
    result |= (1 << 23);
  
  return result;	
}

static esBL sdmmcCheckCardSupport(spiHANDLE h, SdmmcInfo* info, esU16 v)
{
  esBL result = FALSE;
  esU32 vMask = sdmmcMakeVoltageMask(v);
  // send CMD8 with proper CRC to check if card is sd2 and host voltage is supported
  if( !sdmmcSendCmd(h, info, CMD8, 0x01AA) || 
      (sdmmcStatIllegalCmd & info->stat) )
    // error or no response - try sd1 or mmc3
    result = sdmmcCheckSd1(h, info, vMask) || 
          sdmmcCheckMmc3(h, info, vMask);
  // check if voltage and bit pattern match
  else if( s_sdmmcBuff[3] == 0x01 && 
          s_sdmmcBuff[4] == 0xAA )
    result = sdmmcCheckSd2(h, info, vMask);

  return result;
}

static esBL sdmmcReadDataPacket(spiHANDLE h, SdmmcInfo* info, esU8 dataToken, esU8* data, esU32 len)
{
  esU32 retries = 0;
  esBL result = FALSE;

  sdmmcSELECT;
  // skip until data token is read
  do
  {
    spiGetBytes(h, data, 1);

  }	while( 0xFF == *data &&
          ++retries < info->ioRetries );

  // read actual data, if retries not expired, 
  // read data block crc at the end of operation
  if( retries < info->ioRetries )
  {
    if(	DT_RBLOCK_START == *data )
    {
      esU16 crc;
      result = 	len == spiGetBytes(h, data, len) &&
                2 == spiGetBytes(h, (esU8*)&crc, 2);
      if( result && (info->flags & sdmmcUseCrc) )
      {
        result = SWAPB_WORD(crc) == crc16ccitt(0, data, len);
        if( !result ) // set status bit specifying we get corrupt data read
          info->stat |= sdmmcReadCrcError;					
      }
    }
    else // set data error token bits to universal status format
      info->stat = ((esU16)*data) << 7;		
  }

  sdmmcDESELECT;
  
  return result;		
}

static esBL sdmmcConfigureAddressing(spiHANDLE h, SdmmcInfo* info)
{
  if( info->type != sdmmcUnknown &&
      sdmmcSendCmd(h, info, CMD9, 0) &&
      sdmmcStatAOK == info->stat &&
      // read CSD block
      sdmmcReadDataPacket(h, info, DT_RBLOCK_START, s_sdmmcBuff, 16) )
  {
    // adjust CRC7
    s_sdmmcBuff[15] >>= 1;
    // check CRC
    if( s_sdmmcBuff[15] == crc7(0, s_sdmmcBuff, 15) )
    {
      if( info->flags & sdmmcHighCapacity )
      {
        // CSD V2.0
        info->blockCnt = (((esU32)(s_sdmmcBuff[7] & 0x3F) << 16) + 
          ((esU32)s_sdmmcBuff[8] << 8) + (esU32)s_sdmmcBuff[9] + 1);
        info->blockCnt <<= 10;
        info->userBlockSize = info->blockSize = 512;
        info->flags |= sdmmcBlockAddr;
        return TRUE;
      }
      else
      {
        // CSD V1.0
        info->blockCnt = (((esU32)(s_sdmmcBuff[6] & 0x03) << 10) + 
          ((esU32)s_sdmmcBuff[7] << 2) + ((esU32)(s_sdmmcBuff[8] & 0xC0) >> 6) + 1) * 
          (1 << ((((esU32)s_sdmmcBuff[9] & 0x03) << 1) + 
                (((esU32)s_sdmmcBuff[10] & 0x80) >> 7) + 2));
        info->blockSize = 1 << (esU32)(s_sdmmcBuff[5] & 0x0F);
        if( !(s_sdmmcBuff[10] & 0x40) )
          info->flags |= sdmmcSectorEraseUnit;
        // V1 cards allow partial and misaligned data access by-default
        // to eliminate performance flaw, as well as card wearing, we should
        // always configure 512 user block access for such cards
        if( sdmmcSendCmd(h, info, CMD16, 512) &&
            sdmmcStatAOK == info->stat )
        {
          info->userBlockSize = 512;
          return TRUE;
        }
      }
      // get transpeed capability

    }
  }

  return FALSE;
}

// initialize sdmmc card access. v is host-supplied voltage in millivolts
// i.e. 3.6v = 3600 
esBL sdmmcInit(spiHANDLE h, SdmmcInfo* info, esU16 v, esBL useCrc)
{
  if( !info )
    return FALSE;

  // initialize sdmmc structure to all 0s
  memset(info, 0, SdmmcInfo_SZE);
  // set some initial retries	for 1s expected timeout
  sdmmcCalcIoTimings(h, info, 1000);

  if( INVALID_HANDLE == h )
    return FALSE;

  return sdmmcEnterIdle(h, info, useCrc) &&
          sdmmcCheckCardSupport(h, info, v) &&
          sdmmcConfigureAddressing(h, info);
}

// sd card block io. returned is count of blocks actually read|written
esU32 sdmmcBlocksRead(spiHANDLE h, SdmmcInfo* info, esU32 startBlock, esU8* blocks, esU32 count)
{
  esU32	result = 0;
  
  if( spiIsOpen(h) &&
      info &&
      sdmmcUnknown != info->type &&
      blocks &&
      count )
  {
    // if 1 == count issue single block read command
    // else, fetch multiple blocks, terminated with CMD21
    if( 1 < count )
    {
      if( sdmmcSendCmd(h, info, CMD18, (info->flags & sdmmcBlockAddr) ? startBlock : startBlock*info->userBlockSize ) &&
          sdmmcStatAOK == info->stat )
      {
        while( result < count ) 
        {
          if( !sdmmcReadDataPacket(h, info, DT_RBLOCK_MULTI_START, blocks, info->userBlockSize) ) 
            break;
          blocks += info->userBlockSize;
          ++result;
        }
        sdmmcSendCmd(h, info, CMD12, 0); // break multiread
      }
    }
    else if( sdmmcSendCmd(h, info, CMD17, (info->flags & sdmmcBlockAddr) ? startBlock : startBlock*info->userBlockSize ) &&
             sdmmcStatAOK == info->stat && 
             sdmmcReadDataPacket(h, info, DT_RBLOCK_START, blocks, info->userBlockSize) )
      result = 1;

    sdmmcReleaseSpiBus(h);
  }

  return result;
}

static esBL sdmmcWriteDataPacket(	spiHANDLE h, SdmmcInfo* info, esU8 token, const esU8 *buff, esU32 len )
{
  esBL result = FALSE;

  sdmmcSELECT;
  if( sdmmcWaitReady(h, info) &&
      1 == spiPutBytes(h, &token, 1) ) // send token first
  {
    // if not stoptran token, send data block as well
    if(DT_WBLOCK_MULTI_STOP != token) 
    {
      // calc ccitt CRC16 & prepare it for sd (swap bytes)
      esU16 crc = 0;
      if( info->flags & sdmmcUseCrc )
      {
        crc = crc16ccitt(0, buff, len);
        crc = SWAPB_WORD(crc);
      }
      // send data + crc
      if( len == spiPutBytes(h, buff, len) &&
          2 == spiPutBytes(h, (const esU8*)&crc, 2) &&
          1 == spiGetBytes(h, s_sdmmcBuff, 1) ) // receive block write response
      {
        // decypher write response
        switch( DWRT_EXTRACT(s_sdmmcBuff[0]) )
        {
        case DWRT_CRC_ERROR:
          info->stat |= sdmmcStatWriteCrcError;
          break;
        case DWRT_WRITE_ERROR:
          info->stat |= sdmmcStatWriteError;
          break;
        case DWRT_AOK:
          result = TRUE;
          break;
        }
      }
    }
  }
  sdmmcDESELECT;

  return result;
}

esU32 sdmmcBlocksWrite(spiHANDLE h, SdmmcInfo* info, esU32 startBlock, const esU8* blocks, esU32 count)
{
  esU32	result = 0;

  if( spiIsOpen(h) &&
    info &&
    sdmmcUnknown != info->type &&
    blocks &&
    count )
  {
    // if 1 == count issue single block write command
    // else, write multiple blocks, terminated with CMD21
    if( 1 < count )
    {
      esBL ok;
      // inform card controller about count of blocks to be written, then start multiblock write operation
      if( info->type == sdmmcMmc3 )
        ok = sdmmcSendCmd(h, info, CMD23, count) &&
          sdmmcStatAOK == info->stat;
      else
        ok = sdmmcSendCmd(h, info, ACMD23, count) &&
          sdmmcStatAOK == info->stat;

      if( ok &&
          sdmmcSendCmd(h, info, CMD25, (info->flags & sdmmcBlockAddr) ? startBlock : startBlock*info->userBlockSize ) &&
          sdmmcStatAOK == info->stat )
      {
        while( result < count ) 
        {
          if( !sdmmcWriteDataPacket(h, info, DT_WBLOCK_MULTI_START, blocks, info->userBlockSize) ) 
            break;
          blocks += info->userBlockSize;
          ++result;
        }
        sdmmcWriteDataPacket(	h, info, DT_WBLOCK_MULTI_STOP, 0, 0 ); // break multiwrite
      }
    }
    else if( sdmmcSendCmd(h, info, CMD24, (info->flags & sdmmcBlockAddr) ? startBlock : startBlock*info->userBlockSize ) &&
             sdmmcStatAOK == info->stat &&
             sdmmcWriteDataPacket(h, info, DT_WBLOCK_START, blocks, info->userBlockSize) )
      result = 1;

    // release spi bus as well as add stuff byte (needed) after write operations
    sdmmcReleaseSpiBus(h);
    // analyse card status if something gone wrong, and we cannot deduce the reason from existing status info
    // R2 will be automatically parsed into universal sdmmc status during this request
    if( result != count &&
        !(info->stat & (sdmmcWaitReadyExpired|sdmmcStatWriteCrcError|sdmmcStatWriteError)) ) 
      sdmmcSendCmd(h, info, CMD13, 0);
  }

  return result;
}

// sd spi compatibility bus release call. sd releases bus synchronously with sck pulses, not cs, 
// so we have to do 1 dummy bus read with cs deasserted to release spi properly (avoiding multislave conflicts)
void sdmmcReleaseSpiBus(spiHANDLE h)
{
  sdmmcDESELECT;
  spiGetBytes(h, s_sdmmcBuff, 1);
}

// calculate card io retries in accordance with bus settings and requested timeout value in ms
void sdmmcCalcIoTimings(spiHANDLE h, SdmmcInfo* info, esU32 tmo)
{
  if( h && 
      info && 
      tmo )
  {
#ifndef USE_CUSTOM_SPI  
    spiDCB dcb;
    spiGetDCB(h, &dcb);
    info->ioRetries = MAX((esU32)sdmmcIoInitialRetries, (tmo * dcb.freqHz) / (esU32)10000 );
#else
    info->ioRetries = MAX((esU32)sdmmcIoInitialRetries, (tmo * spiRateGet(h)) / (esU32)10000 );
#endif    
  }
}

// sd card block erase. returned is amount of erased blocks
esU32 sdmmcBlocksErase(spiHANDLE h, SdmmcInfo* info, esU32 startBlock, esU32 count)
{
  esU32 result = 0;

  if( h && 
      info && 
      sdmmcUnknown != info->type && 
      count )
  {
    if( sdmmcSendCmd(h, info, CMD32, (info->flags & sdmmcBlockAddr) ? startBlock : startBlock*info->userBlockSize) &&
        sdmmcStatAOK == info->stat &&
        sdmmcSendCmd(h, info, CMD33, (info->flags & sdmmcBlockAddr) ? startBlock+count-1 : (startBlock+count-1)*info->userBlockSize) &&
        sdmmcStatAOK == info->stat &&
        sdmmcSendCmd(h, info, CMD38, 0) &&
        sdmmcStatAOK == info->stat )
      result = count;
  }

  return result;
}

// retrieve card id
void sdmmcCardIdGet(spiHANDLE h, SdmmcInfo* info, SdmmcCID cid)
{
  if( h && 
      info && 
      sdmmcUnknown != info->type )
  {
    if( sdmmcSendCmd(h, info, CMD10, 0) &&
        sdmmcStatAOK == info->stat )
      sdmmcReadDataPacket(h, info, DT_RBLOCK_START, cid, sizeof(SdmmcCID));
  }
}

SDMMC over SPI generic API C header

#ifndef _sd_mmc_spi_h_
#define _sd_mmc_spi_h_

// sdmmc over spi driver header
//
#ifdef USE_CUSTOM_SPI
# include <sdmmc_spiIntf.h>
#endif

#ifdef __cplusplus
  extern "C" {
#endif

// sdmmc info struct
typedef struct _SdmmcInfo_ {
  esU32	blockCnt;				// blocks count
  esU32	blockSize;			// block size in bytes
  esU32	userBlockSize;	// user block size in bytes
  esU32	stat;						// sdmmc last operation status flags
  esU32 ioRetries;			// sdmmc io attempts
  esU8 	flags;					// sdmmc card info flags
  esU8	type;						// sdmmc card type

} SdmmcInfo;

// CID block
typedef esU8 SdmmcCID[16];

// consts
enum {
  // sdmmc workmode mode flags
  sdmmcOk										= 0x01,	// sd card was initialized. check sdmmcVoltageMismatch flag to see if card is ok to use 
  sdmmcVoltageMismatch 			= 0x02, // sd card was not initialized properly due to wrong working voltage range
  sdmmcBlockAddr						= 0x04, // block addressing mode. if not set - byte addressing is used
  sdmmcHighCapacity					= 0x08,	// high capacity card detected
  sdmmcUseCrc								= 0x10, // CRC usage was configured for the card io
  sdmmcSectorEraseUnit			= 0x20,	// device supports erasing by sectors (multiple of blocks) otherwise, explicit block erase is supported

  // sdmmc status flags
  //
  sdmmcStatAOK							= 0,
  sdmmcStatIdle							= 0x00000001, // sdmmc card is in idle (non-initialized) state
  sdmmcStatEraseAborted			= 0x00000002, // block erase sequence was cancelled before erase was complete
  sdmmcStatIllegalCmd				= 0x00000004, // an illegal command was issued to card
  sdmmcStatCmdCrcError			= 0x00000008, // card refuses command CRC block
  sdmmcStatEraseSeqError 		= 0x00000010, // an error occurred in erase commands sequence
  sdmmcStatAddrError				= 0x00000020, // misaligned block access is detected
  sdmmcStatParamError				= 0x00000040, // command parameter is out of allowed range
  sdmmcStatCardLocked				= 0x00000080, // card is in locked sate
  sdmmcStatEraseWprotError 	= 0x00000100,	// write protected sector erase was attempted
  sdmmcStatLockUnlockError 	= sdmmcStatEraseWprotError,	// lock|unlock of password protected area failed
  sdmmcStatUnknownError			= 0x00000200, // unspecified|unknown error occurred
  sdmmcStatControllerError 	= 0x00000400, // internal card controller error
  sdmmcStatEccError					= 0x00000800, // card internal ECC was applied, but failed to correct the data
  sdmmcStatWprotViolation		= 0x00001000, // write protected area write access was attempted
  sdmmcStatEraseParamError	= 0x00002000, // invalid parameter selecting erase blocks, sectors of groups
  sdmmcStatOutOfRange				= 0x00004000, // 
  sdmmcStatCsdOverwrite			= sdmmcStatOutOfRange, //
  sdmmcStatWriteCrcError		= 0x00008000, // data block write was rejected due to CRC error
  sdmmcStatWriteError				= 0x00010000,	// data block write was rejected due to write error
  sdmmcWaitReadyExpired			= 0x00020000, // retries of wait for bus to become ready have expired
  sdmmcReadCrcError					= 0x00040000, // read data block crc check failed
  
  // card types (NB! not bitfield, but value)
  sdmmcUnknown					    = 0,
  sdmmcSd1,	
  sdmmcSd2,
  sdmmcMmc3,	
  // misc
  SdmmcInfo_SZE 				    = sizeof(SdmmcInfo),
  // spi bus must be configured for this frequency upon initialization
  sdmmcInitFreq					    = 300000,
  // initial sd retries to apply upon initialization
  // final values are subject to change upon initialization completion,
  // according to card settings
  sdmmcIoInitialRetries     = 1000,
};

// SDMMC over SPI API
//
// application-dependent sdmmc power control
void sdmmcPowerOn(spiHANDLE h, SdmmcInfo* info, esBL on);
// wait until card releases the bus
esBL sdmmcWaitReady(spiHANDLE h, SdmmcInfo* info);
// initialize sdmmc card access. v is host-supplied voltage in millivolts
// i.e. 3.6v = 3600
esBL sdmmcInit(spiHANDLE h, SdmmcInfo* info, esU16 v, esBL useCrc);
// sd card block io. returned is count of blocks actually read|written
esU32 sdmmcBlocksRead(spiHANDLE h, SdmmcInfo* info, esU32 startBlock, esU8* blocks, esU32 count);
esU32 sdmmcBlocksWrite(spiHANDLE h, SdmmcInfo* info, esU32 startBlock, const esU8* blocks, esU32 count);
// sd card block erase. returned is amount of erased blocks
esU32 sdmmcBlocksErase(spiHANDLE h, SdmmcInfo* info, esU32 startBlock, esU32 count);
// sd spi compatibility bus release call. sd releases bus synchronously with sck pulses, not cs, 
// so we have to do 1 dummy bus read with cs deasserted to release spi properly (avoiding multislave conflicts)
void sdmmcReleaseSpiBus(spiHANDLE h);
// calculate card io retries in accordance with bus settings and requested timeout value in ms
void sdmmcCalcIoTimings(spiHANDLE h, SdmmcInfo* info, esU32 tmo);
// retrieve card id
void sdmmcCardIdGet(spiHANDLE h, SdmmcInfo* info, SdmmcCID cid);

#ifdef __cplusplus
  }
#endif

#endif // _sd_mmc_spi_h_

Necessary SDMMC SPI adapter API implementation. C source

#ifndef _sdmmc_spi_intf_h_
#define _sdmmc_spi_intf_h_

#include "retarget.h"

#ifdef __cplusplus
	extern "C" {
#endif

void sdmmcSpiInit(spiHANDLE h);

void spiConfig( spiHANDLE h, esU32 rate );
esU32 spiRateGet( spiHANDLE h );

esU32 spiPutBytes( spiHANDLE h, const esU8* Bytes, esU32 length );
esU32 spiGetBytes( spiHANDLE h, esU8* Bytes, esU32 length );

#ifdef __cplusplus
	}
#endif

#define sdmmcSELECT sdmmcCS_ON()
#define sdmmcDESELECT sdmmcCS_OFF()

#endif // _sdmmc_spi_intf_h_

Necessary SDMMC SPI adapter API implementation. C header.

// ESP includes
#include <esp_common.h>
#include <gpio.h>
#include <spi_interface.h>

#include "retarget.h"
#include <esfwxe/drivers/flash/sdmmc_spi.h>
#include "sdmmc_spiIntf.h"
#include "gpioUser.h"

#define spiHANDLE_CAST(h) ((SpiAttr*)(h))

void spiConfig(spiHANDLE h, esU32 rate)
{
  ES_DEBUG_TRACE("spiConfig(h, rate=%d)...\n", rate);
 
  // Initialze SPI Pins on ESP8266
  WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105);
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_HSPIQ_MISO);
//	PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_HSPID_MOSI); //< Use "manual" MOSI level during data reception
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_HSPI_CLK);
  //PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_HSPI_CS0); //< Use "manual" chipselect on the same port
 
	spiHANDLE_CAST(h)->mode = SpiMode_Master;
	spiHANDLE_CAST(h)->subMode = SpiSubMode_0;
	spiHANDLE_CAST(h)->rate = rate;
	spiHANDLE_CAST(h)->bitOrder = SpiBitOrder_MSBFirst;
  
	SPIInit(
    SpiNum_HSPI,
    spiHANDLE_CAST(h),
    true
  );

  ES_DEBUG_TRACE("...OK\n");
}

esU32 spiRateGet(spiHANDLE h)
{
  return spiHANDLE_CAST(h)->rate;
}

void sdmmcSpiInit(spiHANDLE h)
{
	ES_DEBUG_TRACE("sdmmcSpiInit...\n");
  
  // Put CS to 0
  sdmmcCS_ON();

  // Switch power off immediately
  sdmmcPWR_OFF();
  
  spiConfig(
    h, 
    sdmmcInitFreq
  );
  
	ES_DEBUG_TRACE("...OK\n");
}

void sdmmcPowerOn( spiHANDLE h, SdmmcInfo* info, esBL on )
{
  ES_DEBUG_TRACE("sdmmcPowerOn( h, info, on=%d )...\n", on);

	if( on )
	{
    sdmmcPWR_ON();
    
    // wait for 10ms to power-up
    rtosDelay(30);
	}
	else
	{
		// wait for free bus and close spi
		sdmmcWaitReady(
      h, 
      info
    );
    
    SPIWaitUntilIdle(SpiNum_HSPI);
		
    // wait for sdmmc to complete all internal processing
    rtosDelay(500);
		
    // Remove power
		sdmmcPWR_OFF();
	} 

  ES_DEBUG_TRACE("...OK\n");
}

static int ICACHE_FLASH_ATTR spiInternalChunkSend(SpiData* data, const esU8* buff, esU32 chunkLen)
{
#ifdef SPI_IO_DATA_BUFF_ALWAYS_4_ALIGNED

  data->dataOut = (esU8*)buff;
  data->dataLen = chunkLen;

  return SPIMasterTransferData(
    SpiNum_HSPI, 
    data
  );

#else

  int result = -1;
  esU32 residueLen = chunkLen % 4;

  data->dataOut = (esU8*)buff;
  data->dataLen = chunkLen-residueLen;
  
  if( chunkLen == residueLen )
    result = 0;
  else
    result = SPIMasterTransferData(
      SpiNum_HSPI, 
      data
    );
  
  if( -1 < result && residueLen )
  {
    buff += data->dataLen;
    
    uint32_t residue = 0;
    memcpy(
      &residue,
      buff,
      residueLen
    );
    data->dataOut = (esU8*)&residue;
    data->dataLen = residueLen;
    
    result = SPIMasterTransferData(
      SpiNum_HSPI, 
      data
    );
  }
  
  return result;
#endif
}

static bool s_mosiIsGpio = true;
static void ICACHE_FLASH_ATTR mosiCfgToGpioAndSet(void)
{
  if( s_mosiIsGpio )
  {
    GPIO_OUTPUT(SDMMC_MOSI_PIN, 1);
    return;
  }
  
  // Wait until IO is complete
  SPIWaitUntilIdle(SpiNum_HSPI);
  // Switch from HSPI MOSI to GPIO, set to 1
  WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105);  
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);
  GPIO_OUTPUT(SDMMC_MOSI_PIN, 1);
  s_mosiIsGpio = true;
}

static void ICACHE_FLASH_ATTR mosiCfgFromGpio(void)
{
  if( !s_mosiIsGpio )
    return;

  GPIO_OUTPUT(SDMMC_MOSI_PIN, 0);
  WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105);
 	PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_HSPID_MOSI);  
  s_mosiIsGpio = false;
}

esU32 ICACHE_FLASH_ATTR spiPutBytes( spiHANDLE h, const esU8* Bytes, esU32 length )
{
  if( !h || !Bytes || !length )
    return 0;
  
  const esU8* pos = Bytes;
  const esU8* end = pos+length;

	SpiData spiData;
	spiData.cmd = 0;        ///< Command value
	spiData.cmdLen = 0;     ///< Command byte length
	spiData.addr = NULL;    ///< Point to address value
	spiData.addrLen = 0;    ///< Address byte length
  spiData.dummyBits = 0;  ///< Not used
  spiData.dataIn = NULL;  ///< Not used
  
  // Switch to HSPI MOSI from GPIO
  mosiCfgFromGpio();
  
  while( pos < end )
  {
    esU32 chunkLen = end-pos;
    if( chunkLen > ESP_SPI_MAX_BLOCK )
      chunkLen = ESP_SPI_MAX_BLOCK;
    
    if( -1 == spiInternalChunkSend(
        &spiData, 
        pos, 
        chunkLen
      ) 
    )
      break;
    
    pos += chunkLen;
  }
  
  mosiCfgToGpioAndSet();
  
  return pos-Bytes;
}

static int ICACHE_FLASH_ATTR spiInternalChunkReceive(SpiData* data, esU8* buff, esU32 chunkLen)
{
#ifdef SPI_IO_DATA_BUFF_ALWAYS_4_ALIGNED

  data->dataIn = buff;
  data->dataLen = chunkLen;
  
  return SPIMasterTransferData(
    SpiNum_HSPI, 
    data
  );

#else

  int result = -1;
  esU32 residueLen = chunkLen % 4;

  data->dataIn = buff;
  data->dataLen = chunkLen-residueLen;
  
  if( chunkLen == residueLen )
    result = 0;
  else
    result = SPIMasterTransferData(
      SpiNum_HSPI, 
      data
    );
  
  if( -1 < result && residueLen )
  {
    buff += data->dataLen;
    
    uint32_t residue = 0;
    data->dataIn = (esU8*)&residue;
    data->dataLen = residueLen;
    
    result = SPIMasterTransferData(
      SpiNum_HSPI, 
      data
    );
    
    if( -1 < result )
      memcpy(
        buff,
        &residue,
        residueLen
      );    
  }
 
  return result;
#endif 
}

esU32 ICACHE_FLASH_ATTR spiGetBytes( spiHANDLE h, esU8* Bytes, esU32 length )
{
  if( !h || !Bytes || !length )
    return 0;
  
  esU8* pos = Bytes;
  esU8* end = pos+length;

	SpiData spiData;
	spiData.cmd = 0;        ///< Command value
	spiData.cmdLen = 0;     ///< Command byte length
	spiData.addr = NULL;    ///< Point to address value
	spiData.addrLen = 0;    ///< Address byte length
  spiData.dummyBits = 0;  ///< Not used
  spiData.dataOut = NULL; ///< Not used
  
  // Switch from HSPI MOSI to GPIO, set to 1 while reading
  mosiCfgToGpioAndSet();
  
  while( pos < end )
  {
    esU32 chunkLen = end-pos;
    if( chunkLen > ESP_SPI_MAX_BLOCK )
      chunkLen = ESP_SPI_MAX_BLOCK;
    
    if( -1 == spiInternalChunkReceive(
        &spiData,
        pos,
        chunkLen
      ) 
    )
      break;
    
    pos += chunkLen;
  }
  
  return pos-Bytes;
}

ESP8266 SPI driver implementation. C source.

/**
 * @file spi_interface.c
 * @brief Defines and Macros for the SPI.
 * Modified and re-worked driver source.
 */

#include "spi_interface.h"
#include "esp8266/eagle_soc.h"
#include "esp8266/ets_sys.h"
#include "esp_libc.h"
//*****************************************************************************
//
// Make sure all of the definitions in this header have a C binding.
//
//*****************************************************************************
#ifdef __cplusplus
extern "C"
{
#endif

#define WAIT_UNTIL_FREE(spiNum)    while(READ_PERI_REG(SPI_CMD(spiNum))&SPI_USR)

// Show the spi registers.
#define SHOWDEBUG

void __ShowRegValue(const char * func, uint32_t line)
{
#ifndef SHOWDEBUG
    int i;
    uint32_t regAddr = 0x60000140; // SPI--0x60000240, HSPI--0x60000140;
    printf("\r\n FUNC[%s],line[%d]\r\n", func, line);
    printf(" SPI_ADDR      [0x%08x]\r\n", READ_PERI_REG(SPI_ADDR(SpiNum_HSPI)));
    printf(" SPI_CMD       [0x%08x]\r\n", READ_PERI_REG(SPI_CMD(SpiNum_HSPI)));
    printf(" SPI_CTRL      [0x%08x]\r\n", READ_PERI_REG(SPI_CTRL(SpiNum_HSPI)));
    printf(" SPI_CTRL2     [0x%08x]\r\n", READ_PERI_REG(SPI_CTRL2(SpiNum_HSPI)));
    printf(" SPI_CLOCK     [0x%08x]\r\n", READ_PERI_REG(SPI_CLOCK(SpiNum_HSPI)));
    printf(" SPI_RD_STATUS [0x%08x]\r\n", READ_PERI_REG(SPI_RD_STATUS(SpiNum_HSPI)));
    printf(" SPI_WR_STATUS [0x%08x]\r\n", READ_PERI_REG(SPI_WR_STATUS(SpiNum_HSPI)));
    printf(" SPI_USER      [0x%08x]\r\n", READ_PERI_REG(SPI_USER(SpiNum_HSPI)));
    printf(" SPI_USER1     [0x%08x]\r\n", READ_PERI_REG(SPI_USER1(SpiNum_HSPI)));
    printf(" SPI_USER2     [0x%08x]\r\n", READ_PERI_REG(SPI_USER2(SpiNum_HSPI)));
    printf(" SPI_PIN       [0x%08x]\r\n", READ_PERI_REG(SPI_PIN(SpiNum_HSPI)));
    printf(" SPI_SLAVE     [0x%08x]\r\n", READ_PERI_REG(SPI_SLAVE(SpiNum_HSPI)));
    printf(" SPI_SLAVE1    [0x%08x]\r\n", READ_PERI_REG(SPI_SLAVE1(SpiNum_HSPI)));
    printf(" SPI_SLAVE2    [0x%08x]\r\n", READ_PERI_REG(SPI_SLAVE2(SpiNum_HSPI)));

    for (i = 0; i < 16; ++i) {
        printf(" ADDR[0x%08x],Value[0x%08x]\r\n", regAddr, READ_PERI_REG(regAddr));
        regAddr += 4;
    }
#endif
}

// Define SPI interrupt enable macro
#define ETS_SPI_INTR_ENABLE()  _xt_isr_unmask(1 << ETS_SPI_INUM)

// min & max macros
#ifndef MAX
# define MAX(a,b)	((a) < (b) ? (b) : (a))
#endif

#ifndef MIN
# define MIN(a,b)	((a) < (b) ? (a) : (b))
#endif

void SPIMasterRateSet(SpiNum spiNum, uint32_t rate)
{
  if( rate > CPU_CLK_FREQ )
    rate = CPU_CLK_FREQ;
    
  if( CPU_CLK_FREQ == rate )
  {
    WRITE_PERI_REG(
      SPI_CLOCK(spiNum), 
      SPI_CLK_EQU_SYSCLK
    ); // 80Mhz speed
    
    return;
  }

  CLEAR_PERI_REG_MASK(
    SPI_CLOCK(spiNum), 
    SPI_CLK_EQU_SYSCLK
  );
  
  uint32_t cntdiv = 2;
  uint32_t prediv = CPU_CLK_FREQ/(rate * cntdiv);
  
  WRITE_PERI_REG(
    SPI_CLOCK(spiNum),
    (((prediv-1) & SPI_CLKDIV_PRE)  <<  SPI_CLKDIV_PRE_S) |
    (((cntdiv-1) & SPI_CLKCNT_N)    <<  SPI_CLKCNT_N_S)   |
    (((cntdiv>>1)& SPI_CLKCNT_H)    <<  SPI_CLKCNT_H_S)   |
    ((0&SPI_CLKCNT_L)               <<  SPI_CLKCNT_L_S)
  ); //clear bit 31,set SPI clock div
}

void ICACHE_FLASH_ATTR SPIWaitUntilIdle(SpiNum spiNum)
{
  if(spiNum > SpiNum_HSPI)
    return;
  
  WAIT_UNTIL_FREE(spiNum);
}

/**
 * @brief Based on pAttr initialize SPI module.
 *
 */
void ICACHE_FLASH_ATTR SPIInit(SpiNum spiNum, SpiAttr* pAttr, bool manualCS)
{
    if(
      (spiNum > SpiNum_HSPI) || 
      (NULL == pAttr)
    )
      return;

    // Disable flash operation mode
    // As earlier as better, if not SPI_CTRL2 can not to be set delay cycles.
    CLEAR_PERI_REG_MASK(SPI_USER(spiNum), SPI_FLASH_MODE | SPI_CS_SETUP | SPI_CS_HOLD);
    
    // Clear Dual or Quad lines transmission mode
    CLEAR_PERI_REG_MASK(SPI_CTRL(spiNum), SPI_QIO_MODE | SPI_DIO_MODE | SPI_DOUT_MODE | SPI_QOUT_MODE);
          
    // SPI_CPOL & SPI_CPHA
    switch (pAttr->subMode) 
    {
    case SpiSubMode_1:
        CLEAR_PERI_REG_MASK(SPI_PIN(spiNum), SPI_IDLE_EDGE);
        SET_PERI_REG_MASK(SPI_USER(spiNum),  SPI_CK_OUT_EDGE); // CHPA_FALLING_EDGE_SAMPLE
        break;
    case SpiSubMode_2:
        SET_PERI_REG_MASK(SPI_PIN(spiNum), SPI_IDLE_EDGE);
        SET_PERI_REG_MASK(SPI_USER(spiNum),  SPI_CK_OUT_EDGE); // CHPA_FALLING_EDGE_SAMPLE
        break;
    case SpiSubMode_3:
        SET_PERI_REG_MASK(SPI_PIN(spiNum), SPI_IDLE_EDGE);
        CLEAR_PERI_REG_MASK(SPI_USER(spiNum),  SPI_CK_OUT_EDGE);
        break;
    case SpiSubMode_0:
    default:
        CLEAR_PERI_REG_MASK(SPI_PIN(spiNum), SPI_IDLE_EDGE);
        CLEAR_PERI_REG_MASK(SPI_USER(spiNum), SPI_CK_OUT_EDGE);
        // To do nothing
        break;
    }

    // SPI bit order
    if (SpiBitOrder_MSBFirst == pAttr->bitOrder) {
        CLEAR_PERI_REG_MASK(SPI_CTRL(spiNum), SPI_WR_BIT_ORDER);
        CLEAR_PERI_REG_MASK(SPI_CTRL(spiNum), SPI_RD_BIT_ORDER);
    } else if (SpiBitOrder_LSBFirst == pAttr->bitOrder) {
        SET_PERI_REG_MASK(SPI_CTRL(spiNum), SPI_WR_BIT_ORDER);
        SET_PERI_REG_MASK(SPI_CTRL(spiNum), SPI_RD_BIT_ORDER);
    } else {
        // To do nothing
    }

    // SPI mode type
    if( SpiMode_Master == pAttr->mode ) 
    {
        // SPI mode type
        CLEAR_PERI_REG_MASK(SPI_SLAVE(spiNum), SPI_SLAVE_MODE);
        // SPI Send buffer
        CLEAR_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MISO_HIGHPART );// By default slave send buffer C0-C7
        
        // SPI Speed
        SPIMasterRateSet(
          spiNum, 
          pAttr->rate
        );

        if( !manualCS )
          SET_PERI_REG_MASK(SPI_USER(spiNum), SPI_CS_SETUP | SPI_CS_HOLD);

        // delays num
        SET_PERI_REG_MASK(
          SPI_CTRL2(spiNum), 
          ((0x1 & SPI_MISO_DELAY_NUM) << SPI_MISO_DELAY_NUM_S)
        );
        
    } else if(SpiMode_Slave == pAttr->mode) 
    {
        // BIT19 must do
        SET_PERI_REG_MASK(SPI_PIN(spiNum), BIT19);

        // SPI mode type
        SET_PERI_REG_MASK(SPI_SLAVE(spiNum), SPI_SLAVE_MODE);
        // SPI Send buffer
        SET_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MISO_HIGHPART);// By default slave send buffer C8-C15

        SET_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MOSI);

        // If do not set delay cycles, slave not working,master cann't get the data.
        SET_PERI_REG_MASK(SPI_CTRL2(spiNum), ((0x1 & SPI_MOSI_DELAY_NUM) << SPI_MOSI_DELAY_NUM_S)); //delay num
        // SPI Speed
        WRITE_PERI_REG(SPI_CLOCK(spiNum), 0);

        // By default format::CMD(8bits)+ADDR(8bits)+DATA(32bytes).
        SET_PERI_REG_BITS(SPI_USER2(spiNum), SPI_USR_COMMAND_BITLEN,
                          7, SPI_USR_COMMAND_BITLEN_S);
        SET_PERI_REG_BITS(SPI_SLAVE1(spiNum), SPI_SLV_WR_ADDR_BITLEN,
                          7, SPI_SLV_WR_ADDR_BITLEN_S);
        SET_PERI_REG_BITS(SPI_SLAVE1(spiNum), SPI_SLV_RD_ADDR_BITLEN,
                          7, SPI_SLV_RD_ADDR_BITLEN_S);
        SET_PERI_REG_BITS(SPI_SLAVE1(spiNum), SPI_SLV_BUF_BITLEN,
                          (32 * 8 - 1), SPI_SLV_BUF_BITLEN_S);
        // For 8266 work on slave mode.
        SET_PERI_REG_BITS(SPI_SLAVE1(spiNum), SPI_SLV_STATUS_BITLEN,
                          7, SPI_SLV_STATUS_BITLEN_S);
    }
}

/**
 * @brief Transfer data between slave and master.
 *
 */
int ICACHE_FLASH_ATTR SPIMasterTransferData(SpiNum spiNum, SpiData* pData)
{
  if( spiNum > SpiNum_HSPI )
    return -1;

  WAIT_UNTIL_FREE(spiNum);
  
	//disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set
	CLEAR_PERI_REG_MASK(
    SPI_USER(spiNum), 
    SPI_USR_MOSI | 
    SPI_USR_MISO | 
    SPI_USR_COMMAND |
    SPI_USR_ADDR |
    SPI_USR_DUMMY
  );

	if( pData->dummyBits ) 
  {
    SET_PERI_REG_BITS(
      SPI_USER1(spiNum), 
      SPI_USR_DUMMY_CYCLELEN,
      (pData->dummyBits-1),
      SPI_USR_DUMMY_CYCLELEN_S
    );

    SET_PERI_REG_MASK(
      SPI_USER(spiNum), 
      SPI_USR_DUMMY
    );
  }
  else
  {
    SET_PERI_REG_BITS(
      SPI_USER1(spiNum), 
      SPI_USR_DUMMY_CYCLELEN,
      0,
      SPI_USR_DUMMY_CYCLELEN_S
    );
  }
  
  // Set command block
  if( pData->cmdLen ) 
  {
    // Max command length 16 bits.
    SET_PERI_REG_BITS(
      SPI_USER2(spiNum), 
      SPI_USR_COMMAND_BITLEN,
      ((pData->cmdLen << 3) - 1), 
      SPI_USR_COMMAND_BITLEN_S
    );
    
    // Enable command
    SET_PERI_REG_MASK(
      SPI_USER(spiNum), 
      SPI_USR_COMMAND
    );
    
    // Load command;
    // SPI_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1,
    // bit15-0 is cmd value.
    SET_PERI_REG_BITS(
      SPI_USER2(spiNum), 
      SPI_USR_COMMAND_VALUE, 
      pData->cmd, 
      SPI_USR_COMMAND_VALUE_S
    );
  } 
  else 
  {
    SET_PERI_REG_BITS(
      SPI_USER2(spiNum), 
      SPI_USR_COMMAND_BITLEN,
      0, 
      SPI_USR_COMMAND_BITLEN_S
    );
  }
  
  // Set Address by user.
  if( pData->addrLen ) 
  {
    if( NULL == pData->addr )
      return -1;

    SET_PERI_REG_BITS(
      SPI_USER1(spiNum), 
      SPI_USR_ADDR_BITLEN,
      ((pData->addrLen << 3) - 1), 
      SPI_USR_ADDR_BITLEN_S
    );
    
    // Enable address
    SET_PERI_REG_MASK(
      SPI_USER(spiNum), 
      SPI_USR_ADDR
    );
    
    // Load address
    WRITE_PERI_REG(
      SPI_ADDR(spiNum), 
      *pData->addr
    );
  }
  else
  {
    SET_PERI_REG_BITS(
      SPI_USER1(spiNum), 
      SPI_USR_ADDR_BITLEN,
      0, 
      SPI_USR_ADDR_BITLEN_S
    );
  }

  uint8_t* in = pData->dataIn;
  uint8_t* out = pData->dataOut;
  uint32_t tmpio;
  int idx, dataLeft, x4cnt, x4part;
  
  // Set output data block
  if( pData->dataLen ) 
  {
    if( !out && !in )
      return -1;
    
    if( in )
    {
      // Enable MISO
      SET_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MISO);
      
      SET_PERI_REG_BITS(
        SPI_USER1(spiNum), 
        SPI_USR_MISO_BITLEN, 
        ((pData->dataLen << 3) - 1), 
        SPI_USR_MISO_BITLEN_S
      );
    }
    
    if( out )
    {
      // Enable MOSI
      SET_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MOSI);
        
      // Load send buffer. 
      // NB!!! Buffer must always have x4 alignment, even if dataLen is not aligned to 4
      idx = 0;
      dataLeft = pData->dataLen;
      x4cnt = ( pData->dataLen % 4 ) ?
        (pData->dataLen / 4) + 1 :
        (pData->dataLen / 4);
      
      while( idx < x4cnt ) 
      {
        tmpio = 0; //< Allow for an arbitrary buffer alignment and offset
        x4part = MIN(4, dataLeft);
        memcpy(
          &tmpio,
          out,
          x4part
        );
        dataLeft -= x4part;
        out += x4part;

        WRITE_PERI_REG(
          (SPI_W0(spiNum) + (idx << 2)),
          tmpio
        );
        
        ++idx;
      }
        
      // Set data send buffer length.Max data length 64 bytes.
      SET_PERI_REG_BITS(
        SPI_USER1(spiNum), 
        SPI_USR_MOSI_BITLEN, 
        ((pData->dataLen << 3) - 1), 
        SPI_USR_MOSI_BITLEN_S
      );
    }
  }
  else
  {
    SET_PERI_REG_BITS(
      SPI_USER1(spiNum), 
      SPI_USR_MOSI_BITLEN,
      0, 
      SPI_USR_MOSI_BITLEN_S
    );

    SET_PERI_REG_BITS(
      SPI_USER1(spiNum), 
      SPI_USR_MISO_BITLEN, 
      0, 
      SPI_USR_MISO_BITLEN_S
    );
  }  

  SET_PERI_REG_MASK(
    SPI_CMD(spiNum), 
    SPI_USR
  );
  
  if( pData->dataLen && in )
  {
    WAIT_UNTIL_FREE(spiNum);

    // Read data out
    // NB! inbound SPI data buffer must always be aligned to 4, even if dataLen is not alighned to 32 bits
    idx = 0;
    dataLeft = pData->dataLen;
    x4cnt = (pData->dataLen % 4) ?
      (pData->dataLen / 4) + 1 :
      (pData->dataLen / 4);
      
    while( idx < x4cnt ) 
    {
      // Allow for an arbitrary buffer alignment and offset
      tmpio = READ_PERI_REG(
        SPI_W0(spiNum) + (idx << 2)
      );
      
      x4part = MIN(4, dataLeft);
      memcpy(
        in,
        &tmpio,
        x4part
      );
      dataLeft -= x4part;
      in += x4part;
      
      ++idx;
    }
  }
  
  SHOWREG();  
  
  return 0;  
}

/**
 * @brief Load data to send buffer by slave mode.
 *
 */
int ICACHE_FLASH_ATTR SPISlaveSendData(SpiNum spiNum, uint32_t *pInData, uint8_t outLen)
{
    if (NULL == pInData) {
        return -1;
    }
    char i;
    for (i = 0; i < outLen; ++i) {
        WRITE_PERI_REG((SPI_W8(spiNum) + (i << 2)), *pInData++);
    }
    return 0;
}

/**
 * @brief Configurate slave prepare for receive data.
 *
 */
int ICACHE_FLASH_ATTR SPISlaveRecvData(SpiNum spiNum, void(*isrFunc)(void*))
{
    if ((spiNum > SpiNum_HSPI)) {
        return -1;
    }

    SPIIntEnable(SpiNum_HSPI, SpiIntSrc_WrStaDoneEn
                 | SpiIntSrc_RdStaDoneEn | SpiIntSrc_WrBufDoneEn | SpiIntSrc_RdBufDoneEn);
    SPIIntDisable(SpiNum_HSPI, SpiIntSrc_TransDoneEn);

    // Maybe enable slave transmission liston
    SET_PERI_REG_MASK(SPI_CMD(spiNum), SPI_USR);
    //
     _xt_isr_attach(ETS_SPI_INUM, isrFunc, NULL);
   // ETS_SPI_INTR_ATTACH(isrFunc, NULL);
    // Enable isr
    ETS_SPI_INTR_ENABLE();

    SHOWREG();

    return 0;
}

/**
 * @brief Send data to slave(ESP8266 register of RD_STATUS or WR_STATUS).
 *
 */
void ICACHE_FLASH_ATTR SPIMasterSendStatus(SpiNum spiNum, uint8_t data)
{
    if (spiNum > SpiNum_HSPI) {
        return;
    }
    
    WAIT_UNTIL_FREE(spiNum);
    
    // Enable MOSI
    SET_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MOSI);
    CLEAR_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MISO | SPI_USR_DUMMY | SPI_USR_ADDR);

    // 8bits cmd, 0x04 is eps8266 slave write cmd value
    WRITE_PERI_REG(SPI_USER2(spiNum),
                   ((7 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S)
                   | MASTER_WRITE_STATUS_TO_SLAVE_CMD);
    // Set data send buffer length.
    SET_PERI_REG_BITS(SPI_USER1(spiNum), SPI_USR_MOSI_BITLEN,
                      ((sizeof(data) << 3) - 1), SPI_USR_MOSI_BITLEN_S);

    WRITE_PERI_REG(SPI_W0(spiNum), (uint32)(data));
    // Start SPI
    SET_PERI_REG_MASK(SPI_CMD(spiNum), SPI_USR);

    SHOWREG();
}

/**
 * @brief Receive status register from slave(ESP8266).
 *
 */
int ICACHE_FLASH_ATTR SPIMasterRecvStatus(SpiNum spiNum)
{
    if (spiNum > SpiNum_HSPI) {
        return -1;
    }

    WAIT_UNTIL_FREE(spiNum);
    
    // Enable MISO
    SET_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MISO);
    CLEAR_PERI_REG_MASK(SPI_USER(spiNum), SPI_USR_MOSI | SPI_USR_DUMMY | SPI_USR_ADDR);

    // 8bits cmd, 0x06 is eps8266 slave read status cmd value
    WRITE_PERI_REG(SPI_USER2(spiNum),
                   ((7 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S)
                   | MASTER_READ_STATUS_FROM_SLAVE_CMD);
    // Set revcive buffer length.
    SET_PERI_REG_BITS(SPI_USER1(spiNum), SPI_USR_MISO_BITLEN,
                      7, SPI_USR_MISO_BITLEN_S);

    // start spi module.
    SET_PERI_REG_MASK(SPI_CMD(spiNum), SPI_USR);

    WAIT_UNTIL_FREE(spiNum);

    uint8_t data = (uint8)(READ_PERI_REG(SPI_W0(spiNum)) & 0xff);
    SHOWREG();

    return (uint8)(READ_PERI_REG(SPI_W0(spiNum)) & 0xff);
}

/**
 * @brief Select SPI CS pin.
 *
 */
void ICACHE_FLASH_ATTR SPICsPinSelect(SpiNum spiNum, SpiPinCS pinCs)
{
    if (spiNum > SpiNum_HSPI) {
        return;
    }
    // clear select
    SET_PERI_REG_BITS(SPI_PIN(spiNum), 3, 0, 0);
    SET_PERI_REG_MASK(SPI_PIN(spiNum), pinCs);
}

/**
 * @brief Enable SPI interrupt source.
 *
 */
void ICACHE_FLASH_ATTR SPIIntEnable(SpiNum spiNum, SpiIntSrc intSrc)
{
    if (spiNum > SpiNum_HSPI) {
        return;
    }
    SET_PERI_REG_MASK(SPI_SLAVE(spiNum), intSrc);
}

/**
 * @brief Disable SPI interrupt source.
 *
 */
void ICACHE_FLASH_ATTR SPIIntDisable(SpiNum spiNum, SpiIntSrc intSrc)
{
    if (spiNum > SpiNum_HSPI) {
        return;
    }
    CLEAR_PERI_REG_MASK(SPI_SLAVE(spiNum), intSrc);
}

/**
 * @brief Clear all of SPI interrupt source.
 *
 */
void ICACHE_FLASH_ATTR SPIIntClear(SpiNum spiNum)
{
    if (spiNum > SpiNum_HSPI) {
        return;
    }
    CLEAR_PERI_REG_MASK(SPI_SLAVE(spiNum), SpiIntSrc_TransDoneEn
                        | SpiIntSrc_WrStaDoneEn
                        | SpiIntSrc_RdStaDoneEn
                        | SpiIntSrc_WrBufDoneEn
                        | SpiIntSrc_RdBufDoneEn);
}


#ifdef __cplusplus
}
#endif

ESP8266 SPI driver implementation. C header.

/**
 * @file spi_interface.c
 * @brief Defines and Macros for the SPI.
 * Modified and re-worked RTOS SDK driver source.
 */

#ifndef __SPI_INTERFACE_H__
#define __SPI_INTERFACE_H__

#include "spi_register.h"
#include "c_types.h"

//*****************************************************************************
//
// Make sure all of the definitions in this header have a C binding.
//
//*****************************************************************************

#ifdef __cplusplus
extern "C"
{
#endif


/**
 * @brief Defines slave commands. Default value based on slave ESP8266.
 */
#define MASTER_WRITE_DATA_TO_SLAVE_CMD                      2
#define MASTER_READ_DATA_FROM_SLAVE_CMD                     3

#define MASTER_WRITE_STATUS_TO_SLAVE_CMD                    1
#define MASTER_READ_STATUS_FROM_SLAVE_CMD                   4

/**
 * @brief Support HSPI and SPI module.
 *
 */
typedef enum
{
    SpiNum_SPI   = 0,
    SpiNum_HSPI  = 1,
    
} SpiNum;

/**
 * @brief The SPI module can work in either master or slave mode.
 *
 */
typedef enum
{
    SpiMode_Master = 0,
    SpiMode_Slave  = 1,
    
} SpiMode;

/**
 *  @brief SPI sub mode
 *
 * Support 4 sub modes based on SPI clock polarity and phase.
 * SPI_CPOL SPI_CPHA  SubMode
 *   0        0        0
 *   0        1        1
 *   1        0        2
 *   1        1        3
 */
typedef enum
{
    SpiSubMode_0 = 0,
    SpiSubMode_1 = 1,
    SpiSubMode_2 = 2,
    SpiSubMode_3 = 3,
    
} SpiSubMode;

/**
 * @brief The SPI mode working speed.
 *
 */
typedef enum
{
    SpiBitOrder_MSBFirst = 0,
    SpiBitOrder_LSBFirst = 1,
    
} SpiBitOrder;

// @brief SPI interrupt source defined.
typedef enum
{
    SpiIntSrc_TransDoneEn = SPI_TRANS_DONE_EN,
    SpiIntSrc_WrStaDoneEn = SPI_SLV_WR_STA_DONE_EN,
    SpiIntSrc_RdStaDoneEn = SPI_SLV_RD_STA_DONE_EN,
    SpiIntSrc_WrBufDoneEn = SPI_SLV_WR_BUF_DONE_EN,
    SpiIntSrc_RdBufDoneEn = SPI_SLV_RD_BUF_DONE_EN,
    
} SpiIntSrc;

// @brief SPI CS pin.
typedef enum
{
    SpiPinCS_0 = 0,
    SpiPinCS_1 = 1,
    SpiPinCS_2 = 2,
    
} SpiPinCS;

/**
 * @brief SPI attribute
 */
typedef struct
{
    SpiMode        mode;           ///< Master or slave mode
    SpiSubMode     subMode;        ///< SPI SPI_CPOL SPI_CPHA mode
    uint32_t       rate;           ///< SPI Clock rate
    SpiBitOrder    bitOrder;       ///< SPI bit order
    
} SpiAttr;

/**
 * @brief SPI attribute
 */
typedef struct
{
    uint32_t*   addr;           ///< Point to address value
    uint8_t*    dataIn;         ///< Point to data in buffer
    uint8_t*    dataOut;        ///< Point to data out buffer
    uint16_t    cmd;            ///< Command value
    uint8_t     addrLen;        ///< Address byte length
    uint8_t     cmdLen;         ///< Command byte length
    uint8_t     dataLen;        ///< Data IO byte length.
    uint8_t     dummyBits;      ///< Optional dummy bits count
    
} SpiData;

#define SHOWREG() __ShowRegValue(__func__, __LINE__);

/**
 * @brief Print debug information.
 *
 */
void __ShowRegValue(const char * func, uint32_t line);

/**
 * @brief Initialize SPI module.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] pAttr
 *             Pointer to a struct SpiAttr that indicates SPI working attribution.
 * @param [in] manualCs
 *             If set, we do not need to configure CS_SETUP and CS_HOLD bits.
 *
 * @return void.
 */
void SPIInit(SpiNum spiNum, SpiAttr* pAttr, bool manualCs);

/**
 * @brief Set Master SPI Clock rate.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] rate
 *             Requested SPI rate value, in Hz.
 *
 * @return void.
 */
void SPIMasterRateSet(SpiNum spiNum, uint32_t rate);

/**
 * @brief Transfer data between slave and master.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] pInData
 *             Pointer to a strcuture that will be send.
 *
 * @return int, -1:indicates failure,others indicates success.
 */
int SPIMasterTransferData(SpiNum spiNum, SpiData* pInData);

/**
 * @brief Load data to slave send buffer.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] pInData
 *             Point to data buffer.
 * @param [in] outLen
 *             The number of bytes to be set.
 *
 * @return int, -1:indicates failure,others indicates success.
 */
int SPISlaveSendData(SpiNum spiNum, uint32_t *pInData, uint8_t outLen);

/**
 * @brief Receive data by slave.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] isrFunc
 *             isrFunc is a pointer to the function to be called when the SPI interrupt occurs.
 *
 * @return int, -1:indicates failure,others indicates success.
 */
int SPISlaveRecvData(SpiNum spiNum, void(*isrFunc)(void*));

/**
 * @brief Set slave status by master.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] data
 *             Data will be write to slave SPI_WR_STATUS.
 *
 * @return void.
 *
 * @attention Just for ESP8266(slave) register of RD_STATUS or WR_STATUS.
 */
void SPIMasterSendStatus(SpiNum spiNum, uint8_t data);

/**
 * @brief Get salve status by master.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 *
 * @return int, -1: indicates failure; other value in slave status.
 *
 * @attention Just for ESP8266(slave) register of RD_STATUS or WR_STATUS.
 */
int SPIMasterRecvStatus(SpiNum spiNum);

/**
 * @brief Select SPI CS pin.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] pinCs
 *             Indicates which SPI pin to choose.
 *
 * @return void.
 */
void SPICsPinSelect(SpiNum spiNum, SpiPinCS pinCs);

/**
 * @brief Enable SPI module interrupt source.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] intSrc
 *             Indicates which interrupt source to enable.
 *
 * @return void.
 */
void SPIIntEnable(SpiNum spiNum, SpiIntSrc intSrc);

/**
 * @brief Disable SPI module interrupt source.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 * @param [in] intSrc
 *             Indicates which interrupt source to disable.
 *
 * @return void.
 */
void SPIIntDisable(SpiNum spiNum, SpiIntSrc intSrc);

/**
 * @brief Clear all of spi interrupt.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 *
 * @return void.
 */
void SPIIntClear(SpiNum spiNum);

/**
 * @brief Wait until SPI IO is complete.
 *
 * @param [in] spiNum
 *             Indicates which submode to be used, SPI or HSPI.
 *
 * @return void.
 */
void SPIWaitUntilIdle(SpiNum spiNum);

#ifdef __cplusplus
}
#endif

#endif //  __SPI_INTERFACE_H__