From 3d491553dd5dbaabde8daec092196787652c623a Mon Sep 17 00:00:00 2001 From: soypat Date: Sun, 14 Jan 2024 16:10:31 -0300 Subject: [PATCH] passing tests --- examples/sd/main.go | 5 ++- sd/card.go | 1 + sd/card_test.go | 10 +++--- sd/definitions.go | 88 ++++++++++++++++++++++++++++++--------------- 4 files changed, 69 insertions(+), 35 deletions(-) diff --git a/examples/sd/main.go b/examples/sd/main.go index f4e5f39..b4a42c7 100644 --- a/examples/sd/main.go +++ b/examples/sd/main.go @@ -41,7 +41,10 @@ func main() { cid := sdcard.CID() pname := cid.ProductName() csd := sdcard.CSD() - + 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() diff --git a/sd/card.go b/sd/card.go index 40914bf..ba704fd 100644 --- a/sd/card.go +++ b/sd/card.go @@ -12,6 +12,7 @@ import ( ) 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") diff --git a/sd/card_test.go b/sd/card_test.go index f6dda74..c465631 100644 --- a/sd/card_test.go +++ b/sd/card_test.go @@ -35,27 +35,27 @@ func TestCRC7(t *testing.T) { 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 + data: []byte{cmdSendMask, 4: 0}, // CMD0, arg=0 wantCRC: 0b1001010, }, { - data: []byte{cmdSendMask | 17, 5: 0}, // CMD17, arg=0 + data: []byte{cmdSendMask | 17, 4: 0}, // CMD17, arg=0 wantCRC: 0b0101010, }, { - data: []byte{17, 4: 0b1001, 5: 0}, // Response of CMD17 + data: []byte{17, 3: 0b1001, 4: 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, + wantCRC: 0b1110010, }, } for _, tt := range tests { gotcrc := CRC7(tt.data[:]) if gotcrc != tt.wantCRC { - t.Errorf("got crc=%#b, want=%#b", gotcrc, tt.wantCRC) + t.Errorf("got crc=%#b, want=%#b for %#b", gotcrc, tt.wantCRC, tt.data) } } diff --git a/sd/definitions.go b/sd/definitions.go index a7a331d..282ee29 100644 --- a/sd/definitions.go +++ b/sd/definitions.go @@ -8,6 +8,9 @@ import ( "time" ) +// For reference of CID/CSD structs see: +// See https://github.com/arduino-libraries/SD/blob/1c56f58252553c7537f7baf62798cacc625aa543/src/utility/SdInfo.h#L110 + type CardKind uint8 const ( @@ -18,44 +21,66 @@ const ( ) 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 + data [16]byte } -func DecodeCID(b []byte) (CID, error) { +func DecodeCID(b []byte) (cid 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]}, + copy(cid.data[:], b) + if !cid.IsValid() { + return cid, errBadCSDCID } - return cid, nil } -func (c *CID) ProductName() string { - return string(upToNull(c.prodName[:])) +// RawCopy returns a copy of the raw CID data. +func (c *CID) RawCopy() [16]byte { return c.data } + +// ManufacturerID is an 8-bit binary number that identifies the card manufacturer. The MID number is controlled, defined, and allocated to a SD Memory Card manufacturer by the SD-3C, LLC. +func (c *CID) ManufacturerID() uint8 { return c.data[0] } + +// OEMApplicationID A 2-character ASCII string that identifies the card OEM and/or the card contents (when used as a +// distribution media either on ROM or FLASH cards). The OID number is controlled, defined, and allocated +// to a SD Memory Card manufacturer by the SD-3C, LLC +func (c *CID) OEMApplicationID() uint16 { + return binary.BigEndian.Uint16(c.data[1:3]) } -func (c *CID) ProductRevision() (n, m uint8) { - return c.productRev >> 4, c.productRev & 0x0F +// The product name is a string, 5-character ASCII string. +func (c *CID) ProductName() string { + return string(upToNull(c.data[3:8])) } +// ProductRevision is composed of two Binary Coded Decimal (BCD) digits, four bits each, representing +// an "n.m" revision number. The "n" is the most significant nibble and "m" is the least significant nibble. +// As an example, the PRV binary value field for product revision "6.2" will be: 0110 0010b +func (c *CID) ProductRevision() (n, m uint8) { + rev := c.data[8] + return rev >> 4, rev & 0x0F +} + +// The Serial Number is 32 bits of binary number. +func (c *CID) ProductSerialNumber() uint32 { + return binary.BigEndian.Uint32(c.data[9:13]) +} + +// ManufacturingDate returns the manufacturing date of the card. +func (c *CID) ManufacturingDate() (year uint16, month uint8) { + date := binary.BigEndian.Uint16(c.data[13:15]) + return (date >> 4) + 2000, uint8(date & 0x0F) +} + +// CRC7 returns the CRC7 checksum for this CID. May be invalid. Use [IsValid] to check validity of CRC7+Always1 fields. +func (c *CID) CRC7() uint8 { return c.data[15] >> 1 } + +// Always1 checks the presence of the Always 1 bit. Should return true for valid CIDs. +func (c *CID) Always1() bool { return c.data[15]&1 != 0 } + +// IsValid checks if the CRC and always1 fields are expected values. +func (c *CID) IsValid() bool { return c.Always1() && CRC7(c.data[:15]) == c.CRC7() } + // 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. This type is // shared among V1 and V2 type devices. @@ -77,6 +102,9 @@ func DecodeCSD(b []byte) (CSD, error) { } csd := CSD{} copy(csd.data[:], b) + if !csd.IsValid() { + return csd, errBadCSDCID + } return csd, nil } @@ -129,13 +157,15 @@ func (c *CSD) AllowsWriteBlockMisalignment() bool { return c.data[6]&(1<<6) != 0 func (c *CSD) AllowsReadBlockMisalignment() bool { return c.data[6]&(1<<5) != 0 } // CRC7 returns the CRC read for this CSD. May be invalid. Use [IsValid] to check validity of CRC7+Always1 fields. -func (c *CSD) CRC7() uint8 { return c.data[15] & 0b111_1111 } +func (c *CSD) CRC7() uint8 { return c.data[15] >> 1 } + +// Always1 checks the Always 1 bit. Should always evaluate to true for valid CSDs. +func (c *CSD) Always1() bool { return c.data[15]&1 != 0 } // IsValid checks if the CRC and always1 fields are expected values. func (c *CSD) IsValid() bool { // Compare last byte with CRC and also the always1 bit. - got := CRC7(c.data[:15]) - return got|(1<<7) == c.data[15] + return c.Always1() && CRC7(c.data[:15]) == c.CRC7() } // ImplementsDSR defines if the configurable driver stage is integrated on the card. @@ -470,7 +500,7 @@ func CRC7(data []byte) (crc uint8) { for _, b := range data { crc = crc7_table[crc^b] } - return crc + return crc >> 1 } var crc7_table = [256]byte{