From 5b6571350d8f2cbdae28eb7c128d1141b3e5794e Mon Sep 17 00:00:00 2001 From: soypat Date: Sun, 14 Jan 2024 02:31:41 -0300 Subject: [PATCH] begin adding sd.Card refactor --- examples/sd/main.go | 54 ++++++ sd/card.go | 409 ++++++++++++++++++++++++++++++++++++++++++++ sd/card_test.go | 45 +++++ sd/definitions.go | 300 ++++++++++++++++++++++++++++++++ sd/timer.go | 22 +++ 5 files changed, 830 insertions(+) create mode 100644 examples/sd/main.go create mode 100644 sd/card.go create mode 100644 sd/card_test.go create mode 100644 sd/definitions.go create mode 100644 sd/timer.go diff --git a/examples/sd/main.go b/examples/sd/main.go new file mode 100644 index 0000000..440180c --- /dev/null +++ b/examples/sd/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "machine" + "time" + + "tinygo.org/x/drivers/sd" +) + +const ( + SPI_RX_PIN = machine.GP16 + SPI_TX_PIN = machine.GP19 + SPI_SCK_PIN = machine.GP18 + SPI_CS_PIN = machine.GP15 +) + +var ( + spibus = machine.SPI0 +) + +func main() { + time.Sleep(time.Second) + SPI_CS_PIN.Configure(machine.PinConfig{Mode: machine.PinOutput}) + err := spibus.Configure(machine.SPIConfig{ + Frequency: 250000, + Mode: 0, + SCK: SPI_SCK_PIN, + SDO: SPI_TX_PIN, + SDI: SPI_RX_PIN, + }) + if err != nil { + panic(err.Error()) + } + sdcard := sd.NewCard(spibus, SPI_CS_PIN.Set) + + err = sdcard.Init() + if err != nil { + panic(err.Error()) + } + csd := sdcard.CSD() + fmt.Printf("cid=%+v\ncsd=%+v\ncsdfmt=\n%s\ndone!", sdcard.CID(), csd, csd.String()) + + var buf [512]byte + for i := 1; i < 11; i += 1 { + time.Sleep(time.Millisecond) + err = sdcard.ReadBlock(uint32(i), buf[:]) + if err != nil { + println("err reading block", i, ":", err.Error()) + continue + } + fmt.Printf("block %d crc=%#x:\n\t%#x\n", i, sdcard.LastReadCRC(), buf[:]) + } +} diff --git a/sd/card.go b/sd/card.go new file mode 100644 index 0000000..190fb83 --- /dev/null +++ b/sd/card.go @@ -0,0 +1,409 @@ +package sd + +import ( + "encoding/binary" + "errors" + "runtime" + "strconv" + "time" + + "tinygo.org/x/drivers" +) + +var ( + 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:wait start block") + errNeed512 = errors.New("sd:need 512 bytes for I/O") + errWrite = errors.New("sd:write") + errWriteTimeout = errors.New("sd:write timeout") +) + +type digitalPinout func(b bool) + +type Card struct { + bus drivers.SPI + cs digitalPinout + bufcmd [6]byte + buf [512]byte + bufTok [1]byte + kind CardKind + cid CID + csd CSD + lastCRC uint16 +} + +func NewCard(spi drivers.SPI, cs digitalPinout) *Card { + return &Card{bus: spi, cs: cs} +} + +func (c *Card) csEnable(b bool) { c.cs(!b) } + +// LastReadCRC returns the CRC for the last ReadBlock operation. +func (c *Card) LastReadCRC() uint16 { return c.lastCRC } + +func (d *Card) Init() error { + dummy := d.buf[:] + for i := range dummy { + dummy[i] = 0xFF + } + defer d.csEnable(false) + + d.csEnable(true) + // clock card at least 100 cycles with cs high + d.bus.Tx(dummy[:10], nil) + d.csEnable(false) + + d.bus.Tx(dummy[:], nil) + + // CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts) + ok := false + tm := setTimeout(0, 2*time.Second) + for !tm.expired() { + // Wait up to 2 seconds to be the same as the Arduino + result, err := d.cmd(CMD0_GO_IDLE_STATE, 0, 0x95) + if err != nil { + return err + } + if result == _R1_IDLE_STATE { + ok = true + break + } + } + if !ok { + return errNoSDCard + } + + // CMD8: determine card version + r1, err := d.cmd(CMD8_SEND_IF_COND, 0x01AA, 0x87) + if err != nil { + return err + } + if r1.IllegalCmdError() { + d.kind = TypeSD1 + return errCardNotSupported + } 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 makeResponseError(response1(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 makeResponseError(response1(status)) + } + d.kind = TypeSD2 + } + + // initialize card and send host supports SDHC if SD2 + arg := uint32(0) + if d.kind == TypeSD2 { + arg = 0x40000000 + } + + // check for timeout + ok = false + tm = setTimeout(0, 2*time.Second) + for !tm.expired() { + r1, err = d.appCmd(ACMD41_SD_APP_OP_COND, arg) + if err != nil { + return err + } + if r1 == 0 { + break + } + } + if r1 != 0 { + return makeResponseError(r1) + } + + // if SD2 read OCR register to check for SDHC card + if d.kind == TypeSD2 { + err := d.cmdEnsure0Status(CMD58_READ_OCR, 0, 0xFF) + if err != nil { + return err + } + + statusb, err := d.bus.Transfer(byte(0xFF)) + if err != nil { + return err + } + if (statusb & 0xC0) == 0xC0 { + d.kind = TypeSDHC + } + // discard rest of ocr - contains allowed voltage range + for i := 1; i < 4; i++ { + d.bus.Transfer(byte(0xFF)) + } + } + err = d.cmdEnsure0Status(CMD16_SET_BLOCKLEN, 0x0200, 0xff) + if err != nil { + return err + } + + // read CID + d.cid, err = d.readCID() + if err != nil { + return err + } + d.csd, err = d.readCSD() + if err != nil { + return err + } + return nil +} + +// ReadData reads 512 bytes from sdcard into dst. +func (d *Card) ReadBlock(block uint32, dst []byte) error { + if len(dst) != 512 { + return errNeed512 + } + + // use address if not SDHC card + if d.kind != TypeSDHC { + block <<= 9 + } + err := d.cmdEnsure0Status(CMD17_READ_SINGLE_BLOCK, 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(byte(0xFF)) + lo, _ := d.bus.Transfer(byte(0xFF)) + d.lastCRC = uint16(hi)<<8 | uint16(lo) + return nil +} + +// WriteBlock writes 512 bytes from dst to sdcard. +func (d *Card) WriteBlock(block uint32, src []byte) error { + if len(src) != 512 { + return errNeed512 + } + + // use address if not SDHC card + if d.kind != TypeSDHC { + block <<= 9 + } + err := d.cmdEnsure0Status(CMD24_WRITE_BLOCK, 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(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 errWrite + } + + // wait no busy + err = d.waitNotBusy(600 * time.Millisecond) + if err != nil { + return errWriteTimeout + } + + return nil +} + +// CID returns a copy of the Card Identification Register value last read. +func (d *Card) CID() CID { return d.cid } + +// CSD returns a copy of the Card Specific Data Register value last read. +func (d *Card) CSD() CSD { return d.csd } + +func (d *Card) readCID() (CID, error) { + buf := d.buf[len(d.buf)-16:] + if err := d.readRegister(CMD10_SEND_CID, buf); err != nil { + return CID{}, err + } + return DecodeCID(buf) +} + +func (d *Card) readCSD() (CSD, error) { + buf := d.buf[len(d.buf)-16:] + if err := d.readRegister(CMD9_SEND_CSD, buf); err != nil { + return CSD{}, err + } + return DecodeCSD(buf) +} + +func (d *Card) readRegister(cmd uint8, dst []byte) error { + err := d.cmdEnsure0Status(cmd, 0, 0xFF) + if err != nil { + return err + } + 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 + } + // skip CRC. + d.bus.Transfer(byte(0xFF)) + d.bus.Transfer(byte(0xFF)) + d.csEnable(false) + return nil +} + +func (d *Card) appCmd(cmd byte, arg uint32) (response1, error) { + status, err := d.cmd(CMD55_APP_CMD, 0, 0xFF) + if err != nil { + return status, err + } + return d.cmd(cmd, arg, 0xFF) +} + +func (d *Card) cmdEnsure0Status(cmd byte, arg uint32, crc byte) error { + status, err := d.cmd(cmd, arg, crc) + if err != nil { + return err + } + if status != 0 { + return makeResponseError(status) + } + return nil +} + +func (d *Card) cmd(cmd byte, arg uint32, crc byte) (response1, error) { + d.csEnable(true) + + if cmd != 12 { + d.waitNotBusy(300 * time.Millisecond) + } + + // create and send the command + buf := d.bufcmd[:] + buf[0] = 0x40 | cmd + binary.BigEndian.PutUint32(buf[1:5], 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) + d.buf[0] = 0xFF + dummy := d.buf[:1] + for i := 0; i < 0xFFFF; i++ { + d.bus.Tx(dummy, d.bufTok[:]) + response := response1(d.bufTok[0]) + if (response & 0x80) == 0 { + return response, nil + } + } + + // TODO + //// timeout + d.csEnable(false) + d.bus.Transfer(byte(0xFF)) + + return 0xFF, nil // -1 +} + +func (d *Card) waitNotBusy(timeout time.Duration) error { + tm := setTimeout(1, timeout) + for !tm.expired() { + r, err := d.bus.Transfer(byte(0xFF)) + if err != nil { + return err + } + if r == 0xFF { + return nil + } + runtime.Gosched() + } + return nil +} + +func (d *Card) waitStartBlock() error { + status := byte(0xFF) + + tm := setTimeout(0, 300*time.Millisecond) + for !tm.expired() { + var err error + status, err = d.bus.Transfer(byte(0xFF)) + if err != nil { + d.csEnable(false) + return err + } + if status != 0xFF { + break + } + } + + if status != 254 { + d.csEnable(false) + return errWaitStartBlock + } + + return nil +} + +type response1Err struct { + context string + status response1 +} + +func (e response1Err) Error() string { + if e.context != "" { + return "sd:" + e.context + " " + strconv.Itoa(int(e.status)) + } + return "sd:status " + strconv.Itoa(int(e.status)) +} + +func makeResponseError(status response1) error { + return response1Err{ + status: status, + } +} diff --git a/sd/card_test.go b/sd/card_test.go new file mode 100644 index 0000000..91fa129 --- /dev/null +++ b/sd/card_test.go @@ -0,0 +1,45 @@ +package sd + +import ( + "encoding/hex" + "testing" +) + +func TestCRC(t *testing.T) { + tests := []struct { + block string + wantcrc uint16 + }{ + { + block: "fa33c08ed0bc007c8bf45007501ffbfcbf0006b90001f2a5ea1d060000bebe07b304803c80740e803c00751c83c610fecb75efcd188b148b4c028bee83c610fecb741a803c0074f4be8b06ac3c00740b56bb0700b40ecd105eebf0ebfebf0500bb007cb8010257cd135f730c33c0cd134f75edbea306ebd3bec206bffe7d813d55aa75c78bf5ea007c0000496e76616c696420706172746974696f6e207461626c65004572726f72206c6f6164696e67206f7065726174696e672073797374656d004d697373696e67206f7065726174696e672073797374656d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000094c6dffd0000000401040cfec2ff000800000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055aa", + wantcrc: 0x52ce, + }, + } + + for _, tt := range tests { + b, err := hex.DecodeString(tt.block) + if err != nil { + t.Fatal(err) + } + gotcrc := CRC16(b) + if gotcrc != tt.wantcrc { + t.Errorf("calculateCRC(%s) = %#x, want %#x", tt.block, gotcrc, tt.wantcrc) + } + } +} + +func CRC16(buf []byte) (sum uint16) { + const poly uint16 = 0x1021 // Generator polynomial G(x) = x^16 + x^12 + x^5 + 1 + var crc uint16 = 0x0000 // Initial value + for _, b := range buf { + crc ^= (uint16(b) << 8) // Shift byte into MSB of crc + for i := 0; i < 8; i++ { // Process each bit + if crc&0x8000 != 0 { + crc = (crc << 1) ^ poly + } else { + crc <<= 1 + } + } + } + return crc +} diff --git a/sd/definitions.go b/sd/definitions.go new file mode 100644 index 0000000..433d8bf --- /dev/null +++ b/sd/definitions.go @@ -0,0 +1,300 @@ +package sd + +import ( + "bytes" + "encoding/binary" + "io" + "strconv" + "time" +) + +type CardKind uint8 + +const ( + // card types + TypeSD1 CardKind = 1 // Standard capacity V1 SD card + TypeSD2 CardKind = 2 // Standard capacity V2 SD card + TypeSDHC CardKind = 3 // High Capacity SD card +) + +type CID struct { + ManufacturerID uint8 // 0:1 + OEMApplicationID uint16 // 1:3 + prodName [5]byte // 3:8 + // productRevision n.m + productRev byte // 8:9 + ProductSerialNumber uint32 // 9:13 + // Manufacturing date bitfield: + // - yearhi=0:4 + // - reserved=4:8 + // - month=8:12 + // - yearlo=12:16 + date [2]byte // 13:15 +} + +func DecodeCID(b []byte) (CID, error) { + if len(b) < 16 { + return CID{}, io.ErrShortBuffer + } + cid := CID{ + ManufacturerID: b[0], + OEMApplicationID: binary.BigEndian.Uint16(b[1:3]), + prodName: [5]byte{b[3], b[4], b[5], b[6], b[7]}, + productRev: b[8], + ProductSerialNumber: binary.BigEndian.Uint32(b[9:13]), + date: [2]byte{b[13], b[14]}, + } + + return cid, nil +} + +func (c *CID) ProductName() []byte { + return upToNull(c.prodName[:]) +} + +func (c *CID) ProductRevision() (n, m uint8) { + return c.productRev >> 4, c.productRev & 0x0F +} + +/* +CSD Register Fields: +Name: Field: Width: Value: CSD-Slice: +CSD Structure CSD_STRUCTURE 2 00b R[127:128] +TAAC TAAC 8 00h R[119:113] +NSAC NSAC 8 00h R[111:105] +TRAN_SPEED TRAN_SPEED 8 32h or 5Ah R[103:97] +CCC CCC 12 01x110110101b R[95:85] +READ_BL_LEN READ_BL_LEN 4 xh R[83:81] +READ_BL_PARTIAL READ_BL_PARTIAL 1 1b R[79:80] +WRITE_BLK_MISALIGN WRITE_BLK_MISALIGN 1 xb R[78:79] +READ_BLK_MISALIGN READ_BLK_MISALIGN 1 xb R[77:78] +DSR_IMP DSR_IMP 1 xb R[76:77] +C_SIZE C_SIZE 12 xxxh R[73:63] +VDD_R_CURR_MIN VDD_R_CURR_MIN 3 xxxb R[61:60] +VDD_R_CURR_MAX VDD_R_CURR_MAX 3 xxxb R[58:57] +VDD_W_CURR_MIN VDD_W_CURR_MIN 3 xxxb R[55:54] +VDD_W_CURR_MAX VDD_W_CURR_MAX 3 xxxb R[52:51] +C_SIZE_MULT C_SIZE_MULT 3 xxxb R[49:48] +ERASE_BLK_EN ERASE_BLK_EN 1 xb R[46:47] +SECTOR_SIZE SECTOR_SIZE 7 xxxxxxxb R[45:40] +WP_GRP_SIZE WP_GRP_SIZE 7 xxxxxxxb R[38:33] +WP_GRP_ENABLE WP_GRP_ENABLE 1 xb R[31:32] +R2W_FACTOR R2W_FACTOR 2 xxxb R[28:27] +WRITE_BL_LEN WRITE_BL_LEN 4 xxxxb R[25:23] +WRITE_BL_PARTIAL WRITE_BL_PARTIAL 1 xb R[21:22] +FILE_FORMAT_GRP FILE_FORMAT_GRP 1 xb R[15:16] +COPY COPY 1 xb R[14:15] +PERM_WRITE_PROTECT PERM_WRITE_PROTECT 1 xb R[13:14] +TMP_WRITE_PROTECT TMP_WRITE_PROTECT 1 xb R[12:13] +FILE_FORMAT FILE_FORMAT 2 xxb R[11:11] +CRC CRC 7 xxxxxxxb R[7:2] +Not Used - 1 1b R[0:1] + +Note: 'R' indicates read-only fields, 'R/W' indicates read/write fields. +The values in the 'CSD-Slice' column indicate the bit positions in the CSD register. +*/ + +// CSD is the Card Specific Data register, a 128-bit (16-byte) register that defines how +// the SD card standard communicates with the memory field or register. +type CSD struct { + data [16]byte +} + +type CSDv1 struct { + CSD +} + +func DecodeCSD(b []byte) (CSD, error) { + if len(b) < 16 { + return CSD{}, io.ErrShortBuffer + } + csd := CSD{} + copy(csd.data[:], b) + return csd, nil +} + +// CSDStructure returns the version of the CSD structure. +func (c *CSD) CSDStructure() uint8 { return c.data[0] >> 6 } + +func (c CSD) MustV1() CSDv1 { + if c.CSDStructure() != 1 { + panic("CSD is not version 1") + } + return CSDv1{CSD: c} +} + +// TAAC returns the Time Access Attribute Class (data read access-time-1). +func (c *CSD) TAAC() TAAC { return TAAC(c.data[1]) } + +// NSAC returns the Data Read Access-time 2 in CLK cycles (NSAC*100). +func (c *CSD) NSAC() uint8 { return c.data[2] } + +// TransferSpeed returns the Max Data Transfer Rate. Either 0x32 or 0x5A. +func (c *CSD) TransferSpeed() TransferSpeed { return TransferSpeed(c.data[3]) } + +// CCC returns the Card Command Classes. +func (c *CSD) CCC() uint16 { + return uint16(c.data[4])<<4 | uint16(c.data[5]&0xf0)>>4 +} + +// ReadBlockLen returns the Max Read Data Block Length in bytes. +func (c *CSD) ReadBlockLen() uint16 { return 1 << (c.data[5] & 0x0F) } + +func (c *CSD) ReadBlockPartial() bool { return c.data[6]&(1<<7) != 0 } +func (c *CSD) WriteBlockMisalignment() bool { return c.data[6]&(1<<6) != 0 } +func (c *CSD) ReadBlockMisalignment() bool { return c.data[6]&(1<<5) != 0 } + +// ImplementsDSR returns whether the card implements the DSR register. +func (c *CSD) ImplementsDSR() bool { return c.data[6]&(1<<4) != 0 } + +func (c *CSDv1) CSize() uint16 { + // Jesus, why did SD make this so complicated? + return uint16(c.data[8]>>6) | uint16(c.data[7])<<2 | uint16(c.data[6]&0b11)<<10 +} + +func (c *CSD) String() string { + buf := make([]byte, 0, 64) + return string(c.appendf(buf, '\n')) +} + +func (c *CSD) appendf(b []byte, delim byte) []byte { + b = appendnum(b, "CSDStructure", uint64(c.CSDStructure()), delim) + b = appendnum(b, "TimeAccess_ns", uint64(c.TAAC().AccessTime()), delim) + b = appendnum(b, "NSAC", uint64(c.NSAC()), delim) + b = appendnum(b, "Tx_kb/s", uint64(c.TransferSpeed().RateKilobits()), delim) + b = appendnum(b, "CCC", uint64(c.CCC()), delim) + b = appendnum(b, "ReadBlockLen", uint64(c.ReadBlockLen()), delim) + b = appendbit(b, "ReadBlockPartial", c.ReadBlockPartial(), delim) + b = appendbit(b, "WriteBlockMisalignment", c.WriteBlockMisalignment(), delim) + b = appendbit(b, "ReadBlockMisalignment", c.ReadBlockMisalignment(), delim) + b = appendbit(b, "ImplementsDSR", c.ImplementsDSR(), delim) + return b +} + +func appendnum(b []byte, label string, n uint64, delim byte) []byte { + b = append(b, label...) + b = append(b, ':') + b = strconv.AppendUint(b, n, 10) + b = append(b, delim) + return b +} + +func appendbit(b []byte, label string, n bool, delim byte) []byte { + b = append(b, label...) + b = append(b, ':') + b = append(b, '0'+b2u8(n)) + b = append(b, delim) + return b +} + +func upToNull(buf []byte) []byte { + nullIdx := bytes.IndexByte(buf, 0) + if nullIdx < 0 { + return buf + } + return buf[:nullIdx] +} + +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 +) + +type ( + TransferSpeed uint8 + TAAC uint8 +) + +var log10table = [...]int64{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, +} + +// RateMegabits returns the transfer rate in megabits per second. +func (t TransferSpeed) RateKilobits() int64 { + return 100 * log10table[t&0b111] +} + +func (t TAAC) AccessTime() (d time.Duration) { + return time.Duration(log10table[t&0b111]) * time.Nanosecond +} + +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 +) + +type response1 uint8 + +func (r response1) IsIdle() bool { return r&_R1_IDLE_STATE != 0 } +func (r response1) IllegalCmdError() bool { return r&_R1_ILLEGAL_COMMAND != 0 } +func (r response1) CRCError() bool { return r&_R1_COM_CRC_ERROR != 0 } +func (r response1) EraseReset() bool { return r&_R1_ERASE_RESET != 0 } +func (r response1) EraseSeqError() bool { return r&_R1_ERASE_SEQUENCE_ERROR != 0 } +func (r response1) AddressError() bool { return r&_R1_ADDRESS_ERROR != 0 } +func (r response1) ParamError() bool { return r&_R1_PARAMETER_ERROR != 0 } + +func b2u8(b bool) uint8 { + if b { + return 1 + } + return 0 +} diff --git a/sd/timer.go b/sd/timer.go new file mode 100644 index 0000000..53c2754 --- /dev/null +++ b/sd/timer.go @@ -0,0 +1,22 @@ +package sd + +import ( + "time" +) + +var timeoutTimer [2]timer + +type timer struct { + start int64 + timeout int64 +} + +func setTimeout(timerID int, timeout time.Duration) *timer { + timeoutTimer[timerID].start = time.Now().UnixNano() + timeoutTimer[timerID].timeout = timeout.Nanoseconds() + return &timeoutTimer[timerID] +} + +func (t timer) expired() bool { + return time.Now().UnixNano() > (t.start + t.timeout) +}