mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-14 11:53:41 +00:00
sdcard: add support for spi sdcard driver
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
package sdcard
|
||||
|
||||
import "fmt"
|
||||
|
||||
type CID struct {
|
||||
//// byte 0
|
||||
ManufacturerID byte
|
||||
//uint8_t mid; // Manufacturer ID
|
||||
//// byte 1-2
|
||||
OEMApplicationID uint16
|
||||
//char oid[2]; // OEM/Application ID
|
||||
//// byte 3-7
|
||||
ProductName string
|
||||
//char pnm[5]; // Product name
|
||||
//// byte 8
|
||||
ProductVersion string
|
||||
//unsigned prv_m : 4; // Product revision n.m
|
||||
//unsigned prv_n : 4;
|
||||
//// byte 9-12
|
||||
ProductSerialNumber uint32
|
||||
//uint32_t psn; // Product serial number
|
||||
//// byte 13
|
||||
ManufacturingYear byte
|
||||
ManufacturingMonth byte
|
||||
//unsigned mdt_year_high : 4; // Manufacturing date
|
||||
//unsigned reserved : 4;
|
||||
//// byte 14
|
||||
//unsigned mdt_month : 4;
|
||||
//unsigned mdt_year_low : 4;
|
||||
//// byte 15
|
||||
Always1 byte
|
||||
//unsigned always1 : 1;
|
||||
CRC byte
|
||||
//unsigned crc : 7;
|
||||
}
|
||||
|
||||
func NewCID(buf []byte) *CID {
|
||||
return &CID{
|
||||
ManufacturerID: buf[0],
|
||||
OEMApplicationID: (uint16(buf[0]) << 8) | uint16(buf[1]),
|
||||
ProductName: string(buf[3:8]),
|
||||
ProductVersion: fmt.Sprintf("%d.%d", (buf[8]&0xF0)>>4, buf[8]&0x0F),
|
||||
ProductSerialNumber: (uint32(buf[9]) << 24) | (uint32(buf[10]) << 16) | (uint32(buf[11]) << 8) | uint32(buf[12]),
|
||||
ManufacturingYear: (buf[13] & 0xF0) | (buf[14] & 0x0F),
|
||||
ManufacturingMonth: (buf[14] & 0xF0) >> 4,
|
||||
Always1: (buf[15] & 0x80) >> 7,
|
||||
CRC: buf[15] & 0x7F,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package sdcard
|
||||
|
||||
const (
|
||||
CMD0_GO_IDLE_STATE = 0
|
||||
CMD1_SEND_OP_CND = 1
|
||||
CMD2_ALL_SEND_CID = 2
|
||||
CMD3_SEND_RELATIVE_ADDR = 3
|
||||
CMD4_SET_DSR = 4
|
||||
CMD6_SWITCH_FUNC = 6
|
||||
CMD7_SELECT_DESELECT_CARD = 7
|
||||
CMD8_SEND_IF_COND = 8
|
||||
CMD9_SEND_CSD = 9
|
||||
CMD10_SEND_CID = 10
|
||||
CMD12_STOP_TRANSMISSION = 12
|
||||
CMD13_SEND_STATUS = 13
|
||||
CMD15_GO_INACTIVE_STATE = 15
|
||||
CMD16_SET_BLOCKLEN = 16
|
||||
CMD17_READ_SINGLE_BLOCK = 17
|
||||
CMD18_READ_MULTIPLE_BLOCK = 18
|
||||
CMD24_WRITE_BLOCK = 24
|
||||
CMD25_WRITE_MULTIPLE_BLOCK = 25
|
||||
CMD27_PROGRAM_CSD = 27
|
||||
CMD28_SET_WRITE_PROT = 28
|
||||
CMD29_CLR_WRITE_PROT = 29
|
||||
CMD30_SEND_WRITE_PROT = 30
|
||||
CMD32_ERASE_WR_BLK_START_ADDR = 32
|
||||
CMD33_ERASE_WR_BLK_END_ADDR = 33
|
||||
CMD38_ERASE = 38
|
||||
CMD42_LOCK_UNLOCK = 42
|
||||
CMD55_APP_CMD = 55
|
||||
CMD56_GEN_CMD = 56
|
||||
CMD58_READ_OCR = 58
|
||||
CMD59_CRC_ON_OFF = 59
|
||||
ACMD6_SET_BUS_WIDTH = 6
|
||||
ACMD13_SD_STATUS = 13
|
||||
ACMD22_SEND_NUM_WR_BLOCKS = 22
|
||||
ACMD23_SET_WR_BLK_ERASE_COUNT = 23
|
||||
ACMD41_SD_APP_OP_COND = 41
|
||||
ACMD42_SET_CLR_CARD_DETECT = 42
|
||||
ACMD51_SEND_SCR = 51
|
||||
ACMD18_SECURE_READ_MULTI_BLOCK = 18
|
||||
ACMD25_SECURE_WRITE_MULTI_BLOCK = 25
|
||||
ACMD26_SECURE_WRITE_MKB = 26
|
||||
ACMD38_SECURE_ERASE = 38
|
||||
ACMD43_GET_MKB = 43
|
||||
ACMD44_GET_MID = 44
|
||||
ACMD45_SET_CER_RN1 = 45
|
||||
ACMD46_SET_CER_RN2 = 46
|
||||
ACMD47_SET_CER_RES2 = 47
|
||||
ACMD48_SET_CER_RES1 = 48
|
||||
ACMD49_CHANGE_SECURE_AREA = 49
|
||||
)
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package sdcard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CSD struct {
|
||||
CSD_STRUCTURE byte // 2 R [127:126] 0x01 : CSD Structure
|
||||
TAAC byte // 8 R [119:112] 0x0E : Data Read Access-Time-1
|
||||
NSAC byte // 8 R [111:104] 0x00 : Data Read Access-Time-2 in CLK Cycles (NSAC*100)
|
||||
TRAN_SPEED byte // 8 R [103:96] 0x5A : Max. Data Transfer Rate
|
||||
CCC uint16 // 12 R [95:84] 0x5B5 : Card Command Classes
|
||||
READ_BL_LEN byte // 4 R [83:80] 0x09 : Max. Read Data Block Length
|
||||
READ_BL_PARTIAL byte // 1 R [79:79] 0x00 : Partial Blocks for Read Allowed
|
||||
WRITE_BLK_MISALIGN byte // 1 R [78:78] 0x00 : Write Block Misalignment
|
||||
READ_BLK_MISALIGN byte // 1 R [77:77] 0x00 : Read Block Misalignment
|
||||
DSR_IMP byte // 1 R [76:76] 0x00 : DSR Implemented
|
||||
C_SIZE uint32 // 22 R [69:48] 0xXXXXXX : Device Size
|
||||
ERASE_BLK_EN byte // 1 R [46:46] 0x01 : Erase Single Block Enable
|
||||
SECTOR_SIZE byte // 7 R [45:39] 0x7F : Erase Sector Size
|
||||
WP_GRP_SIZE byte // 7 R [38:32] 0x00 : Write Protect Group Size
|
||||
WP_GRP_ENABLE byte // 1 R [31:31] 0x00 : Write Protect Group Enable
|
||||
R2W_FACTOR byte // 3 R [28:26] 0x02 : Write Speed Factor
|
||||
WRITE_BL_LEN byte // 4 R [25:22] 0x09 : Max. Write data Block Length
|
||||
WRITE_BL_PARTIAL byte // 1 R [21:21] 0x00 : Partial Blocks for Write Allowed
|
||||
FILE_FORMAT_GRP byte // 1 R [15:15] 0x00 : File Format Group
|
||||
COPY byte // 1 RW [14:14] 0x00 :Copy Flag
|
||||
PERM_WRITE_PROTECT byte // 1 RW [13:13] 0x00 : Permanent Write Protection
|
||||
TMP_WRITE_PROTECT byte // 1 RW [12:12] 0x00 : Temporary Write Protection
|
||||
FILE_FORMAT byte // 2 R [11:10] 0x00 : File Format
|
||||
CRC byte // 7 RW [7:1] 0xXX : CRC
|
||||
}
|
||||
|
||||
func NewCSD(buf []byte) *CSD {
|
||||
return &CSD{
|
||||
CSD_STRUCTURE: (buf[0] & 0xC0) >> 6,
|
||||
TAAC: buf[1],
|
||||
NSAC: buf[2],
|
||||
TRAN_SPEED: buf[3],
|
||||
CCC: uint16(buf[4])<<4 | uint16(buf[5])>>4,
|
||||
READ_BL_LEN: buf[5] & 0x0F,
|
||||
READ_BL_PARTIAL: (buf[6] & 0x80) >> 7,
|
||||
WRITE_BLK_MISALIGN: (buf[6] & 0x40) >> 6,
|
||||
READ_BLK_MISALIGN: (buf[6] & 0x20) >> 5,
|
||||
DSR_IMP: (buf[6] & 0x10) >> 4,
|
||||
C_SIZE: uint32(buf[7]&0x3F)<<16 | uint32(buf[8])<<8 | uint32(buf[9]),
|
||||
ERASE_BLK_EN: (buf[10] & 0x40) >> 6,
|
||||
SECTOR_SIZE: (buf[10]&0x3F)<<1 | (buf[11]&0x80)>>7,
|
||||
WP_GRP_SIZE: buf[11] & 0x7F,
|
||||
WP_GRP_ENABLE: (buf[12] & 0x80) >> 7,
|
||||
R2W_FACTOR: (buf[12] & 0x1C) >> 2,
|
||||
WRITE_BL_LEN: (buf[12]&0x03)<<2 | (buf[13]&0xC0)>>6,
|
||||
WRITE_BL_PARTIAL: (buf[13] & 0x20) >> 5,
|
||||
FILE_FORMAT_GRP: (buf[14] & 0x80) >> 7,
|
||||
COPY: (buf[14] & 0x40) >> 6,
|
||||
PERM_WRITE_PROTECT: (buf[14] & 0x20) >> 5,
|
||||
TMP_WRITE_PROTECT: (buf[14] & 0x10) >> 4,
|
||||
FILE_FORMAT: (buf[14] & 0x0C) >> 2,
|
||||
CRC: (buf[15] & 0xFE) >> 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CSD) Dump() {
|
||||
fmt.Printf("CSD_STRUCTURE: %X\r\n", c.CSD_STRUCTURE)
|
||||
fmt.Printf("TAAC: %X\r\n", c.TAAC)
|
||||
fmt.Printf("NSAC: %X\r\n", c.NSAC)
|
||||
fmt.Printf("TRAN_SPEED: %X\r\n", c.TRAN_SPEED)
|
||||
fmt.Printf("CCC: %X\r\n", c.CCC)
|
||||
fmt.Printf("READ_BL_LEN: %X\r\n", c.READ_BL_LEN)
|
||||
fmt.Printf("READ_BL_PARTIAL: %X\r\n", c.READ_BL_PARTIAL)
|
||||
fmt.Printf("WRITE_BLK_MISALIGN: %X\r\n", c.WRITE_BLK_MISALIGN)
|
||||
fmt.Printf("READ_BLK_MISALIGN: %X\r\n", c.READ_BLK_MISALIGN)
|
||||
fmt.Printf("DSR_IMP: %X\r\n", c.DSR_IMP)
|
||||
fmt.Printf("C_SIZE: %X\r\n", c.C_SIZE)
|
||||
fmt.Printf("ERASE_BLK_EN: %X\r\n", c.ERASE_BLK_EN)
|
||||
fmt.Printf("SECTOR_SIZE: %X\r\n", c.SECTOR_SIZE)
|
||||
fmt.Printf("WP_GRP_SIZE: %X\r\n", c.WP_GRP_SIZE)
|
||||
fmt.Printf("WP_GRP_ENABLE: %X\r\n", c.WP_GRP_ENABLE)
|
||||
fmt.Printf("R2W_FACTOR: %X\r\n", c.R2W_FACTOR)
|
||||
fmt.Printf("WRITE_BL_LEN: %X\r\n", c.WRITE_BL_LEN)
|
||||
fmt.Printf("WRITE_BL_PARTIAL: %X\r\n", c.WRITE_BL_PARTIAL)
|
||||
fmt.Printf("FILE_FORMAT_GRP: %X\r\n", c.FILE_FORMAT_GRP)
|
||||
fmt.Printf("COPY: %X\r\n", c.COPY)
|
||||
fmt.Printf("PERM_WRITE_PROTECT: %X\r\n", c.PERM_WRITE_PROTECT)
|
||||
fmt.Printf("TMP_WRITE_PROTECT: %X\r\n", c.TMP_WRITE_PROTECT)
|
||||
fmt.Printf("FILE_FORMAT: %X\r\n", c.FILE_FORMAT)
|
||||
fmt.Printf("CRC: %X\r\n", c.CRC)
|
||||
}
|
||||
|
||||
func (c *CSD) Sectors() (int64, error) {
|
||||
sectors := int64(0)
|
||||
if c.CSD_STRUCTURE == 0x01 {
|
||||
// CSD version 2.0
|
||||
sectors = (int64(c.C_SIZE) + 1) * 1024
|
||||
} else if c.CSD_STRUCTURE == 0x00 {
|
||||
// CSD version 1.0 (old, <=2GB)
|
||||
return 0, fmt.Errorf("CSD format version 1.0 is not supported")
|
||||
} else {
|
||||
return 0, fmt.Errorf("unknown CSD format")
|
||||
}
|
||||
return sectors, nil
|
||||
}
|
||||
|
||||
func (c *CSD) Size() uint64 {
|
||||
return uint64(c.C_SIZE) * 512 * 1024
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
package sdcard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
_CMD_TIMEOUT = 100
|
||||
|
||||
_R1_IDLE_STATE = 1 << 0
|
||||
_R1_ERASE_RESET = 1 << 1
|
||||
_R1_ILLEGAL_COMMAND = 1 << 2
|
||||
_R1_COM_CRC_ERROR = 1 << 3
|
||||
_R1_ERASE_SEQUENCE_ERROR = 1 << 4
|
||||
_R1_ADDRESS_ERROR = 1 << 5
|
||||
_R1_PARAMETER_ERROR = 1 << 6
|
||||
|
||||
// card types
|
||||
SD_CARD_TYPE_SD1 = 1 // Standard capacity V1 SD card
|
||||
SD_CARD_TYPE_SD2 = 2 // Standard capacity V2 SD card
|
||||
SD_CARD_TYPE_SDHC = 3 // High Capacity SD card
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus machine.SPI
|
||||
cs machine.Pin
|
||||
cmdbuf []byte
|
||||
dummybuf []byte
|
||||
tokenbuf []byte
|
||||
sdCardType byte
|
||||
CID *CID
|
||||
CSD *CSD
|
||||
}
|
||||
|
||||
func New(b machine.SPI, cs machine.Pin) Device {
|
||||
return Device{
|
||||
bus: b,
|
||||
cs: cs,
|
||||
cmdbuf: make([]byte, 6),
|
||||
dummybuf: make([]byte, 512),
|
||||
tokenbuf: make([]byte, 1),
|
||||
sdCardType: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) Configure() error {
|
||||
return d.initCard()
|
||||
}
|
||||
|
||||
func (d *Device) initCard() error {
|
||||
// set pin modes
|
||||
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.cs.High()
|
||||
|
||||
// clock card at least 100 cycles with cs high
|
||||
for i := 0; i < 16; i++ {
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
}
|
||||
|
||||
d.cs.Low()
|
||||
|
||||
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
|
||||
ok := false
|
||||
for i := 0; i < 2000; i++ {
|
||||
// Wait up to 2 seconds to be the same as the Arduino
|
||||
if d.cmd(CMD0_GO_IDLE_STATE, 0, 0x95) == _R1_IDLE_STATE {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("no SD card")
|
||||
}
|
||||
|
||||
// CMD8: determine card version
|
||||
r := d.cmd(CMD8_SEND_IF_COND, 0x01AA, 0x87)
|
||||
if (r & _R1_ILLEGAL_COMMAND) == _R1_ILLEGAL_COMMAND {
|
||||
d.sdCardType = SD_CARD_TYPE_SD1
|
||||
return fmt.Errorf("init_card_v1 not impl\r\n")
|
||||
} else {
|
||||
// r7 response
|
||||
status := byte(0)
|
||||
for i := 0; i < 3; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if (status & 0x0F) != 0x01 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD8 %02X", status)
|
||||
}
|
||||
|
||||
for i := 3; i < 4; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if status != 0xAA {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD8 %02X", status)
|
||||
}
|
||||
d.sdCardType = SD_CARD_TYPE_SD2
|
||||
}
|
||||
|
||||
// initialize card and send host supports SDHC if SD2
|
||||
arg := uint32(0)
|
||||
if d.sdCardType == SD_CARD_TYPE_SD2 {
|
||||
arg = 0x40000000
|
||||
}
|
||||
|
||||
// check for timeout
|
||||
ok = false
|
||||
for i := 0; i < 2000; i++ {
|
||||
if d.acmd(ACMD41_SD_APP_OP_COND, arg) == 0 {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("SD_CARD_ERROR_ACMD41")
|
||||
}
|
||||
|
||||
// if SD2 read OCR register to check for SDHC card
|
||||
if d.sdCardType == SD_CARD_TYPE_SD2 {
|
||||
if d.cmd(CMD58_READ_OCR, 0, 0) != 0 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD58")
|
||||
}
|
||||
|
||||
status, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (status & 0xC0) == 0xC0 {
|
||||
d.sdCardType = SD_CARD_TYPE_SDHC
|
||||
}
|
||||
// discard rest of ocr - contains allowed voltage range
|
||||
for i := 1; i < 4; i++ {
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
}
|
||||
}
|
||||
|
||||
var buf [16]byte
|
||||
// read CID
|
||||
err := d.ReadCID(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.CID = NewCID(buf[:])
|
||||
|
||||
// read CSD
|
||||
err = d.ReadCSD(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.CSD = NewCSD(buf[:])
|
||||
|
||||
d.cs.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Device) acmd(cmd byte, arg uint32) byte {
|
||||
d.cmd(CMD55_APP_CMD, 0, 0)
|
||||
return d.cmd(cmd, arg, 0)
|
||||
}
|
||||
|
||||
func (d Device) cmd(cmd byte, arg uint32, crc byte) byte {
|
||||
d.cs.Low()
|
||||
|
||||
if cmd != 12 {
|
||||
d.waitNotBusy(300)
|
||||
}
|
||||
|
||||
// create and send the command
|
||||
buf := d.cmdbuf
|
||||
buf[0] = 0x40 | cmd
|
||||
buf[1] = byte(arg >> 24)
|
||||
buf[2] = byte(arg >> 16)
|
||||
buf[3] = byte(arg >> 8)
|
||||
buf[4] = byte(arg)
|
||||
buf[5] = crc
|
||||
d.bus.Tx(buf, nil)
|
||||
|
||||
if cmd == 12 {
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
}
|
||||
|
||||
// wait for the response (response[7] == 0)
|
||||
for i := 0; i < 0xFFFF; i++ {
|
||||
d.bus.Tx([]byte{0xFF}, d.tokenbuf)
|
||||
response := d.tokenbuf[0]
|
||||
if (response & 0x80) == 0 {
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
//// timeout
|
||||
d.cs.High()
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
return 0xFF // -1
|
||||
}
|
||||
|
||||
func (d Device) waitNotBusy(timeoutMs int) error {
|
||||
for i := 0; i < timeoutMs; i++ {
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r == 0xFF {
|
||||
return nil
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Device) waitStartBlock() error {
|
||||
status := byte(0xFF)
|
||||
|
||||
for i := 0; i < 3000; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
d.cs.High()
|
||||
return err
|
||||
}
|
||||
if status != 0xFF {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
}
|
||||
|
||||
if status != 254 {
|
||||
d.cs.High()
|
||||
return fmt.Errorf("SD_CARD_START_BLOCK")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadCSD reads the CSD using CMD9.
|
||||
func (d Device) ReadCSD(csd []byte) error {
|
||||
return d.readRegister(CMD9_SEND_CSD, csd)
|
||||
}
|
||||
|
||||
// ReadCID reads the CID using CMD10
|
||||
func (d Device) ReadCID(csd []byte) error {
|
||||
return d.readRegister(CMD10_SEND_CID, csd)
|
||||
}
|
||||
|
||||
func (d Device) readRegister(cmd uint8, dst []byte) error {
|
||||
if d.cmd(cmd, 0, 0) != 0 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_READ_REG")
|
||||
}
|
||||
if err := d.waitStartBlock(); err != nil {
|
||||
return err
|
||||
}
|
||||
// transfer data
|
||||
for i := uint16(0); i < 16; i++ {
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst[i] = r
|
||||
}
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.cs.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadData reads 512 bytes from sdcard into dst.
|
||||
func (d Device) ReadData(block uint32, dst []byte) error {
|
||||
if len(dst) < 512 {
|
||||
return fmt.Errorf("len(dst) must be greater than or equal to 512")
|
||||
}
|
||||
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD17_READ_SINGLE_BLOCK, block, 0) != 0 {
|
||||
return fmt.Errorf("CMD17 error")
|
||||
}
|
||||
if err := d.waitStartBlock(); err != nil {
|
||||
return fmt.Errorf("waitStartBlock()")
|
||||
}
|
||||
|
||||
err := d.bus.Tx(nil, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip CRC (2byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// TODO: probably not necessary
|
||||
d.cs.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMultiStart starts the continuous write mode using CMD25.
|
||||
func (d Device) WriteMultiStart(block uint32) error {
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD25_WRITE_MULTIPLE_BLOCK, block, 0) != 0 {
|
||||
return fmt.Errorf("CMD25 error")
|
||||
}
|
||||
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMulti performs continuous writing. It is necessary to call
|
||||
// WriteMultiStart() in prior.
|
||||
func (d Device) WriteMulti(buf []byte) error {
|
||||
// send Data Token for CMD25
|
||||
d.bus.Transfer(byte(0xFC))
|
||||
|
||||
for i := 0; i < 512; i++ {
|
||||
_, err := d.bus.Transfer(buf[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// send dummy CRC (2 byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// Data Resp.
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (r & 0x1F) != 0x05 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE")
|
||||
}
|
||||
|
||||
// wait no busy
|
||||
err = d.waitNotBusy(600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMultiStop exits the continuous write mode.
|
||||
func (d Device) WriteMultiStop() error {
|
||||
defer d.cs.High()
|
||||
|
||||
// Stop Tran token for CMD25
|
||||
d.bus.Transfer(0xFD)
|
||||
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
err := d.waitNotBusy(600)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteData writes 512 bytes from dst to sdcard.
|
||||
func (d Device) WriteData(block uint32, src []byte) error {
|
||||
if len(src) < 512 {
|
||||
return fmt.Errorf("len(src) must be greater than or equal to 512")
|
||||
}
|
||||
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD24_WRITE_BLOCK, block, 0) != 0 {
|
||||
return fmt.Errorf("CMD24 error")
|
||||
}
|
||||
|
||||
// wait 1 byte?
|
||||
token := byte(0xFE)
|
||||
d.bus.Transfer(token)
|
||||
|
||||
err := d.bus.Tx(src[:512], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send dummy CRC (2 byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// Data Resp.
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (r & 0x1F) != 0x05 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE")
|
||||
}
|
||||
|
||||
// wait no busy
|
||||
err = d.waitNotBusy(600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
|
||||
}
|
||||
|
||||
// TODO: probably not necessary
|
||||
d.cs.High()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadAt reads the given number of bytes from the sdcard.
|
||||
func (dev *Device) ReadAt(buf []byte, addr int64) (int, error) {
|
||||
block := uint64(addr)
|
||||
// use address if not SDHC card
|
||||
if dev.sdCardType == SD_CARD_TYPE_SDHC {
|
||||
block >>= 9
|
||||
}
|
||||
|
||||
idx := uint32(0)
|
||||
|
||||
start := uint32(addr % 512)
|
||||
end := uint32(0)
|
||||
remain := uint32(len(buf))
|
||||
|
||||
// If data starts in the middle
|
||||
if 0 < start {
|
||||
if start+remain <= 512 {
|
||||
end = start + remain
|
||||
} else {
|
||||
end = 512
|
||||
}
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// If more than 512 bytes left
|
||||
for 512 <= remain {
|
||||
start = 0
|
||||
end = 512
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// Read to the end
|
||||
if 0 < remain {
|
||||
start = 0
|
||||
end = remain
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
return int(idx), nil
|
||||
}
|
||||
|
||||
// WriteAt writes the given number of bytes to sdcard.
|
||||
func (dev *Device) WriteAt(buf []byte, addr int64) (n int, err error) {
|
||||
block := uint64(addr)
|
||||
// use address if not SDHC card
|
||||
if dev.sdCardType == SD_CARD_TYPE_SDHC {
|
||||
block >>= 9
|
||||
}
|
||||
|
||||
idx := uint32(0)
|
||||
|
||||
start := uint32(addr % 512)
|
||||
end := uint32(0)
|
||||
remain := uint32(len(buf))
|
||||
|
||||
// If data starts in the middle
|
||||
if 0 < start {
|
||||
if start+remain <= 512 {
|
||||
end = start + remain
|
||||
} else {
|
||||
end = 512
|
||||
}
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(dev.dummybuf[start:end], buf[idx:])
|
||||
|
||||
err = dev.WriteData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// If more than 512 bytes left
|
||||
for 512 <= remain {
|
||||
start = 0
|
||||
end = 512
|
||||
|
||||
err := dev.WriteData(uint32(block), buf[idx:idx+512])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// Write to the end
|
||||
if 0 < remain {
|
||||
start = 0
|
||||
end = remain
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(dev.dummybuf[start:end], buf[idx:])
|
||||
|
||||
err = dev.WriteData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
return int(idx), nil
|
||||
}
|
||||
|
||||
// Size returns the number of bytes in this sdcard.
|
||||
func (dev *Device) Size() int64 {
|
||||
return int64(dev.CSD.Size())
|
||||
}
|
||||
|
||||
// WriteBlockSize returns the block size in which data can be written to
|
||||
// memory.
|
||||
func (dev *Device) WriteBlockSize() int64 {
|
||||
return 512
|
||||
}
|
||||
|
||||
// EraseBlockSize returns the smallest erasable area on this sdcard in bytes.
|
||||
func (dev *Device) EraseBlockSize() int64 {
|
||||
return 512
|
||||
}
|
||||
|
||||
// EraseBlocks erases the given number of blocks.
|
||||
func (dev *Device) EraseBlocks(start, len int64) error {
|
||||
dev.WriteMultiStart(uint32(start))
|
||||
|
||||
for i := range dev.dummybuf {
|
||||
dev.dummybuf[i] = 0
|
||||
}
|
||||
|
||||
for i := 0; i < int(len); i++ {
|
||||
dev.WriteMulti(dev.dummybuf)
|
||||
}
|
||||
|
||||
dev.WriteMultiStop()
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user