From 46cd56951c62b7c3877fa3eb20d5a35d54edd78f Mon Sep 17 00:00:00 2001 From: soypat Date: Sun, 14 Jan 2024 14:17:04 -0300 Subject: [PATCH] failing CRC7 implementation --- examples/sd/main.go | 18 +++--- sd/card.go | 133 ++++++++++++++++++++++++++------------------ sd/card_test.go | 59 +++++++++++++++++++- sd/definitions.go | 64 ++++++++++++++++----- 4 files changed, 198 insertions(+), 76 deletions(-) diff --git a/examples/sd/main.go b/examples/sd/main.go index 555da63..f4e5f39 100644 --- a/examples/sd/main.go +++ b/examples/sd/main.go @@ -32,7 +32,7 @@ func main() { if err != nil { panic(err.Error()) } - sdcard := sd.NewCard(spibus, SPI_CS_PIN.Set) + sdcard := sd.NewSPICard(spibus, SPI_CS_PIN.Set) err = sdcard.Init() if err != nil { @@ -47,20 +47,24 @@ func main() { data := csd.RawCopy() crc := sd.CRC7(data[:15]) always1 := data[15]&(1<<7) != 0 - println("CSD not valid got", crc, "want", data[15]&^(1<<7), "always1:", always1) + 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()) - return + var buf [512]byte - for i := 1; i < 11; i += 1 { - time.Sleep(time.Millisecond) - err = sdcard.ReadBlock(uint32(i), buf[:]) + for i := 0; i < 11; i += 1 { + time.Sleep(100 * time.Millisecond) + err = sdcard.ReadBlock(int64(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[:]) + expectCRC := sd.CRC16(buf[:]) + fmt.Printf("block %d theircrc=%#x ourcrc=%#x:\n\t%#x\n", i, sdcard.LastReadCRC(), expectCRC, buf[:]) } } diff --git a/sd/card.go b/sd/card.go index 51f32db..40914bf 100644 --- a/sd/card.go +++ b/sd/card.go @@ -3,6 +3,7 @@ package sd import ( "encoding/binary" "errors" + "math" "runtime" "strconv" "time" @@ -21,33 +22,36 @@ var ( errNeed512 = errors.New("sd:need 512 bytes for I/O") errWrite = errors.New("sd:write") errWriteTimeout = errors.New("sd:write timeout") + errOOB = errors.New("sd:oob block access") + errNoblocks = errors.New("sd:no readable blocks") ) 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 - timers [2]timer +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 } -func NewCard(spi drivers.SPI, cs digitalPinout) *Card { - return &Card{bus: spi, cs: cs} +func NewSPICard(spi drivers.SPI, cs digitalPinout) *SPICard { + return &SPICard{bus: spi, cs: cs} } -func (c *Card) 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 *Card) LastReadCRC() uint16 { return c.lastCRC } +func (c *SPICard) LastReadCRC() uint16 { return c.lastCRC } -func (d *Card) Init() error { +func (d *SPICard) Init() error { dummy := d.buf[:] for i := range dummy { dummy[i] = 0xFF @@ -92,7 +96,7 @@ func (d *Card) Init() error { status := byte(0) for i := 0; i < 3; i++ { var err error - status, err = d.bus.Transfer(byte(0xFF)) + status, err = d.bus.Transfer(0xFF) if err != nil { return err } @@ -103,7 +107,7 @@ func (d *Card) Init() error { for i := 3; i < 4; i++ { var err error - status, err = d.bus.Transfer(byte(0xFF)) + status, err = d.bus.Transfer(0xFF) if err != nil { return err } @@ -143,7 +147,7 @@ func (d *Card) Init() error { return err } - statusb, err := d.bus.Transfer(byte(0xFF)) + statusb, err := d.bus.Transfer(0xFF) if err != nil { return err } @@ -152,7 +156,7 @@ func (d *Card) Init() error { } // discard rest of ocr - contains allowed voltage range for i := 1; i < 4; i++ { - d.bus.Transfer(byte(0xFF)) + d.bus.Transfer(0xFF) } } err = d.cmdEnsure0Status(CMD16_SET_BLOCKLEN, 0x0200, 0xff) @@ -169,20 +173,33 @@ func (d *Card) Init() error { if err != nil { return err } + nb := d.csd.NumberOfBlocks() + if nb > math.MaxUint32 { + return errCardNotSupported + } else if nb == 0 { + return errNoblocks + } + d.numblocks = int64(nb) return nil } -// ReadData reads 512 bytes from sdcard into dst. -func (d *Card) ReadBlock(block uint32, dst []byte) error { +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(CMD17_READ_SINGLE_BLOCK, block, 0xFF) + err := d.cmdEnsure0Status(CMD17_READ_SINGLE_BLOCK, uint32(block), 0xFF) if err != nil { return err } @@ -198,23 +215,25 @@ func (d *Card) ReadBlock(block uint32, dst []byte) error { } // skip CRC (2byte) - hi, _ := d.bus.Transfer(byte(0xFF)) - lo, _ := d.bus.Transfer(byte(0xFF)) + 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 *Card) WriteBlock(block uint32, src []byte) error { +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(CMD24_WRITE_BLOCK, block, 0xFF) + err := d.cmdEnsure0Status(CMD24_WRITE_BLOCK, uint32(block), 0xFF) if err != nil { return err } @@ -229,11 +248,11 @@ func (d *Card) WriteBlock(block uint32, src []byte) error { } // send dummy CRC (2 byte) - d.bus.Transfer(byte(0xFF)) - d.bus.Transfer(byte(0xFF)) + d.bus.Transfer(0xFF) + d.bus.Transfer(0xFF) // Data Resp. - r, err := d.bus.Transfer(byte(0xFF)) + r, err := d.bus.Transfer(0xFF) if err != nil { return err } @@ -251,12 +270,12 @@ func (d *Card) WriteBlock(block uint32, src []byte) error { } // CID returns a copy of the Card Identification Register value last read. -func (d *Card) CID() CID { return d.cid } +func (d *SPICard) 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 *SPICard) CSD() CSD { return d.csd } -func (d *Card) readCID() (CID, error) { +func (d *SPICard) readCID() (CID, error) { buf := d.buf[len(d.buf)-16:] if err := d.readRegister(CMD10_SEND_CID, buf); err != nil { return CID{}, err @@ -264,7 +283,7 @@ func (d *Card) readCID() (CID, error) { return DecodeCID(buf) } -func (d *Card) readCSD() (CSD, error) { +func (d *SPICard) readCSD() (CSD, error) { buf := d.buf[len(d.buf)-16:] if err := d.readRegister(CMD9_SEND_CSD, buf); err != nil { return CSD{}, err @@ -272,7 +291,7 @@ func (d *Card) readCSD() (CSD, error) { return DecodeCSD(buf) } -func (d *Card) readRegister(cmd uint8, dst []byte) error { +func (d *SPICard) readRegister(cmd uint8, dst []byte) error { err := d.cmdEnsure0Status(cmd, 0, 0xFF) if err != nil { return err @@ -282,20 +301,20 @@ func (d *Card) readRegister(cmd uint8, dst []byte) error { } // transfer data for i := uint16(0); i < 16; i++ { - r, err := d.bus.Transfer(byte(0xFF)) + r, err := d.bus.Transfer(0xFF) if err != nil { return err } dst[i] = r } // skip CRC. - d.bus.Transfer(byte(0xFF)) - d.bus.Transfer(byte(0xFF)) + d.bus.Transfer(0xFF) + d.bus.Transfer(0xFF) d.csEnable(false) return nil } -func (d *Card) appCmd(cmd byte, arg uint32) (response1, error) { +func (d *SPICard) appCmd(cmd byte, arg uint32) (response1, error) { status, err := d.cmd(CMD55_APP_CMD, 0, 0xFF) if err != nil { return status, err @@ -303,7 +322,7 @@ func (d *Card) appCmd(cmd byte, arg uint32) (response1, error) { return d.cmd(cmd, arg, 0xFF) } -func (d *Card) cmdEnsure0Status(cmd byte, arg uint32, crc byte) error { +func (d *SPICard) cmdEnsure0Status(cmd byte, arg uint32, crc byte) error { status, err := d.cmd(cmd, arg, crc) if err != nil { return err @@ -314,7 +333,17 @@ func (d *Card) cmdEnsure0Status(cmd byte, arg uint32, crc byte) error { return nil } -func (d *Card) cmd(cmd byte, arg uint32, crc byte) (response1, error) { +func putCmd(dst []byte, cmd byte, arg uint32) { + if len(dst) < 6 { + panic("bad buflength") + } + dst[0] = 0x40 | cmd + binary.BigEndian.PutUint32(dst[1:5], arg) + dst[5] = CRC7(dst[:5])<<1 | 1 // CRC and stop bit. +} + +// 0100000000000000000000000000000000000000 +func (d *SPICard) cmd(cmd byte, arg uint32, crc byte) (response1, error) { d.csEnable(true) if cmd != 12 { @@ -322,9 +351,8 @@ func (d *Card) cmd(cmd byte, arg uint32, crc byte) (response1, error) { } // create and send the command - buf := d.bufcmd[:] - buf[0] = 0x40 | cmd - binary.BigEndian.PutUint32(buf[1:5], arg) + buf := d.bufcmd[:6] + putCmd(buf, cmd, arg) buf[5] = crc err := d.bus.Tx(buf, nil) if err != nil { @@ -332,14 +360,13 @@ func (d *Card) cmd(cmd byte, arg uint32, crc byte) (response1, error) { } if cmd == 12 { // skip 1 byte - d.bus.Transfer(byte(0xFF)) + d.bus.Transfer(0xFF) } // wait for the response (response[7] == 0) - d.buf[0] = 0xFF - dummy := d.buf[:1] + buf[0] = 0xFF for i := 0; i < 0xFFFF; i++ { - d.bus.Tx(dummy, d.bufTok[:]) + d.bus.Tx(buf[:1], d.bufTok[:]) response := response1(d.bufTok[0]) if (response & 0x80) == 0 { return response, nil @@ -349,15 +376,15 @@ func (d *Card) cmd(cmd byte, arg uint32, crc byte) (response1, error) { // TODO //// timeout d.csEnable(false) - d.bus.Transfer(byte(0xFF)) + d.bus.Transfer(0xFF) return 0xFF, nil // -1 } -func (d *Card) waitNotBusy(timeout time.Duration) error { +func (d *SPICard) waitNotBusy(timeout time.Duration) error { tm := d.timers[1].setTimeout(timeout) for !tm.expired() { - r, err := d.bus.Transfer(byte(0xFF)) + r, err := d.bus.Transfer(0xFF) if err != nil { return err } @@ -369,12 +396,12 @@ func (d *Card) waitNotBusy(timeout time.Duration) error { return nil } -func (d *Card) waitStartBlock() error { +func (d *SPICard) waitStartBlock() error { status := byte(0xFF) tm := d.timers[0].setTimeout(300 * time.Millisecond) for !tm.expired() { var err error - status, err = d.bus.Transfer(byte(0xFF)) + status, err = d.bus.Transfer(0xFF) if err != nil { d.csEnable(false) return err diff --git a/sd/card_test.go b/sd/card_test.go index 419723a..f6dda74 100644 --- a/sd/card_test.go +++ b/sd/card_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestCRC(t *testing.T) { +func TestCRC16(t *testing.T) { tests := []struct { block string wantcrc uint16 @@ -27,3 +27,60 @@ func TestCRC(t *testing.T) { } } } + +func TestCRC7(t *testing.T) { + const cmdSendMask = 0x40 + tests := []struct { + data []byte + wantCRC uint8 + }{ + { // See CRC7 Examples from section 4.5 of the SD Card Physical Layer Simplified Specification. + data: []byte{cmdSendMask, 5: 0}, // CMD0, arg=0 + wantCRC: 0b1001010, + }, + { + data: []byte{cmdSendMask | 17, 5: 0}, // CMD17, arg=0 + wantCRC: 0b0101010, + }, + { + data: []byte{17, 4: 0b1001, 5: 0}, // Response of CMD17 + wantCRC: 0b0110011, + }, + { // CSD for a 8GB card. + data: []byte{64, 14, 0, 50, 83, 89, 0, 0, 60, 1, 127, 128, 10, 64, 0}, + wantCRC: 0b1100101, + }, + } + + for _, tt := range tests { + gotcrc := CRC7(tt.data[:]) + if gotcrc != tt.wantCRC { + t.Errorf("got crc=%#b, want=%#b", gotcrc, tt.wantCRC) + } + } + + cmdTests := []struct { + cmd byte + arg uint32 + wantCRC uint8 + }{ + { + cmd: CMD0_GO_IDLE_STATE, + arg: 0, + wantCRC: 0x95, + }, + { + cmd: CMD8_SEND_IF_COND, + arg: 0x1AA, + wantCRC: 0x87, + }, + } + var dst [6]byte + for _, test := range cmdTests { + putCmd(dst[:], test.cmd, test.arg) + gotcrc := dst[5] + if gotcrc != test.wantCRC { + t.Errorf("got crc=%#x, want=%#x", gotcrc, test.wantCRC) + } + } +} diff --git a/sd/definitions.go b/sd/definitions.go index 0cb26ec..a7a331d 100644 --- a/sd/definitions.go +++ b/sd/definitions.go @@ -196,6 +196,15 @@ func (c *CSD) DeviceCapacity() (size uint64) { return size } +// NumberOfBlocks returns amount of readable blocks in the device given by Capacity/ReadBlockLength. +func (c *CSD) NumberOfBlocks() (numBlocks uint64) { + rblocks := c.ReadBlockLen() + if rblocks == 0 { + return 0 + } + return c.DeviceCapacity() / uint64(rblocks) +} + // After byte 5 CSDv1 and CSDv2 differ in structure at some fields. // DeviceCapacity returns the device capacity in bytes. @@ -440,9 +449,9 @@ func b2u8(b bool) uint8 { } // CRC16 computes the CRC16 checksum for a given payload using the CRC-16-CCITT polynomial. -func CRC16(buf []byte) (sum uint16) { +func CRC16(buf []byte) (crc 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 @@ -457,19 +466,44 @@ func CRC16(buf []byte) (sum uint16) { } // CRC7 computes the CRC7 checksum for a given payload using the polynomial x^7 + x^3 + 1. -func CRC7(data []byte) uint8 { - const poly uint8 = 0x09 // Generator polynomial G(x) = x^7 + x^3 + 1 - var crc uint8 = 0x00 // Initial value - +func CRC7(data []byte) (crc uint8) { for _, b := range data { - crc ^= b // Initial XOR - for i := 0; i < 8; i++ { // Process each bit - if crc&0x80 != 0 { - crc = (crc << 1) ^ poly - } else { - crc <<= 1 - } - } + crc = crc7_table[crc^b] } - return crc >> 1 + return crc +} + +var crc7_table = [256]byte{ + 0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, + 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee, + 0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c, + 0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc, + 0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a, + 0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a, + 0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28, + 0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8, + 0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6, + 0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26, + 0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84, + 0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14, + 0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2, + 0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42, + 0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0, + 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70, + 0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc, + 0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c, + 0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce, + 0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e, + 0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98, + 0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08, + 0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa, + 0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a, + 0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34, + 0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4, + 0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06, + 0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96, + 0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50, + 0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0, + 0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62, + 0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2, }