mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-17 05:13:23 +00:00
still working on consolidation of init
This commit is contained in:
+3
-20
@@ -34,7 +34,7 @@ func main() {
|
|||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
sdcard := sd.NewSPICard(spibus, SPI_CS_PIN.Set)
|
sdcard := sd.NewSPICard(spibus, SPI_CS_PIN.Set)
|
||||||
|
println("start init")
|
||||||
err = sdcard.Init()
|
err = sdcard.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("sd card init:" + err.Error())
|
panic("sd card init:" + err.Error())
|
||||||
@@ -46,28 +46,11 @@ func main() {
|
|||||||
err = spibus.Configure(spicfg)
|
err = spibus.Configure(spicfg)
|
||||||
|
|
||||||
cid := sdcard.CID()
|
cid := sdcard.CID()
|
||||||
pname := cid.ProductName()
|
fmt.Printf("name=%s\ncsd=\n%s\n", cid.ProductName(), csd.String())
|
||||||
if !cid.IsValid() {
|
|
||||||
copy := cid.RawCopy()
|
|
||||||
println("CID not valid: theirCRC=", cid.CRC7(), "ourCRC=", sd.CRC7(copy[:15]))
|
|
||||||
}
|
|
||||||
valid := csd.IsValid()
|
|
||||||
if !valid {
|
|
||||||
data := csd.RawCopy()
|
|
||||||
crc := sd.CRC7(data[:15])
|
|
||||||
always1 := data[15]&(1<<7) != 0
|
|
||||||
fmt.Printf("ourCRC7=%#b theirCRC7=%#b for data %d\n", crc, csd.CRC7(), data[:15])
|
|
||||||
println("CSD not valid got", crc, "want", csd.CRC7(), "always1:", always1)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
println("CSD valid!")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("name=%s\ncsd=\n%s\n", pname, csd.String())
|
|
||||||
|
|
||||||
var buf [512]byte
|
var buf [512]byte
|
||||||
for i := 0; i < 11; i += 1 {
|
for i := 0; i < 11; i += 1 {
|
||||||
err = sdcard.ReadBlock(0, buf[:])
|
err = sdcard.ReadBlocks(buf[:], 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("err reading block", i, ":", err.Error())
|
println("err reading block", i, ":", err.Error())
|
||||||
continue
|
continue
|
||||||
|
|||||||
+60
-136
@@ -9,40 +9,45 @@ import (
|
|||||||
"tinygo.org/x/drivers"
|
"tinygo.org/x/drivers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// See rustref.go for the new implementation.
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errBadCSDCID = errors.New("sd:bad CSD/CID in CRC or always1")
|
errBadCSDCID = errors.New("sd:bad CSD/CID in CRC or always1")
|
||||||
errNoSDCard = errors.New("sd:no card")
|
errNoSDCard = errors.New("sd:no card")
|
||||||
errCardNotSupported = errors.New("sd:card not supported")
|
errCardNotSupported = errors.New("sd:card not supported")
|
||||||
errCmd8 = errors.New("sd:cmd8")
|
errCmd8 = errors.New("sd:cmd8")
|
||||||
errCmdOCR = errors.New("sd:cmd_ocr")
|
errCmdOCR = errors.New("sd:cmd_ocr")
|
||||||
errCmdBlkLen = errors.New("sd:cmd_blklen")
|
errCmdBlkLen = errors.New("sd:cmd_blklen")
|
||||||
errAcmdAppCond = errors.New("sd:acmd_appOrCond")
|
errAcmdAppCond = errors.New("sd:acmd_appOrCond")
|
||||||
errWaitStartBlock = errors.New("sd:did not find start block token")
|
errWaitStartBlock = errors.New("sd:did not find start block token")
|
||||||
errNeed512 = errors.New("sd:need 512 bytes for I/O")
|
errNeedBlockLenMultiple = errors.New("sd:need blocksize multiple for I/O")
|
||||||
errWrite = errors.New("sd:write")
|
errWrite = errors.New("sd:write")
|
||||||
errWriteTimeout = errors.New("sd:write timeout")
|
errWriteTimeout = errors.New("sd:write timeout")
|
||||||
errBusyTimeout = errors.New("sd:busy card timeout")
|
errReadTimeout = errors.New("sd:read timeout")
|
||||||
errOOB = errors.New("sd:oob block access")
|
errBusyTimeout = errors.New("sd:busy card timeout")
|
||||||
errNoblocks = errors.New("sd:no readable blocks")
|
errOOB = errors.New("sd:oob block access")
|
||||||
errCmdGeneric = errors.New("sd:command error")
|
errNoblocks = errors.New("sd:no readable blocks")
|
||||||
|
errCmdGeneric = errors.New("sd:command error")
|
||||||
)
|
)
|
||||||
|
|
||||||
type digitalPinout func(b bool)
|
type digitalPinout func(b bool)
|
||||||
|
|
||||||
type SPICard struct {
|
type SPICard struct {
|
||||||
bus drivers.SPI
|
bus drivers.SPI
|
||||||
cs digitalPinout
|
cs digitalPinout
|
||||||
bufcmd [6]byte
|
bufcmd [6]byte
|
||||||
buf [512]byte
|
buf [512]byte
|
||||||
bufTok [1]byte
|
bufTok [1]byte
|
||||||
kind CardKind
|
kind CardKind
|
||||||
cid CID
|
cid CID
|
||||||
csd CSD
|
csd CSD
|
||||||
lastCRC uint16
|
lastCRC uint16
|
||||||
timers [2]timer
|
// shift to calculate blocksize, taken from CSD.
|
||||||
numblocks int64
|
blockshift uint8
|
||||||
timeout time.Duration
|
timers [2]timer
|
||||||
wait time.Duration
|
numblocks int64
|
||||||
|
timeout time.Duration
|
||||||
|
wait time.Duration
|
||||||
// relative card address.
|
// relative card address.
|
||||||
rca uint32
|
rca uint32
|
||||||
lastr1 r1
|
lastr1 r1
|
||||||
@@ -67,11 +72,12 @@ func (c *SPICard) setTimeout(timeout time.Duration) {
|
|||||||
c.wait = timeout / 512
|
c.wait = timeout / 512
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SPICard) csEnable(b bool) { c.cs(!b) }
|
func (c *SPICard) csEnable(b bool) {
|
||||||
|
c.cs(!b)
|
||||||
|
}
|
||||||
|
|
||||||
// LastReadCRC returns the CRC for the last ReadBlock operation.
|
// LastReadCRC returns the CRC for the last ReadBlock operation.
|
||||||
func (c *SPICard) LastReadCRC() uint16 { return c.lastCRC }
|
func (c *SPICard) LastReadCRC() uint16 { return c.lastCRC }
|
||||||
func (c *SPICard) LastR1() r1 { return c.lastr1 }
|
|
||||||
|
|
||||||
// Init initializes the SD card. This routine should be performed with a SPI clock
|
// Init initializes the SD card. This routine should be performed with a SPI clock
|
||||||
// speed of around 100..400kHz. One may increase the clock speed after initialization.
|
// speed of around 100..400kHz. One may increase the clock speed after initialization.
|
||||||
@@ -90,6 +96,7 @@ func (d *SPICard) Init() error {
|
|||||||
d.bus.Tx(dummy[:], nil)
|
d.bus.Tx(dummy[:], nil)
|
||||||
|
|
||||||
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
|
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
|
||||||
|
println("first timer")
|
||||||
ok := false
|
ok := false
|
||||||
tm := d.timers[0].setTimeout(2 * time.Second)
|
tm := d.timers[0].setTimeout(2 * time.Second)
|
||||||
for !tm.expired() {
|
for !tm.expired() {
|
||||||
@@ -148,9 +155,11 @@ func (d *SPICard) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check for timeout
|
// check for timeout
|
||||||
|
println("app cmd")
|
||||||
ok = false
|
ok = false
|
||||||
tm = tm.setTimeout(2 * time.Second)
|
tm = tm.setTimeout(2 * time.Second)
|
||||||
for !tm.expired() {
|
for !tm.expired() {
|
||||||
|
println("timer")
|
||||||
r1, err = d.appCmd(acmdSD_APP_OP_COND, arg)
|
r1, err = d.appCmd(acmdSD_APP_OP_COND, arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -162,7 +171,7 @@ func (d *SPICard) Init() error {
|
|||||||
if r1 != 0 {
|
if r1 != 0 {
|
||||||
return makeResponseError(r1)
|
return makeResponseError(r1)
|
||||||
}
|
}
|
||||||
|
println("preensure")
|
||||||
// if SD2 read OCR register to check for SDHC card
|
// if SD2 read OCR register to check for SDHC card
|
||||||
if d.kind == TypeSD2 {
|
if d.kind == TypeSD2 {
|
||||||
err := d.cmdEnsure0Status(cmdReadOCR, 0, 0xFF)
|
err := d.cmdEnsure0Status(cmdReadOCR, 0, 0xFF)
|
||||||
@@ -182,11 +191,16 @@ func (d *SPICard) Init() error {
|
|||||||
d.bus.Transfer(0xFF)
|
d.bus.Transfer(0xFF)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
println("ensure")
|
||||||
err = d.cmdEnsure0Status(cmdSetBlocklen, 0x0200, 0xff)
|
err = d.cmdEnsure0Status(cmdSetBlocklen, 0x0200, 0xff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
println("get to update csdid")
|
||||||
|
return d.updateCSDCID()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SPICard) updateCSDCID() (err error) {
|
||||||
// read CID
|
// read CID
|
||||||
d.cid, err = d.readCID()
|
d.cid, err = d.readCID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -196,113 +210,23 @@ func (d *SPICard) Init() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
nb := d.csd.NumberOfBlocks()
|
blockshift := d.csd.ReadBlockLenShift()
|
||||||
if nb > math.MaxUint32 {
|
blocklen := uint16(1) << blockshift
|
||||||
return errCardNotSupported
|
capacity := d.csd.DeviceCapacity()
|
||||||
} else if nb == 0 {
|
if blocklen == 0 || capacity < uint64(blocklen) {
|
||||||
return errNoblocks
|
return errNoblocks
|
||||||
}
|
}
|
||||||
|
nb := capacity / uint64(blocklen)
|
||||||
|
if nb > math.MaxUint32 {
|
||||||
|
return errCardNotSupported
|
||||||
|
}
|
||||||
|
d.blockshift = blockshift
|
||||||
d.numblocks = int64(nb)
|
d.numblocks = int64(nb)
|
||||||
return nil
|
return nil
|
||||||
err = d.readRegister(cmdSendRelativeAddr, d.buf[:4])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.rca = binary.BigEndian.Uint32(d.buf[:4])
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SPICard) NumberOfBlocks() uint64 {
|
func (d *SPICard) NumberOfBlocks() int64 {
|
||||||
return uint64(d.numblocks)
|
return d.numblocks
|
||||||
}
|
|
||||||
|
|
||||||
// ReadBlock reads 512 bytes from sdcard into dst.
|
|
||||||
func (d *SPICard) ReadBlock(block int64, dst []byte) error {
|
|
||||||
if len(dst) != 512 {
|
|
||||||
return errNeed512
|
|
||||||
} else if block >= d.numblocks {
|
|
||||||
return errOOB
|
|
||||||
}
|
|
||||||
|
|
||||||
// use address if not SDHC card
|
|
||||||
if d.kind != TypeSDHC {
|
|
||||||
block <<= 9
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.cmdEnsure0Status(cmdReadSingleBlock, uint32(block), 0xff)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer d.csEnable(false)
|
|
||||||
|
|
||||||
if err := d.waitStartBlock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buf := d.buf[:]
|
|
||||||
err = d.bus.Tx(buf, dst)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip CRC (2byte)
|
|
||||||
hi, _ := d.bus.Transfer(0xFF)
|
|
||||||
lo, _ := d.bus.Transfer(0xFF)
|
|
||||||
d.lastCRC = uint16(hi)<<8 | uint16(lo)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteBlock writes 512 bytes from dst to sdcard.
|
|
||||||
func (d *SPICard) WriteBlock(block int64, src []byte) error {
|
|
||||||
if len(src) != 512 {
|
|
||||||
return errNeed512
|
|
||||||
} else if block >= d.numblocks {
|
|
||||||
return errOOB
|
|
||||||
}
|
|
||||||
|
|
||||||
// use address if not SDHC card
|
|
||||||
if d.kind != TypeSDHC {
|
|
||||||
block <<= 9
|
|
||||||
}
|
|
||||||
err := d.cmdEnsure0Status(cmdWriteBlock, uint32(block), 0xFF)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer d.csEnable(false)
|
|
||||||
// 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(0xFF)
|
|
||||||
d.bus.Transfer(0xFF)
|
|
||||||
|
|
||||||
// Data Resp.
|
|
||||||
r, err := d.bus.Transfer(0xFF)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if (r & 0x1F) != 0x05 {
|
|
||||||
return errWrite
|
|
||||||
}
|
|
||||||
|
|
||||||
err = d.waitNotBusy(2 * d.timeout)
|
|
||||||
if err != nil {
|
|
||||||
return errWriteTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *SPICard) ReadStatus() (response1, error) {
|
|
||||||
if err := d.readRegister(cmdSendStatus, d.buf[:4]); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return response1(binary.BigEndian.Uint32(d.buf[:4])), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CID returns a copy of the Card Identification Register value last read.
|
// CID returns a copy of the Card Identification Register value last read.
|
||||||
@@ -400,23 +324,24 @@ func (d *SPICard) cmd(cmd command, arg uint32, precalcCRC byte) (response1, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if cmd == 12 {
|
if cmd == cmdStopTransmission {
|
||||||
// skip 1 byte
|
// skip 1 byte
|
||||||
d.bus.Transfer(0xFF)
|
d.bus.Transfer(0xFF)
|
||||||
}
|
}
|
||||||
|
|
||||||
tm := d.timers[0].setTimeout(d.timeout)
|
tm := d.timers[1].setTimeout(d.timeout)
|
||||||
for {
|
for {
|
||||||
tok, _ := d.bus.Transfer(0xff)
|
tok, _ := d.bus.Transfer(0xff)
|
||||||
response := response1(tok)
|
response := response1(tok)
|
||||||
if (response & 0x80) == 0 {
|
if (response & 0x80) == 0 {
|
||||||
|
// NOMINAL FUNCTION EXIT HERE
|
||||||
return response, nil
|
return response, nil
|
||||||
} else if tm.expired() {
|
} else if tm.expired() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
d.yield()
|
d.yield()
|
||||||
}
|
}
|
||||||
|
println("============== BAD EXIT ================")
|
||||||
d.csEnable(false)
|
d.csEnable(false)
|
||||||
d.bus.Transfer(0xFF)
|
d.bus.Transfer(0xFF)
|
||||||
return 0xFF, errCmdGeneric
|
return 0xFF, errCmdGeneric
|
||||||
@@ -435,7 +360,6 @@ func (d *SPICard) waitStartBlock() error {
|
|||||||
if _, ok := d.waitToken(d.timeout, tokSTART_BLOCK); ok {
|
if _, ok := d.waitToken(d.timeout, tokSTART_BLOCK); ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
d.csEnable(false)
|
|
||||||
return errWaitStartBlock
|
return errWaitStartBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -13,6 +13,10 @@ import (
|
|||||||
|
|
||||||
type CardKind uint8
|
type CardKind uint8
|
||||||
|
|
||||||
|
func isTimeout(err error) bool {
|
||||||
|
return err == errReadTimeout || err == errWriteTimeout || err == errBusyTimeout
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// card types
|
// card types
|
||||||
TypeSD1 CardKind = 1 // Standard capacity V1 SD card
|
TypeSD1 CardKind = 1 // Standard capacity V1 SD card
|
||||||
@@ -150,7 +154,8 @@ func (c *CSD) CommandClasses() CommandClasses {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadBlockLen returns the Max Read Data Block Length in bytes.
|
// ReadBlockLen returns the Max Read Data Block Length in bytes.
|
||||||
func (c *CSD) ReadBlockLen() uint16 { return 1 << (c.data[5] & 0x0F) }
|
func (c *CSD) ReadBlockLen() uint16 { return 1 << c.ReadBlockLenShift() }
|
||||||
|
func (c *CSD) ReadBlockLenShift() uint8 { return c.data[5] & 0x0F }
|
||||||
|
|
||||||
// AllowsReadBlockPartial should always return true. Indicates that
|
// AllowsReadBlockPartial should always return true. Indicates that
|
||||||
func (c *CSD) AllowsReadBlockPartial() bool { return c.data[6]&(1<<7) != 0 }
|
func (c *CSD) AllowsReadBlockPartial() bool { return c.data[6]&(1<<7) != 0 }
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ const (
|
|||||||
_R1_ERASE_SEQUENCE_ERROR = 1 << 4
|
_R1_ERASE_SEQUENCE_ERROR = 1 << 4
|
||||||
_R1_ADDRESS_ERROR = 1 << 5
|
_R1_ADDRESS_ERROR = 1 << 5
|
||||||
_R1_PARAMETER_ERROR = 1 << 6
|
_R1_PARAMETER_ERROR = 1 << 6
|
||||||
|
|
||||||
|
_DATA_RES_MASK = 0x1F
|
||||||
|
_DATA_RES_ACCEPTED = 0x05
|
||||||
)
|
)
|
||||||
|
|
||||||
type response1 uint8
|
type response1 uint8
|
||||||
@@ -81,6 +84,8 @@ func makeResponseError(status response1) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func (c *SPICard) lastR1() r1 { return c.lastr1 }
|
||||||
|
|
||||||
// is part of specification but not used in every implementation out there...
|
// is part of specification but not used in every implementation out there...
|
||||||
func (d *SPICard) readR1() (resp r1, err error) {
|
func (d *SPICard) readR1() (resp r1, err error) {
|
||||||
first, ok := d.waitToken(d.timeout, 0xff)
|
first, ok := d.waitToken(d.timeout, 0xff)
|
||||||
|
|||||||
+368
@@ -0,0 +1,368 @@
|
|||||||
|
package sd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"math/bits"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference for this implementation:
|
||||||
|
// https://github.com/embassy-rs/embedded-sdmmc-rs/blob/master/src/sdmmc.rs
|
||||||
|
|
||||||
|
// Not used currently. We'd want to switch over to one way of doing things, Rust way.
|
||||||
|
func (d *SPICard) initRs() error {
|
||||||
|
// Supply minimum of 74 clock cycles with CS high.
|
||||||
|
d.csEnable(true)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
d.send(0xff)
|
||||||
|
}
|
||||||
|
d.csEnable(false)
|
||||||
|
|
||||||
|
d.csEnable(true)
|
||||||
|
defer d.csEnable(false)
|
||||||
|
// Enter SPI mode
|
||||||
|
const maxRetries = 32
|
||||||
|
retries := maxRetries
|
||||||
|
tm := d.timers[0].setTimeout(2 * time.Second)
|
||||||
|
for retries > 0 {
|
||||||
|
stat, err := d.card_command(cmdGoIdleState, 0)
|
||||||
|
if err != nil {
|
||||||
|
if isTimeout(err) {
|
||||||
|
retries--
|
||||||
|
continue // Try again!
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if stat == _R1_IDLE_STATE {
|
||||||
|
break
|
||||||
|
} else if tm.expired() {
|
||||||
|
retries = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
retries--
|
||||||
|
}
|
||||||
|
if retries <= 0 {
|
||||||
|
return errNoSDCard
|
||||||
|
}
|
||||||
|
const enableCRC = false
|
||||||
|
if enableCRC {
|
||||||
|
stat, err := d.card_command(cmdCRCOnOff, 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if stat != _R1_IDLE_STATE {
|
||||||
|
return errors.New("sd:cant enable CRC")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tm.setTimeout(d.timeout)
|
||||||
|
for {
|
||||||
|
stat, err := d.card_command(cmdSendIfCond, 0x1aa)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if stat == _R1_IDLE_STATE || stat == _R1_ILLEGAL_COMMAND {
|
||||||
|
d.kind = TypeSD1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.receive()
|
||||||
|
d.receive()
|
||||||
|
d.receive()
|
||||||
|
status, err := d.receive()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status == 0xaa {
|
||||||
|
d.kind = TypeSD2
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.yield()
|
||||||
|
}
|
||||||
|
|
||||||
|
var arg uint32
|
||||||
|
if d.kind != TypeSD1 {
|
||||||
|
arg = 0x4000_0000
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
stat, err := d.card_acmd(acmdSD_APP_OP_COND, arg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if stat == 0 { // READY state.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.yield()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := d.updateCSDCID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.kind != TypeSD2 {
|
||||||
|
return nil // Done if not SD2.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover if card is high capacity.
|
||||||
|
stat, err := d.card_command(cmdReadOCR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if stat != 0 {
|
||||||
|
return makeResponseError(response1(stat))
|
||||||
|
}
|
||||||
|
ocr, err := d.receive()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if ocr&0xc0 == 0xc0 {
|
||||||
|
d.kind = TypeSDHC
|
||||||
|
}
|
||||||
|
// Discard next 3 bytes.
|
||||||
|
d.receive()
|
||||||
|
d.receive()
|
||||||
|
d.receive()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadBlock reads to a buffer multiple of 512 bytes from sdcard into dst starting at block `startBlockIdx`.
|
||||||
|
func (d *SPICard) ReadBlocks(dst []byte, startBlockIdx int64) error {
|
||||||
|
numblocks, err := d.checkBounds(startBlockIdx, len(dst))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d.kind != TypeSDHC {
|
||||||
|
startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards.
|
||||||
|
}
|
||||||
|
d.csEnable(true)
|
||||||
|
defer d.csEnable(false)
|
||||||
|
|
||||||
|
if numblocks == 1 {
|
||||||
|
_, err = d.card_command(cmdReadSingleBlock, uint32(startBlockIdx))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.read_data(dst)
|
||||||
|
|
||||||
|
} else if numblocks > 1 {
|
||||||
|
blocksize := 1 << d.blockshift
|
||||||
|
_, err = d.card_command(cmdReadMultipleBlock, uint32(startBlockIdx))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < numblocks; i++ {
|
||||||
|
offset := i * blocksize
|
||||||
|
err = d.read_data(dst[offset : offset+blocksize])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = d.card_command(cmdStopTransmission, 0)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
panic("unreachable numblocks<=0")
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteBlocks writes to sdcard from a buffer multiple of 512 bytes from src starting at block `startBlockIdx`.
|
||||||
|
func (d *SPICard) WriteBlocks(data []byte, startBlockIdx int64) error {
|
||||||
|
numblocks, err := d.checkBounds(startBlockIdx, len(data))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d.kind != TypeSDHC {
|
||||||
|
startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards.
|
||||||
|
}
|
||||||
|
d.csEnable(true)
|
||||||
|
defer d.csEnable(false)
|
||||||
|
|
||||||
|
if numblocks == 1 {
|
||||||
|
_, err = d.card_command(cmdWriteBlock, uint32(startBlockIdx))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.write_data(tokSTART_BLOCK, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.wait_not_busy()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
status, err := d.card_command(cmdSendStatus, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if status != 0 {
|
||||||
|
return makeResponseError(response1(status))
|
||||||
|
}
|
||||||
|
status, err = d.receive()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if status != 0 {
|
||||||
|
return errWrite
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
} else if numblocks > 1 {
|
||||||
|
// Start multi block write.
|
||||||
|
blocksize := 1 << d.blockshift
|
||||||
|
_, err = d.card_command(cmdWriteMultipleBlock, uint32(startBlockIdx))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < numblocks; i++ {
|
||||||
|
offset := i * blocksize
|
||||||
|
err = d.waitNotBusy(d.timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.write_data(tokWRITE_MULT, data[offset:offset+blocksize])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Stop the multi write operation.
|
||||||
|
err = d.waitNotBusy(d.timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.send(tokSTOP_TRAN)
|
||||||
|
}
|
||||||
|
panic("unreachable numblocks<=0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SPICard) checkBounds(startBlockIdx int64, datalen int) (numblocks int, err error) {
|
||||||
|
if startBlockIdx >= d.numblocks {
|
||||||
|
return 0, errOOB
|
||||||
|
} else if startBlockIdx > math.MaxUint32 {
|
||||||
|
return 0, errCardNotSupported
|
||||||
|
}
|
||||||
|
tz := bits.TrailingZeros(uint(datalen))
|
||||||
|
if tz < int(d.blockshift) {
|
||||||
|
return 0, errNeedBlockLenMultiple
|
||||||
|
}
|
||||||
|
numblocks = datalen >> d.blockshift
|
||||||
|
if numblocks == 0 {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
return numblocks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SPICard) card_acmd(acmd appcommand, args uint32) (uint8, error) {
|
||||||
|
_, err := d.card_command(cmdAppCmd, 0)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return d.card_command(command(acmd), args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SPICard) card_command(cmd command, args uint32) (uint8, error) {
|
||||||
|
const transmitterBit = 1 << 6
|
||||||
|
err := d.wait_not_busy()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
buf := d.bufcmd[:6]
|
||||||
|
// Start bit is always zero; transmitter bit is one since we are Host.
|
||||||
|
|
||||||
|
buf[0] = transmitterBit | byte(cmd)
|
||||||
|
binary.BigEndian.PutUint32(buf[1:5], args)
|
||||||
|
buf[5] = crc7noshift(buf[:5]) | 1 // CRC and end bit which is always 1.
|
||||||
|
|
||||||
|
err = d.bus.Tx(buf, nil)
|
||||||
|
if cmd == cmdStopTransmission {
|
||||||
|
d.receive() // skip stuff byte for stop read.
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 512; i++ {
|
||||||
|
result, err := d.receive()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if result&0x80 == 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, errReadTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SPICard) read_data(data []byte) (err error) {
|
||||||
|
var status uint8
|
||||||
|
for {
|
||||||
|
status, err = d.receive()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status != 0xff {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.yield()
|
||||||
|
}
|
||||||
|
if status != tokSTART_BLOCK {
|
||||||
|
return errWaitStartBlock
|
||||||
|
}
|
||||||
|
err = d.bus.Tx(nil, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// CRC16 is always sent on a data block.
|
||||||
|
crchi, _ := d.receive()
|
||||||
|
crclo, _ := d.receive()
|
||||||
|
d.lastCRC = uint16(crclo) | uint16(crchi)<<8
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SPICard) wait_not_busy() error {
|
||||||
|
tm := s.timers[0].setTimeout(s.timeout)
|
||||||
|
for {
|
||||||
|
tok, err := s.receive()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if tok == 0xff {
|
||||||
|
break
|
||||||
|
} else if tm.expired() {
|
||||||
|
return errBusyTimeout
|
||||||
|
}
|
||||||
|
s.yield()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SPICard) write_data(tok byte, data []byte) error {
|
||||||
|
if len(data) > 512 {
|
||||||
|
return errors.New("data too long for write_data")
|
||||||
|
}
|
||||||
|
crc := CRC16(data)
|
||||||
|
err := s.send(tok)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.bus.Tx(data, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.send(byte(crc >> 8))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.send(byte(crc))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
status, err := s.receive()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status&_DATA_RES_MASK != _DATA_RES_ACCEPTED {
|
||||||
|
return makeResponseError(response1(status))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SPICard) receive() (byte, error) {
|
||||||
|
return s.bus.Transfer(0xFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SPICard) send(b byte) error {
|
||||||
|
_, err := s.bus.Transfer(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user