mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-05 07:27:49 +00:00
still working on consolidation of init
This commit is contained in:
+3
-20
@@ -34,7 +34,7 @@ func main() {
|
||||
panic(err.Error())
|
||||
}
|
||||
sdcard := sd.NewSPICard(spibus, SPI_CS_PIN.Set)
|
||||
|
||||
println("start init")
|
||||
err = sdcard.Init()
|
||||
if err != nil {
|
||||
panic("sd card init:" + err.Error())
|
||||
@@ -46,28 +46,11 @@ func main() {
|
||||
err = spibus.Configure(spicfg)
|
||||
|
||||
cid := sdcard.CID()
|
||||
pname := cid.ProductName()
|
||||
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())
|
||||
fmt.Printf("name=%s\ncsd=\n%s\n", cid.ProductName(), csd.String())
|
||||
|
||||
var buf [512]byte
|
||||
for i := 0; i < 11; i += 1 {
|
||||
err = sdcard.ReadBlock(0, buf[:])
|
||||
err = sdcard.ReadBlocks(buf[:], 0)
|
||||
if err != nil {
|
||||
println("err reading block", i, ":", err.Error())
|
||||
continue
|
||||
|
||||
+60
-136
@@ -9,40 +9,45 @@ import (
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// See rustref.go for the new implementation.
|
||||
|
||||
var (
|
||||
errBadCSDCID = errors.New("sd:bad CSD/CID in CRC or always1")
|
||||
errNoSDCard = errors.New("sd:no card")
|
||||
errCardNotSupported = errors.New("sd:card not supported")
|
||||
errCmd8 = errors.New("sd:cmd8")
|
||||
errCmdOCR = errors.New("sd:cmd_ocr")
|
||||
errCmdBlkLen = errors.New("sd:cmd_blklen")
|
||||
errAcmdAppCond = errors.New("sd:acmd_appOrCond")
|
||||
errWaitStartBlock = errors.New("sd:did not find start block token")
|
||||
errNeed512 = errors.New("sd:need 512 bytes for I/O")
|
||||
errWrite = errors.New("sd:write")
|
||||
errWriteTimeout = errors.New("sd:write timeout")
|
||||
errBusyTimeout = errors.New("sd:busy card timeout")
|
||||
errOOB = errors.New("sd:oob block access")
|
||||
errNoblocks = errors.New("sd:no readable blocks")
|
||||
errCmdGeneric = errors.New("sd:command error")
|
||||
errBadCSDCID = errors.New("sd:bad CSD/CID in CRC or always1")
|
||||
errNoSDCard = errors.New("sd:no card")
|
||||
errCardNotSupported = errors.New("sd:card not supported")
|
||||
errCmd8 = errors.New("sd:cmd8")
|
||||
errCmdOCR = errors.New("sd:cmd_ocr")
|
||||
errCmdBlkLen = errors.New("sd:cmd_blklen")
|
||||
errAcmdAppCond = errors.New("sd:acmd_appOrCond")
|
||||
errWaitStartBlock = errors.New("sd:did not find start block token")
|
||||
errNeedBlockLenMultiple = errors.New("sd:need blocksize multiple for I/O")
|
||||
errWrite = errors.New("sd:write")
|
||||
errWriteTimeout = errors.New("sd:write timeout")
|
||||
errReadTimeout = errors.New("sd:read timeout")
|
||||
errBusyTimeout = errors.New("sd:busy card timeout")
|
||||
errOOB = errors.New("sd:oob block access")
|
||||
errNoblocks = errors.New("sd:no readable blocks")
|
||||
errCmdGeneric = errors.New("sd:command error")
|
||||
)
|
||||
|
||||
type digitalPinout func(b bool)
|
||||
|
||||
type SPICard struct {
|
||||
bus drivers.SPI
|
||||
cs digitalPinout
|
||||
bufcmd [6]byte
|
||||
buf [512]byte
|
||||
bufTok [1]byte
|
||||
kind CardKind
|
||||
cid CID
|
||||
csd CSD
|
||||
lastCRC uint16
|
||||
timers [2]timer
|
||||
numblocks int64
|
||||
timeout time.Duration
|
||||
wait time.Duration
|
||||
bus drivers.SPI
|
||||
cs digitalPinout
|
||||
bufcmd [6]byte
|
||||
buf [512]byte
|
||||
bufTok [1]byte
|
||||
kind CardKind
|
||||
cid CID
|
||||
csd CSD
|
||||
lastCRC uint16
|
||||
// shift to calculate blocksize, taken from CSD.
|
||||
blockshift uint8
|
||||
timers [2]timer
|
||||
numblocks int64
|
||||
timeout time.Duration
|
||||
wait time.Duration
|
||||
// relative card address.
|
||||
rca uint32
|
||||
lastr1 r1
|
||||
@@ -67,11 +72,12 @@ func (c *SPICard) setTimeout(timeout time.Duration) {
|
||||
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.
|
||||
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
|
||||
// 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)
|
||||
|
||||
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
|
||||
println("first timer")
|
||||
ok := false
|
||||
tm := d.timers[0].setTimeout(2 * time.Second)
|
||||
for !tm.expired() {
|
||||
@@ -148,9 +155,11 @@ func (d *SPICard) Init() error {
|
||||
}
|
||||
|
||||
// check for timeout
|
||||
println("app cmd")
|
||||
ok = false
|
||||
tm = tm.setTimeout(2 * time.Second)
|
||||
for !tm.expired() {
|
||||
println("timer")
|
||||
r1, err = d.appCmd(acmdSD_APP_OP_COND, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -162,7 +171,7 @@ func (d *SPICard) Init() error {
|
||||
if r1 != 0 {
|
||||
return makeResponseError(r1)
|
||||
}
|
||||
|
||||
println("preensure")
|
||||
// if SD2 read OCR register to check for SDHC card
|
||||
if d.kind == TypeSD2 {
|
||||
err := d.cmdEnsure0Status(cmdReadOCR, 0, 0xFF)
|
||||
@@ -182,11 +191,16 @@ func (d *SPICard) Init() error {
|
||||
d.bus.Transfer(0xFF)
|
||||
}
|
||||
}
|
||||
println("ensure")
|
||||
err = d.cmdEnsure0Status(cmdSetBlocklen, 0x0200, 0xff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println("get to update csdid")
|
||||
return d.updateCSDCID()
|
||||
}
|
||||
|
||||
func (d *SPICard) updateCSDCID() (err error) {
|
||||
// read CID
|
||||
d.cid, err = d.readCID()
|
||||
if err != nil {
|
||||
@@ -196,113 +210,23 @@ func (d *SPICard) Init() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nb := d.csd.NumberOfBlocks()
|
||||
if nb > math.MaxUint32 {
|
||||
return errCardNotSupported
|
||||
} else if nb == 0 {
|
||||
blockshift := d.csd.ReadBlockLenShift()
|
||||
blocklen := uint16(1) << blockshift
|
||||
capacity := d.csd.DeviceCapacity()
|
||||
if blocklen == 0 || capacity < uint64(blocklen) {
|
||||
return errNoblocks
|
||||
}
|
||||
nb := capacity / uint64(blocklen)
|
||||
if nb > math.MaxUint32 {
|
||||
return errCardNotSupported
|
||||
}
|
||||
d.blockshift = blockshift
|
||||
d.numblocks = int64(nb)
|
||||
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 {
|
||||
return uint64(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
|
||||
func (d *SPICard) NumberOfBlocks() int64 {
|
||||
return d.numblocks
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return 0, err
|
||||
}
|
||||
if cmd == 12 {
|
||||
if cmd == cmdStopTransmission {
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(0xFF)
|
||||
}
|
||||
|
||||
tm := d.timers[0].setTimeout(d.timeout)
|
||||
tm := d.timers[1].setTimeout(d.timeout)
|
||||
for {
|
||||
tok, _ := d.bus.Transfer(0xff)
|
||||
response := response1(tok)
|
||||
if (response & 0x80) == 0 {
|
||||
// NOMINAL FUNCTION EXIT HERE
|
||||
return response, nil
|
||||
} else if tm.expired() {
|
||||
break
|
||||
}
|
||||
d.yield()
|
||||
}
|
||||
|
||||
println("============== BAD EXIT ================")
|
||||
d.csEnable(false)
|
||||
d.bus.Transfer(0xFF)
|
||||
return 0xFF, errCmdGeneric
|
||||
@@ -435,7 +360,6 @@ func (d *SPICard) waitStartBlock() error {
|
||||
if _, ok := d.waitToken(d.timeout, tokSTART_BLOCK); ok {
|
||||
return nil
|
||||
}
|
||||
d.csEnable(false)
|
||||
return errWaitStartBlock
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -13,6 +13,10 @@ import (
|
||||
|
||||
type CardKind uint8
|
||||
|
||||
func isTimeout(err error) bool {
|
||||
return err == errReadTimeout || err == errWriteTimeout || err == errBusyTimeout
|
||||
}
|
||||
|
||||
const (
|
||||
// card types
|
||||
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.
|
||||
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
|
||||
func (c *CSD) AllowsReadBlockPartial() bool { return c.data[6]&(1<<7) != 0 }
|
||||
|
||||
@@ -15,6 +15,9 @@ const (
|
||||
_R1_ERASE_SEQUENCE_ERROR = 1 << 4
|
||||
_R1_ADDRESS_ERROR = 1 << 5
|
||||
_R1_PARAMETER_ERROR = 1 << 6
|
||||
|
||||
_DATA_RES_MASK = 0x1F
|
||||
_DATA_RES_ACCEPTED = 0x05
|
||||
)
|
||||
|
||||
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...
|
||||
func (d *SPICard) readR1() (resp r1, err error) {
|
||||
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