mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-03 06:27:47 +00:00
begin adding sd.Card refactor
This commit is contained in:
@@ -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[:])
|
||||
}
|
||||
}
|
||||
+409
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+22
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user