~ekangmonyet/bare-pizero

71cc5c7ecea2b8263c487ae84eff1b1f58d85da4 — ekangmonyet 13 days ago 803c555 master
**SD WIP**
5 files changed, 142 insertions(+), 2539 deletions(-)

M sd/README
D sd/SDCard.c
M sd/hal.h
M sd/memmap
M sd/sd.c
M sd/README => sd/README +7 -0
@@ 43,3 43,10 @@ OSDEV FAT ENTRY

    https://wiki.osdev.org/FAT

** Scratch **
https://github.com/bztsrc/raspi3-tutorial/tree/master/0B_readsector
https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=94133&p=656428
https://github.com/blippy/rpi/tree/master/crunky

Global
https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=72260

D sd/SDCard.c => sd/SDCard.c +0 -2496
@@ 1,2496 0,0 @@
#include <stdbool.h>							// Needed for bool and true/false
#include <stdint.h>								// Needed for uint8_t, uint32_t, uint64_t etc
#include <ctype.h>								// Needed for toupper for wildcard string match
#include <wchar.h>								// Needed for UTF for long file name support
#include <string.h>								// Needed for string copy
#include "rpi-smartstart.h"						// Provides all basic hardware access
#include "emb-stdio.h"							// Provides printf functionality
#include "SDCard.h"								// This units header

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{																			}
{       Filename: SDCard.c													}
{       Copyright(c): Leon de Boer(LdB) 2017								}
{       Version: 1.01														}
{       Original start code from hldswrth use from the Pi Forum             }
{																			}
{***************[ THIS CODE IS FREEWARE UNDER CC Attribution]***************}
{																            }
{     This sourcecode is released for the purpose to promote programming    }
{  on the Raspberry Pi. You may redistribute it and/or modify with the      }
{  following disclaimer and condition.                                      }
{																            }
{      The SOURCE CODE is distributed "AS IS" WITHOUT WARRANTIES AS TO      }
{   PERFORMANCE OF MERCHANTABILITY WHETHER EXPRESSED OR IMPLIED.            }
{   Redistributions of source code must retain the copyright notices to     }
{   maintain the author credit (attribution) .								}
{																			}
{***************************************************************************/

/*--------------------------------------------------------------------------}
{    These are SmartStart functions we use. If you wish to port this code   }
{  you will need to provide equivalent 3 functions on any new system.       }
{--------------------------------------------------------------------------*/
#define waitMicro timer_wait							// Waits a number of microseconds
#define TICKCOUNT timer_getTickCount					// Gets current system clock value (1Mhz accuracy)
#define TIMEDIFF  tick_difference						// Given two TICKCOUNT values calculates microseconds between them.
/* Smartstart also defines a function pointer ....  int (*printhandler) (const char *fmt, ...); */
printhandler LOG_ERROR = NULL;							// LOG_ERROR is a function pointer of that printhandler format

/*--------------------------------------------------------------------------}
{  This controls if debugging code is compiled or removed at compile time   }
{--------------------------------------------------------------------------*/
#define DEBUG_INFO 0									// Compile debugging message code .... set to 1 and other value means no compilation 

/*--------------------------------------------------------------------------}
{  The working part of the DEBUG_INFO macro to make compilation on or off   }
{--------------------------------------------------------------------------*/
#if DEBUG_INFO == 1
#define LOG_DEBUG(...) printf( __VA_ARGS__ )			// This displays debugging messages to function given
#else
#define LOG_DEBUG(...)									// This just swallows debug code, it doesn't even get compiled
#endif

/***************************************************************************}
{    PRIVATE INTERNAL SD HOST REGISTER STRUCTURES AS PER BCM2835 MANUAL     }
****************************************************************************/

/*--------------------------------------------------------------------------}
{      EMMC BLKSIZECNT register - BCM2835.PDF Manual Section 5 page 68      }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regBLKSIZECNT {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned BLKSIZE : 10;				// @0-9		Block size in bytes
			unsigned reserved : 6;						// @10-15	Write as zero read as don't care
			volatile unsigned BLKCNT : 16;				// @16-31	Number of blocks to be transferred 
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{      EMMC CMDTM register - BCM2835.PDF Manual Section 5 pages 69-70       }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regCMDTM {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			unsigned reserved : 1;						// @0		Write as zero read as don't care
			volatile unsigned TM_BLKCNT_EN : 1;			// @1		Enable the block counter for multiple block transfers
			volatile enum {	TM_NO_COMMAND = 0,			//			no command 
							TM_CMD12 = 1,				//			command CMD12 
							TM_CMD23 = 2,				//			command CMD23
							TM_RESERVED = 3,
						  } TM_AUTO_CMD_EN : 2;			// @2-3		Select the command to be send after completion of a data transfer
			volatile unsigned TM_DAT_DIR : 1;			// @4		Direction of data transfer (0 = host to card , 1 = card to host )
			volatile unsigned TM_MULTI_BLOCK : 1;		// @5		Type of data transfer (0 = single block, 1 = muli block)
			unsigned reserved1 : 10;					// @6-15	Write as zero read as don't care
			volatile enum {	CMD_NO_RESP = 0,			//			no response
							CMD_136BIT_RESP = 1,		//			136 bits response 
							CMD_48BIT_RESP = 2,			//			48 bits response 
							CMD_BUSY48BIT_RESP = 3,		//			48 bits response using busy 
						  } CMD_RSPNS_TYPE : 2;			// @16-17
			unsigned reserved2 : 1;						// @18		Write as zero read as don't care
			volatile unsigned CMD_CRCCHK_EN : 1;		// @19		Check the responses CRC (0=disabled, 1= enabled)
			volatile unsigned CMD_IXCHK_EN : 1;			// @20		Check that response has same index as command (0=disabled, 1= enabled)
			volatile unsigned CMD_ISDATA : 1;			// @21		Command involves data transfer (0=disabled, 1= enabled)
			volatile enum {	CMD_TYPE_NORMAL = 0,		//			normal command
							CMD_TYPE_SUSPEND = 1,		//			suspend command 
							CMD_TYPE_RESUME = 2,		//			resume command 
							CMD_TYPE_ABORT = 3,			//			abort command 
						  } CMD_TYPE : 2;				// @22-23 
			volatile unsigned CMD_INDEX : 6;			// @24-29
			unsigned reserved3 : 2;						// @30-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{        EMMC STATUS register - BCM2835.PDF Manual Section 5 page 72        }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regSTATUS {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CMD_INHIBIT : 1;			// @0		Command line still used by previous command
			volatile unsigned DAT_INHIBIT : 1;			// @1		Data lines still used by previous data transfer
			volatile unsigned DAT_ACTIVE : 1;			// @2		At least one data line is active
			unsigned reserved : 5;						// @3-7		Write as zero read as don't care
			volatile unsigned WRITE_TRANSFER : 1;		// @8		New data can be written to EMMC
			volatile unsigned READ_TRANSFER : 1;		// @9		New data can be read from EMMC
			unsigned reserved1 : 10;					// @10-19	Write as zero read as don't care
			volatile unsigned DAT_LEVEL0 : 4;			// @20-23	Value of data lines DAT3 to DAT0
			volatile unsigned CMD_LEVEL : 1;			// @24		Value of command line CMD 
			volatile unsigned DAT_LEVEL1 : 4;			// @25-28	Value of data lines DAT7 to DAT4
			unsigned reserved2 : 3;						// @29-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{      EMMC CONTROL0 register - BCM2835.PDF Manual Section 5 page 73/74     }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regCONTROL0 {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			unsigned reserved : 1;						// @0		Write as zero read as don't care
			volatile unsigned HCTL_DWIDTH : 1;			// @1		Use 4 data lines (true = enable)
			volatile unsigned HCTL_HS_EN : 1;			// @2		Select high speed mode (true = enable)
			unsigned reserved1 : 2;						// @3-4		Write as zero read as don't care
			volatile unsigned HCTL_8BIT : 1;			// @5		Use 8 data lines (true = enable)
			unsigned reserved2 : 10;					// @6-15	Write as zero read as don't care
			volatile unsigned GAP_STOP : 1;				// @16		Stop the current transaction at the next block gap
			volatile unsigned GAP_RESTART : 1;			// @17		Restart a transaction last stopped using the GAP_STOP
			volatile unsigned READWAIT_EN : 1;			// @18		Use DAT2 read-wait protocol for cards supporting this
			volatile unsigned GAP_IEN : 1;				// @19		Enable SDIO interrupt at block gap 
			volatile unsigned SPI_MODE : 1;				// @20		SPI mode enable
			volatile unsigned BOOT_EN : 1;				// @21		Boot mode access
			volatile unsigned ALT_BOOT_EN : 1;			// @22		Enable alternate boot mode access
			unsigned reserved3 : 9;						// @23-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{      EMMC CONTROL1 register - BCM2835.PDF Manual Section 5 page 74/75     }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regCONTROL1 {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CLK_INTLEN : 1;			// @0		Clock enable for internal EMMC clocks for power saving
			volatile const unsigned CLK_STABLE : 1;		// @1		SD clock stable  0=No 1=yes   **read only
			volatile unsigned CLK_EN : 1;				// @2		SD clock enable  0=disable 1=enable
			unsigned reserved : 2;						// @3-4		Write as zero read as don't care
			volatile unsigned CLK_GENSEL : 1;			// @5		Mode of clock generation (0=Divided, 1=Programmable)
			volatile unsigned CLK_FREQ_MS2 : 2;			// @6-7		SD clock base divider MSBs (Version3+ only)
			volatile unsigned CLK_FREQ8 : 8;			// @8-15	SD clock base divider LSBs
			volatile unsigned DATA_TOUNIT : 4;			// @16-19	Data timeout unit exponent
			unsigned reserved1 : 4;						// @20-23	Write as zero read as don't care
			volatile unsigned SRST_HC : 1;				// @24		Reset the complete host circuit
			volatile unsigned SRST_CMD : 1;				// @25		Reset the command handling circuit
			volatile unsigned SRST_DATA : 1;			// @26		Reset the data handling circuit
			unsigned reserved2 : 5;						// @27-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{    EMMC CONTROL2 register - BCM2835.PDF Manual Section 5 pages 81-84      }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regCONTROL2 {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile const unsigned ACNOX_ERR : 1;		// @0		Auto command not executed due to an error **read only
			volatile const unsigned ACTO_ERR : 1;		// @1		Timeout occurred during auto command execution **read only
			volatile const unsigned ACCRC_ERR : 1;		// @2		Command CRC error occurred during auto command execution **read only
			volatile const unsigned ACEND_ERR : 1;		// @3		End bit is not 1 during auto command execution **read only
			volatile const unsigned ACBAD_ERR : 1;		// @4		Command index error occurred during auto command execution **read only
			unsigned reserved : 2;						// @5-6		Write as zero read as don't care
			volatile const unsigned NOTC12_ERR : 1;		// @7		Error occurred during auto command CMD12 execution **read only
			unsigned reserved1 : 8;						// @8-15	Write as zero read as don't care			
			volatile enum { SDR12 = 0,
							SDR25 = 1, 
							SDR50 = 2,
							SDR104 = 3,
							DDR50 = 4,
						  } UHSMODE : 3;				// @16-18	Select the speed mode of the SD card (SDR12, SDR25 etc)
			unsigned reserved2 : 3;						// @19-21	Write as zero read as don't care
			volatile unsigned TUNEON : 1;				// @22		Start tuning the SD clock
			volatile unsigned TUNED : 1;				// @23		Tuned clock is used for sampling data
			unsigned reserved3 : 8;						// @24-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{     EMMC INTERRUPT register - BCM2835.PDF Manual Section 5 pages 75-77    }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regINTERRUPT {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CMD_DONE : 1;				// @0		Command has finished
			volatile unsigned DATA_DONE : 1;			// @1		Data transfer has finished
			volatile unsigned BLOCK_GAP : 1;			// @2		Data transfer has stopped at block gap
			unsigned reserved : 1;						// @3		Write as zero read as don't care
			volatile unsigned WRITE_RDY : 1;			// @4		Data can be written to DATA register
			volatile unsigned READ_RDY : 1;				// @5		DATA register contains data to be read
			unsigned reserved1 : 2;						// @6-7		Write as zero read as don't care
			volatile unsigned CARD : 1;					// @8		Card made interrupt request
			unsigned reserved2 : 3;						// @9-11	Write as zero read as don't care
			volatile unsigned RETUNE : 1;				// @12		Clock retune request was made
			volatile unsigned BOOTACK : 1;				// @13		Boot acknowledge has been received
			volatile unsigned ENDBOOT : 1;				// @14		Boot operation has terminated
			volatile unsigned ERR : 1;					// @15		An error has occured
			volatile unsigned CTO_ERR : 1;				// @16		Timeout on command line
			volatile unsigned CCRC_ERR : 1;				// @17		Command CRC error
			volatile unsigned CEND_ERR : 1;				// @18		End bit on command line not 1
			volatile unsigned CBAD_ERR : 1;				// @19		Incorrect command index in response
			volatile unsigned DTO_ERR : 1;				// @20		Timeout on data line
			volatile unsigned DCRC_ERR : 1;				// @21		Data CRC error
			volatile unsigned DEND_ERR : 1;				// @22		End bit on data line not 1
			unsigned reserved3 : 1;						// @23		Write as zero read as don't care
			volatile unsigned ACMD_ERR : 1;				// @24		Auto command error
			unsigned reserved4 : 7;						// @25-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{     EMMC IRPT_MASK register - BCM2835.PDF Manual Section 5 pages 77-79    }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regIRPT_MASK {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CMD_DONE : 1;				// @0		Command has finished
			volatile unsigned DATA_DONE : 1;			// @1		Data transfer has finished
			volatile unsigned BLOCK_GAP : 1;			// @2		Data transfer has stopped at block gap
			unsigned reserved : 1;						// @3		Write as zero read as don't care
			volatile unsigned WRITE_RDY : 1;			// @4		Data can be written to DATA register
			volatile unsigned READ_RDY : 1;				// @5		DATA register contains data to be read
			unsigned reserved1 : 2;						// @6-7		Write as zero read as don't care
			volatile unsigned CARD : 1;					// @8		Card made interrupt request
			unsigned reserved2 : 3;						// @9-11	Write as zero read as don't care
			volatile unsigned RETUNE : 1;				// @12		Clock retune request was made
			volatile unsigned BOOTACK : 1;				// @13		Boot acknowledge has been received
			volatile unsigned ENDBOOT : 1;				// @14		Boot operation has terminated
			volatile unsigned ERR : 1;					// @15		An error has occured
			volatile unsigned CTO_ERR : 1;				// @16		Timeout on command line
			volatile unsigned CCRC_ERR : 1;				// @17		Command CRC error
			volatile unsigned CEND_ERR : 1;				// @18		End bit on command line not 1
			volatile unsigned CBAD_ERR : 1;				// @19		Incorrect command index in response
			volatile unsigned DTO_ERR : 1;				// @20		Timeout on data line
			volatile unsigned DCRC_ERR : 1;				// @21		Data CRC error
			volatile unsigned DEND_ERR : 1;				// @22		End bit on data line not 1
			unsigned reserved3 : 1;						// @23		Write as zero read as don't care
			volatile unsigned ACMD_ERR : 1;				// @24		Auto command error
			unsigned reserved4 : 7;						// @25-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{      EMMC IRPT_EN register - BCM2835.PDF Manual Section 5 pages 79-71     }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regIRPT_EN {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned CMD_DONE : 1;				// @0		Command has finished
			volatile unsigned DATA_DONE : 1;			// @1		Data transfer has finished
			volatile unsigned BLOCK_GAP : 1;			// @2		Data transfer has stopped at block gap
			unsigned reserved : 1;						// @3		Write as zero read as don't care
			volatile unsigned WRITE_RDY : 1;			// @4		Data can be written to DATA register
			volatile unsigned READ_RDY : 1;				// @5		DATA register contains data to be read
			unsigned reserved1 : 2;						// @6-7		Write as zero read as don't care
			volatile unsigned CARD : 1;					// @8		Card made interrupt request
			unsigned reserved2 : 3;						// @9-11	Write as zero read as don't care
			volatile unsigned RETUNE : 1;				// @12		Clock retune request was made
			volatile unsigned BOOTACK : 1;				// @13		Boot acknowledge has been received
			volatile unsigned ENDBOOT : 1;				// @14		Boot operation has terminated
			volatile unsigned ERR : 1;					// @15		An error has occured
			volatile unsigned CTO_ERR : 1;				// @16		Timeout on command line
			volatile unsigned CCRC_ERR : 1;				// @17		Command CRC error
			volatile unsigned CEND_ERR : 1;				// @18		End bit on command line not 1
			volatile unsigned CBAD_ERR : 1;				// @19		Incorrect command index in response
			volatile unsigned DTO_ERR : 1;				// @20		Timeout on data line
			volatile unsigned DCRC_ERR : 1;				// @21		Data CRC error
			volatile unsigned DEND_ERR : 1;				// @22		End bit on data line not 1
			unsigned reserved3 : 1;						// @23		Write as zero read as don't care
			volatile unsigned ACMD_ERR : 1;				// @24		Auto command error
			unsigned reserved4 : 7;						// @25-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{       EMMC TUNE_STEP  register - BCM2835.PDF Manual Section 5 page 86     }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regTUNE_STEP {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile enum {	TUNE_DELAY_200ps  = 0,
							TUNE_DELAY_400ps  = 1,
							TUNE_DELAY_400psA = 2,		// I dont understand the duplicate value???
							TUNE_DELAY_600ps  = 3,
							TUNE_DELAY_700ps  = 4,
							TUNE_DELAY_900ps  = 5,
							TUNE_DELAY_900psA = 6,		// I dont understand the duplicate value??
							TUNE_DELAY_1100ps = 7,
						   } DELAY : 3;					// @0-2		Select the speed mode of the SD card (SDR12, SDR25 etc)
			unsigned reserved : 29;						// @3-31	Write as zero read as don't care
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{    EMMC SLOTISR_VER register - BCM2835.PDF Manual Section 5 pages 87-88   }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regSLOTISR_VER {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned SLOT_STATUS : 8;			// @0-7		Logical OR of interrupt and wakeup signal for each slot
			unsigned reserved : 8;						// @8-15	Write as zero read as don't care
			volatile unsigned SDVERSION : 8;			// @16-23	Host Controller specification version 
			volatile unsigned VENDOR : 8;				// @24-31	Vendor Version Number 
		};
		volatile uint32_t Raw32;						// @0-31	Union to access all 32 bits as a uint32_t
	};
};


/***************************************************************************}
{         PRIVATE POINTERS TO ALL THE BCM2835 EMMC HOST REGISTERS           }
****************************************************************************/
#define EMMC_ARG2			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300000))
#define EMMC_BLKSIZECNT		((volatile struct __attribute__((aligned(4))) regBLKSIZECNT*)(uintptr_t)(RPi_IO_Base_Addr + 0x300004))
#define EMMC_ARG1			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300008))
#define EMMC_CMDTM			((volatile struct __attribute__((aligned(4))) regCMDTM*)(uintptr_t)(RPi_IO_Base_Addr + 0x30000c))
#define EMMC_RESP0			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300010))
#define EMMC_RESP1			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300014))
#define EMMC_RESP2			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300018))
#define EMMC_RESP3			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x30001C))
#define EMMC_DATA			((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + 0x300020))
#define EMMC_STATUS			((volatile struct __attribute__((aligned(4))) regSTATUS*)(uintptr_t)(RPi_IO_Base_Addr + 0x300024))
#define EMMC_CONTROL0		((volatile struct __attribute__((aligned(4))) regCONTROL0*)(uintptr_t)(RPi_IO_Base_Addr + 0x300028))
#define EMMC_CONTROL1		((volatile struct __attribute__((aligned(4))) regCONTROL1*)(uintptr_t)(RPi_IO_Base_Addr + 0x30002C))
#define EMMC_INTERRUPT		((volatile struct __attribute__((aligned(4))) regINTERRUPT*)(uintptr_t)(RPi_IO_Base_Addr + 0x300030))
#define EMMC_IRPT_MASK		((volatile struct __attribute__((aligned(4))) regIRPT_MASK*)(uintptr_t)(RPi_IO_Base_Addr + 0x300034))
#define EMMC_IRPT_EN		((volatile struct __attribute__((aligned(4))) regIRPT_EN*)(uintptr_t)(RPi_IO_Base_Addr + 0x300038))
#define EMMC_CONTROL2		((volatile struct __attribute__((aligned(4))) regCONTROL2*)(uintptr_t)(RPi_IO_Base_Addr + 0x30003C))
#define EMMC_TUNE_STEP 		((volatile struct __attribute__((aligned(4))) regTUNE_STEP*)(uintptr_t)(RPi_IO_Base_Addr + 0x300088))
#define EMMC_SLOTISR_VER	((volatile struct __attribute__((aligned(4))) regSLOTISR_VER*)(uintptr_t)(RPi_IO_Base_Addr + 0x3000fC))


/***************************************************************************}
{   PRIVATE INTERNAL SD CARD REGISTER STRUCTURES AS PER SD CARD STANDARD    }
****************************************************************************/

/*--------------------------------------------------------------------------}
{						   SD CARD OCR register							    }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regOCR {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			unsigned reserved : 15;						// @0-14	Write as zero read as don't care
			unsigned voltage2v7to2v8 : 1;				// @15		Voltage window 2.7v to 2.8v
			unsigned voltage2v8to2v9 : 1;				// @16		Voltage window 2.8v to 2.9v
			unsigned voltage2v9to3v0 : 1;				// @17		Voltage window 2.9v to 3.0v
			unsigned voltage3v0to3v1 : 1;				// @18		Voltage window 3.0v to 3.1v
			unsigned voltage3v1to3v2 : 1;				// @19		Voltage window 3.1v to 3.2v
			unsigned voltage3v2to3v3 : 1;				// @20		Voltage window 3.2v to 3.3v
			unsigned voltage3v3to3v4 : 1;				// @21		Voltage window 3.3v to 3.4v
			unsigned voltage3v4to3v5 : 1;				// @22		Voltage window 3.4v to 3.5v
			unsigned voltage3v5to3v6 : 1;				// @23		Voltage window 3.5v to 3.6v
			unsigned reserved1 : 6;						// @24-29	Write as zero read as don't care
			unsigned card_capacity : 1;					// @30		Card Capacity status
			unsigned card_power_up_busy : 1;			// @31		Card power up status (busy)
		};
		volatile uint32_t Raw32;						// @0-31	Union to access 32 bits as a uint32_t		
	};
};

/*--------------------------------------------------------------------------}
{						   SD CARD SCR register							    }
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__, aligned(4))) regSCR {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile enum { SD_SPEC_1_101 = 0,			// ..enum..	Version 1.0-1.01 
							SD_SPEC_11 = 1,				// ..enum..	Version 1.10 
							SD_SPEC_2_3 = 2,			// ..enum..	Version 2.00 or Version 3.00 (check bit SD_SPEC3)
						  } SD_SPEC : 4;				// @0-3		SD Memory Card Physical Layer Specification version
			volatile enum {	SCR_VER_1 = 0,				// ..enum..	SCR version 1.0
						  } SCR_STRUCT : 4;				// @4-7		SCR structure version
			volatile enum { BUS_WIDTH_1 = 1,			// ..enum..	Card supports bus width 1
							BUS_WIDTH_4 = 4,			// ..enum.. Card supports bus width 4
						   } BUS_WIDTH : 4;				// @8-11	SD Bus width
			volatile enum { SD_SEC_NONE = 0,			// ..enum..	No Security
							SD_SEC_NOT_USED = 1,		// ..enum..	Security Not Used
							SD_SEC_101 = 2,				// ..enum..	SDSC Card (Security Version 1.01)
							SD_SEC_2 = 3,				// ..enum..	SDHC Card (Security Version 2.00)
							SD_SEC_3 = 4,				// ..enum..	SDXC Card (Security Version 3.xx)
						  } SD_SECURITY : 3;			// @12-14	Card security in use
			volatile unsigned DATA_AFTER_ERASE : 1;		// @15		Defines the data status after erase, whether it is ‘0’ or ‘1
			unsigned reserved : 3;						// @16-18	Write as zero read as don't care
			volatile enum {	EX_SEC_NONE = 0,			// ..enum..	No extended Security
						  } EX_SECURITY : 4;			// @19-22	Extended security
			volatile unsigned SD_SPEC3 : 1;				// @23		Spec. Version 3.00 or higher
			volatile enum { CMD_SUPP_SPEED_CLASS = 1,
							CMD_SUPP_SET_BLKCNT = 2,
						   } CMD_SUPPORT : 2;			// @24-25	CMD support
			unsigned reserved1 : 6;						// @26-63	Write as zero read as don't care
		};
		volatile uint32_t Raw32_Lo;						// @0-31	Union to access low 32 bits as a uint32_t		
	};
	volatile uint32_t Raw32_Hi;							// @32-63	Access upper 32 bits as a uint32_t
};

/*--------------------------------------------------------------------------}
{						  PI SD CARD CID register						    }
{--------------------------------------------------------------------------*/
/* The CID is Big Endian and secondly the Pi butchers it by not having CRC */
/*  So the CID appears shifted 8 bits right with first 8 bits reading zero */
struct __attribute__((__packed__, aligned(4))) regCID {
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile uint8_t OID_Lo;					
			volatile uint8_t OID_Hi;					// @0-15	Identifies the card OEM. The OID is assigned by the SD-3C, LLC
			volatile uint8_t MID;						// @16-23	Manufacturer ID, assigned by the SD-3C, LLC
			unsigned reserved : 8;						// @24-31	PI butcher with CRC removed these bits end up empty
		};
		volatile uint32_t Raw32_0;						// @0-31	Union to access 32 bits as a uint32_t		
	};
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile char ProdName4 : 8;				// @0-7		Product name character four
			volatile char ProdName3 : 8;				// @8-15	Product name character three
			volatile char ProdName2 : 8;				// @16-23	Product name character two
			volatile char ProdName1 : 8;				// @24-31	Product name character one	
		};
		volatile uint32_t Raw32_1;						// @0-31	Union to access 32 bits as a uint32_t		
	};
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned SerialNumHi : 16;			// @0-15	Serial number upper 16 bits
			volatile unsigned ProdRevLo : 4;			// @16-19	Product revision low value in BCD
			volatile unsigned ProdRevHi : 4;			// @20-23	Product revision high value in BCD
			volatile char ProdName5 : 8;				// @24-31	Product name character five
		};
		volatile uint32_t Raw32_2;						// @0-31	Union to access 32 bits as a uint32_t		
	};
	union {
		struct __attribute__((__packed__, aligned(1))) {
			volatile unsigned ManufactureMonth : 4;		// @0-3		Manufacturing date month (1=Jan, 2=Feb, 3=Mar etc)
			volatile unsigned ManufactureYear : 8;		// @4-11	Manufacturing date–year (offset from 2000 .. 1=2001,2=2002,3=2003 etc)
			unsigned reserved1 : 4;						// @12-15 	Write as zero read as don't care
			volatile unsigned SerialNumLo : 16;			// @16-23	Serial number lower 16 bits
		};
		volatile uint32_t Raw32_3;						// @0-31	Union to access 32 bits as a uint32_t		
	};
};

/***************************************************************************}
{			 PRIVATE INTERNAL SD CARD REGISTER STRUCTURE CHECKS             }
****************************************************************************/
/*--------------------------------------------------------------------------}
{					 INTERNAL STRUCTURE COMPILE TIME CHECKS		            }
{--------------------------------------------------------------------------*/
/* GIVEN THE AMOUNT OF PRECISE PACKING OF THESE STRUCTURES .. IT'S PRUDENT */
/* TO CHECK THEM AT COMPILE TIME. USE IS POINTLESS IF THE SIZES ARE WRONG. */
/*-------------------------------------------------------------------------*/
/* If you have never seen compile time assertions it's worth google search */
/* on "Compile Time Assertions". It is part of the C11++ specification and */
/* all compilers that support the standard will have them (GCC, MSC inc)   */
/* This actually produces no code it only executes as a compile time check */
/*-------------------------------------------------------------------------*/
#include <assert.h>								// Need for compile time static_assert

/* Check the main register section group sizes */
static_assert(sizeof(struct regBLKSIZECNT) == 0x04, "EMMC register BLKSIZECNT should be 0x04 bytes in size");
static_assert(sizeof(struct regCMDTM) == 0x04, "EMMC register CMDTM should be 0x04 bytes in size");
static_assert(sizeof(struct regSTATUS) == 0x04, "EMMC register STATUS should be 0x04 bytes in size");
static_assert(sizeof(struct regCONTROL0) == 0x04, "EMMC register CONTROL0 should be 0x04 bytes in size");
static_assert(sizeof(struct regCONTROL1) == 0x04, "EMMC register CONTROL1 should be 0x04 bytes in size");
static_assert(sizeof(struct regCONTROL2) == 0x04, "EMMC register CONTROL2 should be 0x04 bytes in size");
static_assert(sizeof(struct regINTERRUPT) == 0x04, "EMMC register INTERRUPT should be 0x04 bytes in size");
static_assert(sizeof(struct regIRPT_MASK) == 0x04, "EMMC register IRPT_MASK should be 0x04 bytes in size");
static_assert(sizeof(struct regIRPT_EN) == 0x04, "EMMC register IRPT_EN should be 0x04 bytes in size");
static_assert(sizeof(struct regTUNE_STEP) == 0x04, "EMMC register TUNE_STEP should be 0x04 bytes in size");
static_assert(sizeof(struct regSLOTISR_VER) == 0x04, "EMMC register SLOTISR_VER should be 0x04 bytes in size");

static_assert(sizeof(struct regOCR) == 0x04, "EMMC register OCR should be 0x04 bytes in size");
static_assert(sizeof(struct regSCR) == 0x08, "EMMC register SCR should be 0x08 bytes in size");
static_assert(sizeof(struct regCID) == 0x10, "EMMC register CID should be 0x10 bytes in size");

/*--------------------------------------------------------------------------}
{			  INTERRUPT REGISTER TURN TO MASK BIT DEFINITIONS			    }
{--------------------------------------------------------------------------*/
#define INT_AUTO_ERROR   0x01000000									// ACMD_ERR bit in register
#define INT_DATA_END_ERR 0x00400000									// DEND_ERR bit in register
#define INT_DATA_CRC_ERR 0x00200000									// DCRC_ERR bit in register
#define INT_DATA_TIMEOUT 0x00100000									// DTO_ERR bit in register
#define INT_INDEX_ERROR  0x00080000									// CBAD_ERR bit in register
#define INT_END_ERROR    0x00040000									// CEND_ERR bit in register
#define INT_CRC_ERROR    0x00020000									// CCRC_ERR bit in register
#define INT_CMD_TIMEOUT  0x00010000									// CTO_ERR bit in register
#define INT_ERR          0x00008000									// ERR bit in register
#define INT_ENDBOOT      0x00004000									// ENDBOOT bit in register
#define INT_BOOTACK      0x00002000									// BOOTACK bit in register
#define INT_RETUNE       0x00001000									// RETUNE bit in register
#define INT_CARD         0x00000100									// CARD bit in register
#define INT_READ_RDY     0x00000020									// READ_RDY bit in register
#define INT_WRITE_RDY    0x00000010									// WRITE_RDY bit in register
#define INT_BLOCK_GAP    0x00000004									// BLOCK_GAP bit in register
#define INT_DATA_DONE    0x00000002									// DATA_DONE bit in register
#define INT_CMD_DONE     0x00000001									// CMD_DONE bit in register
#define INT_ERROR_MASK   (INT_CRC_ERROR|INT_END_ERROR|INT_INDEX_ERROR| \
                          INT_DATA_TIMEOUT|INT_DATA_CRC_ERR|INT_DATA_END_ERR| \
                          INT_ERR|INT_AUTO_ERROR)
#define INT_ALL_MASK     (INT_CMD_DONE|INT_DATA_DONE|INT_READ_RDY|INT_WRITE_RDY|INT_ERROR_MASK)


/*--------------------------------------------------------------------------}
{						  SD CARD FREQUENCIES							    }
{--------------------------------------------------------------------------*/
#define FREQ_SETUP				400000  // 400 Khz
#define FREQ_NORMAL			  25000000  // 25 Mhz

/*--------------------------------------------------------------------------}
{						  CMD 41 BIT SELECTIONS							    }
{--------------------------------------------------------------------------*/
#define ACMD41_HCS           0x40000000
#define ACMD41_SDXC_POWER    0x10000000
#define ACMD41_S18R          0x04000000
#define ACMD41_VOLTAGE       0x00ff8000
/* PI DOES NOT SUPPORT VOLTAGE SWITCH */
#define ACMD41_ARG_HC        (ACMD41_HCS|ACMD41_SDXC_POWER|ACMD41_VOLTAGE)//(ACMD41_HCS|ACMD41_SDXC_POWER|ACMD41_VOLTAGE|ACMD41_S18R)
#define ACMD41_ARG_SC        (ACMD41_VOLTAGE)   //(ACMD41_VOLTAGE|ACMD41_S18R)

/*--------------------------------------------------------------------------}
{						   SD CARD COMMAND RECORD						    }
{--------------------------------------------------------------------------*/
typedef struct EMMCCommand
{
	const char cmd_name[16];
	struct regCMDTM code;
	struct __attribute__((__packed__)) {
		unsigned use_rca : 1;										// @0		Command uses rca										
		unsigned reserved : 15;										// @1-15	Write as zero read as don't care
		uint16_t delay;												// @16-31	Delay to apply after command
	};
} EMMCCommand;

/*--------------------------------------------------------------------------}
{						SD CARD COMMAND INDEX DEFINITIONS				    }
{--------------------------------------------------------------------------*/
#define IX_GO_IDLE_STATE    0
#define IX_ALL_SEND_CID     1
#define IX_SEND_REL_ADDR    2
#define IX_SET_DSR          3
#define IX_SWITCH_FUNC      4
#define IX_CARD_SELECT      5
#define IX_SEND_IF_COND     6
#define IX_SEND_CSD         7
#define IX_SEND_CID         8
#define IX_VOLTAGE_SWITCH   9
#define IX_STOP_TRANS       10
#define IX_SEND_STATUS      11
#define IX_GO_INACTIVE      12
#define IX_SET_BLOCKLEN     13
#define IX_READ_SINGLE      14
#define IX_READ_MULTI       15
#define IX_SEND_TUNING      16
#define IX_SPEED_CLASS      17
#define IX_SET_BLOCKCNT     18
#define IX_WRITE_SINGLE     19
#define IX_WRITE_MULTI      20
#define IX_PROGRAM_CSD      21
#define IX_SET_WRITE_PR     22
#define IX_CLR_WRITE_PR     23
#define IX_SND_WRITE_PR     24
#define IX_ERASE_WR_ST      25
#define IX_ERASE_WR_END     26
#define IX_ERASE            27
#define IX_LOCK_UNLOCK      28
#define IX_APP_CMD          29
#define IX_APP_CMD_RCA      30
#define IX_GEN_CMD          31

// Commands hereafter require APP_CMD.
#define IX_APP_CMD_START    32
#define IX_SET_BUS_WIDTH    32
#define IX_SD_STATUS        33
#define IX_SEND_NUM_WRBL    34
#define IX_SEND_NUM_ERS     35
#define IX_APP_SEND_OP_COND 36
#define IX_SET_CLR_DET      37
#define IX_SEND_SCR         38

/*--------------------------------------------------------------------------}
{							  SD CARD COMMAND TABLE						    }
{--------------------------------------------------------------------------*/
static EMMCCommand sdCommandTable[IX_SEND_SCR + 1] =  {
	[IX_GO_IDLE_STATE] =	{ "GO_IDLE_STATE", .code.CMD_INDEX = 0x00, .code.CMD_RSPNS_TYPE = CMD_NO_RESP        , .use_rca = 0 , .delay = 0},
	[IX_ALL_SEND_CID] =		{ "ALL_SEND_CID" , .code.CMD_INDEX = 0x02, .code.CMD_RSPNS_TYPE = CMD_136BIT_RESP    , .use_rca = 0 , .delay = 0},
	[IX_SEND_REL_ADDR] =	{ "SEND_REL_ADDR", .code.CMD_INDEX = 0x03, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SET_DSR] =			{ "SET_DSR"      , .code.CMD_INDEX = 0x04, .code.CMD_RSPNS_TYPE = CMD_NO_RESP        , .use_rca = 0 , .delay = 0},
	[IX_SWITCH_FUNC] =		{ "SWITCH_FUNC"  , .code.CMD_INDEX = 0x06, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_CARD_SELECT] =		{ "CARD_SELECT"  , .code.CMD_INDEX = 0x07, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 1 , .delay = 0},
	[IX_SEND_IF_COND] = 	{ "SEND_IF_COND" , .code.CMD_INDEX = 0x08, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 100},
	[IX_SEND_CSD] =			{ "SEND_CSD"     , .code.CMD_INDEX = 0x09, .code.CMD_RSPNS_TYPE = CMD_136BIT_RESP    , .use_rca = 1 , .delay = 0},
	[IX_SEND_CID] =			{ "SEND_CID"     , .code.CMD_INDEX = 0x0A, .code.CMD_RSPNS_TYPE = CMD_136BIT_RESP    , .use_rca = 1 , .delay = 0},
	[IX_VOLTAGE_SWITCH] =	{ "VOLT_SWITCH"  , .code.CMD_INDEX = 0x0B, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_STOP_TRANS] =		{ "STOP_TRANS"   , .code.CMD_INDEX = 0x0C, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_SEND_STATUS] =		{ "SEND_STATUS"  , .code.CMD_INDEX = 0x0D, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 1 , .delay = 0},
	[IX_GO_INACTIVE] =		{ "GO_INACTIVE"  , .code.CMD_INDEX = 0x0F, .code.CMD_RSPNS_TYPE = CMD_NO_RESP        , .use_rca = 1 , .delay = 0},
	[IX_SET_BLOCKLEN] =		{ "SET_BLOCKLEN" , .code.CMD_INDEX = 0x10, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_READ_SINGLE] =		{ "READ_SINGLE"  , .code.CMD_INDEX = 0x11, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
	                                           .code.CMD_ISDATA = 1  , .code.TM_DAT_DIR = 1,					   .use_rca = 0 , .delay = 0},
	[IX_READ_MULTI] =		{ "READ_MULTI"   , .code.CMD_INDEX = 0x12, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
											   .code.CMD_ISDATA = 1 ,  .code.TM_DAT_DIR = 1,
											   .code.TM_BLKCNT_EN =1 , .code.TM_MULTI_BLOCK = 1,                   .use_rca = 0 , .delay = 0},
	[IX_SEND_TUNING] =		{ "SEND_TUNING"  , .code.CMD_INDEX = 0x13, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SPEED_CLASS] =		{ "SPEED_CLASS"  , .code.CMD_INDEX = 0x14, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_SET_BLOCKCNT] =		{ "SET_BLOCKCNT" , .code.CMD_INDEX = 0x17, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_WRITE_SINGLE] =		{ "WRITE_SINGLE" , .code.CMD_INDEX = 0x18, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
											   .code.CMD_ISDATA = 1  ,											   .use_rca = 0 , .delay = 0},
	[IX_WRITE_MULTI] =		{ "WRITE_MULTI"  , .code.CMD_INDEX = 0x19, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
											   .code.CMD_ISDATA = 1  ,  
											   .code.TM_BLKCNT_EN = 1, .code.TM_MULTI_BLOCK = 1,				   .use_rca = 0 , .delay = 0},
	[IX_PROGRAM_CSD] =		{ "PROGRAM_CSD"  , .code.CMD_INDEX = 0x1B, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SET_WRITE_PR] =		{ "SET_WRITE_PR" , .code.CMD_INDEX = 0x1C, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_CLR_WRITE_PR] =		{ "CLR_WRITE_PR" , .code.CMD_INDEX = 0x1D, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_SND_WRITE_PR] =		{ "SND_WRITE_PR" , .code.CMD_INDEX = 0x1E, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_ERASE_WR_ST] =		{ "ERASE_WR_ST"  , .code.CMD_INDEX = 0x20, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_ERASE_WR_END] =		{ "ERASE_WR_END" , .code.CMD_INDEX = 0x21, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_ERASE] =			{ "ERASE"        , .code.CMD_INDEX = 0x26, .code.CMD_RSPNS_TYPE = CMD_BUSY48BIT_RESP , .use_rca = 0 , .delay = 0},
	[IX_LOCK_UNLOCK] =		{ "LOCK_UNLOCK"  , .code.CMD_INDEX = 0x2A, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_APP_CMD] =			{ "APP_CMD"      , .code.CMD_INDEX = 0x37, .code.CMD_RSPNS_TYPE = CMD_NO_RESP        , .use_rca = 0 , .delay = 100},
	[IX_APP_CMD_RCA] =		{ "APP_CMD"      , .code.CMD_INDEX = 0x37, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 1 , .delay = 0},
	[IX_GEN_CMD] =			{ "GEN_CMD"      , .code.CMD_INDEX = 0x38, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},

	// APP commands must be prefixed by an APP_CMD.
	[IX_SET_BUS_WIDTH] =	{ "SET_BUS_WIDTH", .code.CMD_INDEX = 0x06, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SD_STATUS] =		{ "SD_STATUS"    , .code.CMD_INDEX = 0x0D, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 1 , .delay = 0},
	[IX_SEND_NUM_WRBL] =	{ "SEND_NUM_WRBL", .code.CMD_INDEX = 0x16, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SEND_NUM_ERS] =		{ "SEND_NUM_ERS" , .code.CMD_INDEX = 0x17, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_APP_SEND_OP_COND] =	{ "SD_SENDOPCOND", .code.CMD_INDEX = 0x29, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 1000},
	[IX_SET_CLR_DET] =		{ "SET_CLR_DET"  , .code.CMD_INDEX = 0x2A, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , .use_rca = 0 , .delay = 0},
	[IX_SEND_SCR] =			{ "SEND_SCR"     , .code.CMD_INDEX = 0x33, .code.CMD_RSPNS_TYPE = CMD_48BIT_RESP     , 
											   .code.CMD_ISDATA = 1  , .code.TM_DAT_DIR = 1,					   .use_rca = 0 , .delay = 0},
};

static const char* SD_TYPE_NAME[] = { "Unknown", "MMC", "Type 1", "Type 2 SC", "Type 2 HC" };

/*--------------------------------------------------------------------------}
{						  SD CARD DESCRIPTION RECORD					    }
{--------------------------------------------------------------------------*/
typedef struct SDDescriptor {
	struct regCID cid;							// Card cid
	struct CSD csd;								// Card csd
	struct regSCR scr;							// Card scr
	uint64_t CardCapacity;						// Card capacity expanded .. calculated from card details
	SDCARD_TYPE type;							// Card type
	uint32_t rca;								// Card rca
	struct regOCR ocr;							// Card ocr
	uint32_t status;							// Card last status

	EMMCCommand* lastCmd;

	struct {
		uint32_t rootCluster;					// Active partition rootCluster
		uint32_t sectorPerCluster;				// Active partition sectors per cluster
		uint32_t bytesPerSector;				// Active partition bytes per sector
		uint32_t firstDataSector;				// Active partition first data sector
		uint32_t dataSectors;					// Active partition data sectors
		uint32_t unusedSectors;					// Active partition unused sectors
		uint32_t reservedSectorCount;			// Active partition reserved sectors
	} partition;

	SFN_NAME partitionLabe1;					// Partition label
} SDDescriptor;

/*--------------------------------------------------------------------------}
{					    CURRENT SD CARD DATA STORAGE					    }
{--------------------------------------------------------------------------*/
static SDDescriptor sdCard = { 0 };


//**************************************************************************
// SD Card PUBLIC functions.
//**************************************************************************

static int sdDebugResponse( int resp ) 
{
	LOG_DEBUG("EMMC: Status: %08x, control1: %08x, interrupt: %08x\n", 
		(unsigned int)EMMC_STATUS->Raw32, (unsigned int)EMMC_CONTROL1->Raw32, 
		(unsigned int)EMMC_INTERRUPT->Raw32);
	LOG_DEBUG("EMMC: Command %s resp %08x: %08x %08x %08x %08x\n",
		sdCard.lastCmd->cmd_name, (unsigned int)resp,(unsigned int)*EMMC_RESP3, 
		(unsigned int)*EMMC_RESP2, (unsigned int)*EMMC_RESP1, 
		(unsigned int)*EMMC_RESP0);
	return resp;
}

/*-[INTERNAL: sdWaitForInterrupt]-------------------------------------------}
. Given an interrupt mask the routine loops polling for the condition for up
. to 1 second.
. RETURN: SD_TIMEOUT - the condition mask flags where not met in 1 second
.		  SD_ERROR - an identifiable error occurred
.		  SD_OK - the wait completed with a mask state as requested
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdWaitForInterrupt (uint32_t mask ) 
{
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time
	uint32_t tMask = mask | INT_ERROR_MASK;							// Add fatal error masks to mask provided
	while (!(EMMC_INTERRUPT->Raw32 & tMask) && (td < 1000000)) {
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	uint32_t ival = EMMC_INTERRUPT->Raw32;							// Fetch all the interrupt flags
	if( td >= 1000000 ||											// No reponse timeout occurred
		(ival & INT_CMD_TIMEOUT) ||									// Command timeout occurred 
		(ival & INT_DATA_TIMEOUT) )									// Data timeout occurred
	{
		if (LOG_ERROR) LOG_ERROR("EMMC: Wait for interrupt %08x timeout: %08x %08x %08x\n", 
			(unsigned int)mask, (unsigned int)EMMC_STATUS->Raw32, 
			(unsigned int)ival, (unsigned int)*EMMC_RESP0);			// Log any error if requested

		// Clear the interrupt register completely.
		EMMC_INTERRUPT->Raw32 = ival;								// Clear any interrupt that occured

		return SD_TIMEOUT;											// Return SD_TIMEOUT
	} else if ( ival & INT_ERROR_MASK ) {
		if (LOG_ERROR) LOG_ERROR("EMMC: Error waiting for interrupt: %08x %08x %08x\n", 
			(unsigned int)EMMC_STATUS->Raw32, (unsigned int)ival, 
			(unsigned int)*EMMC_RESP0);								// Log any error if requested

		// Clear the interrupt register completely.
		EMMC_INTERRUPT->Raw32 = ival;								// Clear any interrupt that occured

		return SD_ERROR;											// Return SD_ERROR
    }

	// Clear the interrupt we were waiting for, leaving any other (non-error) interrupts.
	EMMC_INTERRUPT->Raw32 = mask;									// Clear any interrupt we are waiting on

	return SD_OK;													// Return SD_OK
}


/*-[INTERNAL: sdWaitForCommand]---------------------------------------------}
. Waits for up to 1 second for any command that may be in progress.
. RETURN: SD_BUSY - the command was not completed within 1 second period
.		  SD_OK - the wait completed sucessfully
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdWaitForCommand (void) 
{
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time
	while ((EMMC_STATUS->CMD_INHIBIT) &&							// Command inhibit signal
		  !(EMMC_INTERRUPT->Raw32 & INT_ERROR_MASK) &&				// No error occurred
		   (td < 1000000))											// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// Get start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start and now
	}
	if( (td >= 1000000) || (EMMC_INTERRUPT->Raw32 & INT_ERROR_MASK) )// Error occurred or it timed out
    {
		if (LOG_ERROR) LOG_ERROR("EMMC: Wait for command aborted: %08x %08x %08x\n", 
			(unsigned int)EMMC_STATUS->Raw32, (unsigned int)EMMC_INTERRUPT->Raw32, 
			(unsigned int)*EMMC_RESP0);								// Log any error if requested
		return SD_BUSY;												// return SD_BUSY
    }

	return SD_OK;													// return SD_OK
}


/*-[INTERNAL: sdWaitForData]------------------------------------------------}
. Waits for up to 1 second for any data transfer that may be in progress.
. RETURN: SD_BUSY - the transfer was not completed within 1 second period
.		  SD_OK - the transfer completed sucessfully
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdWaitForData (void) 
{
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time
	while ((EMMC_STATUS->DAT_INHIBIT) &&							// Data inhibit signal
		  !(EMMC_INTERRUPT->Raw32 & INT_ERROR_MASK) &&				// Some error occurred
		   (td < 500000))											// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	if ( (td >= 500000) || (EMMC_INTERRUPT->Raw32 & INT_ERROR_MASK) )
    {
		if (LOG_ERROR) LOG_ERROR("EMMC: Wait for data aborted: %08x %08x %08x\n", 
			(unsigned int)EMMC_STATUS->Raw32, (unsigned int)EMMC_INTERRUPT->Raw32, 
			(unsigned int)*EMMC_RESP0);								// Log any error if requested
		return SD_BUSY;												// return SD_BUSY
    }
	return SD_OK;													// return SD_OK
}


/*-[INTERNAL: unpack_csd]---------------------------------------------------}
. Unpacks a CSD in Resp0,1,2,3 into the proper CSD structure we defined.
. 16Aug17 LdB
.--------------------------------------------------------------------------*/
static void unpack_csd(struct CSD* csd)
{
	uint8_t buf[16] = { 0 };

	/* Fill buffer CSD comes IN MSB so I will invert so its sort of right way up so I can debug it */
	__attribute__((aligned(4))) uint32_t* p;
	p = (uint32_t*)&buf[12];
	*p = *EMMC_RESP0;
	p = (uint32_t*)&buf[8];
	*p = *EMMC_RESP1;
	p = (uint32_t*)&buf[4];
	*p = *EMMC_RESP2;
	p = (uint32_t*)&buf[0];
	*p = *EMMC_RESP3;

	/* Display raw CSD - values of my SANDISK ultra 16GB shown under each */
	LOG_DEBUG("CSD Contents : %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
		buf[2], buf[1], buf[0], buf[7], buf[6], buf[5], buf[4],
		/*    40       e0      00     32        5b     59     00               */
		buf[11], buf[10], buf[9], buf[8], buf[15], buf[14], buf[13], buf[12]);
	/*    00       73       a7     7f       80       0a       40       00 */

	/* Populate CSD structure */
	csd->csd_structure = (buf[2] & 0xc0) >> 6;								// @126-127 ** correct 
	csd->spec_vers = buf[2] & 0x3F;											// @120-125 ** correct
	csd->taac = buf[1];														// @112-119 ** correct
	csd->nsac = buf[0];														// @104-111 ** correct
	csd->tran_speed = buf[7];												// @96-103  ** correct
	csd->ccc = (((uint16_t)buf[6]) << 4) | ((buf[5] & 0xf0) >> 4);			// @84-95   ** correct
	csd->read_bl_len = buf[5] & 0x0f;										// @80-83   ** correct
	csd->read_bl_partial = (buf[4] & 0x80) ? 1 : 0;							// @79		** correct
	csd->write_blk_misalign = (buf[4] & 0x40) ? 1 : 0;						// @78      ** correct
	csd->read_blk_misalign = (buf[4] & 0x20) ? 1 : 0;						// @77		** correct
	csd->dsr_imp = (buf[4] & 0x10) ? 1 : 0;									// @76		** correct

	if (csd->csd_structure == 0x1) {										// CSD VERSION 2.0 
		/* Basically absorbs bottom of buf[4] to align to next byte */		// @@75-70  ** Correct
		csd->ver2_c_size = (uint32_t)(buf[11] & 0x3F) << 16;				// @69-64
		csd->ver2_c_size += (uint32_t)buf[10] << 8;							// @63-56
		csd->ver2_c_size += (uint32_t)buf[9];								// @55-48
		sdCard.CardCapacity = csd->ver2_c_size;
		sdCard.CardCapacity *= (512 * 1024);								// Calculate Card capacity
	}
	else {																	// CSD VERSION 1.0
		csd->c_size = (uint32_t)(buf[4] & 0x03) << 8;
		csd->c_size += (uint32_t)buf[11];
		csd->c_size <<= 2;
		csd->c_size += (buf[10] & 0xc0) >> 6;								// @62-73
		csd->vdd_r_curr_min = (buf[10] & 0x38) >> 3;						// @59-61
		csd->vdd_r_curr_max = buf[10] & 0x07;								// @56-58
		csd->vdd_w_curr_min = (buf[9] & 0xe0) >> 5;							// @53-55
		csd->vdd_w_curr_max = (buf[9] & 0x1c) >> 2;							// @50-52	
		csd->c_size_mult = ((buf[9] & 0x03) << 1) | ((buf[8] & 0x80) >> 7);	// @47-49
		sdCard.CardCapacity = (csd->c_size + 1) * (1 << (csd->c_size_mult + 2)) * (1 << csd->read_bl_len);
	}

	csd->erase_blk_en = (buf[8] & 0x40) >> 6;								// @46
	csd->sector_size = ((buf[15] & 0x80) >> 1) | (buf[8] & 0x3F);			// @39-45
	csd->wp_grp_size = buf[15] & 0x7f;										// @32-38
	csd->wp_grp_enable = (buf[14] & 0x80) ? 1 : 0;							// @31  
	csd->default_ecc = (buf[14] & 0x60) >> 5;								// @29-30
	csd->r2w_factor = (buf[14] & 0x1c) >> 2;								// @26-28   ** correct
	csd->write_bl_len = ((buf[14] & 0x03) << 2) | ((buf[13] & 0xc0) >> 6);  // @22-25   **correct
	csd->write_bl_partial = (buf[13] & 0x20) ? 1 : 0;						// @21 
																			// @16-20 are reserved
	csd->file_format_grp = (buf[12] & 0x80) ? 1 : 0;						// @15
	csd->copy = (buf[12] & 0x40) ? 1 : 0;									// @14
	csd->perm_write_protect = (buf[12] & 0x20) ? 1 : 0;						// @13
	csd->tmp_write_protect = (buf[12] & 0x10) ? 1 : 0;						// @12
	csd->file_format = (buf[12] & 0x0c) >> 2;								// @10-11    **correct
	csd->ecc = buf[12] & 0x03;												// @8-9      **corrrect   

	LOG_DEBUG("  csd_structure=%d\t  spec_vers=%d\t  taac=%02x\t nsac=%02x\t  tran_speed=%02x\t  ccc=%04x\n"
		"  read_bl_len=%d\t  read_bl_partial=%d\t  write_blk_misalign=%d\t  read_blk_misalign=%d\n"
		"  dsr_imp=%d\t  sector_size =%d\t  erase_blk_en=%d\n",
		csd->csd_structure, csd->spec_vers, csd->taac, csd->nsac, csd->tran_speed, csd->ccc,
		csd->read_bl_len, csd->read_bl_partial, csd->write_blk_misalign, csd->read_blk_misalign,
		csd->dsr_imp, csd->sector_size, csd->erase_blk_en);

	if (csd->csd_structure == 0x1) {
		LOG_DEBUG("CSD 2.0: ver2_c_size = %d\t  card capacity: %lu\n",
			csd->ver2_c_size, sdCard.CardCapacity);
	}
	else {
		LOG_DEBUG("CSD 1.0: c_size = %d\t  c_size_mult=%d\t card capacity: %lu\n"
			"  vdd_r_curr_min = %d\t  vdd_r_curr_max=%d\t  vdd_w_curr_min = %d\t  vdd_w_curr_max=%d\n",
			csd->c_size, csd->c_size_mult, sdCard.CardCapacity,
			csd->vdd_r_curr_min, csd->vdd_r_curr_max, csd->vdd_w_curr_min, csd->vdd_w_curr_max);
	}

	LOG_DEBUG("  wp_grp_size=%d\t  wp_grp_enable=%d\t  default_ecc=%d\t  r2w_factor=%d\n"
		"  write_bl_len=%d\t  write_bl_partial=%d\t  file_format_grp=%d\t  copy=%d\n"
		"  perm_write_protect=%d\t  tmp_write_protect=%d\t  file_format=%d\t  ecc=%d\n",
		csd->wp_grp_size, csd->wp_grp_enable, csd->default_ecc, csd->r2w_factor,
		csd->write_bl_len, csd->write_bl_partial, csd->file_format_grp, csd->copy,
		csd->perm_write_protect, csd->tmp_write_protect, csd->file_format, csd->ecc);
}

/*-[INTERNAL: sdSendCommandP]-----------------------------------------------}
. Send command and handle response.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
#define R1_ERRORS_MASK       0xfff9c004
static int sdSendCommandP( EMMCCommand* cmd, uint32_t arg )
{
	SDRESULT res;

	/* Check for command in progress */
	if ( sdWaitForCommand() != SD_OK ) return SD_BUSY;				// Check command wait

	LOG_DEBUG("EMMC: Sending command %s code %08x arg %08x\n",
		cmd->cmd_name, (unsigned int)cmd->code.CMD_INDEX, (unsigned int)arg);
	sdCard.lastCmd = cmd;

	/* Clear interrupt flags.  This is done by setting the ones that are currently set */
	EMMC_INTERRUPT->Raw32 = EMMC_INTERRUPT->Raw32;					// Clear interrupts

	/* Set the argument and the command code, Some commands require a delay before reading the response */
	*EMMC_ARG1 = arg;												// Set argument to SD card
	*EMMC_CMDTM = cmd->code;										// Send command to SD card								
	if ( cmd->delay ) waitMicro(cmd->delay);						// Wait for required delay

	/* Wait until command complete interrupt */
	if ( (res = sdWaitForInterrupt(INT_CMD_DONE))) return res;		// In non zero return result 

	/* Get response from RESP0 */
	uint32_t resp0 = *EMMC_RESP0;									// Fetch SD card response 0 to command

	/* Handle response types for command */
	switch ( cmd->code.CMD_RSPNS_TYPE) {
		// no response
		case CMD_NO_RESP:
			return SD_OK;											// Return okay then

		case CMD_BUSY48BIT_RESP:
			sdCard.status = resp0;
			// Store the card state.  Note that this is the state the card was in before the
			// command was accepted, not the new state.
			//sdCard.cardState = (resp0 & ST_CARD_STATE) >> R1_CARD_STATE_SHIFT;
			return resp0 & R1_ERRORS_MASK;

		// RESP0 contains card status, no other data from the RESP* registers.
		// Return value non-zero if any error flag in the status value.
		case CMD_48BIT_RESP:
			switch (cmd->code.CMD_INDEX) {
				case 0x03:											// SEND_REL_ADDR command
					// RESP0 contains RCA and status bits 23,22,19,12:0
					sdCard.rca = resp0 & 0xffff0000;				// RCA[31:16] of response
					sdCard.status = ((resp0 & 0x00001fff)) |		// 12:0 map directly to status 12:0
						((resp0 & 0x00002000) << 6) |				// 13 maps to status 19 ERROR
						((resp0 & 0x00004000) << 8) |				// 14 maps to status 22 ILLEGAL_COMMAND
						((resp0 & 0x00008000) << 8);				// 15 maps to status 23 COM_CRC_ERROR
					// Store the card state.  Note that this is the state the card was in before the
					// command was accepted, not the new state.
					// sdCard.cardState = (resp0 & ST_CARD_STATE) >> R1_CARD_STATE_SHIFT;
					return sdCard.status & R1_ERRORS_MASK;
				case 0x08:											// SEND_IF_COND command
					// RESP0 contains voltage acceptance and check pattern, which should match
					// the argument.
					sdCard.status = 0;
					return resp0 == arg ? SD_OK : SD_ERROR;
					// RESP0 contains OCR register
					// TODO: What is the correct time to wait for this?
				case 0x29:											// SD_SENDOPCOND command
					sdCard.status = 0;
					sdCard.ocr.Raw32 = resp0;
					return SD_OK;
				default:
					sdCard.status = resp0;
					// Store the card state.  Note that this is the state the card was in before the
					// command was accepted, not the new state.
					//sdCard.cardState = (resp0 & ST_CARD_STATE) >> R1_CARD_STATE_SHIFT;
					return resp0 & R1_ERRORS_MASK;
			}
		// RESP0..3 contains 128 bit CID or CSD shifted down by 8 bits as no CRC
		// Note: highest bits are in RESP3.
		case CMD_136BIT_RESP:		
			sdCard.status = 0;
			if (cmd->code.CMD_INDEX == 0x09) {
				unpack_csd(&sdCard.csd);
			} else {
				uint32_t* data = (uint32_t*)(uintptr_t)&sdCard.cid;
				data[3] = resp0;
				data[2] = *EMMC_RESP1;
				data[1] = *EMMC_RESP2;
				data[0] = *EMMC_RESP3;
			}
			return SD_OK;
    }

	return SD_ERROR;
}


/*-[INTERNAL: sdSendAppCommand]---------------------------------------------}
. Send APP command and handle response.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
#define ST_APP_CMD           0x00000020
static SDRESULT sdSendAppCommand (void)
{
	SDRESULT resp;
	// If no RCA, send the APP_CMD and don't look for a response.
	if ( !sdCard.rca )
		sdSendCommandP(&sdCommandTable[IX_APP_CMD], 0x00000000);
	// If there is an RCA, include that in APP_CMD and check card accepted it.
	else {
		if( (resp = sdSendCommandP(&sdCommandTable[IX_APP_CMD_RCA], sdCard.rca)) ) 
			return sdDebugResponse(resp);
		// Debug - check that status indicates APP_CMD accepted.
		if( !(sdCard.status & ST_APP_CMD) ) return SD_ERROR;
	}
	return SD_OK;
}

 /*-[INTERNAL: sdSendCommand]------------------------------------------------}
 . Send a command with no argument. RCA automatically added if required.
 . APP_CMD sent automatically if required.
 . 10Aug17 LdB
 .--------------------------------------------------------------------------*/
static SDRESULT sdSendCommand ( int index )
{
	// Issue APP_CMD if needed.
	SDRESULT resp;
	if ( index >= IX_APP_CMD_START && (resp = sdSendAppCommand()) )
		return sdDebugResponse(resp);

	// Get the command and set RCA if required.
	EMMCCommand* cmd = &sdCommandTable[index];
	uint32_t arg = 0;
	if( cmd->use_rca == 1 ) arg = sdCard.rca;

	if( (resp = sdSendCommandP(cmd, arg)) ) return resp;

	// Check that APP_CMD was correctly interpreted.
	if( index >= IX_APP_CMD_START && sdCard.rca && !(sdCard.status & ST_APP_CMD) )
		return SD_ERROR_APP_CMD;

	return resp;
}

 /*-[INTERNAL: sdSendCommandA]-----------------------------------------------}
 . Send a command with argument. APP_CMD sent automatically if required.
 . APP_CMD sent automatically if required.
 . 10Aug17 LdB
 .--------------------------------------------------------------------------*/
static SDRESULT sdSendCommandA ( int index, uint32_t arg )
{
	// Issue APP_CMD if needed.
	SDRESULT resp;
	if( index >= IX_APP_CMD_START && (resp = sdSendAppCommand()) )
		return sdDebugResponse(resp);

	// Get the command and pass the argument through.
	if( (resp = sdSendCommandP(&sdCommandTable[index],arg)) ) return resp;

	// Check that APP_CMD was correctly interpreted.
	if( index >= IX_APP_CMD_START && sdCard.rca && !(sdCard.status & ST_APP_CMD) )
		return SD_ERROR_APP_CMD;

	return resp;
}


/*-[INTERNAL: sdReadSCR]----------------------------------------------------}
. Read card's SCR
. APP_CMD sent automatically if required.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdReadSCR (void)
{
	// SEND_SCR command is like a READ_SINGLE but for a block of 8 bytes.
	// Ensure that any data operation has completed before reading the block.
	if( sdWaitForData() ) return SD_TIMEOUT;

	// Set BLKSIZECNT to 1 block of 8 bytes, send SEND_SCR command
	EMMC_BLKSIZECNT->BLKCNT = 1;
	EMMC_BLKSIZECNT->BLKSIZE = 8;
	int resp;
	if( (resp = sdSendCommand(IX_SEND_SCR)) ) return sdDebugResponse(resp);

	// Wait for READ_RDY interrupt.
	if( (resp = sdWaitForInterrupt(INT_READ_RDY)) )
	{
		if (LOG_ERROR) LOG_ERROR("EMMC: Timeout waiting for ready to read\n");
		return sdDebugResponse(resp);
	}

	// Allow maximum of 100ms for the read operation.
	int numRead = 0, count = 100000;
	while( numRead < 2 )  {
		if (EMMC_STATUS->READ_TRANSFER) {
			//sdCard.scr[numRead++] = *EMMC_DATA;
			if (numRead == 0) sdCard.scr.Raw32_Lo = *EMMC_DATA;
				else sdCard.scr.Raw32_Hi = *EMMC_DATA;
			numRead++;
		} else {
			waitMicro(1);
			if( --count == 0 ) break;
		}
	}

	// If SCR not fully read, the operation timed out.
	if( numRead != 2 )
	{
		if (LOG_ERROR) {
			LOG_ERROR("EMMC: SEND_SCR ERR: %08x %08x %08x\n", 
				(unsigned int)EMMC_STATUS->Raw32, 
				(unsigned int)EMMC_INTERRUPT->Raw32, 
				(unsigned int)*EMMC_RESP0);
			LOG_ERROR("EMMC: Reading SCR, only read %d words\n", numRead);
		}
		return SD_TIMEOUT;
	}

	return SD_OK;
}


/*-[INTERNAL: fls_uint32_t]-------------------------------------------------}
. Find Last Set bit in given uint32_t value. That is find the bit index of 
. the MSB that is set in the value. 
. RETURN: 0 - 32 are only possible answers given uint32_t is 32 bits.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static uint_fast8_t fls_uint32_t (uint32_t x) 
{
	uint_fast8_t r = 32;											// Start at 32
	if (!x)  return 0;												// If x is zero answer must be zero
	if (!(x & 0xffff0000u)) {										// If none of the upper word bits are set
		x <<= 16;													// We can roll it up 16 bits
		r -= 16;													// Reduce r by 16
	}
	if (!(x & 0xff000000u)) {										// If none of uppermost byte bits are set
		x <<= 8;													// We can roll it up by 8 bits
		r -= 8;														// Reduce r by 8
	}
	if (!(x & 0xf0000000u)) {										// If none of the uppermost 4 bits are set
		x <<= 4;													// We can roll it up by 4 bits
		r -= 4;														// Reduce r by 4
	}
	if (!(x & 0xc0000000u)) {										// If none of the uppermost 2 bits are set
		x <<= 2;													// We can roll it up by 2 bits
		r -= 2;														// Reduce r by 2
	}
	if (!(x & 0x80000000u)) {										// If the uppermost bit is not set
		x <<= 1;													// We can roll it up by 1 bit
		r -= 1;														// Reduce r by 1
	}
	return r;														// Return the number of the uppermost set bit
}

/*-[INTERNAL: sdGetClockDivider]--------------------------------------------}
. Get clock divider for the given requested frequency. This is calculated 
. relative to the SD base clock of 41.66667Mhz
. RETURN: 3 - 0x3FF are only possible answers for the divisor
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static uint32_t sdGetClockDivider (uint32_t freq) 
{
	uint32_t divisor = (41666667 + freq - 1) / freq;				// Pi SD frequency is always 41.66667Mhz on baremetal
	if (divisor > 0x3FF) divisor = 0x3FF;							// Constrain divisor to max 0x3FF
	if (EMMC_SLOTISR_VER->SDVERSION < 2) {							// Any version less than HOST SPECIFICATION 3 (Aka numeric 2)						
		uint_fast8_t shiftcount = fls_uint32_t(divisor);			// Only 8 bits and set pwr2 div on Hosts specs 1 & 2
		if (shiftcount > 0) shiftcount--;							// Note the offset of shift by 1 (look at the spec)
		if (shiftcount > 7) shiftcount = 7;							// It's only 8 bits maximum on HOST_SPEC_V2
		divisor = ((uint32_t)1 << shiftcount);						// Version 1,2 take power 2
	} else if (divisor < 3) divisor = 4;							// Set minimum divisor limit
	LOG_DEBUG("Divisor = %i, Freq Set = %i\n", (int)divisor, (int)(41666667/divisor));
	return divisor;													// Return divisor that would be required
}

/*-[INTERNAL: sdGetClockDivider]--------------------------------------------}
. Set the SD clock to the given frequency from the 41.66667Mhz base clock
. RETURN: SD_ERROR_CLOCK - A fatal error occurred setting the clock
.		  SD_OK - the clock change to given frequency
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdSetClock (uint32_t freq)
{
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time
	LOG_DEBUG("EMMC: setting clock speed to %i.\n", (unsigned int)freq);
	while ((EMMC_STATUS->CMD_INHIBIT || EMMC_STATUS->DAT_INHIBIT)	// Data inhibit signal
  		   && (td < 100000))										// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	if (td >= 100000) {												// Timeout waiting for inghibit flags
		if (LOG_ERROR) LOG_ERROR("EMMC: Set clock: timeout waiting for inhibit flags. Status %08x.\n",
			(unsigned int)EMMC_STATUS->Raw32);
		return SD_ERROR_CLOCK;										// Return clock error
	}

	/* Switch clock off */
	EMMC_CONTROL1->CLK_EN = 0;										// Disable clock
	waitMicro(10);													// We must now wait 10 microseconds

	/* Request the divisor for new clock setting */
	uint_fast32_t cdiv = sdGetClockDivider(freq);					// Fetch divisor for new frequency
	uint_fast32_t divlo = (cdiv & 0xff) << 8;						// Create divisor low bits value
	uint_fast32_t divhi = ((cdiv & 0x300) >> 2);					// Create divisor high bits value

	/* Set new clock frequency by setting new divisor */
	EMMC_CONTROL1->Raw32 = (EMMC_CONTROL1->Raw32 & 0xffff001f) | divlo | divhi;
	waitMicro(10);													// We must now wait 10 microseconds

	/* Enable the clock. */
	EMMC_CONTROL1->CLK_EN = 1;										// Enable the clock

	/* Wait for clock to be stablize */
	td = 0;															// Zero time difference
	start_time = 0;													// Zero start time
	while (!(EMMC_CONTROL1->CLK_STABLE)								// Clock not stable yet
		&& (td < 100000))											// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	if (td >= 100000) {												// Timeout waiting for stability flag
		if (LOG_ERROR) LOG_ERROR("EMMC: ERROR: failed to get stable clock.\n");
		return SD_ERROR_CLOCK;										// Return clock error
	}
	return SD_OK;													// Clock frequency set worked
}

/*-[INTERNAL: sdResetCard]--------------------------------------------------}
. Reset the SD Card
. RETURN: SD_ERROR_RESET - A fatal error occurred resetting the SD Card
.		  SD_OK - SD Card reset correctly
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static SDRESULT sdResetCard (void)
{
	SDRESULT resp;
	uint64_t td = 0;												// Zero time difference
	uint64_t start_time = 0;										// Zero start time


	/* Send reset host controller and wait for complete */
	EMMC_CONTROL0->Raw32 = 0;										// Zero control0 register
	EMMC_CONTROL1->Raw32 = 0;										// Zero control1 register
	EMMC_CONTROL1->SRST_HC = 1;										// Reset the complete host circuit
  	//EMMC_CONTROL2->UHSMODE = SDR12;
	waitMicro(10);													// Wait 10 microseconds
	LOG_DEBUG("EMMC: reset card.\n");
	while ((EMMC_CONTROL1->SRST_HC)									// Host circuit reset not clear
			&& (td < 100000))										// Timeout not reached
	{
		if (!start_time) start_time = TICKCOUNT();					// If start time not set the set start time
			else td = TIMEDIFF(start_time, TICKCOUNT());			// Time difference between start time and now
	}
	if (td >= 100000) {												// Timeout waiting for reset flag
		if (LOG_ERROR) LOG_ERROR("EMMC: ERROR: failed to reset.\n");
		return SD_ERROR_RESET;										// Return reset SD Card error
    }

	/* Enable internal clock and set data timeout */
	EMMC_CONTROL1->DATA_TOUNIT = 0xE;								// Maximum timeout value
	EMMC_CONTROL1->CLK_INTLEN = 1;									// Enable internal clock
	waitMicro(10);													// Wait 10 microseconds

	/* Set clock to setup frequency */
	if ( (resp = sdSetClock(FREQ_SETUP)) ) return resp;				// Set low speed setup frequency (400Khz)

	/* Enable interrupts for command completion values */
	EMMC_IRPT_EN->Raw32   = 0xffffffff;
	EMMC_IRPT_MASK->Raw32 = 0xffffffff;

	/* Reset our card structure entries */
	sdCard.rca = 0;													// Zero rca
	sdCard.ocr.Raw32 = 0;											// Zero ocr
	sdCard.lastCmd = 0;												// Zero lastCmd
	sdCard.status = 0;												// Zero status
	sdCard.type = SD_TYPE_UNKNOWN;									// Set card type unknown

	/* Send GO_IDLE_STATE to card */
	resp = sdSendCommand(IX_GO_IDLE_STATE);							// Send GO idle state

	return resp;													// Return response
}

 /*-[INTERNAL: sdAppSendOpCond]---------------------------------------------}
 . Common routine for APP_SEND_OP_COND
 . This is used for both SC and HC cards based on the parameter
 . 10Aug17 LdB
 .-------------------------------------------------------------------------*/
static SDRESULT sdAppSendOpCond (uint32_t arg )
{
	// Send APP_SEND_OP_COND with the given argument (for SC or HC cards).
	// Note: The host shall set ACMD41 timeout more than 1 second to abort repeat of issuing ACMD41
	SDRESULT  resp;
	if( (resp = sdSendCommandA(IX_APP_SEND_OP_COND,arg)) && resp != SD_TIMEOUT )
    {
		if (LOG_ERROR) LOG_ERROR("EMMC: ACMD41 returned non-timeout error %d\n",resp);
			return resp;
    }
	int count = 6;
	while( (sdCard.ocr.card_power_up_busy == 0) && count-- )
	{
		//scPrintf("EMMC: Retrying ACMD SEND_OP_COND status %08x\n",*EMMC_STATUS);
		waitMicro(400000);
		if( (resp = sdSendCommandA(IX_APP_SEND_OP_COND,arg)) && resp != SD_TIMEOUT )
		{
			if (LOG_ERROR) LOG_ERROR("EMMC: ACMD41 returned non-timeout error %d\n",resp);
			return resp;
		}
	}

	// Return timeout error if still not busy.
	if( sdCard.ocr.card_power_up_busy == 0 )
	return SD_TIMEOUT;

	// Pi is 3.3v SD only so check that one voltage values around 3.3v was returned.
	if(  sdCard.ocr.voltage3v2to3v3 == 0  && sdCard.ocr.voltage3v3to3v4 == 0)
	return SD_ERROR_VOLTAGE;

	return SD_OK;
}

/*-[sdTransferBlocks]-------------------------------------------------------}
. Transfer the count blocks starting at given block to/from SD Card.
. 21Aug17 LdB
.--------------------------------------------------------------------------*/
SDRESULT sdTransferBlocks (uint32_t startBlock, uint32_t numBlocks, uint8_t* buffer, bool write )
{
	if ( sdCard.type == SD_TYPE_UNKNOWN ) return SD_NO_RESP;		// If card not known return error
	if ( sdWaitForData() ) return SD_TIMEOUT;						// Ensure any data operation has completed before doing the transfer.

	// Work out the status, interrupt and command values for the transfer.
	int readyInt = write ? INT_WRITE_RDY : INT_READ_RDY;

	int transferCmd = write ? ( numBlocks == 1 ? IX_WRITE_SINGLE : IX_WRITE_MULTI) :
							( numBlocks == 1 ? IX_READ_SINGLE : IX_READ_MULTI);

	// If more than one block to transfer, and the card supports it,
	// send SET_BLOCK_COUNT command to indicate the number of blocks to transfer.
	SDRESULT resp;
	if ( numBlocks > 1 &&
		(sdCard.scr.CMD_SUPPORT == CMD_SUPP_SET_BLKCNT) &&
		(resp = sdSendCommandA(IX_SET_BLOCKCNT, numBlocks)) ) return sdDebugResponse(resp);

	// Address is different depending on the card type.
	// HC pass address as block # so just pass it thru.
	// SC pass address so need to multiply by 512 which is shift left 9.
	uint32_t blockAddress = sdCard.type == SD_TYPE_2_SC ? (uint32_t)(startBlock << 9) : (uint32_t)startBlock;

	// Set BLKSIZECNT to number of blocks * 512 bytes, send the read or write command.
	// Once the data transfer has started and the TM_BLKCNT_EN bit in the CMDTM register is
	// set the EMMC module automatically decreases the BLKCNT value as the data blocks
	// are transferred and stops the transfer once BLKCNT reaches 0.
	// TODO: TM_AUTO_CMD12 - is this needed?  What effect does it have?
	EMMC_BLKSIZECNT->BLKCNT = numBlocks;
	EMMC_BLKSIZECNT->BLKSIZE = 512;
	if ((resp = sdSendCommandA(transferCmd, blockAddress))) {
		return sdDebugResponse(resp);
	}

	// Transfer all blocks.
	uint_fast32_t blocksDone = 0;
	while ( blocksDone < numBlocks )
    {
		// Wait for ready interrupt for the next block.
		if( (resp = sdWaitForInterrupt(readyInt)) )
		{
			if (LOG_ERROR) LOG_ERROR("EMMC: Timeout waiting for ready to read\n");
			return sdDebugResponse(resp);
		}

		// Handle non-word-aligned buffers byte-by-byte.
		// Note: the entire block is sent without looking at status registers.
		if ((uintptr_t)buffer & 0x03) {
			for (uint_fast16_t i = 0; i < 512; i++ ) {
				if ( write ) {
					uint32_t data = (buffer[i]      );
					data |=    (buffer[i+1] << 8 );
					data |=    (buffer[i+2] << 16);
					data |=    (buffer[i+3] << 24);
					*EMMC_DATA = data;
				} else {
					uint32_t data = *EMMC_DATA;
					buffer[i] =   (data      ) & 0xff;
					buffer[i+1] = (data >> 8 ) & 0xff;
					buffer[i+2] = (data >> 16) & 0xff;
					buffer[i+3] = (data >> 24) & 0xff;
				}
			}
		}
	    // Handle word-aligned buffers more efficiently.
		// Hopefully people smart enough to privide aligned data buffer
		else {
			uint32_t* intbuff = (uint32_t*)buffer;
			for (uint_fast16_t i = 0; i < 128; i++ ) {
				if ( write ) *EMMC_DATA = intbuff[i];
					else intbuff[i] = *EMMC_DATA;
			}
		}

		blocksDone++;
		buffer += 512;
	}

	// If not all bytes were read, the operation timed out.
	if( blocksDone != numBlocks ) {
		if (LOG_ERROR) LOG_ERROR("EMMC: Transfer error only done %d/%d blocks\n",blocksDone,numBlocks);
		LOG_DEBUG("EMMC: Transfer: %08x %08x %08x %08x\n", (unsigned int)EMMC_STATUS->Raw32, 
			(unsigned int)EMMC_INTERRUPT->Raw32, (unsigned int)*EMMC_RESP0, 
			(unsigned int)EMMC_BLKSIZECNT->Raw32);
		if( !write && numBlocks > 1 && (resp = sdSendCommand(IX_STOP_TRANS)) )
			LOG_DEBUG("EMMC: Error response from stop transmission: %d\n",resp);

		return SD_TIMEOUT;
    }

	// For a write operation, ensure DATA_DONE interrupt before we stop transmission.
	if( write && (resp = sdWaitForInterrupt(INT_DATA_DONE)) )
	{
		if (LOG_ERROR) LOG_ERROR("EMMC: Timeout waiting for data done\n");
		return sdDebugResponse(resp);
	}

	// For a multi-block operation, if SET_BLOCKCNT is not supported, we need to indicate
	// that there are no more blocks to be transferred.
	if( (numBlocks > 1) && (sdCard.scr.CMD_SUPPORT != CMD_SUPP_SET_BLKCNT) &&
		(resp = sdSendCommand(IX_STOP_TRANS)) ) return sdDebugResponse(resp);

	return SD_OK;
}

/*-[sdClearBlocks]----------------------------------------------------------}
. Clears the count blocks starting at given block from SD Card.
. 21Aug17 LdB
.--------------------------------------------------------------------------*/
SDRESULT sdClearBlocks(uint32_t startBlock , uint32_t numBlocks)
{
	if (sdCard.type == SD_TYPE_UNKNOWN) return SD_NO_RESP;

	// Ensure that any data operation has completed before doing the transfer.
	if ( sdWaitForData() ) return SD_TIMEOUT;

	// Address is different depending on the card type.
	// HC pass address as block # which is just address/512.
	// SC pass address straight through.
	uint32_t startAddress = sdCard.type == SD_TYPE_2_SC ? (uint32_t)(startBlock << 9) : (uint32_t)startBlock;
	uint32_t endAddress = sdCard.type == SD_TYPE_2_SC ? (uint32_t)( (startBlock +numBlocks) << 9) : (uint32_t)(startBlock + numBlocks);
	SDRESULT resp;
	LOG_DEBUG("EMMC: erasing blocks from %d to %d\n", startAddress, endAddress);
	if ( (resp = sdSendCommandA(IX_ERASE_WR_ST,startAddress)) ) return sdDebugResponse(resp);
	if ( (resp = sdSendCommandA(IX_ERASE_WR_END,endAddress)) ) return sdDebugResponse(resp);
	if ( (resp = sdSendCommand(IX_ERASE)) ) return sdDebugResponse(resp);

	// Wait for data inhibit status to drop.
	int count = 1000000;
	while( EMMC_STATUS->DAT_INHIBIT )
	{
	if ( --count == 0 )
		{
		if (LOG_ERROR) LOG_ERROR("EMMC: Timeout waiting for erase: %08x %08x\n", 
			(unsigned int)EMMC_STATUS->Raw32, (unsigned int)EMMC_INTERRUPT->Raw32);
		return SD_TIMEOUT;
		}

		waitMicro(10);
	}

	//scPrintf("EMMC: completed erase command int %08x\n",*EMMC_INTERRUPT);

	return SD_OK;
}

/*==========================================================================}
{				      FAT32 STRUCTURES AND ROUTINES							}
{==========================================================================*/

/*--------------------------------------------------------------------------}
{                        FAT12/16 SPECIFIC STRUCTURE		 	            }
{--------------------------------------------------------------------------*/
// Offset 36 into the BPB, structure for a FAT16/12 table entry varies this defines it
struct __attribute__((packed, aligned(1))) BPBFAT1612_struct {
	uint8_t			BS_DriveNumber;			// 36       ( As used in INT 13)
	uint8_t			BS_Reserved1;           // 37
	uint8_t			BS_BootSig;             // 38		(0x29)
	uint32_t		BS_VolumeID;            // 42
	uint8_t			BS_VolumeLabel[11];     // 43-53	(Label or  "NO NAME    ")
	char			BS_FileSystemType[8];   // 54-61    ("FAT16   ", "FAT     ", or all zero.)
};

/*--------------------------------------------------------------------------}
{                         FAT32 SPECIFIC STRUCTURE				            }
{--------------------------------------------------------------------------*/
// Offset 36 into the BPB, structure for a FAT32 table entry varies this defines it
struct __attribute__((packed, aligned(1))) BPBFAT32_struct {
	uint32_t		FATSize32;				// 36-39   (Number of sectors per FAT on FAT32)
	uint16_t		ExtFlags;				// 40-41   (Mirror flags Bits 0-3: number of active FAT (if bit 7 is 1) Bit 7: 1 = single active FAT; zero: all FATs are updated at runtime; Bits 4-6 & 8-15 : reserved)
	uint16_t		FSVersion;				// 42-43
	uint32_t		RootCluster;			// 44-47   (usually 2)
	uint16_t		FSInfo;					// 48-49   (usually 1)
	uint16_t		BkBootSec;				// 50-51   (usually 6)
	uint8_t			Reserved[12];			// 52-63
	uint8_t			BS_DriveNumber;			// 64
	uint8_t			BS_Reserved1;           // 65
	uint8_t		    BS_BootSig;				// 66      (0x29: extend signature .. Early FAT32 stop here ) 
	uint32_t		BS_VolumeID;			// 67-70
	char			BS_VolumeLabel[11];     // 71-81   (Label or  "NO NAME    ")
	char			BS_FileSystemType[8];   // 82-89   ("FAT32   ", "FAT     ", or all zero.)
};

/*--------------------------------------------------------------------------}
{          FAT12/16/32 UNIONIZED BIOS PARAMETER BLOCK STRUCTURE		        }
{--------------------------------------------------------------------------*/
// Full unionized BPB struct valid for 12/16/32 bit
struct __attribute__((packed, aligned(4))) FAT_BPB_struct {			// OFFSET LOCATIONS
	uint8_t			BS_JmpBoot[3];			// 0-2   (eb xx 90, or e9 xx xx)
	char			BS_OEMName[8];			// 3-10
	uint16_t		BytesPerSector;			// 11-12 (valid 512, 1024, 2048, 4096)
	uint8_t			SectorsPerCluster;		// 13    (valid 1, 2, 4, 8, 16, 32, 64, 128, 32768, 65536)
	uint16_t		ReservedSectorCount;	// 14-15 
	uint8_t			NumFATs;				// 16    (2)
	uint16_t		RootEntryCount;			// 17-18 (224 = FAT12, 512 = FAT16, 0 = FAT 32**)
	uint16_t		TotalSectors16;			// 19-20
	uint8_t			Media;					// 21    (Media descriptor f0: 1.4 MB floppy, f8 : hard disk; etc)
	uint16_t		FATSize16;				// 22-23 (Number of sectors per FAT .. 0 on FAT32**)
	uint16_t		SectorsPerTrack;		// 24-25
	uint16_t		NumberOfHeads;			// 26-27
	uint32_t		HiddenSectors;			// 28-31
	uint32_t		TotalSectors32;			// 32-35
	union {									// Unionize the two structures at offset 36
		struct BPBFAT1612_struct fat1612;	// Fat 16/12 structure
		struct BPBFAT32_struct	 fat32;		// Fat 32 structure
	} FSTypeData;
};

/*--------------------------------------------------------------------------}
{				    PARTITION DECRIPTION BLOCK STRUCTURE				    }
{--------------------------------------------------------------------------*/
//Structure to access info of a partition on the disk
struct __attribute__((packed, aligned(2))) partition_info {
	uint8_t		status;							// 0x80 - active partition
	uint8_t		headStart;						// starting head
	uint16_t	cylSectStart;					// starting cylinder and sector
	uint8_t		type;							// partition type (01h = 12bit FAT, 04h = 16bit FAT, 05h = Ex MSDOS, 06h = 16bit FAT (>32Mb), 0Bh = 32bit FAT (<2048GB))
	uint8_t		headEnd;						// ending head of the partition
	uint16_t	cylSectEnd;						// ending cylinder and sector
	uint32_t	firstSector;					// total sectors between MBR & the first sector of the partition
	uint32_t	sectorsTotal;					// size of this partition in sectors
};

/*--------------------------------------------------------------------------}
{				     MASTER BOOT RECORD BLOCK STRUCTURE					    }
{--------------------------------------------------------------------------*/
// Structure to access Master Boot Record for getting info about partitions
struct __attribute__((packed, aligned(4))) MBR_info {
	uint8_t					nothing[446];		// Filler the gap in the structure
	struct partition_info partitionData[4];		// partition records (16x4)
	uint16_t				signature;			// 0xaa55
};


/*--------------------------------------------------------------------------}
{				  FAT 12/16/32 (8:3) DIRECTORY ENTRY STRUCTURE			    }
{--------------------------------------------------------------------------*/
//Structure to access Directory Entry in the FAT
struct __attribute__((packed, aligned(1))) dir_Structure {
	SFN_NAME	name;						// 8.3 filename
	uint8_t		attrib;						// file attributes
	uint8_t		NTreserved;					// always 0
	uint8_t		timeTenth;					// tenths of seconds, set to 0 here
	uint16_t	writeTime;					// time file was last written
	uint16_t	writeDate;					// date file was last written
	uint16_t	lastAccessDate;				// date file was last accessed
	uint16_t	firstClusterHI;				// higher word of the first cluster number
	uint16_t	createTime;					// time file was created
	uint16_t	createDate;					// date file was created
	uint16_t	firstClusterLO;				// lower word of the first cluster number
	uint32_t	fileSize;					// size of file in bytes
};

/*--------------------------------------------------------------------------}
{				  FAT 12/16/32 (LFN) DIRECTORY ENTRY STRUCTURE			    }
{--------------------------------------------------------------------------*/
//Structure to access FAT long filename Directory Entry in the FAT
struct __attribute__((packed, aligned(1))) dir_LFN_Structure {
	uint8_t		LDIR_SeqNum;				// Long directory order		
	uint8_t		LDIR_Name1[10];				// Characters 1-5 of long name (UTF 16) but misaligned again
	uint8_t		LDIR_Attr;					// Long directory attribute .. always 0x0f	
	uint8_t		LDIR_Type;					// Long directory type ... always 0x00
	uint8_t		LDIR_ChkSum;				// Long directory checksum
	uint16_t	LDIR_Name2[6];				// Characters 6-11 of long name (UTF 16)
	uint16_t	LDIR_firstClusterLO;		// First cluster lo
	uint16_t	LDIR_Name3[2];				// Characters 12-13 of long name (UTF 16)
};

/*-[INTERNAL: LoadDrivePartition]-------------------------------------------}
. Attempts to load the partition on the SD Card. This involves detecting the
. type of partiton and saving values that will be needed for IO access.
. 10Aug17 LdB
.--------------------------------------------------------------------------*/
static bool LoadDrivePartition (printhandler prn_basic) {
	uint32_t partition_totalClusters;
	uint8_t buffer[512] __attribute__((aligned(4)));

	if (sdTransferBlocks(0, 1, (uint8_t*)&buffer[0], false) != SD_OK) return false;
	struct FAT_BPB_struct* bpb = (struct FAT_BPB_struct *)buffer;
	if (bpb->BS_JmpBoot[0] != 0xE9 && bpb->BS_JmpBoot[0] != 0xEB) { // Check if it is boot sector
		struct MBR_info* mbr = (struct MBR_info*)&buffer[0];		// if it is not boot sector, it must be MBR
		if (mbr->signature != 0xaa55) return false;					// if it is not even MBR then it's not FAT
		struct partition_info* pd = &mbr->partitionData[0];			// First partition
		sdCard.partition.unusedSectors = pd->firstSector;			// FAT16 needs this value so hold it
		sdTransferBlocks(pd->firstSector, 1, &buffer[0], false);	// Read first sector of partition
		if (bpb->BS_JmpBoot[0] != 0xE9 && bpb->BS_JmpBoot[0] != 0xEB)  
			return false;											// Not an MBR
	}
	sdCard.partition.bytesPerSector = bpb->BytesPerSector;			// Bytes per sector on partition
	sdCard.partition.sectorPerCluster = bpb->SectorsPerCluster;		// Hold the sector per cluster count
	sdCard.partition.reservedSectorCount = bpb->ReservedSectorCount;// Hold the reserved sector count
	if ((bpb->FATSize16 == 0) && (bpb->RootEntryCount == 0)) {		// Check if FAT16/FAT32
		// FAT32
		sdCard.partition.rootCluster = bpb->FSTypeData.fat32.RootCluster;// Hold partition root cluster
		sdCard.partition.firstDataSector = bpb->ReservedSectorCount + bpb->HiddenSectors + (bpb->FSTypeData.fat32.FATSize32 * bpb->NumFATs);
		// data sectors x sectorsize = capacity ... I have check this on PC and it gives right calc
		sdCard.partition.dataSectors = bpb->TotalSectors32 - bpb->ReservedSectorCount - (bpb->FSTypeData.fat32.FATSize32 * bpb->NumFATs);
		if (prn_basic) prn_basic("FAT32 Volume Label: %s, ID: %08x\n", 
			bpb->FSTypeData.fat32.BS_VolumeLabel, 
			(unsigned int)bpb->FSTypeData.fat32.BS_VolumeID);		// Basic detail print if requested
	}
	else {
		// FAT16
		sdCard.partition.rootCluster = 2;							// Hold partition root cluster, FAT16 always start at 2
		sdCard.partition.firstDataSector = sdCard.partition.unusedSectors + (bpb->NumFATs * bpb->FATSize16) + 1;
		// data sectors x sectorsize = capacity ... I have check this on PC and gives right calc
		sdCard.partition.dataSectors = bpb->TotalSectors32 - (bpb->NumFATs * bpb->FATSize16) - 33;  // -1 see above +1 and 32 fixed sectors 
		if (bpb->FSTypeData.fat1612.BS_BootSig == 0x29) {
			if (prn_basic) prn_basic("FAT12/16 Volume Label: %s, Volume ID %08x\n", 
				bpb->FSTypeData.fat1612.BS_VolumeLabel, 
				(unsigned int)bpb->FSTypeData.fat1612.BS_VolumeID);	// Basic detail print if requested
		}
	}
	
	// total clusters *  clustersize = capacity  ... see data sectors above ... another way to say same thing 
	partition_totalClusters = sdCard.partition.dataSectors / sdCard.partition.sectorPerCluster;
	if (prn_basic) prn_basic("First Sector: %lu, Data Sectors: %lu, TotalClusters: %lu, RootCluster: %lu\n",
		(unsigned long)sdCard.partition.firstDataSector, (unsigned long)sdCard.partition.dataSectors, 
		(unsigned long)partition_totalClusters, (unsigned long) sdCard.partition.rootCluster);
	return true;
}


/*-[INTERNAL: getFirstSector]-----------------------------------------------}
. Calculate first sector address of any given cluster number on a partition.
. beside the cluster number you need the sectorPerCluster and FirstDataSector
. of the partition on which you wish to calculate.
. 12Feb17 LdB
.--------------------------------------------------------------------------*/
static uint32_t getFirstSector (uint32_t clusterNumber, uint32_t sectorPerCluster, uint32_t firstDataSector) {
	return (((clusterNumber - 2) * sectorPerCluster) + firstDataSector);
}

/*-getSetNextCluster---------------------------------------------------------
Simply gets or sets next cluster entry value of the FAT chain
12Feb17 LdB
--------------------------------------------------------------------------*/
uint32_t getSetNextCluster (uint32_t clusterNumber, bool set, uint32_t clusterEntry) {
	uint32_t FATEntrySector, FATEntryOffset;
	uint32_t* FATEntryValue;
	uint8_t buffer[512];
	FATEntrySector = sdCard.partition.unusedSectors + sdCard.partition.reservedSectorCount +
		((clusterNumber * 4) / sdCard.partition.bytesPerSector);	// Get sector number of the cluster entry in the FAT
	FATEntryOffset = ((clusterNumber * 4) % sdCard.partition.bytesPerSector); // Get the offset address in that sector number
	if (sdTransferBlocks(FATEntrySector, 1, (uint8_t*)&buffer[0], false) != SD_OK)
	{
		return 0xFFFFFFFF;											// Sector read failed twice so fail exit
	}
	FATEntryValue = (uint32_t*)&buffer[FATEntryOffset];				// Get the cluster address from the buffer .. fortunately always aligned
	if (set == false) return (*FATEntryValue);						// If not setting exit with the retrieved value
	*FATEntryValue = clusterEntry;									// Setting new value in cluster entry in FAT
	if (sdTransferBlocks(FATEntrySector, 1, (uint8_t*)&buffer[0], true) != SD_OK)
	{
		return 0xFFFFFFFF;											// Sector write failed twice so fail exit
	}
	return (0);														// return zero
}


/*==========================================================================}
{				      PRIVATE SEARCH STRUCTURES AND ROUTINES				}
{==========================================================================*/

/*--------------------------------------------------------------------------}
{                         PRIVATE SEARCH DATA STRUCTURE			            }
{--------------------------------------------------------------------------*/
struct __attribute__((packed, aligned(4))) PRIV_SEARCH_DATA {
	uint32_t cluster;												// Current cluster
	uint32_t firstSector;											// First sector
	uint32_t sector;												// Current sector
	uint32_t bPos;													// Buffer position
	uint8_t buffer[512] __attribute__((aligned(4)));				// Data buffer
	LFN_NAME searchPattern;											// Search pattern find was started with
};

/*--------------------------------------------------------------------------}
{               INTERNAL SEARCHS THAT ARE CURRENTLY RUNNING		            }
{--------------------------------------------------------------------------*/
#define MAX_SEARCH_RECORDS 4										// Current limit is 4 (if you need more simultaneous searches increase)
static struct PRIV_SEARCH_DATA __attribute__((aligned(4))) intSearchRecord[MAX_SEARCH_RECORDS] = { 0 };

/*-[INTERNAL: SetFindDataFromFATEntry]--------------------------------------}
. Transfers the normal directory entry values to a FIND_DATA structure ptr.
. If both pointers it will transfer the various time, size and name records.
. 18Feb17 LdB
.--------------------------------------------------------------------------*/
static void SetFindDataFromFATEntry (struct dir_Structure* dir, FIND_DATA* lpFD) {
	if ((dir) && (lpFD)) {
		lpFD->dwFileAttributes = dir->attrib;						// Transfer file attributes	
		lpFD->CreateDT.tm_year = ((dir->createDate & 0xFE00) >> 9) + 80;// Looks weird but file date is from 1980 and C standard from 1900 so add 80
		lpFD->CreateDT.tm_mon = ((dir->createDate & 0x01E0) >> 5) - 1;// file date is 1-12 .. C standard is 0-11
		lpFD->CreateDT.tm_mday = (dir->createDate & 0x001F);		// day is strangely correct both 1-31 
		lpFD->CreateDT.tm_hour = ((dir->createTime & 0xF800) >> 11);// Hour when file was created
		lpFD->CreateDT.tm_min = ((dir->createTime & 0x07E0) >> 5);	// Minute when file was created
		lpFD->CreateDT.tm_sec = ((dir->createTime & 0x001F) << 1);	// Seconds when file was created

		lpFD->LastAccessDate.tm_year = ((dir->lastAccessDate & 0xFE00) >> 9) + 80; // Looks weird but file date is from 1980 and C standard from 1900
		lpFD->LastAccessDate.tm_mon = ((dir->lastAccessDate & 0x01E0) >> 5) - 1; // file date is 1-12 .. C standard is 0-11
		lpFD->LastAccessDate.tm_mday = (dir->lastAccessDate & 0x001F);	// day is strangely correct both 1-31 
		

		lpFD->WriteDT.tm_year = ((dir->writeDate & 0xFE00) >> 9) + 80;// Looks weird but file date is from 1980 and C standard from 1900
		lpFD->WriteDT.tm_mon = ((dir->writeDate & 0x01E0) >> 5) - 1;// file date is 1-12 .. C standard is 0-11
		lpFD->WriteDT.tm_mday = (dir->writeDate & 0x001F);			// day is strangely correct both 1-31 
		lpFD->WriteDT.tm_hour = ((dir->writeTime & 0xF800) >> 11);	// Hour of last file write
		lpFD->WriteDT.tm_min = ((dir->writeTime & 0x07E0) >> 5);	// Minute of last file write
		lpFD->WriteDT.tm_sec = ((dir->writeTime & 0x001F) << 1);	// Second of last file write

		lpFD->nFileSizeLow = dir->fileSize;							// Low 32 bits of file size
		lpFD->nFileSizeHigh = 0;									// Zero high 32 bits as not supported

		for (int i = 0; i < sizeof(SFN_NAME); i++)					// This is all unaligned so has to be manhandled
			lpFD->cAlternateFileName[i] = dir->name[i];				// Transfer short name (8:3)
	}
}

/*-[INTERNAL: ReadLFNEntry]-------------------------------------------------}
. Transfers up to 13 characters from the LFN directory entry to the buffer
. and returns the count of characters placed in the buffer. The first five
. characters in Name1 have alignment issues for the ARM 7/8, care needed.
. 18Feb17 LdB
.--------------------------------------------------------------------------*/
static uint8_t ReadLFNEntry (struct dir_LFN_Structure* LFdir, char LFNtext[14]) {
	uint8_t utf1, utf2;
	uint16_t utf, j;
	bool lfn_done = false;
	uint8_t LFN_blockcount = 0;
	if (LFdir) {													// Check the directory pointer valid
		for (j = 0; ((j < 5) && (lfn_done == false)); j++) {		// For each of the first 5 characters
			/* Alignment problems on this 1st block .. please leave alone */
			utf1 = LFdir->LDIR_Name1[j * 2];						// Load the low byte  ***unaligned access
			utf2 = LFdir->LDIR_Name1[j * 2 + 1];					// Load the high byte
			utf = (utf2 << 8) | utf1;								// Roll and join to make word
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = (char)wctob(utf);	// Otherwise narrow to ascii and place in buffer
		}
		for (j = 0; ((j < 6) && (lfn_done == false)); j++) {		// For the next six characters
			utf = LFdir->LDIR_Name2[j];								// Fetch the character
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = (char)wctob(utf);	// Otherwise narrow to ascii and place in buffer
		}
		for (j = 0; ((j < 2) && (lfn_done == false)); j++) {		// For the last two characters
			utf = LFdir->LDIR_Name3[j];								// Fetch the character
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = (char)wctob(utf);	// Otherwise narrow to ascii and place in buffer
		}
	}
	LFNtext[LFN_blockcount] = '\0';									// Make ascizz incase it needs to be used as string									
	return LFN_blockcount;											// Return characters in buffer 0-13 is valid range
}


/*-[INTERNAL: WildcardMatch ]-----------------------------------------------}
. Given a pattern string which may include wildcard characters "*","?" and
. sets the given text string is compared for a match. It is the classic code
. that checks if a given filename matches the provided pattern. 
. 18Feb17 LdB
.--------------------------------------------------------------------------*/
bool WildcardMatch (const char * pattern, const char * text)
{
	const char * retryPat = NULL;
	const char * retryText = NULL;
	int	ch;
	bool found;
	while (toupper(*text) || toupper(*pattern)) {					// While neither string terminated
		ch = *pattern++;											// Fetch next pattern character
		switch (ch)
		{
			case '*':												// If character is wildcard
				retryPat = pattern;									// Hold pattern for retry
				retryText = text;									// Hold text for retry
				break;
			case '[':												// Multi character select wildcard open
				found = false;										// found = false
				while ((ch = *pattern++) != ']')					// While not close multicharacter
				{
					if (ch == '\\')	ch = *pattern++;				// Literal request
					if (ch == '\0')	return false;					// Pattern terminated abnormally without match
					if (*text == ch) found = false;					// character not found
				}
				if (!found)											// If not found
				{
					pattern = retryPat;								// Reset pattern to retry string
					text = ++retryText;								// Set text string to next character on from retry
				}
				/* fall into next case */
			case '?':												// Single character wildcard
				if (*text++ == '\0') return false;					// text string ended so no match
				break;
			case '\\':												// Literal slash
				ch = *pattern++;									// Load next character
				if (ch == '\0')	return false;						// Pattern terminated abnormally without match
				/* fall into next case */
			default:
				if (toupper(*text) == toupper(ch))					// If pattern character matches text character
				{
					if (*text) text++;								// Move the text pointer forward
					break;
				}
				if (*text)											// If end of text string
				{
					pattern = retryPat;								// Reset pattern to retry point
					text = ++retryText;								// Set text string to next character on from retry
					break;
				}
				return false;										// No match
		}
		if (pattern == NULL) return false;							// No pattern provided
	}
	return true;													// Text string matches pattern
}

/*--------------------------------------------------------------------------}
{						  FAT SYSTEM ERROR CONSTANTS		 	            }
{--------------------------------------------------------------------------*/
#define FAT_RESULT_OK				0								// FAT function success
#define FAT_END_REACHED				1								// FAT table end reached (did not find requested before)
#define FAT_INVALID_DATAPTR			2								// FAT function given invalid pointer
#define FAT_READSECTOR_FAIL			3								// FAT sector read failed
#define FAT_WRITESECTOR_FAIL		4								// FAT sector write failed

/*-[INTERNAL: LocateFATEntry]-----------------------------------------------}
. Return the next diretory entry on the file system. It will return NULL if
. an error occurs and if you provide an ErrorID pointer it will set value
. to a value to help identify the error.
. 18Feb17 LdB
.--------------------------------------------------------------------------*/
#define FILE_EMPTY					0x00							// File empty
#define FILE_DELETED				0xE5							// File was deleted
#define FILE_ATTRIBUTE_LONGNAME		0x0F							// Long filename

static struct dir_Structure* LocateFATEntry (const char* searchPat, struct PRIV_SEARCH_DATA* priv, bool dirChange, LFN_NAME LFN_Name, uint32_t* ErrorID) {
	struct dir_Structure* dir;
	uint32_t LFN_count = 0;
	while ((priv->cluster < 0x0ffffff6)	&& (priv->cluster != 0)) {	// Check cluster valid and read successful	
		while (priv->sector < sdCard.partition.sectorPerCluster) {
			while (priv->bPos <  512) {								// While not a buffer end
				dir = (struct dir_Structure *) &priv->buffer[priv->bPos];// Transfer buffer position to pointer				
				if (dir->name[0] == FILE_EMPTY) {
					if (ErrorID) *ErrorID = FAT_END_REACHED;		// End of FAT entry chain reached
					return NULL;									// Return null pointer
				}
				if (dir->name[0] != FILE_DELETED) {					// If entry not deleted
					if (dir->attrib == FILE_ATTRIBUTE_LABEL) {		// FAT LABEL ENTRY
						/*printf("LABEL: %c%c%c%c%c%c%c%c%c%c%c\n",
							dir->name[0], dir->name[1], dir->name[2], dir->name[3],
							dir->name[4], dir->name[5], dir->name[6], dir->name[7],
							dir->name[8], dir->name[9], dir->name[10]);*/
					} else if (dir->attrib == FILE_ATTRIBUTE_LONGNAME)// LFN block precede the normal block
					{
						uint8_t LFN_blockcount;									
						char LFNtext[14] = { 0 };					// Each block can hold max 13 characters						
						// Read the LFN
						LFN_blockcount = ReadLFNEntry((struct dir_LFN_Structure*)&priv->buffer[priv->bPos], LFNtext);
						// Transfer the max 13 characters to front of reverse growing string
						for (int j = 0; j < LFN_blockcount; j++)
							LFN_Name[255 - LFN_count - LFN_blockcount + j] = LFNtext[j];
						LFN_count += LFN_blockcount;				// Increment long filename count
					} else {
						uint_fast32_t dotadded = 0;					// Track if we add a dot .. it starts zero
						if (LFN_count != 0) {						// Filename is LFN
							bool hasdot = false;					// Track if filname has dot in it, start false
							for (int j = 0; j < LFN_count; j++) {	// For each character in LFN
								LFN_Name[j] = LFN_Name[255 - LFN_count + j]; // Transfer it from back to front
								if (LFN_Name[j] == '.')				// If we see a dot 
									hasdot = true;					// Mark it as true
							}
							if ((!hasdot) &&						// If do not have a dot
							   (dirChange == false))				// This is not a directory change call
							{							
								dotadded = LFN_count;				// Set where we are going to add dot
								LFN_Name[LFN_count++] = '.';		// Add dot as req for wildcard name check
							}
							LFN_Name[LFN_count] = '\0';				// Make asciiz
							LFN_count = 0;							// Make sure we clear the internal LFN_count
						} else {
							int j = 0;								// Start on zero
							while (j < 8 && dir->name[j] != 0 && dir->name[j] != ' ') {
								LFN_Name[j] = dir->name[j];			// From SFN string to LFN
								j++;
							}
							if (dirChange == false) {
								LFN_Name[j++] = '.';				// Add a "." at end of name
								if (dir->attrib != FILE_ATTRIBUTE_DIRECTORY) {
									int k = 0;						// Start on zero
									while (k < 3 && dir->name[8 + k] != 0 && dir->name[8 + k] != ' ') {
										LFN_Name[j++] = dir->name[8 + k];// From SFN string to LFN
										k++;						// Next ext character
									}
								} else  dotadded = j - 1;
							}
							LFN_Name[j] = '\0';						// Make LFN asciiz
						}
						if ((dir->attrib != FILE_ATTRIBUTE_LABEL) &&// If not a label 
						   (WildcardMatch(searchPat, &LFN_Name[0])))// And LFN matches search pattern
						{
							if (dotadded) LFN_Name[dotadded] = '\0';// Make asciiz
							priv->bPos += sizeof(struct dir_Structure);// Move pointer down 	
							if (ErrorID) *ErrorID = FAT_RESULT_OK;	// Return result of FAT_RESULT_OK if requested
							return dir;								// Return directory pointer	
						}
						/*printf("Rejected match %s vs %s\n", searchPat, &LFN_Name[0]);*/
					}
				}
				priv->bPos += sizeof(struct dir_Structure);			// Buffer position moves forward
			}
			priv->sector++;											// Increment sector
			if (priv->sector < sdCard.partition.sectorPerCluster) { // Next sector valid do disk read
				priv->bPos = 0;										// Reset buffer position to top of buffer
				if (sdTransferBlocks(priv->firstSector + priv->sector, 
					1,	&priv->buffer[0], false) != SD_OK)
				{
					if (ErrorID) *ErrorID = FAT_READSECTOR_FAIL;	// Check for read failure
					return NULL;									// Return null pointer
				}
			}
		}
		priv->cluster = getSetNextCluster(priv->cluster, false, 0); // Fetch the next cluster
		priv->firstSector = getFirstSector(priv->cluster, 
			sdCard.partition.sectorPerCluster, 
			sdCard.partition.firstDataSector);						// Hold the first sector of this new cluster		
		priv->sector = 0;											// Zero the sector count of this new cluster 
		priv->bPos = 0;												// Reset buffer position to top of buffer
		if (sdTransferBlocks(priv->firstSector + priv->sector, 
			1,	&priv->buffer[0], false) != SD_OK) 
		{
			if (ErrorID) *ErrorID = FAT_READSECTOR_FAIL;			// Check for read failure
			return NULL;											// Return null pointer
		}
	}
	if (ErrorID) *ErrorID = FAT_INVALID_DATAPTR;					// If valid pointer return error id
	return NULL;													// Find FAT entry failed
}

/*-[CopyUnAlignedString]----------------------------------------------------}
. Copy a string byte by byte allowing for unaligned allocation
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
static bool CopyUnAlignedString (char* Dest, const char* Src) {
	if ((Src) && (Dest)) {
		while (*Src) *Dest++ = *Src++;								// Transfer source byte to dest and increment pointers
		*Dest = '\0';												// Make sure Dest is terminated
		return true;												// Return success
	}
	return false;													// One of the pointers was invalid
}


/*==========================================================================}
{						 PUBLIC FILE SEARCH ROUTINES						}
{==========================================================================*/

/*-[sdFindFirstFile]--------------------------------------------------------}
. This is an exact replica of Windows FindFirst function but restricted to
. this SD card.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
HANDLE sdFindFirstFile (const char* lpFileName, FIND_DATA* lpFFData) 
{												
	uint32_t errID = FAT_RESULT_OK;
	struct dir_Structure* dir;
	int i = 0;
	LFN_NAME tempName;												// We will cut and chop name so we need local copy
	if ((lpFileName == 0) || (lpFFData == 0)) return 0;				// One of the pointers is invalid so fail
	CopyUnAlignedString(&tempName[0], lpFileName);					// Copy the name string as we will chop and change it
	const char* searchStr = &tempName[0];							// Search string starts as lpFilename
	if (searchStr[0] == '\\') searchStr++;							// We sometimes write root directory with leadslash .. remove it
	while ((intSearchRecord[i].firstSector != 0) && i < MAX_SEARCH_RECORDS) i++;
	if (i != MAX_SEARCH_RECORDS) {									// Empty search record found
		intSearchRecord[i].cluster = sdCard.partition.rootCluster;	// We are going to start on FAT which starts at rootCluster
		intSearchRecord[i].firstSector = getFirstSector(
			sdCard.partition.rootCluster,
			sdCard.partition.sectorPerCluster,
			sdCard.partition.firstDataSector);						// Hold the first sector of FAT32
		intSearchRecord[i].sector = 0;								// Zero sector count
		intSearchRecord[i].bPos = 0;								// Zero buffer position
		if (sdTransferBlocks(intSearchRecord[i].firstSector, 1,
			&intSearchRecord[i].buffer[0], false) == SD_OK) {
			char* p;
			do {
				p = strchr(searchStr, '\\');						// Check for sub-directory slash
				if (p) {											// Directory slash found	
					*p = '\0';										// Turn the slash into a terminate
					dir = LocateFATEntry(searchStr, &intSearchRecord[i],
						true, lpFFData->cFileName, &errID);			// Locate directory FAT entry
					if (dir == NULL) return 0;						// Did not find subdirectory
					intSearchRecord[i].cluster = (((uint32_t)dir->firstClusterHI) << 16) | dir->firstClusterLO;
					intSearchRecord[i].firstSector = getFirstSector(
						intSearchRecord[i].cluster,
						sdCard.partition.sectorPerCluster,
						sdCard.partition.firstDataSector);			// Hold the first sector
					intSearchRecord[i].sector = 0;					// Zero sector count
					intSearchRecord[i].bPos = 0;					// Buffer pos starts at top of buffer
					if (sdTransferBlocks(intSearchRecord[i].firstSector, 1,
						&intSearchRecord[i].buffer[0], false) != SD_OK)
						return 0;									// Some read error occured
					searchStr = p + 1;								// Search string now starts after directory
				}
			} while (p != NULL);
			dir = LocateFATEntry(searchStr, &intSearchRecord[i],
				false, lpFFData->cFileName, &errID);				// Locate first FAT entry
			if ((errID == FAT_RESULT_OK) && (dir)) {				// First FAT entry returned		
				int j = 0;
				while (searchStr[j] != 0) {
					intSearchRecord[i].searchPattern[j] = searchStr[j];// Hold the search pattern
					j++;
				}
				SetFindDataFromFATEntry(dir, lpFFData);				// Set all the find data fields from FAT entry
				return (i+1);										// Return internal search record index as handle
			}
		}
		intSearchRecord[i].firstSector = 0;							// Clear first sector which is used to mark in use
	}
	return 0;														// Return the handle and its error
}

/*-[sdFindNextFile]---------------------------------------------------------}
. Continues a file search from a previous call to the FindFirstFile.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
HANDLE sdFindNextFile (HANDLE hFindFile, FIND_DATA* lpFFData)
{
	uint32_t errID = FAT_RESULT_OK;
	struct dir_Structure* dir;
	if ((hFindFile > 0) && (hFindFile <= MAX_SEARCH_RECORDS) &&
		(intSearchRecord[hFindFile-1].firstSector != 0)) {			// File handle maps to an internal search record in use 
		dir = LocateFATEntry(&intSearchRecord[hFindFile-1].searchPattern[0],
			&intSearchRecord[hFindFile-1],
			false, lpFFData->cFileName, &errID);					// Locate first FAT entry
		if ((errID == FAT_RESULT_OK) && (dir)) {					// First FAT entry returned
			SetFindDataFromFATEntry(dir, lpFFData);					// Set all the find data fields from FAT entry
			return (hFindFile);										// Return internal serach record index as handle
		}
		intSearchRecord[hFindFile - 1].firstSector = 0;				// Clear first sector which is used to mark in use
	}
	return 0;														// Return the handle and its error
}

/*-[sdFindClose]------------------------------------------------------------}
. Closes and releases a file search handle opened by the FindFirstFile.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
bool sdFindClose (HANDLE hFindFile)
{
	if ((hFindFile > 0) && (hFindFile <= MAX_SEARCH_RECORDS) &&
		(intSearchRecord[hFindFile - 1].firstSector != 0)) {		// File handle maps to an internal search record in use 
		intSearchRecord[hFindFile - 1].firstSector = 0;				// Clear first sector which is used to mark in use
		return true;
	}
	return false;
}

/*==========================================================================}
{						 PUBLIC FILE IO ROUTINES							}
{==========================================================================*/


/*--------------------------------------------------------------------------}
{                        PRIVATE FILE IO DATA STRUCTURE		            }
{--------------------------------------------------------------------------*/
struct __attribute__((packed, aligned(4))) PRIV_FILE_IO_DATA {
	struct PRIV_SEARCH_DATA srec;									// Search record
	uint32_t fileStart;												// File start sector
	uint32_t filePos;												// Current file position
	uint32_t fileSize;												// Current file size
};

/*--------------------------------------------------------------------------}
{               INTERNAL FILE IO THAT ARE CURRENTLY RUNNING		            }
{--------------------------------------------------------------------------*/
#define MAX_FILE_IO_RECORDS 8										// Maximum number of file IO that can occur at same time
static struct PRIV_FILE_IO_DATA __attribute__((aligned(4))) intFileIORecord[MAX_FILE_IO_RECORDS] = { 0 };

/*-[sdCreateFile]-----------------------------------------------------------}
. Creates or opens a file or I/O device. The function returns a handle that 
. can be used to access the file for followup I/O operations. The file or 
. device will be openned with flags and attributes specified.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
HANDLE sdCreateFile (const char* lpFileName,						// Filename or device to open
					 uint32_t	dwDesiredAccess,					// GENERIC_READ or GENERIC_WRITE
					 uint32_t	dwShareMode,						// Currently not supported (use 0)
					 uint32_t	lpSecurityAttributes,				// Currently not supported (use 0)
					 uint32_t	dwCreationDisposition,				// CREATE_ALWAYS, CREATE_NEW, OPEN_EXISTING, OPEN_ALWAYS
					 uint32_t	dwFlagsAndAttributes,				// Standard file attributes
					 HANDLE hTemplateFile)							// Currently not supported (use 0)
{
	uint32_t errID = FAT_RESULT_OK;
	struct dir_Structure* dir;
	LFN_NAME openName;
	int i = 0;
	LFN_NAME tempName;												// We will cut and chop name so we need local copy
	if (lpFileName == 0) return 0;									// Name pointer is invalid so fail
	CopyUnAlignedString(&tempName[0], lpFileName);					// Make local copy of name as we will chop and change it
	const char* searchStr = &tempName[0];							// Search string starts as lpFilename
	if (searchStr[0] == '\\') searchStr++;							// We sometimes write root directory with lead slash .. remove it
	while ((intFileIORecord[i].srec.firstSector != 0) && i < MAX_FILE_IO_RECORDS) i++;
	if (i != MAX_FILE_IO_RECORDS) {									// Empty search record found
		intFileIORecord[i].srec.cluster = sdCard.partition.rootCluster;
		intFileIORecord[i].srec.firstSector = getFirstSector(
			sdCard.partition.rootCluster,
			sdCard.partition.sectorPerCluster,
			sdCard.partition.firstDataSector);						// Hold the first sector
		intFileIORecord[i].srec.sector = 0;							// Zero sector count
		intFileIORecord[i].srec.bPos = 0;							// Zero buffer position
		if (sdTransferBlocks(intFileIORecord[i].srec.firstSector, 1,
			&intFileIORecord[i].srec.buffer[0], false) == SD_OK)	// Read a sector of data
		{
			char* p;
			do {
				p = strchr(searchStr, '\\');						// Check for sub-directory slash
				if (p) {											// Directory slash found	
					*p = '\0';										// Turn the slash into a terminate
					dir = LocateFATEntry(searchStr, &intFileIORecord[i].srec,
						true, openName, &errID);					// Locate directory FAT entry
					if (dir == NULL) return 0;						// Did not find subdirectory
					intFileIORecord[i].srec.cluster = (((uint32_t)dir->firstClusterHI) << 16) | dir->firstClusterLO;
					intFileIORecord[i].srec.firstSector = getFirstSector(
						intFileIORecord[i].srec.cluster,
						sdCard.partition.sectorPerCluster,
						sdCard.partition.firstDataSector);			// Hold the first sector
					intFileIORecord[i].srec.sector = 0;				// Zero sector count
					intFileIORecord[i].srec.bPos = 0;				// Buffer pos starts at top of buffer
					if (sdTransferBlocks(intFileIORecord[i].srec.firstSector, 1,
						&intFileIORecord[i].srec.buffer[0], false) != SD_OK)
						return 0;									// Some read error occured
					searchStr = p + 1;								// Search string now starts after directory
				}
			} while (p != NULL);
			dir = LocateFATEntry(searchStr, &intFileIORecord[i].srec,
				false, openName, &errID);							// Locate file first FAT entry
			if ((errID == FAT_RESULT_OK) && (dir)) {				// First FAT matching entry returned
				intFileIORecord[i].fileStart = (((uint32_t)dir->firstClusterHI) << 16) | dir->firstClusterLO;
				intFileIORecord[i].filePos = 0;						// Zero file position
				intFileIORecord[i].fileSize = dir->fileSize;		// Transfer file size
				intFileIORecord[i].srec.cluster = intFileIORecord[i].fileStart;
				intFileIORecord[i].srec.firstSector = getFirstSector(
					intFileIORecord[i].srec.cluster,
					sdCard.partition.sectorPerCluster,
					sdCard.partition.firstDataSector);				// Hold the first sector
				intFileIORecord[i].srec.sector = 0;					// Zero sector count
				intFileIORecord[i].srec.bPos = 0;					// Buffer pos starts at top of buffer		
				if (sdTransferBlocks(intFileIORecord[i].srec.firstSector,
					1, &intFileIORecord[i].srec.buffer[0], false) == SD_OK) 
				{
					return (i + 1);									// Return internal file io record index as handle
				}
			}
		}
	}
	return 0;														// Create file failed
}


/*-[sdReadFile]-------------------------------------------------------------}
. Reads data from the specified file or input/output (I/O) device. The file
. must have been opened with CreateFile and the handle is returned from that
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
bool sdReadFile (HANDLE hFile,										// Handle as returned from CreateFile
				 void* lpBuffer,									// Pointer to buffer read data will be placed
				 uint32_t nNumberOfBytesToRead,						// Number of bytes requested to read
				 uint32_t* lpNumberOfBytesRead,						// Provide a pointer to a value which updated to bytes actually placed in buffer
				 void* lpOverlapped)								// Currently not supported (use 0)
{
	if ((hFile > 0) && (hFile <= MAX_FILE_IO_RECORDS) &&
		(intFileIORecord[hFile - 1].srec.firstSector != 0)) {		// File handle maps to an internal file io record in use 
		uint32_t bytesRead = 0;										// Zero bytes read
		uint32_t retErrId = FAT_RESULT_OK;							// Preset error id clear

		while ((retErrId == FAT_RESULT_OK) && (intFileIORecord[hFile - 1].srec.cluster < 0x0ffffff6)
			&& (intFileIORecord[hFile - 1].srec.cluster != 0)) {	// Check cluster valid and read successful	

			while (	(nNumberOfBytesToRead > bytesRead) &&			// Still more bytes to read
				(intFileIORecord[hFile - 1].filePos < intFileIORecord[hFile - 1].fileSize) && // Still inside fileSize
				(intFileIORecord[hFile - 1].srec.bPos <  512) )		// While not a buffer end
			{
				((uint8_t*)lpBuffer)[bytesRead] = intFileIORecord[hFile - 1].srec.buffer[intFileIORecord[hFile - 1].srec.bPos++]; // Transfer byte and increment pointer
				bytesRead++;										// Increment bytes read	
				intFileIORecord[hFile - 1].filePos++;				// Increment file position
			}
			if (nNumberOfBytesToRead == bytesRead) {				// Finished
				if (lpNumberOfBytesRead) *lpNumberOfBytesRead = bytesRead;
				return true;										// Read completed successfully
			}
			if (intFileIORecord[hFile - 1].filePos >= intFileIORecord[hFile - 1].fileSize) 
			{														// File end reached
				if (lpNumberOfBytesRead) *lpNumberOfBytesRead = bytesRead;
				return false;										// Read ended as file ran out of data
			}

			intFileIORecord[hFile - 1].srec.sector++;				// Increment sector
			if (intFileIORecord[hFile - 1].srec.sector < sdCard.partition.sectorPerCluster) // Next sector still in current cluster
			{
				intFileIORecord[hFile - 1].srec.bPos = 0;			// Reset buffer position to top of buffer
				if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector + intFileIORecord[hFile - 1].srec.sector,
					1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
					return false;									// Return read failure
				}
			} else {												// Need to move to next cluster
				intFileIORecord[hFile - 1].srec.cluster = getSetNextCluster(
					intFileIORecord[hFile - 1].srec.cluster, false, 0);// Fetch the next cluster
				intFileIORecord[hFile - 1].srec.firstSector = getFirstSector(
					intFileIORecord[hFile - 1].srec.cluster,
					sdCard.partition.sectorPerCluster,
					sdCard.partition.firstDataSector);				// Hold the first sector of this new cluster
				intFileIORecord[hFile - 1].srec.sector = 0;			// Zero the sector count 
				intFileIORecord[hFile - 1].srec.bPos = 0;			// Reset buffer position to top of buffer
				if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector + intFileIORecord[hFile - 1].srec.sector,
					1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
					return false;									// Return read failure
				}
			}
		}
	}
	return false;													// Return read failure
}
	
/*-[sdCloseHandle]----------------------------------------------------------}
. Closes file or device as per the handle that was given when it opened.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
bool sdCloseHandle (HANDLE hFile)
{
	if ((hFile > 0) && (hFile <= MAX_FILE_IO_RECORDS) &&
		(intFileIORecord[hFile - 1].srec.firstSector != 0)) {		// File handle maps to an internal file io record in use 
		intFileIORecord[hFile - 1].srec.firstSector = 0;			// Clear first sector which is used to mark in use
		return true;												// Return handle successfully closed
	}
	return false;													// Return handle did not close
}

/*-[sdSetFilePointer]-------------------------------------------------------}
. The function stores the file pointer in two LONG values. To work with file
. pointers that are larger than a single LONG value, it is easier to use the
. SetFilePointerEx function.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
uint32_t sdSetFilePointer (HANDLE hFile,							// Handle as returned from CreateFile
						   uint32_t lDistanceToMove,				// low order 32-bit signed value of bytes to move the file pointer
						   uint32_t* lpDistanceToMoveHigh,			// A pointer to high order 32-bits of the signed 64-bit distance to move
						   uint32_t dwMoveMethod)					// FILE_BEGIN, FILE_CURRENT, FILE_END
{
	if ((hFile > 0) && (hFile <= MAX_FILE_IO_RECORDS) &&
		(intFileIORecord[hFile - 1].srec.firstSector != 0)) {		// File handle maps to an internal file io record in use 

		switch (dwMoveMethod) {
			case FILE_CURRENT:										// Movement from current position
				lDistanceToMove += intFileIORecord[hFile - 1].filePos;// So simply add current position
				break;
			case FILE_END:											// Movement from end of file
				lDistanceToMove += intFileIORecord[hFile - 1].fileSize;// So simply add filesize
				break;
			default:												// Default will be file begin and do nothing
				break;
		}

		if (lDistanceToMove > intFileIORecord[hFile - 1].fileSize)	// You cant request a move larger than filesize
			return INVALID_SET_FILE_POINTER;						// Return set file position failure

		if (lDistanceToMove == intFileIORecord[hFile - 1].filePos)  // Request move is where we already are
			return (intFileIORecord[hFile - 1].filePos);			// So return position as if we did something

		intFileIORecord[hFile - 1].srec.cluster = intFileIORecord[hFile - 1].fileStart;
		intFileIORecord[hFile - 1].srec.firstSector = getFirstSector(
			intFileIORecord[hFile - 1].srec.cluster,
			sdCard.partition.sectorPerCluster,
			sdCard.partition.firstDataSector);						// Hold the first sector
		intFileIORecord[hFile - 1].srec.sector = 0;					// Zero sector count
		intFileIORecord[hFile - 1].srec.bPos = 0;					// Buffer pos starts at top of buffer	
		intFileIORecord[hFile - 1].filePos = 0;						// Zero file position
		if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector,
			1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
			return INVALID_SET_FILE_POINTER;						// Return set file position failure
		}

		while (lDistanceToMove > 512) {								// Position not in current sector
			intFileIORecord[hFile - 1].srec.sector++;				// Increment sector
			if (intFileIORecord[hFile - 1].srec.sector < sdCard.partition.sectorPerCluster) // Next sector still in current cluster
			{
				intFileIORecord[hFile - 1].srec.bPos = 0;			// Reset buffer position to top of buffer
				if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector + intFileIORecord[hFile - 1].srec.sector,
					1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
					return INVALID_SET_FILE_POINTER;				// Return set file position failure
				}
			} else {												// Need to move to next cluster
				intFileIORecord[hFile - 1].srec.cluster = getSetNextCluster(
					intFileIORecord[hFile - 1].srec.cluster, false, 0);  // Fetch the next cluster
				intFileIORecord[hFile - 1].srec.firstSector = getFirstSector(
					intFileIORecord[hFile - 1].srec.cluster, 
					sdCard.partition.sectorPerCluster, 
					sdCard.partition.firstDataSector);				// Hold the first sector of this new cluster
				intFileIORecord[hFile - 1].srec.sector = 0;			// Zero the sector count 
				intFileIORecord[hFile - 1].srec.bPos = 0;			// Reset buffer position to top of buffer
				if (sdTransferBlocks(intFileIORecord[hFile - 1].srec.firstSector + intFileIORecord[hFile - 1].srec.sector,
					1, &intFileIORecord[hFile - 1].srec.buffer[0], false) != SD_OK) {
					return INVALID_SET_FILE_POINTER;				// Return set file position failure
				}
			}
			lDistanceToMove -= 512;									// We have moved 512 bytes
			intFileIORecord[hFile - 1].filePos += 512;				// Increment file position
		}
		intFileIORecord[hFile - 1].srec.bPos = lDistanceToMove;		// Buffer position is whatever is left below 512
		intFileIORecord[hFile - 1].filePos += lDistanceToMove;		// Add left over movement to file position
		if (lpDistanceToMoveHigh) *lpDistanceToMoveHigh = 0;		// We don't use this
		return (intFileIORecord[hFile - 1].filePos);				// Return the file position
	}
	return INVALID_SET_FILE_POINTER;								// Return set file position failure
}


/*-[sdGetFileSize]----------------------------------------------------------}
. Retrieves the size of the specified file, in bytes. As we don't support
. individual file size beyond 4GB lpFileSizeHigh is unused.
. 23Feb17 LdB
.--------------------------------------------------------------------------*/
uint32_t sdGetFileSize (HANDLE  hFile, uint32_t* lpFileSizeHigh) {
	if ((hFile > 0) && (hFile <= MAX_FILE_IO_RECORDS) &&
		(intFileIORecord[hFile - 1].srec.firstSector != 0)) 		// File handle maps to an internal file io record in use
	{
		return(intFileIORecord[hFile - 1].fileSize);				// Return the file size
	}
	return 0;														// Function failed
}


/*-[sdInitCard]-------------------------------------------------------------}
. Attempts to initializes current SD Card and returns success/error status.
. This call should be done before any attempt to do anything with a SD card.
. RETURN: SD_OK indicates the current card successfully initialized.
.         !SD_OK if card initialize failed with code identifying error.
. 21Aug17 LdB
.--------------------------------------------------------------------------*/
SDRESULT sdInitCard (printhandler prn_basic, printhandler prn_error, bool mount)
{
	SDRESULT resp;

	// First ensure log error handler set
	LOG_ERROR = prn_error;											// Hold the provided print error handler

	// Reset the card.
	if( (resp = sdResetCard()) ) return resp;						// Reset SD card

	// Send SEND_IF_COND,0x000001AA (CMD8) voltage range 0x1 check pattern 0xAA
	// If voltage range and check pattern don't match, look for older card.
	resp = sdSendCommandA(IX_SEND_IF_COND,0x000001AA);
	if( resp == SD_OK )
	{
	// Card responded with voltage and check pattern.
	// Resolve voltage and check for high capacity card.
	if( (resp = sdAppSendOpCond(ACMD41_ARG_HC)) ) return sdDebugResponse(resp);

	// Check for high or standard capacity.
	if( sdCard.ocr.card_capacity )
		sdCard.type = SD_TYPE_2_HC;
	else
		sdCard.type = SD_TYPE_2_SC;
	}
	else if( resp == SD_BUSY ) return resp;
	// No response to SEND_IF_COND, treat as an old card.
	else
    {
		// If there appears to be a command in progress, reset the card.
		if( (EMMC_STATUS->CMD_INHIBIT) &&
			(resp = sdResetCard()) )
			return resp;

		// wait(50);
		// Resolve voltage.
		if( (resp = sdAppSendOpCond(ACMD41_ARG_SC)) ) return sdDebugResponse(resp);

		sdCard.type = SD_TYPE_1;
    }

	// Send ALL_SEND_CID (CMD2)
	if( (resp = sdSendCommand(IX_ALL_SEND_CID)) ) return sdDebugResponse(resp);

	// Send SEND_REL_ADDR (CMD3)
	// TODO: In theory, loop back to SEND_IF_COND to find additional cards.
	if( (resp = sdSendCommand(IX_SEND_REL_ADDR)) ) return sdDebugResponse(resp);

	// From now on the card should be in standby state.
	// Actually cards seem to respond in identify state at this point.
	// Check this with a SEND_STATUS (CMD13)
	//if( (resp = sdSendCommand(IX_SEND_STATUS)) ) return sdDebugResponse(resp);
	//printf("Card current state: %08x %s\n",sdCard.status,STATUS_NAME[sdCard.cardState]);

	// Send SEND_CSD (CMD9) and parse the result.
	if( (resp = sdSendCommand(IX_SEND_CSD)) ) return sdDebugResponse(resp);

	// At this point, set the clock to full speed.
	if( (resp = sdSetClock(FREQ_NORMAL)) ) return sdDebugResponse(resp);

	// Send CARD_SELECT  (CMD7)
	// TODO: Check card_is_locked status in the R1 response from CMD7 [bit 25], if so, use CMD42 to unlock
	// CMD42 structure [4.3.7] same as a single block write; data block includes
	// PWD setting mode, PWD len, PWD data.
	if( (resp = sdSendCommand(IX_CARD_SELECT)) ) return sdDebugResponse(resp);

	// Get the SCR as well.
	// Need to do this before sending ACMD6 so that allowed bus widths are known.
	if( (resp = sdReadSCR()) ) return sdDebugResponse(resp);

	// Send APP_SET_BUS_WIDTH (ACMD6)
	// If supported, set 4 bit bus width and update the CONTROL0 register.
	if (sdCard.scr.BUS_WIDTH == BUS_WIDTH_4)
	{
		if( (resp = sdSendCommandA(IX_SET_BUS_WIDTH, sdCard.rca | 2)) ) 
			return sdDebugResponse(resp);
		EMMC_CONTROL0->HCTL_DWIDTH = 1;
		LOG_DEBUG("EMMC: Bus width set to 4\n");
	}

	// Send SET_BLOCKLEN (CMD16)
	if( (resp = sdSendCommandA(IX_SET_BLOCKLEN,512)) ) return sdDebugResponse(resp);

	// Print out the CID having got this far.
	unsigned int serial = sdCard.cid.SerialNumHi;
	serial <<= 16;
	serial |= sdCard.cid.SerialNumLo;
	if (prn_basic) prn_basic("EMMC: SD Card %s %dMb mfr %d '%c%c:%c%c%c%c%c' r%d.%d %d/%d, #%08x RCA %04x\n",
		SD_TYPE_NAME[sdCard.type], (int)(sdCard.CardCapacity >> 20),
		sdCard.cid.MID, sdCard.cid.OID_Hi, sdCard.cid.OID_Lo,
		sdCard.cid.ProdName1, sdCard.cid.ProdName2, sdCard.cid.ProdName3, sdCard.cid.ProdName4, sdCard.cid.ProdName5,
		sdCard.cid.ProdRevHi, sdCard.cid.ProdRevLo, sdCard.cid.ManufactureMonth, 2000+sdCard.cid.ManufactureYear, serial,
		sdCard.rca >> 16);

	if (mount) if (!LoadDrivePartition(prn_basic)) return SD_MOUNT_FAIL;
	return SD_OK;
}

/*-[sdCardCSD]--------------------------------------------------------------}
. Returns the pointer to the CSD structure for the current SD Card.
. RETURN: Valid CSD pointer if the current card successfully initialized
.         NULL if current card initialize not called or failed.
. 21Aug17 LdB
.--------------------------------------------------------------------------*/
struct CSD* sdCardCSD (void) {
	if (sdCard.type != SD_TYPE_UNKNOWN) return (&sdCard.csd);		// Card success so return structure pointer
		else return NULL;											// Return fail result of null
}

M sd/hal.h => sd/hal.h +4 -4
@@ 23,7 23,7 @@ extern void dummy(uint32_t);
#define GPIO_FUNC_ALT5  2
#define GPIO_PULL_OFF   0
#define GPIO_PULL_DOWN  1
#define GPIO_PULL UP    2
#define GPIO_PULL_UP    2

static inline void HAL_GPIO_FSEL(uint8_t pin, uint8_t function) {
    uint32_t gpfsel = GPFSEL0 + ((pin / 10) * 4);


@@ 35,11 35,11 @@ static inline void HAL_GPIO_FSEL(uint8_t pin, uint8_t function) {
static inline void HAL_GPIO_SET_PULL(uint8_t pin, uint8_t pull) {
    uint8_t ra;
    uint32_t gppudclk = (pin < 32) ? GPPUDCLK0 : GPPUDCLK1;
    PUT32(GPPUD,0);
    PUT32(GPPUD,pull);
    for(ra=0;ra<150;ra++) dummy(ra);
    PUT32(gppudclk, (pull << (pin % 32)));
    PUT32(gppudclk, (1 << (pin % 32)));
    for(ra=0;ra<150;ra++) dummy(ra);
    PUT32(GPPUDCLK0,0);
    PUT32(gppudclk, 0);
}

#define AUX_ENABLES     0x20215004

M sd/memmap => sd/memmap +1 -1
@@ 1,7 1,7 @@

MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 0x1000
    ram : ORIGIN = 0x8000, LENGTH = 0xF000
}

SECTIONS

M sd/sd.c => sd/sd.c +130 -38
@@ 2,12 2,14 @@

/* ------------------------- EMMC -----*/
#define EMMC_BASE       0x20300000
#define EMMC_BLKSIZECNT EMMC_BASE + 0x4
#define EMMC_ARG1       EMMC_BASE + 0x8
#define EMMC_CMDTM      EMMC_BASE + 0xc
#define EMMC_RESP0      EMMC_BASE + 0x10
#define EMMC_RESP1      EMMC_BASE + 0x14
#define EMMC_RESP2      EMMC_BASE + 0x18
#define EMMC_RESP3      EMMC_BASE + 0x1C
#define EMMC_DATA       EMMC_BASE + 0x20
#define EMMC_STATUS     EMMC_BASE + 0x24
#define EMMC_CONTROL0   EMMC_BASE + 0x28
#define EMMC_CONTROL1   EMMC_BASE + 0x2C


@@ 69,6 71,23 @@ void delay_ms(uint32_t ms) {
    }
}

void uart_itoa(uint32_t i) {
/*
 * Extremely inefficient itoa implementation!
 */
    uint8_t leaded = 0;
    // max digit is 10
    for (int j = 0; j < 10; j++) {
        uint32_t _i = i;
        for ( int k = 10; k > j+1; k--) _i /= 10;

        _i = _i % 10;
        if (_i == 0 && !leaded) continue;
        HAL_UART_PUTC(_i + 48);
        leaded = 1;
    }
    if (!leaded) HAL_UART_PUTC('0');
}

void sd_set_clock(uint32_t freq) {
    uart_puts("WAITING FOR CLEARANCE");


@@ 98,30 117,40 @@ void sd_set_clock(uint32_t freq) {
    uart_puts("CLOCK SET!");
}

void sd_wait_for_interrupt(uint32_t mask) {
uint8_t sd_wait_for_interrupt(uint32_t mask) {
    uint32_t tMask = mask | INT_ERROR_MASK;
    uint32_t ra = GET32(EMMC_INTERRUPT);
    while (!(ra & tMask)) ra = GET32(EMMC_INTERRUPT);

    if ((ra & INT_CMD_TIMEOUT) | (ra & INT_DATA_TIMEOUT)) {
        uart_puts("timeout!"); PANIC();
        uart_puts("timeout!");
        uart_itoa(ra);
        uart_puts(" ra");
        uint32_t resp0 = GET32(EMMC_RESP0);
        uart_itoa(resp0); uart_puts(" RESP0");
        PUT32(EMMC_INTERRUPT, resp0);
        return 1;
    }
    if (ra & INT_ERROR_MASK) {
        uart_puts("error!"); PANIC();
    }
    PUT32(EMMC_INTERRUPT, mask); // clear interrupt
    return 0;
}

void sd_send_cmd(uint8_t cmd_index, uint8_t rsp_type, uint32_t arg) {
void sd_send_cmd(uint8_t cmd_index, uint8_t rsp_type, uint32_t arg, uint8_t read) {
    // Does not process response!
    uart_puts("WAITING FOR CLEARANCE");
    while (GET32(EMMC_STATUS) & (1<<0));

    PUT32(EMMC_INTERRUPT, GET32(EMMC_INTERRUPT)); // Clear interrupt
    PUT32(EMMC_ARG1, arg);
    PUT32(EMMC_CMDTM, (cmd_index << 24) | (rsp_type <<16));

    sd_wait_for_interrupt(INT_CMD_DONE);
    while (1) {
        uart_itoa(cmd_index);
        uart_puts(" DOING CMD");
        while (GET32(EMMC_STATUS) & (1<<0));
        uart_puts("CLEARED");
        PUT32(EMMC_INTERRUPT, GET32(EMMC_INTERRUPT)); // Clear interrupt
        PUT32(EMMC_ARG1, arg);
        PUT32(EMMC_CMDTM, (cmd_index << 24) | (rsp_type <<16) | (read << 21) | (read << 4));

        if(!sd_wait_for_interrupt(INT_CMD_DONE)) return;
    }
}

void sd_reset_card() {


@@ 134,7 163,7 @@ void sd_reset_card() {
    while(GET32(EMMC_CONTROL1) & (1<<24)); // Wait until SRST_HC is cleared
    uart_puts("RESET OK!");

    PUT32(EMMC_CONTROL1, (0xE <<16)); // DATA_TOUNIT
    PUT32(EMMC_CONTROL1, (0xF <<16)); // DATA_TOUNIT
    PUT32(EMMC_CONTROL1, 1); // CLK_INTLEN
    HAL_TIMER_WAIT(); HAL_TIMER_WAIT();



@@ 144,34 173,18 @@ void sd_reset_card() {
	PUT32(EMMC_IRPT_EN, 0xFFFFFFFF);
	PUT32(EMMC_IRPT_MASK, 0xFFFFFFFF);

    sd_send_cmd(0x00, CMD_NO_RESP, 0);
    sd_send_cmd(0x00, CMD_NO_RESP, 0, 0);
    uart_puts("RESET COMPLETE");
}

void uart_itoa(uint32_t i) {
/*
 * Extremely inefficient itoa implementation!
 */
    uint8_t leaded = 0;
    // max digit is 10
    for (int j = 0; j < 10; j++) {
        uint32_t _i = i;
        for ( int k = 10; k > j+1; k--) _i /= 10;

        _i = _i % 10;
        if (_i == 0 && !leaded) continue;
        HAL_UART_PUTC(_i + 48);
        leaded = 1;
    }
    if (!leaded) HAL_UART_PUTC('0');
}
uint32_t rca;

void sd_init() {
    uart_puts("INITING SD");

	// Send SEND_IF_COND,0x000001AA (CMD8) voltage range 0x1 check pattern 0xAA
	// If voltage range and check pattern don't match, look for older card.
    sd_send_cmd(0x08, CMD_48BIT_RESP, 0x000001AA);
    sd_send_cmd(0x08, CMD_48BIT_RESP, 0x000001AA, 0);
    uint32_t arg = (GET32(EMMC_RESP0) == 0x000001AA) ? ACMD41_ARG_HC : ACMD41_ARG_SC; // Type 2 : Type 1
    if (arg == ACMD41_ARG_SC) {
        uart_puts("NOT SUPPORTING SC CARD DUE TO PARTIAL IMPLEMENTATION"); PANIC(); }


@@ 180,25 193,27 @@ void sd_init() {
    while (!(ra & ((uint32_t) 1<<31))) {
        delay_ms(400000);
        uart_puts("POWERING UP...");
        sd_send_cmd(0x37, CMD_NO_RESP, 0); // Prepare for APP CMD
        sd_send_cmd(0x37, CMD_NO_RESP, 0, 0); // Prepare for APP CMD
        delay_ms(100);
        sd_send_cmd(0x29, CMD_48BIT_RESP, arg);
        sd_send_cmd(0x29, CMD_48BIT_RESP, arg, 0);
        delay_ms(1000);
        ra = GET32(EMMC_RESP0); // SD OCR register
    }
    uart_puts("POWERED UP");
    if (!(ra & (1<<30))) {//check the capacity bit if HC
        uart_puts("NOT SUPPORTING SC CARD DUE TO PARTIAL IMPLEMENTATION"); PANIC(); }
    // check for voltage


    sd_send_cmd(0x02, CMD_136BIT_RESP, 0); // Get the CID
    sd_send_cmd(0x02, CMD_136BIT_RESP, 0, 0); // Get the CID
    //GET32(EMMC_RESP0); GET32(EMMC_RESP1); GET32(EMMC_RESP2); GET32(EMMC_RESP3);

    sd_send_cmd(0x03, CMD_48BIT_RESP, 0); // Get RCA
    uint32_t rca = GET32(EMMC_RESP0) &  0xffff0000;
    sd_send_cmd(0x03, CMD_48BIT_RESP, 0, 0); // Get RCA
    rca = GET32(EMMC_RESP0) &  0xffff0000;
    uart_itoa(rca);
    uart_puts(" RCA");

    sd_send_cmd(0x09, CMD_136BIT_RESP, rca); // Get CSD, note that data seems to be in weird order
    sd_send_cmd(0x09, CMD_136BIT_RESP, rca, 0); // Get CSD, note that data seems to be in weird order
    uint8_t buf[16] = { 0 };
	__attribute__((aligned(4))) uint32_t* p;
    p = (uint32_t*) &buf[12];


@@ 224,7 239,38 @@ void sd_init() {

    sd_set_clock(FREQ_NORMAL);

    sd_send_cmd(0x07, CMD_BUSY48BIT_RESP, rca);
    sd_send_cmd(0x07, CMD_BUSY48BIT_RESP, rca, 0); // SELECT
    ra = GET32(EMMC_STATUS);
    while (ra & (1<<0) || ra & (1<<1)) ra = GET32(EMMC_STATUS);
    uart_puts("SELECTED");


    // READ SCR
    /*
    while (1) {
        uart_puts("GETTING SCR");
        while (GET32(EMMC_STATUS) & (1<<1)); // WAIT FOR DATA
        PUT32(EMMC_BLKSIZECNT, (1<<16) | 8);
        sd_send_cmd(0x37, CMD_NO_RESP, 0, 0); // Prepare for APP CMD
        delay_ms(100);

        sd_send_cmd(0x33, CMD_48BIT_RESP, rca, 1);

        if (!sd_wait_for_interrupt(INT_READ_RDY)) break;
    }
    uart_puts("GOOD");

    while(!(GET32(EMMC_STATUS) & (1 <<9)));
    GET32(EMMC_DATA); // useless
    while(!(GET32(EMMC_STATUS) & (1 <<9)));
    uint32_t SCR_HI = GET32(EMMC_DATA);
    uint8_t bwidth = (SCR_HI & (0xF << 16))>>16;
    uart_itoa(bwidth);
    uart_puts(" BWIDTH");*/

    /*
    sd_send_cmd(0x06, CMD_48BIT_RESP, rca | 2, 0); // BUS WIDTH 4
    PUT32(EMMC_CONTROL0, (1<<1));*/

    // attempt to use 1bit bus width (default)
    // Assume HC+ card, blocklen is 512


@@ 232,15 278,61 @@ void sd_init() {

}

void sd_read_block(uint32_t block, uint8_t* buffer) {
/*
 * Single block reading
 */
    while(1) {
    PUT32(EMMC_INTERRUPT, 0xFFFFFFFF);
    uart_puts("READ...");
    while (GET32(EMMC_STATUS) & (1<<1));

    // Assume HC addressing !!
    PUT32(EMMC_BLKSIZECNT, (1 << 16) | 128); // 1 block of 512b
    sd_send_cmd(0x11, CMD_48BIT_RESP, block, 1);
    uart_puts("SUBMITTED");
    uart_itoa(GET32(EMMC_RESP0));

    // does not need to loop since we handling 1 block only

    if (!sd_wait_for_interrupt(INT_READ_RDY)) break;
    }
    // assume word aligned buffer
    uint32_t* pbuf = (uint32_t*) buffer;
    for (uint_fast16_t i = 0; i < 32; i++)
        pbuf[i] = GET32(EMMC_DATA);
    uart_puts("READ OK");
}

int notmain()
{

    HAL_UART_INIT();
    for (int i = 48; i < 54; i++) {
        uart_itoa(i); uart_puts(" GPIO");
        HAL_GPIO_SET_PULL(i, GPIO_PULL_UP);
        HAL_GPIO_FSEL(i, GPIO_FUNC_ALT3);
    }
    uart_itoa(GET32(0x20200010)); uart_puts(" FSEL4");
    uart_itoa(GET32(0x20200014)); uart_puts(" FSEL5");
    if (!(GET32(0x20200038) & (1<<(47-32)))) {
        uart_puts("NO CARD WTF"); PANIC();
    }

    HAL_TIMER_INIT(10); // 10 microsecs
    uart_puts("HELO SD");

    sd_reset_card();
    sd_init();

    // FAT32 handling
	uint8_t buffer[512] __attribute__((aligned(4)));
    sd_read_block(1, buffer);
    for (int i = 3; i < 3 + 8; i++)
        HAL_UART_PUTC(buffer[i]);
    uart_puts(" nice");

    while(1);

    return(0);
}