sdcard: add support for spi sdcard driver

This commit is contained in:
sago35
2021-05-14 18:40:29 +09:00
committed by Ron Evans
parent aa17ffd1cb
commit d07ad40373
11 changed files with 1319 additions and 1 deletions
+49
View File
@@ -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,
}
}
+52
View File
@@ -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
View File
@@ -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
}
+606
View File
@@ -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
}