add documentation, smoke test and fix a couple bugs

This commit is contained in:
Patricio Whittingslow
2026-07-14 16:53:11 -03:00
parent 62f51445b6
commit 0bc660e1bc
7 changed files with 195 additions and 32 deletions
+83
View File
@@ -85,6 +85,89 @@ func TestCRC7(t *testing.T) {
}
}
// TestCSDv2Capacity checks CSDv2 C_SIZE decoding and the capacity formula.
//
// Field layout and formula are from the SD Physical Layer Simplified
// Specification Version 9.10, section 5.3.3 "CSD Register (CSD Version 2.0)":
// C_SIZE occupies CSD bits [69:48] and user memory capacity is
//
// memory capacity = (C_SIZE+1) * 512KByte (512KByte = 524288 bytes)
//
// Specification download: https://www.sdcard.org/downloads/pls/
//
// Regression test for two past bugs in CSDv2:
// - csize() read byte 7 as data[7]>>2 instead of data[7]&0x3F, dropping
// C_SIZE bits [17:16] (misdecoded cards > 32GiB).
// - DeviceCapacity() computed csize*512000 instead of (csize+1)*524288.
func TestCSDv2Capacity(t *testing.T) {
tests := []struct {
name string
csd []byte
wantCSize uint32
wantCap int64
}{
{
// Same 8GB-card register as in TestCRC7 above, CRC byte appended
// (CRC7=0b1110010 per that test, stored as crc<<1|always1).
// C_SIZE = 0x003C01 = 15361 -> 15362 * 524288 = 8054112256 bytes.
name: "8GB card (in-repo vector)",
csd: []byte{64, 14, 0, 50, 83, 89, 0, 0, 60, 1, 127, 128, 10, 64, 0, 0b1110010<<1 | 1},
wantCSize: 0x003C01,
wantCap: 8054112256,
},
// {
// name: "8GB card",
// csd: csdv2Bytes(0x003C01),
// wantCSize: 0x003FFF,
// wantCap: 2 << 40,
// },
{
// Synthetic: C_SIZE = 0x01FFFF -> 131072 * 524288 = 64GiB.
// Exercises C_SIZE bits [17:16], stored in CSD byte 7 (CSD bits [65:64]).
name: "64GiB synthetic",
csd: csdv2Bytes(0x01FFFF),
wantCSize: 0x01FFFF,
wantCap: 64 << 30,
},
{
// Synthetic: maximum v2 C_SIZE = 0x3FFFFF -> 4194304 * 524288 = 2TiB,
// the SDXC upper bound per section 5.3.3.
name: "2TiB synthetic (max C_SIZE)",
csd: csdv2Bytes(0x3FFFFF),
wantCSize: 0x3FFFFF,
wantCap: 2 << 40,
},
}
for _, tt := range tests {
csd, err := DecodeCSD(tt.csd)
if err != nil {
t.Fatalf("%s: DecodeCSD: %v", tt.name, err)
}
v2 := csd.MustV2()
if got := v2.csize(); got != tt.wantCSize {
t.Errorf("%s: csize() = %#x, want %#x", tt.name, got, tt.wantCSize)
}
if got := csd.DeviceCapacity(); got != tt.wantCap {
t.Errorf("%s: DeviceCapacity() = %d, want %d", tt.name, got, tt.wantCap)
}
wantBlocks := tt.wantCap / int64(csd.ReadBlockLen())
if got := csd.NumberOfBlocks(); got != wantBlocks {
t.Errorf("%s: NumberOfBlocks() = %d, want %d", tt.name, got, wantBlocks)
}
}
}
// csdv2Bytes returns a 16-byte CSD v2 register with csize spliced into
// CSD bits [69:48] and a freshly computed CRC7+always1 last byte.
// Non-capacity fields are copied from the 8GB-card vector above.
func csdv2Bytes(csize uint32) []byte {
b := []byte{64, 14, 0, 50, 83, 89, 0, 0, 60, 1, 127, 128, 10, 64, 0}
b[7] = byte(csize >> 16 & 0x3F)
b[8] = byte(csize >> 8)
b[9] = byte(csize)
return append(b, crc7noshift(b)|1)
}
func putCmd(dst []byte, cmd command, arg uint32) {
dst[0] = byte(cmd) | (1 << 6)
dst[1] = byte(arg >> 24)