[メモ] STM32CubeMX: USB MassStorageデバイス

## 概要
- STM32CubeMXを使って、USB MassStorageを使ってみる。
- USBを使うときは、外付けのOscillator/Xtalが必要。(48MHzを作るのに、内部のやつは精度がでない？)
- `usbd_storage_if.c`だけ変更. 今回は、ReadOnly.


![Qiita-STM32_USBMS03.png](https://qiita-image-store.s3.amazonaws.com/0/75594/1015b358-59fc-3ecb-f769-edad6c018ca9.png) ![Qiita-STM32_USBMS01.png](https://qiita-image-store.s3.amazonaws.com/0/75594/37809449-92e1-fcee-31b5-9e7f4998564e.png)
![Qiita-STM32_USBMS02.png](https://qiita-image-store.s3.amazonaws.com/0/75594/d2fd66be-c504-479e-95c1-4e7f2d8947a5.png)
![Qiita-STM32_USBMS04.png](https://qiita-image-store.s3.amazonaws.com/0/75594/da8fe67f-3ec6-2355-49b8-aee2c14b87c4.png)

## 環境
- STM32L1系
- ビルド環境
    - Windows7 64bit
    - MDK-ARM Lite v5.20
    - STM32CubeMX v4.18.0
        - ボードが動くくらいの設定(クロックとか、GPIOとか)
        - FreeRTOS : [v] Enabled (Lチカにつかった程度)
        - USB_DEVICE : Class for FS IP `Mass Storage Class`
        - USB : [v] Device (FS)<br>=> コード生成
    - Firmware Package for Family STM32L1 v1.6.0


## 大体の説明

- コールバック
ホストに接続すると、コールバックが呼ばれるので、うまく応答すればよい。


    ```c:usbd_storage_if.c
...
	USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
	{
	  STORAGE_Init_FS,
	  STORAGE_GetCapacity_FS,
	  STORAGE_IsReady_FS,
	  STORAGE_IsWriteProtected_FS,
	  STORAGE_Read_FS,
	  STORAGE_Write_FS,
	  STORAGE_GetMaxLun_FS,
	  (int8_t *)STORAGE_Inquirydata_FS,
	};
...
```

- ディスクの容量は、`STORAGE_BLK_NBR`にセクタ数定義する。<br>今回、`#define STORAGE_BLK_NBR 0x81000`としたので、<br>=> 0x81000 * 512bytes/sector = 258MBくらいのディスク
- コールバックは、だいたい、`STORAGE_Read_FS`で、セクタのデータを要求してくるので、そいつをかえせばいい。
    - ↓の実装では、`_ReadSector()`に飛ばしている。
    - `_ReadSector()`で、要求されたセクタ番号で、MBR, PBR, FAT, ROOT_DIR, DATAの領域で、処理を分けている。
        - MBR,PBRは、固定値を用意して、memcpy
        - FAT, ROOTDIR, DATAは、Offsetを引いて、処理関数(`handleFatClusterChain`,`handleRoot`,`handleData`)へ飛ばして、うまくデータを詰める



## 実際のコード

- もともとのコードの変更箇所

    ```c:usbd_storage_if.c
	...
	#define STORAGE_LUN_NBR                  1  
	#define STORAGE_BLK_NBR                  0x81000  //##mt08
	#define STORAGE_BLK_SIZ    

	...

	int8_t  STORAGE_IsWriteProtected_FS (uint8_t lun)
	{
	  /* USER CODE BEGIN 5 */ 
	  return (USBD_FAIL); //##mt08: Read Only
	  /* USER CODE END 5 */ 
	}

	...
	int8_t STORAGE_Read_FS (uint8_t lun, 
							uint8_t *buf, 
							uint32_t blk_addr,                       
							uint16_t blk_len)
	{
	  /* USER CODE BEGIN 6 */ 
	  _ReadSector(buf, blk_addr, blk_len); //##mt08
	  return (USBD_OK);
	  /* USER CODE END 6 */ 
	}
```


### 追加コード
- 型宣言

    ```c
	#include <stdint.h>
	typedef uint8_t Byte;
	typedef struct MasterBootRecord {
			Byte    checkRoutionOnx86[446];
			struct {
					Byte    bootDescriptor;             /* 0x80: bootable device, 0x00: non-bootable */
					Byte    firstPartitionSector[3];    /* 1st sector number */
					Byte    fileSystemDescriptor;       /* 1:FAT12, 4:FAT16(less than 32MB), 5:Extended-DOS Partition,
																							6:FAT16(more 32MB), 0xb:FAT32(more 2GB),
																							0xc:FAT32 Int32h, 0xe:FAT16 Int32h,
																							0xf:5:Extended-DOS Partition Int32h */
					Byte    lastPartitionSector[3];
					Byte    firstSectorNumbers[4];      /* first sector number (link to BPB sector) */
					Byte    numberOfSectors[4];
			}   partitionTable[4];
			Byte    sig[2];                         /* 0x55, 0xaa */
	}   MBRecord;

	typedef struct FAT16BPB_t {
			/* FAT16 or FAT12 BPB */
			Byte    jmpOpeCode[3];          /* 0xeb ?? 0x90 */
			Byte    OEMName[8];
			/* FAT16 */
			Byte    bytesPerSector[2];      /* bytes/sector */
			Byte    sectorsPerCluster;      /* sectors/cluster */
			Byte    reservedSectors[2];     /* reserved sector, beginning with sector 0 */
			Byte    numberOfFATs;           /* file allocation table */
			Byte    rootEntries[2];         /* root entry (512) */
			Byte    totalSectors[2];        /* partion total secter */
			Byte    mediaDescriptor;        /* 0xf8: Hard Disk */
			Byte    sectorsPerFAT[2];       /* sector/FAT (FAT32 always zero: see bigSectorsPerFAT) */
			Byte    sectorsPerTrack[2];     /* sector/track (not use) */
			Byte    heads[2];               /* heads number (not use) */
			Byte    hiddenSectors[4];       /* hidden sector number */
			Byte    bigTotalSectors[4];     /* total sector number */
																			/* info */
			Byte    driveNumber;
			Byte    unused;
			Byte    extBootSignature;
			Byte    serialNumber[4];
			Byte    volumeLabel[11];
			Byte    fileSystemType[8];      /* "FAT16   " */
			Byte    loadProgramCode[448];
			Byte    sig[2];                 /* 0x55, 0xaa */
	}   BPBlock; // BIOS Parameter Block


	typedef struct DirEntry_t {
			Byte    name[8];            /* file name */
			Byte    extension[3];       /* file name extension */
			Byte    attribute;          /* file attribute
																	bit 4    directory flag
																	bit 3    volume flag
																	bit 2    hidden flag
																	bit 1    system flag
																	bit 0    read only flag */
			Byte    reserved;           /* use NT or same OS */
			Byte    createTimeMs;       /* VFAT 10millsec (0   199) */
			Byte    createTime[2];      /* VFAT */
			Byte    createDate[2];      /* VFAT */
			Byte    accessDate[2];      /* VFAT */
			Byte    clusterHighWord[2]; /* FAT32 MSB 16 bits */
			Byte    updateTime[2];
			Byte    updateDate[2];
			Byte    cluster[2];         /* start cluster number */
			Byte    fileSize[4];        /* file size in bytes (directory is always zero) */
	}   DirEntry;

	#pragma anon_unions
	typedef struct _DirEntTime {
			union {
					uint16_t W;
					struct {
							uint16_t second : 5;
							uint16_t minutes : 6;
							uint16_t hour : 5;
					} B;
			};
	} DirEntTime;

	typedef struct _DirEntDate {
			union {
					uint16_t W;
					struct {
							uint16_t day : 5;
							uint16_t month : 4;
							uint16_t year : 7;
					} B;
			};
	} DirEntDate;
	#pragma no_anon_unions
```

- 固定値: MBRとか、PBSとか。<br>(てきとうなUSBフラッシュメモリで、パーティション切って、フォーマットして、ダンプして、必要なとこを入力)

    ```c:
	const MBRecord sectMBR = {
		.checkRoutionOnx86 = { 0x00 },
		.partitionTable = {
			{
				.bootDescriptor = 0x00,
				.firstPartitionSector = { 0x02, 0x21, 0x00 },
				.fileSystemDescriptor = 0x06, //FAT16
				.lastPartitionSector = { 0xC2, 0x22, 0x20 },
				.firstSectorNumbers = { 0x00, 0x08, 0x00, 0x00 },
				.numberOfSectors = { 0x00, 0x00, 0x08, 0x00 },
			},//[0]
			{ 0 },//[1]
			{ 0 },//[2]
			{ 0 },//[3]
	},
	.sig = { 0x55, 0xAA },
	};
	const BPBlock sectBPB = {
		.jmpOpeCode = { 0xEB, 0x00, 0x90 },
		.OEMName = { ' ',' ',' ',' ',' ',' ',' ',' ' },
		.bytesPerSector = { 0x00, 0x02 },
		.sectorsPerCluster = 0x08, // 4KB/sectors
		.reservedSectors = { 0x08, 0x00 },
		.numberOfFATs = 0x02,
		.rootEntries = { 0x00, 0x02 },
		.totalSectors = { 0x00, 0x00 },
		.mediaDescriptor = 0xF8, // HDD
		.sectorsPerFAT = { 0x00, 0x01 },
		.sectorsPerTrack = { 0x3F,0x00 },
		.heads = { 0xFF,0x00 },
		.hiddenSectors = { 0x00, 0x08, 0x00, 0x00 },
		.bigTotalSectors = { 0x00,0x00,0x08, 0x00 },
		.driveNumber = 0x80,
		.unused = 0,
		.extBootSignature = 0x29,
		.serialNumber = { 0x78,0x56,0x34,0x12 },
		.volumeLabel = { 'N','O',' ','N','A','M','E',' ',' ',' ',' ' },
		.fileSystemType = { 'F','A','T','1','6',' ',' ',' ' },
		.loadProgramCode = { 0 },
		.sig = { 0x55, 0xAA },
	};

	#define SECTOR_MBR  (0x0000)
	#define SECTOR_PBR  (0x0800)
	#define SECTOR_FAT1 (0x0808)
	#define SECTOR_FAT2 (0x0908)
	#define SECTOR_ROOT (0x0A08)
	#define SECTOR_DATA (0x0A28)
```

- セクタ読み出しで、それっぽいデータをわたすとこ。

   ```
	void _handleFatClusterChain(uint32_t sect_offset, uint8_t *buf)
	{
		uint16_t *bufW = (uint16_t *)&buf[0];
		if (sect_offset == 0)
		{
			bufW[0] = 0xfff8;
			bufW[1] = 0xffff;
			bufW[2] = 0xffff; //最初のファイル. 1クラスタでおわり.
		}
	}

	void _handleRoot(uint32_t sect_offset, uint8_t *buf)
	{
		// 1 sector(512bytes) has 16 entries
		DirEntry *pDir = (DirEntry *)buf;
		if (sect_offset == 0)
		{
			memset(pDir, 0x00, sizeof(DirEntry));
			sprintf((char *)pDir->name, "TEXT_123");
			pDir->extension[0] = 'T';
			pDir->extension[1] = 'X';
			pDir->extension[2] = 'T';
			pDir->attribute = 0x00;
			{
				DirEntTime *pT = (DirEntTime *)&pDir->updateTime[0];
				DirEntDate *pD = (DirEntDate *)&pDir->updateDate[0];
				pT->B.hour = 12;
				pT->B.minutes = 34;
				pT->B.second = 56 / 2;
				pD->B.year = 2017 - 1980;
				pD->B.month = 1;
				pD->B.day = 12;
			}

			*(uint16_t*)&pDir->cluster = 0x0002;
			*(uint32_t*)&pDir->fileSize = 123;
		}
	}

	void _handleData(uint32_t sect_offset, uint8_t *buf)
	{
		memset(buf, 'A', 512);
		sprintf((char *)buf, "Hello World!\r\n");
		buf[14]='>';
	}

	uint32_t _ReadSector(uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
	{
		switch (blk_addr)
		{
		case SECTOR_MBR:
			memcpy(buf, (const void *)&sectMBR, 512);
			break;
		case SECTOR_PBR:
			memcpy(buf, (const void *)&sectBPB, 512);
			break;
		default:
			memset(buf, 0x00, 512);
			//FAT cluster chain
			if ((SECTOR_FAT1 <= blk_addr) && (blk_addr < SECTOR_ROOT))
			{
				if (blk_addr >= SECTOR_FAT2) { blk_addr -= (SECTOR_FAT2 - SECTOR_FAT1); }
				_handleFatClusterChain(blk_addr - SECTOR_FAT1, buf);

			}
			else if ((SECTOR_ROOT <= blk_addr) && (blk_addr < SECTOR_DATA))
			{
				_handleRoot(blk_addr - SECTOR_ROOT, buf);

			}
			else if (SECTOR_DATA <= blk_addr)
			{
				_handleData(blk_addr - SECTOR_DATA, buf);

			}
			break;
		}
		return 0;
	}
```

## その他
- 4KB/clusterにしてるのは、STM32の内蔵FLASHが4KB/sectorなので。<br>で、256MBくらいのパーティションで、FAT16フォーマットすると、4KB/clusterになる。<br>(ファイルシステムのクラスタサイズと、フラッシュメモリの物理セクタサイズがちがうと、管理が大変だよね...`;_;`)
- EEPROMにファイル情報(ROOTDIRに入るような情報=>FATチェーン生成)、FLASHにデータのみ、という感じで使用しようかと。



---
### STM32L1xx firmware V1.8.0にて
(2018-01-01追記)

```c:memo-usbd_storage_if.cのうまいとこにこぴぺする
#include <stdint.h>
typedef uint8_t Byte;
typedef struct MasterBootRecord {
        Byte    checkRoutionOnx86[446];
        struct {
                Byte    bootDescriptor;             /* 0x80: bootable device, 0x00: non-bootable */
                Byte    firstPartitionSector[3];    /* 1st sector number */
                Byte    fileSystemDescriptor;       /* 1:FAT12, 4:FAT16(less than 32MB), 5:Extended-DOS Partition,
                                                                                        6:FAT16(more 32MB), 0xb:FAT32(more 2GB),
                                                                                        0xc:FAT32 Int32h, 0xe:FAT16 Int32h,
                                                                                        0xf:5:Extended-DOS Partition Int32h */
                Byte    lastPartitionSector[3];
                Byte    firstSectorNumbers[4];      /* first sector number (link to BPB sector) */
                Byte    numberOfSectors[4];
        }   partitionTable[4];
        Byte    sig[2];                         /* 0x55, 0xaa */
}   MBRecord;

typedef struct FAT16BPB_t {
        /* FAT16 or FAT12 BPB */
        Byte    jmpOpeCode[3];          /* 0xeb ?? 0x90 */
        Byte    OEMName[8];
        /* FAT16 */
        Byte    bytesPerSector[2];      /* bytes/sector */
        Byte    sectorsPerCluster;      /* sectors/cluster */
        Byte    reservedSectors[2];     /* reserved sector, beginning with sector 0 */
        Byte    numberOfFATs;           /* file allocation table */
        Byte    rootEntries[2];         /* root entry (512) */
        Byte    totalSectors[2];        /* partion total secter */
        Byte    mediaDescriptor;        /* 0xf8: Hard Disk */
        Byte    sectorsPerFAT[2];       /* sector/FAT (FAT32 always zero: see bigSectorsPerFAT) */
        Byte    sectorsPerTrack[2];     /* sector/track (not use) */
        Byte    heads[2];               /* heads number (not use) */
        Byte    hiddenSectors[4];       /* hidden sector number */
        Byte    bigTotalSectors[4];     /* total sector number */
                                                                        /* info */
        Byte    driveNumber;
        Byte    unused;
        Byte    extBootSignature;
        Byte    serialNumber[4];
        Byte    volumeLabel[11];
        Byte    fileSystemType[8];      /* "FAT16   " */
        Byte    loadProgramCode[448];
        Byte    sig[2];                 /* 0x55, 0xaa */
}   BPBlock; // BIOS Parameter Block

typedef struct DirEntry_t {
        Byte    name[8];            /* file name */
        Byte    extension[3];       /* file name extension */
        Byte    attribute;          /* file attribute
                                                                bit 4    directory flag
                                                                bit 3    volume flag
                                                                bit 2    hidden flag
                                                                bit 1    system flag
                                                                bit 0    read only flag */
        Byte    reserved;           /* use NT or same OS */
        Byte    createTimeMs;       /* VFAT 10millsec (0   199) */
        Byte    createTime[2];      /* VFAT */
        Byte    createDate[2];      /* VFAT */
        Byte    accessDate[2];      /* VFAT */
        Byte    clusterHighWord[2]; /* FAT32 MSB 16 bits */
        Byte    updateTime[2];
        Byte    updateDate[2];
        Byte    cluster[2];         /* start cluster number */
        Byte    fileSize[4];        /* file size in bytes (directory is always zero) */
}   DirEntry;

#pragma anon_unions
typedef struct _DirEntTime {
        union {
                uint16_t W;
                struct {
                        uint16_t second : 5;
                        uint16_t minutes : 6;
                        uint16_t hour : 5;
                } B;
        };
} DirEntTime;

typedef struct _DirEntDate {
        union {
                uint16_t W;
                struct {
                        uint16_t day : 5;
                        uint16_t month : 4;
                        uint16_t year : 7;
                } B;
        };
} DirEntDate;
#pragma no_anon_unions

/*
mkfs.vfat -v /dev/loop0p1 -s 1 -S 512 -r 512 -f 2 -n COURTMATICS
mkfs.fat 4.1 (2017-01-24)
/dev/loop0p1 has 64 heads and 32 sectors per track,
hidden sectors 0x0000;
logical sector size is 512,
using 0xf8 media descriptor, with 65535 sectors;
drive number 0x80;
filesystem has 2 16-bit FATs and 1 sector per cluster.
FAT size is 254 sectors, and provides 64994 clusters.
There is 1 reserved sector.
Root directory contains 512 slots and uses 32 sectors.
Volume ID is 935c69b7, volume label COURTMATICS.
*/
#define SECTOR_MBR  (0x0000)
#define SECTOR_PBR  (0x0001)
#define SECTOR_FAT1 (0x0002)
#define SECTOR_FAT2 (0x0100)
#define SECTOR_ROOT (0x01FE)
#define SECTOR_DATA (0x021E)
const unsigned char sectMBR[] = {
	0xFA,0xB8,0x00,0x10,0x8E,0xD0,0xBC,0x00,0xB0,0xB8,0x00,0x00,0x8E,0xD8,0x8E,0xC0,
	0xFB,0xBE,0x00,0x7C,0xBF,0x00,0x06,0xB9,0x00,0x02,0xF3,0xA4,0xEA,0x21,0x06,0x00,
	0x00,0xBE,0xBE,0x07,0x38,0x04,0x75,0x0B,0x83,0xC6,0x10,0x81,0xFE,0xFE,0x07,0x75,
	0xF3,0xEB,0x16,0xB4,0x02,0xB0,0x01,0xBB,0x00,0x7C,0xB2,0x80,0x8A,0x74,0x01,0x8B,
	0x4C,0x02,0xCD,0x13,0xEA,0x00,0x7C,0x00,0x00,0xEB,0xFE,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xB8,0x1A,0x6B,0x10,0x00,0x00,0x00,0x00,
	0x02,0x00,0x0C,0x14,0x10,0x04,0x01,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xAA
};
const unsigned char sectBPB[] = {
	0xEB,0x3C,0x90,0x6D,0x6B,0x66,0x73,0x2E,0x66,0x61,0x74,0x00,0x02,0x01,0x01,0x00,
	0x02,0x00,0x02,0xFF,0xFF,0xF8,0xFE,0x00,0x20,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x80,0x00,0x29,0xB7,0x69,0x5C,0x93,0x43,0x4F,0x55,0x52,0x54,
	0x4D,0x41,0x54,0x49,0x43,0x53,0x46,0x41,0x54,0x31,0x36,0x20,0x20,0x20,0x0E,0x1F,
	0xBE,0x5B,0x7C,0xAC,0x22,0xC0,0x74,0x0B,0x56,0xB4,0x0E,0xBB,0x07,0x00,0xCD,0x10,
	0x5E,0xEB,0xF0,0x32,0xE4,0xCD,0x16,0xCD,0x19,0xEB,0xFE,0x54,0x68,0x69,0x73,0x20,
	0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x61,0x20,0x62,0x6F,0x6F,0x74,0x61,0x62,0x6C,
	0x65,0x20,0x64,0x69,0x73,0x6B,0x2E,0x20,0x20,0x50,0x6C,0x65,0x61,0x73,0x65,0x20,
	0x69,0x6E,0x73,0x65,0x72,0x74,0x20,0x61,0x20,0x62,0x6F,0x6F,0x74,0x61,0x62,0x6C,
	0x65,0x20,0x66,0x6C,0x6F,0x70,0x70,0x79,0x20,0x61,0x6E,0x64,0x0D,0x0A,0x70,0x72,
	0x65,0x73,0x73,0x20,0x61,0x6E,0x79,0x20,0x6B,0x65,0x79,0x20,0x74,0x6F,0x20,0x74,
	0x72,0x79,0x20,0x61,0x67,0x61,0x69,0x6E,0x20,0x2E,0x2E,0x2E,0x20,0x0D,0x0A,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xAA
};
void _handleFatClusterChain(uint32_t sect_offset, uint8_t *buf)
{
		uint16_t *bufW = (uint16_t *)&buf[0];
		if (sect_offset == 0)
		{
				bufW[0] = 0xfff8;
				bufW[1] = 0xffff;
				bufW[2] = 0xffff; //???????. 1????????.
		}
}

void _handleRoot(uint32_t sect_offset, uint8_t *buf)
{
		// 1 sector(512bytes) has 16 entries
		DirEntry *pDir = (DirEntry *)buf;
		if (sect_offset == 0)
		{
				memset(pDir, 0x00, sizeof(DirEntry));
				sprintf((char *)pDir->name, "TEXT_123");
				pDir->extension[0] = 'T';
				pDir->extension[1] = 'X';
				pDir->extension[2] = 'T';
				pDir->attribute = 0x00;
				{
						DirEntTime *pT = (DirEntTime *)&pDir->updateTime[0];
						DirEntDate *pD = (DirEntDate *)&pDir->updateDate[0];
						pT->B.hour = 12;
						pT->B.minutes = 34;
						pT->B.second = 56 / 2;
						pD->B.year = 2017 - 1980;
						pD->B.month = 1;
						pD->B.day = 12;
				}

				*(uint16_t*)&pDir->cluster = 0x0002;
				*(uint32_t*)&pDir->fileSize = 123;
		}
}

void _handleData(uint32_t sect_offset, uint8_t *buf)
{
		memset(buf, 'A', 512);
		sprintf((char *)buf, "Hello World!\r\n");
		buf[14]='>';
}

uint32_t _ReadSector(uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
		switch (blk_addr)
		{
		case SECTOR_MBR:
				memcpy(buf, (const void *)&sectMBR, 512);
				break;
		case SECTOR_PBR:
				memcpy(buf, (const void *)&sectBPB, 512);
				break;
		default:
				memset(buf, 0x00, 512);
				//FAT cluster chain
				if ((SECTOR_FAT1 <= blk_addr) && (blk_addr < SECTOR_ROOT))
				{
						if (blk_addr >= SECTOR_FAT2) { blk_addr -= (SECTOR_FAT2 - SECTOR_FAT1); }
						_handleFatClusterChain(blk_addr - SECTOR_FAT1, buf);

				}
				else if ((SECTOR_ROOT <= blk_addr) && (blk_addr < SECTOR_DATA))
				{
						_handleRoot(blk_addr - SECTOR_ROOT, buf);

				}
				else if (SECTOR_DATA <= blk_addr)
				{
						_handleData(blk_addr - SECTOR_DATA, buf);

				}
				break;
		}
		return 0;
}
```
```shell-session:chkdsk
C:\Users\user>chkdsk E:
The type of the file system is FAT.
Volume Serial Number is 935C-69B7
Windows is verifying files and folders...
File and folder verification is complete.

Windows has scanned the file system and found no problems.
No further action is required.

   33,276,928 bytes total disk space.
          512 bytes in 1 files.
   33,276,416 bytes available on disk.

          512 bytes in each allocation unit.
       64,994 total allocation units on disk.
       64,993 allocation units available on disk.

C:\Users\User>
```