From 0d80962dc81c2442d9098105f6148080dc0c0c73 Mon Sep 17 00:00:00 2001 From: soypat Date: Mon, 15 Jan 2024 23:23:08 -0300 Subject: [PATCH] add blkIdxer --- sd/blockdevice.go | 89 +++++++++++++++++++++++++++++++++++------------ sd/card.go | 15 ++++---- sd/definitions.go | 31 ++++++++++------- sd/rustref.go | 45 +++++++++++------------- 4 files changed, 113 insertions(+), 67 deletions(-) diff --git a/sd/blockdevice.go b/sd/blockdevice.go index f3c6d3a..0003ef0 100644 --- a/sd/blockdevice.go +++ b/sd/blockdevice.go @@ -2,6 +2,7 @@ package sd import ( "errors" + "io" "math/bits" ) @@ -11,6 +12,8 @@ var ( // Compile time guarantee of interface implementation. var _ Card = (*SPICard)(nil) +var _ io.ReaderAt = (*BlockDevice)(nil) +var _ io.WriterAt = (*BlockDevice)(nil) type Card interface { // WriteBlocks writes the given data to the card, starting at the given block index. @@ -19,8 +22,8 @@ type Card interface { // ReadBlocks reads the given number of blocks from the card, starting at the given block index. // The dst buffer must be a multiple of the block size. ReadBlocks(dst []byte, startBlockIdx int64) error - // EraseBlocks erases - EraseSectors(startBlockIdx, numBlocks int64) error + // EraseSectors erases sectors starting at startSectorIdx to startSectorIdx+numSectors. + EraseSectors(startSectorIdx, numSectors int64) error } // NewBlockDevice creates a new BlockDevice from a Card. @@ -28,15 +31,14 @@ func NewBlockDevice(card Card, blockSize int, numBlocks, eraseBlockSizeInBytes i if card == nil || blockSize <= 0 || eraseBlockSizeInBytes <= 0 || numBlocks <= 0 { return nil, errors.New("invalid argument(s)") } - tz := bits.TrailingZeros(uint(blockSize)) - if blockSize>>tz != 1 { - return nil, errors.New("blockSize must be a power of 2") + blk, err := makeBlockIndexer(blockSize) + if err != nil { + return nil, err } bd := &BlockDevice{ card: card, blockbuf: make([]byte, blockSize), - blockshift: tz, - blockmask: (1 << tz) - 1, + blk: blk, numblocks: int64(numBlocks), eraseBlockSize: eraseBlockSizeInBytes, } @@ -47,30 +49,22 @@ func NewBlockDevice(card Card, blockSize int, numBlocks, eraseBlockSizeInBytes i type BlockDevice struct { card Card blockbuf []byte - blockshift int - blockmask int64 + blk blkIdxer numblocks int64 eraseBlockSize int64 } -func (bd *BlockDevice) moduloBlockSize(n int64) int64 { - return n & bd.blockmask -} - -func (bd *BlockDevice) divideBlockSize(n int64) int64 { - return n >> bd.blockshift -} - // ReadAt implements [io.ReadAt] interface for an SD card. func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) { if off < 0 { return 0, errNegativeOffset } blockSize := len(bd.blockbuf) - blockIdx := bd.divideBlockSize(off) - blockOff := bd.moduloBlockSize(off) + blockIdx := bd.blk.idx(off) + blockOff := bd.blk.off(off) if blockOff != 0 { // Non-aligned first block case. + println("read", len(bd.blockbuf), "bytes from block", blockIdx) if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil { return n, err } @@ -82,7 +76,7 @@ func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) { remaining := len(p) - n if remaining >= blockSize { // 1 or more full blocks case. - endOffset := remaining - int(bd.moduloBlockSize(int64(remaining))) + endOffset := remaining - int(bd.blk.off(int64(remaining))) err = bd.card.ReadBlocks(p[:endOffset], blockIdx) if err != nil { return n, err @@ -108,8 +102,8 @@ func (bd *BlockDevice) WriteAt(p []byte, off int64) (n int, err error) { return 0, errNegativeOffset } blockSize := len(bd.blockbuf) - blockIdx := bd.divideBlockSize(off) - blockOff := bd.moduloBlockSize(off) + blockIdx := bd.blk.idx(off) + blockOff := bd.blk.off(off) if blockOff != 0 { // Non-aligned first block case. if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil { @@ -126,7 +120,7 @@ func (bd *BlockDevice) WriteAt(p []byte, off int64) (n int, err error) { remaining := len(p) - n if remaining >= blockSize { // 1 or more full blocks case. - endOffset := remaining - int(bd.moduloBlockSize(int64(remaining))) + endOffset := remaining - int(bd.blk.off(int64(remaining))) err = bd.card.WriteBlocks(p[:endOffset], blockIdx) if err != nil { return n, err @@ -168,3 +162,52 @@ func (bd *BlockDevice) EraseBlocks(startEraseBlockIdx, len int64) error { func (bd *BlockDevice) EraseBlockSize() int64 { return bd.eraseBlockSize } + +// blkIdxer is a helper for calculating block indexes and offsets. +type blkIdxer struct { + blockshift int64 + blockmask int64 +} + +func makeBlockIndexer(blockSize int) (blkIdxer, error) { + if blockSize <= 0 { + return blkIdxer{}, errNoblocks + } + tz := bits.TrailingZeros(uint(blockSize)) + if blockSize>>tz != 1 { + return blkIdxer{}, errors.New("blockSize must be a power of 2") + } + blk := blkIdxer{ + blockshift: int64(tz), + blockmask: (1 << tz) - 1, + } + return blk, nil +} + +// size returns the size of a block in bytes. +func (blk *blkIdxer) size() int64 { + return 1 << blk.blockshift +} + +// off gets the offset of the byte at byteIdx from the start of its block. +// +//go:inline +func (blk *blkIdxer) off(byteIdx int64) int64 { + return blk._moduloBlockSize(byteIdx) +} + +// idx gets the block index that contains the byte at byteIdx. +// +//go:inline +func (blk *blkIdxer) idx(byteIdx int64) int64 { + return blk._divideBlockSize(byteIdx) +} + +// modulo and divide are defined in terms of bit operations for speed since +// blockSize is a power of 2. + +//go:inline +func (blk *blkIdxer) _moduloBlockSize(n int64) int64 { return n & blk.blockmask } + +//go:inline +func (blk *blkIdxer) _divideBlockSize(n int64) int64 { return n >> blk.blockshift } diff --git a/sd/card.go b/sd/card.go index 1929227..f014ed5 100644 --- a/sd/card.go +++ b/sd/card.go @@ -29,19 +29,18 @@ type SPICard struct { bus drivers.SPI cs digitalPinout - timers [2]timer - numblocks int64 - timeout time.Duration - wait time.Duration + timers [2]timer + timeout time.Duration + wait time.Duration // Card Identification Register. cid CID // Card Specific Register. csd CSD bufcmd [6]byte kind CardKind - // shift to calculate blocksize, taken from CSD. - blockshift uint8 - lastCRC uint16 + // block indexing helper based on block size. + blk blkIdxer + lastCRC uint16 } func NewSPICard(spi drivers.SPI, cs digitalPinout) *SPICard { @@ -77,7 +76,7 @@ func (d *SPICard) Init() error { } func (d *SPICard) NumberOfBlocks() int64 { - return d.numblocks + return d.csd.NumberOfBlocks() } // CID returns a copy of the Card Identification Register value last read. diff --git a/sd/definitions.go b/sd/definitions.go index 0c4e319..c5df570 100644 --- a/sd/definitions.go +++ b/sd/definitions.go @@ -154,7 +154,7 @@ func (c *CSD) CommandClasses() CommandClasses { } // ReadBlockLen returns the Max Read Data Block Length in bytes. -func (c *CSD) ReadBlockLen() uint16 { return 1 << c.ReadBlockLenShift() } +func (c *CSD) ReadBlockLen() int64 { return 1 << c.ReadBlockLenShift() } func (c *CSD) ReadBlockLenShift() uint8 { return c.data[5] & 0x0F } // AllowsReadBlockPartial should always return true. Indicates that @@ -185,7 +185,14 @@ func (c *CSD) ImplementsDSR() bool { return c.data[6]&(1<<4) != 0 } // EraseSectorSizeInBlocks represents how much memory is erased in an erase // command in multiple of block size. -func (c *CSD) EraseSectorSizeInBlocks() uint8 { +func (c *CSDv1) EraseSectorSizeInBytes() int64 { + blklen := c.WriteBlockLen() + numblocks := c.SectorSize() + return int64(numblocks) * blklen +} + +// SectorSize varies in meaning depending on the version. +func (c *CSD) SectorSize() uint8 { return 1 + ((c.data[10]&0b11_1111)<<1 | (c.data[11] >> 7)) } @@ -201,8 +208,8 @@ func (c *CSD) WriteProtectGroupSizeInSectors() uint8 { return 1 + (c.data[11] & 0b111_1111) } -// WriteBlockLength represents maximum write data block length in bytes. -func (c *CSD) WriteBlockLength() uint16 { +// WriteBlockLen represents maximum write data block length in bytes. +func (c *CSD) WriteBlockLen() int64 { return 1 << ((c.data[12]&0b11)<<2 | (c.data[13] >> 6)) } @@ -226,11 +233,11 @@ func (c *CSD) IsCopy() bool { return c.data[14]&(1<<6) != 0 } func (c *CSD) FileFormatGroup() bool { return c.data[14]&(1<<7) != 0 } -func (c *CSD) DeviceCapacity() (size uint64) { +func (c *CSD) DeviceCapacity() (size int64) { switch c.csdStructure() { case 0: v1 := c.MustV1() - size = uint64(v1.DeviceCapacity()) + size = int64(v1.DeviceCapacity()) case 1: v2 := c.MustV2() size = v2.DeviceCapacity() @@ -239,20 +246,20 @@ func (c *CSD) DeviceCapacity() (size uint64) { } // NumberOfBlocks returns amount of readable blocks in the device given by Capacity/ReadBlockLength. -func (c *CSD) NumberOfBlocks() (numBlocks uint64) { +func (c *CSD) NumberOfBlocks() (numBlocks int64) { rblocks := c.ReadBlockLen() if rblocks == 0 { return 0 } - return c.DeviceCapacity() / uint64(rblocks) + return c.DeviceCapacity() / int64(rblocks) } // After byte 5 CSDv1 and CSDv2 differ in structure at some fields. // DeviceCapacity returns the device capacity in bytes. -func (c *CSDv2) DeviceCapacity() uint64 { +func (c *CSDv2) DeviceCapacity() int64 { csize := c.csize() - return uint64(csize) * 512_000 + return int64(csize) * 512_000 } func (c *CSDv2) csize() uint32 { @@ -311,7 +318,7 @@ func (c *CSDv2) String() string { return c.CSD.String() } func (c *CSD) appendf(b []byte, delim byte) []byte { b = appendnum(b, "Version", uint64(c.Version()), delim) - b = appendnum(b, "Capacity(bytes)", c.DeviceCapacity(), delim) + b = appendnum(b, "Capacity(bytes)", uint64(c.DeviceCapacity()), 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) @@ -322,7 +329,7 @@ func (c *CSD) appendf(b []byte, delim byte) []byte { b = appendbit(b, "AllowReadBlockMisalignment", c.AllowsReadBlockMisalignment(), delim) b = appendbit(b, "ImplementsDSR", c.ImplementsDSR(), delim) b = appendnum(b, "WProtectNumSectors", uint64(c.WriteProtectGroupSizeInSectors()), delim) - b = appendnum(b, "WriteBlockLen", uint64(c.WriteBlockLength()), delim) + b = appendnum(b, "WriteBlockLen", uint64(c.WriteBlockLen()), delim) b = appendbit(b, "WGrpEnable", c.WriteGroupEnabled(), delim) b = appendbit(b, "WPartialAllow", c.AllowsWritePartial(), delim) b = append(b, "FileFmt:"...) diff --git a/sd/rustref.go b/sd/rustref.go index f295441..513088d 100644 --- a/sd/rustref.go +++ b/sd/rustref.go @@ -5,7 +5,6 @@ import ( "errors" "io" "math" - "math/bits" "time" ) @@ -135,18 +134,11 @@ func (d *SPICard) updateCSDCID() (err error) { if err != nil { return err } - blockshift := d.csd.ReadBlockLenShift() - blocklen := uint16(1) << blockshift - capacity := d.csd.DeviceCapacity() - if blocklen == 0 || capacity < uint64(blocklen) { - return errNoblocks + blklen := d.csd.ReadBlockLen() + d.blk, err = makeBlockIndexer(int(blklen)) + if err != nil { + return err } - nb := capacity / uint64(blocklen) - if nb > math.MaxUint32 { - return errCardNotSupported - } - d.blockshift = blockshift - d.numblocks = int64(nb) return nil } @@ -160,17 +152,17 @@ func (d *SPICard) ReadBlocks(dst []byte, startBlockIdx int64) error { startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards. } d.csEnable(true) - defer d.csEnable(false) + defer d.endTx() if numblocks == 1 { - _, err = d.card_command(cmdReadSingleBlock, uint32(startBlockIdx)) + _, err := d.card_command(cmdReadSingleBlock, uint32(startBlockIdx)) if err != nil { return err } return d.read_data(dst) } else if numblocks > 1 { - blocksize := 1 << d.blockshift + blocksize := int(d.blk.size()) _, err = d.card_command(cmdReadMultipleBlock, uint32(startBlockIdx)) if err != nil { return err @@ -183,12 +175,16 @@ func (d *SPICard) ReadBlocks(dst []byte, startBlockIdx int64) error { return err } } - _, err = d.card_command(cmdStopTransmission, 0) - return err + return nil } panic("unreachable numblocks<=0") } +func (d *SPICard) endTx() { + d.card_command(cmdStopTransmission, 0) + d.csEnable(false) +} + func (d *SPICard) EraseSectors(startSector, numberSectors int64) error { return errors.New("sd:erase not implemented") } @@ -203,7 +199,8 @@ func (d *SPICard) WriteBlocks(data []byte, startBlockIdx int64) error { startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards. } d.csEnable(true) - defer d.csEnable(false) + defer d.endTx() + writeTimeout := 2 * d.timeout if numblocks == 1 { _, err = d.card_command(cmdWriteBlock, uint32(startBlockIdx)) @@ -234,7 +231,7 @@ func (d *SPICard) WriteBlocks(data []byte, startBlockIdx int64) error { } else if numblocks > 1 { // Start multi block write. - blocksize := 1 << d.blockshift + blocksize := 1 << d.blk.size() _, err = d.card_command(cmdWriteMultipleBlock, uint32(startBlockIdx)) if err != nil { return err @@ -262,16 +259,15 @@ func (d *SPICard) WriteBlocks(data []byte, startBlockIdx int64) error { } func (d *SPICard) checkBounds(startBlockIdx int64, datalen int) (numblocks int, err error) { - if startBlockIdx >= d.numblocks { + if startBlockIdx >= d.NumberOfBlocks() { return 0, errOOB } else if startBlockIdx > math.MaxUint32 { return 0, errCardNotSupported } - tz := bits.TrailingZeros(uint(datalen)) - if tz < int(d.blockshift) { + if d.blk.off(int64(datalen)) > 0 { return 0, errNeedBlockLenMultiple } - numblocks = datalen >> d.blockshift + numblocks = int(d.blk.idx(int64(datalen))) if numblocks == 0 { return 0, io.ErrShortBuffer } @@ -353,7 +349,8 @@ func (d *SPICard) card_command(cmd command, args uint32) (uint8, error) { func (d *SPICard) read_data(data []byte) (err error) { var status uint8 - for { + tm := d.timers[1].setTimeout(d.timeout) + for !tm.expired() { status, err = d.receive() if err != nil { return err