add blkIdxer

This commit is contained in:
soypat
2024-01-15 23:23:08 -03:00
parent cb2ca239f0
commit 0d80962dc8
4 changed files with 113 additions and 67 deletions
+66 -23
View File
@@ -2,6 +2,7 @@ package sd
import ( import (
"errors" "errors"
"io"
"math/bits" "math/bits"
) )
@@ -11,6 +12,8 @@ var (
// Compile time guarantee of interface implementation. // Compile time guarantee of interface implementation.
var _ Card = (*SPICard)(nil) var _ Card = (*SPICard)(nil)
var _ io.ReaderAt = (*BlockDevice)(nil)
var _ io.WriterAt = (*BlockDevice)(nil)
type Card interface { type Card interface {
// WriteBlocks writes the given data to the card, starting at the given block index. // 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. // 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. // The dst buffer must be a multiple of the block size.
ReadBlocks(dst []byte, startBlockIdx int64) error ReadBlocks(dst []byte, startBlockIdx int64) error
// EraseBlocks erases // EraseSectors erases sectors starting at startSectorIdx to startSectorIdx+numSectors.
EraseSectors(startBlockIdx, numBlocks int64) error EraseSectors(startSectorIdx, numSectors int64) error
} }
// NewBlockDevice creates a new BlockDevice from a Card. // 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 { if card == nil || blockSize <= 0 || eraseBlockSizeInBytes <= 0 || numBlocks <= 0 {
return nil, errors.New("invalid argument(s)") return nil, errors.New("invalid argument(s)")
} }
tz := bits.TrailingZeros(uint(blockSize)) blk, err := makeBlockIndexer(blockSize)
if blockSize>>tz != 1 { if err != nil {
return nil, errors.New("blockSize must be a power of 2") return nil, err
} }
bd := &BlockDevice{ bd := &BlockDevice{
card: card, card: card,
blockbuf: make([]byte, blockSize), blockbuf: make([]byte, blockSize),
blockshift: tz, blk: blk,
blockmask: (1 << tz) - 1,
numblocks: int64(numBlocks), numblocks: int64(numBlocks),
eraseBlockSize: eraseBlockSizeInBytes, eraseBlockSize: eraseBlockSizeInBytes,
} }
@@ -47,30 +49,22 @@ func NewBlockDevice(card Card, blockSize int, numBlocks, eraseBlockSizeInBytes i
type BlockDevice struct { type BlockDevice struct {
card Card card Card
blockbuf []byte blockbuf []byte
blockshift int blk blkIdxer
blockmask int64
numblocks int64 numblocks int64
eraseBlockSize 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. // ReadAt implements [io.ReadAt] interface for an SD card.
func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) { func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if off < 0 { if off < 0 {
return 0, errNegativeOffset return 0, errNegativeOffset
} }
blockSize := len(bd.blockbuf) blockSize := len(bd.blockbuf)
blockIdx := bd.divideBlockSize(off) blockIdx := bd.blk.idx(off)
blockOff := bd.moduloBlockSize(off) blockOff := bd.blk.off(off)
if blockOff != 0 { if blockOff != 0 {
// Non-aligned first block case. // Non-aligned first block case.
println("read", len(bd.blockbuf), "bytes from block", blockIdx)
if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil { if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil {
return n, err return n, err
} }
@@ -82,7 +76,7 @@ func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
remaining := len(p) - n remaining := len(p) - n
if remaining >= blockSize { if remaining >= blockSize {
// 1 or more full blocks case. // 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) err = bd.card.ReadBlocks(p[:endOffset], blockIdx)
if err != nil { if err != nil {
return n, err return n, err
@@ -108,8 +102,8 @@ func (bd *BlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
return 0, errNegativeOffset return 0, errNegativeOffset
} }
blockSize := len(bd.blockbuf) blockSize := len(bd.blockbuf)
blockIdx := bd.divideBlockSize(off) blockIdx := bd.blk.idx(off)
blockOff := bd.moduloBlockSize(off) blockOff := bd.blk.off(off)
if blockOff != 0 { if blockOff != 0 {
// Non-aligned first block case. // Non-aligned first block case.
if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil { 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 remaining := len(p) - n
if remaining >= blockSize { if remaining >= blockSize {
// 1 or more full blocks case. // 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) err = bd.card.WriteBlocks(p[:endOffset], blockIdx)
if err != nil { if err != nil {
return n, err return n, err
@@ -168,3 +162,52 @@ func (bd *BlockDevice) EraseBlocks(startEraseBlockIdx, len int64) error {
func (bd *BlockDevice) EraseBlockSize() int64 { func (bd *BlockDevice) EraseBlockSize() int64 {
return bd.eraseBlockSize 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 }
+7 -8
View File
@@ -29,19 +29,18 @@ type SPICard struct {
bus drivers.SPI bus drivers.SPI
cs digitalPinout cs digitalPinout
timers [2]timer timers [2]timer
numblocks int64 timeout time.Duration
timeout time.Duration wait time.Duration
wait time.Duration
// Card Identification Register. // Card Identification Register.
cid CID cid CID
// Card Specific Register. // Card Specific Register.
csd CSD csd CSD
bufcmd [6]byte bufcmd [6]byte
kind CardKind kind CardKind
// shift to calculate blocksize, taken from CSD. // block indexing helper based on block size.
blockshift uint8 blk blkIdxer
lastCRC uint16 lastCRC uint16
} }
func NewSPICard(spi drivers.SPI, cs digitalPinout) *SPICard { func NewSPICard(spi drivers.SPI, cs digitalPinout) *SPICard {
@@ -77,7 +76,7 @@ func (d *SPICard) Init() error {
} }
func (d *SPICard) NumberOfBlocks() int64 { func (d *SPICard) NumberOfBlocks() int64 {
return d.numblocks return d.csd.NumberOfBlocks()
} }
// CID returns a copy of the Card Identification Register value last read. // CID returns a copy of the Card Identification Register value last read.
+19 -12
View File
@@ -154,7 +154,7 @@ func (c *CSD) CommandClasses() CommandClasses {
} }
// ReadBlockLen returns the Max Read Data Block Length in bytes. // 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 } func (c *CSD) ReadBlockLenShift() uint8 { return c.data[5] & 0x0F }
// AllowsReadBlockPartial should always return true. Indicates that // 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 // EraseSectorSizeInBlocks represents how much memory is erased in an erase
// command in multiple of block size. // 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)) 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) return 1 + (c.data[11] & 0b111_1111)
} }
// WriteBlockLength represents maximum write data block length in bytes. // WriteBlockLen represents maximum write data block length in bytes.
func (c *CSD) WriteBlockLength() uint16 { func (c *CSD) WriteBlockLen() int64 {
return 1 << ((c.data[12]&0b11)<<2 | (c.data[13] >> 6)) 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) FileFormatGroup() bool { return c.data[14]&(1<<7) != 0 }
func (c *CSD) DeviceCapacity() (size uint64) { func (c *CSD) DeviceCapacity() (size int64) {
switch c.csdStructure() { switch c.csdStructure() {
case 0: case 0:
v1 := c.MustV1() v1 := c.MustV1()
size = uint64(v1.DeviceCapacity()) size = int64(v1.DeviceCapacity())
case 1: case 1:
v2 := c.MustV2() v2 := c.MustV2()
size = v2.DeviceCapacity() 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. // 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() rblocks := c.ReadBlockLen()
if rblocks == 0 { if rblocks == 0 {
return 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. // After byte 5 CSDv1 and CSDv2 differ in structure at some fields.
// DeviceCapacity returns the device capacity in bytes. // DeviceCapacity returns the device capacity in bytes.
func (c *CSDv2) DeviceCapacity() uint64 { func (c *CSDv2) DeviceCapacity() int64 {
csize := c.csize() csize := c.csize()
return uint64(csize) * 512_000 return int64(csize) * 512_000
} }
func (c *CSDv2) csize() uint32 { 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 { func (c *CSD) appendf(b []byte, delim byte) []byte {
b = appendnum(b, "Version", uint64(c.Version()), delim) 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, "TimeAccess_ns", uint64(c.TAAC().AccessTime()), delim)
b = appendnum(b, "NSAC", uint64(c.NSAC()), delim) b = appendnum(b, "NSAC", uint64(c.NSAC()), delim)
b = appendnum(b, "Tx_kb/s", uint64(c.TransferSpeed().RateKilobits()), 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, "AllowReadBlockMisalignment", c.AllowsReadBlockMisalignment(), delim)
b = appendbit(b, "ImplementsDSR", c.ImplementsDSR(), delim) b = appendbit(b, "ImplementsDSR", c.ImplementsDSR(), delim)
b = appendnum(b, "WProtectNumSectors", uint64(c.WriteProtectGroupSizeInSectors()), 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, "WGrpEnable", c.WriteGroupEnabled(), delim)
b = appendbit(b, "WPartialAllow", c.AllowsWritePartial(), delim) b = appendbit(b, "WPartialAllow", c.AllowsWritePartial(), delim)
b = append(b, "FileFmt:"...) b = append(b, "FileFmt:"...)
+21 -24
View File
@@ -5,7 +5,6 @@ import (
"errors" "errors"
"io" "io"
"math" "math"
"math/bits"
"time" "time"
) )
@@ -135,18 +134,11 @@ func (d *SPICard) updateCSDCID() (err error) {
if err != nil { if err != nil {
return err return err
} }
blockshift := d.csd.ReadBlockLenShift() blklen := d.csd.ReadBlockLen()
blocklen := uint16(1) << blockshift d.blk, err = makeBlockIndexer(int(blklen))
capacity := d.csd.DeviceCapacity() if err != nil {
if blocklen == 0 || capacity < uint64(blocklen) { return err
return errNoblocks
} }
nb := capacity / uint64(blocklen)
if nb > math.MaxUint32 {
return errCardNotSupported
}
d.blockshift = blockshift
d.numblocks = int64(nb)
return nil 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. startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards.
} }
d.csEnable(true) d.csEnable(true)
defer d.csEnable(false) defer d.endTx()
if numblocks == 1 { if numblocks == 1 {
_, err = d.card_command(cmdReadSingleBlock, uint32(startBlockIdx)) _, err := d.card_command(cmdReadSingleBlock, uint32(startBlockIdx))
if err != nil { if err != nil {
return err return err
} }
return d.read_data(dst) return d.read_data(dst)
} else if numblocks > 1 { } else if numblocks > 1 {
blocksize := 1 << d.blockshift blocksize := int(d.blk.size())
_, err = d.card_command(cmdReadMultipleBlock, uint32(startBlockIdx)) _, err = d.card_command(cmdReadMultipleBlock, uint32(startBlockIdx))
if err != nil { if err != nil {
return err return err
@@ -183,12 +175,16 @@ func (d *SPICard) ReadBlocks(dst []byte, startBlockIdx int64) error {
return err return err
} }
} }
_, err = d.card_command(cmdStopTransmission, 0) return nil
return err
} }
panic("unreachable numblocks<=0") panic("unreachable numblocks<=0")
} }
func (d *SPICard) endTx() {
d.card_command(cmdStopTransmission, 0)
d.csEnable(false)
}
func (d *SPICard) EraseSectors(startSector, numberSectors int64) error { func (d *SPICard) EraseSectors(startSector, numberSectors int64) error {
return errors.New("sd:erase not implemented") 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. startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards.
} }
d.csEnable(true) d.csEnable(true)
defer d.csEnable(false) defer d.endTx()
writeTimeout := 2 * d.timeout writeTimeout := 2 * d.timeout
if numblocks == 1 { if numblocks == 1 {
_, err = d.card_command(cmdWriteBlock, uint32(startBlockIdx)) _, err = d.card_command(cmdWriteBlock, uint32(startBlockIdx))
@@ -234,7 +231,7 @@ func (d *SPICard) WriteBlocks(data []byte, startBlockIdx int64) error {
} else if numblocks > 1 { } else if numblocks > 1 {
// Start multi block write. // Start multi block write.
blocksize := 1 << d.blockshift blocksize := 1 << d.blk.size()
_, err = d.card_command(cmdWriteMultipleBlock, uint32(startBlockIdx)) _, err = d.card_command(cmdWriteMultipleBlock, uint32(startBlockIdx))
if err != nil { if err != nil {
return err 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) { func (d *SPICard) checkBounds(startBlockIdx int64, datalen int) (numblocks int, err error) {
if startBlockIdx >= d.numblocks { if startBlockIdx >= d.NumberOfBlocks() {
return 0, errOOB return 0, errOOB
} else if startBlockIdx > math.MaxUint32 { } else if startBlockIdx > math.MaxUint32 {
return 0, errCardNotSupported return 0, errCardNotSupported
} }
tz := bits.TrailingZeros(uint(datalen)) if d.blk.off(int64(datalen)) > 0 {
if tz < int(d.blockshift) {
return 0, errNeedBlockLenMultiple return 0, errNeedBlockLenMultiple
} }
numblocks = datalen >> d.blockshift numblocks = int(d.blk.idx(int64(datalen)))
if numblocks == 0 { if numblocks == 0 {
return 0, io.ErrShortBuffer 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) { func (d *SPICard) read_data(data []byte) (err error) {
var status uint8 var status uint8
for { tm := d.timers[1].setTimeout(d.timeout)
for !tm.expired() {
status, err = d.receive() status, err = d.receive()
if err != nil { if err != nil {
return err return err