working BlockDevice

This commit is contained in:
soypat
2024-01-16 00:46:50 -03:00
parent 0d80962dc8
commit 964364005d
3 changed files with 178 additions and 97 deletions
+60 -9
View File
@@ -48,14 +48,65 @@ func main() {
cid := sdcard.CID() cid := sdcard.CID()
fmt.Printf("name=%s\ncsd=\n%s\n", cid.ProductName(), csd.String()) fmt.Printf("name=%s\ncsd=\n%s\n", cid.ProductName(), csd.String())
var buf [512]byte const placeholderEraseSectorSize = 512
for i := 0; i < 11; i += 1 { bd, err := sd.NewBlockDevice(sdcard, int(csd.ReadBlockLen()), csd.NumberOfBlocks(), placeholderEraseSectorSize)
err = sdcard.ReadBlocks(buf[:], int64(i)) if err != nil {
if err != nil { panic("block device creation:" + err.Error())
println("err reading block", i, ":", err.Error()) }
continue var mc MemChecker
}
expectCRC := sd.CRC16(buf[:]) ok, badBlkIdx, err := mc.MemCheck(bd, 2, 100)
fmt.Printf("block %d theircrc=%#x ourcrc=%#x:\n\t%#x\n", i, sdcard.LastReadCRC(), expectCRC, buf[:]) if err != nil {
panic("memcheck:" + err.Error())
}
if !ok {
println("bad block", badBlkIdx)
} else {
println("memcheck ok")
} }
} }
type MemChecker struct {
rdBuf []byte
storeBuf []byte
wrBuf []byte
}
func (mc *MemChecker) MemCheck(bd *sd.BlockDevice, blockIdx, numBlocks int64) (memOK bool, badBlockIdx int64, err error) {
size := bd.BlockSize() * numBlocks
if len(mc.rdBuf) < int(size) {
mc.rdBuf = make([]byte, size)
mc.wrBuf = make([]byte, size)
mc.storeBuf = make([]byte, size)
for i := range mc.wrBuf {
mc.wrBuf[i] = byte(i)
}
}
// Start by storing the original block contents.
_, err = bd.ReadAt(mc.storeBuf, blockIdx)
if err != nil {
return false, blockIdx, err
}
// Write the test pattern.
_, err = bd.WriteAt(mc.wrBuf, blockIdx)
if err != nil {
return false, blockIdx, err
}
// Read back the test pattern.
_, err = bd.ReadAt(mc.rdBuf, blockIdx)
if err != nil {
return false, blockIdx, err
}
for j := 0; j < len(mc.rdBuf); j++ {
// Compare the read back data with the test pattern.
if mc.rdBuf[j] != mc.wrBuf[j] {
badBlock := blockIdx + int64(j)/bd.BlockSize()
return false, badBlock, nil
}
mc.rdBuf[j] = 0
}
// Leave the card in it's previous state.
_, err = bd.WriteAt(mc.storeBuf, blockIdx)
return true, -1, nil
}
+44 -30
View File
@@ -18,10 +18,10 @@ 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.
// The data must be a multiple of the block size. // The data must be a multiple of the block size.
WriteBlocks(data []byte, startBlockIdx int64) error WriteBlocks(data []byte, startBlockIdx int64) (int, error)
// 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) (int, error)
// EraseSectors erases sectors starting at startSectorIdx to startSectorIdx+numSectors. // EraseSectors erases sectors starting at startSectorIdx to startSectorIdx+numSectors.
EraseSectors(startSectorIdx, numSectors int64) error EraseSectors(startSectorIdx, numSectors int64) error
} }
@@ -59,13 +59,12 @@ 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)
blockIdx := bd.blk.idx(off) blockIdx := bd.blk.idx(off)
blockOff := bd.blk.off(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
} }
n += copy(p, bd.blockbuf[blockOff:]) n += copy(p, bd.blockbuf[blockOff:])
@@ -73,22 +72,22 @@ func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
blockIdx++ blockIdx++
} }
remaining := len(p) - n fullBlocksToRead := bd.blk.idx(int64(len(p)))
if remaining >= blockSize { if fullBlocksToRead > 0 {
// 1 or more full blocks case. // 1 or more full blocks case.
endOffset := remaining - int(bd.blk.off(int64(remaining))) endOffset := fullBlocksToRead * bd.blk.size()
err = bd.card.ReadBlocks(p[:endOffset], blockIdx) ngot, err := bd.card.ReadBlocks(p[:endOffset], blockIdx)
if err != nil { if err != nil {
return n, err return n + ngot, err
} }
p = p[endOffset:] p = p[endOffset:]
n += endOffset n += ngot
blockIdx += int64(endOffset / blockSize) blockIdx += fullBlocksToRead
} }
if len(p) > 0 { if len(p) > 0 {
// Non-aligned last block case. // Non-aligned last block case.
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
} }
n += copy(p, bd.blockbuf) n += copy(p, bd.blockbuf)
@@ -101,51 +100,66 @@ func (bd *BlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if off < 0 { if off < 0 {
return 0, errNegativeOffset return 0, errNegativeOffset
} }
blockSize := len(bd.blockbuf)
blockIdx := bd.blk.idx(off) blockIdx := bd.blk.idx(off)
blockOff := bd.blk.off(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 {
return n, err return n, err
} }
n += copy(bd.blockbuf[blockOff:], p) nexpect := copy(bd.blockbuf[blockOff:], p)
if err := bd.card.WriteBlocks(bd.blockbuf, blockIdx); err != nil { ngot, err := bd.card.WriteBlocks(bd.blockbuf, blockIdx)
if err != nil {
return n, err return n, err
} else if ngot != len(bd.blockbuf) {
return n, io.ErrShortWrite
} }
p = p[n:] n += nexpect
p = p[nexpect:]
blockIdx++ blockIdx++
} }
remaining := len(p) - n fullBlocksToWrite := bd.blk.idx(int64(len(p)))
if remaining >= blockSize { if fullBlocksToWrite > 0 {
// 1 or more full blocks case. // 1 or more full blocks case.
endOffset := remaining - int(bd.blk.off(int64(remaining))) endOffset := fullBlocksToWrite * bd.blk.size()
err = bd.card.WriteBlocks(p[:endOffset], blockIdx) ngot, err := bd.card.WriteBlocks(p[:endOffset], blockIdx)
n += ngot
if err != nil { if err != nil {
return n, err return n, err
} else if ngot != int(endOffset) {
return n, io.ErrShortWrite
} }
p = p[endOffset:] p = p[ngot:]
n += endOffset blockIdx += fullBlocksToWrite
blockIdx += int64(endOffset / blockSize)
} }
if len(p) > 0 { if len(p) > 0 {
// Non-aligned last block case. // Non-aligned last block case.
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
} }
n += copy(bd.blockbuf, p) copy(bd.blockbuf, p)
if err := bd.card.WriteBlocks(bd.blockbuf, blockIdx); err != nil { ngot, err := bd.card.WriteBlocks(bd.blockbuf, blockIdx)
if err != nil {
return n, err return n, err
} else if ngot != len(bd.blockbuf) {
return n, io.ErrShortWrite
} }
n += len(p)
} }
return n, nil return n, nil
} }
// Size returns the number of bytes in this block device. // Size returns the number of bytes in this block device.
func (bd *BlockDevice) Size() int64 { func (bd *BlockDevice) Size() int64 {
return int64(len(bd.blockbuf)) * bd.numblocks return bd.BlockSize() * bd.numblocks
}
// BlockSize returns the size of a block in bytes.
func (bd *BlockDevice) BlockSize() int64 {
return bd.blk.size()
} }
// EraseBlocks erases the given number of blocks. An implementation may // EraseBlocks erases the given number of blocks. An implementation may
@@ -163,7 +177,7 @@ func (bd *BlockDevice) EraseBlockSize() int64 {
return bd.eraseBlockSize return bd.eraseBlockSize
} }
// blkIdxer is a helper for calculating block indexes and offsets. // blkIdxer is a helper for calculating block indices and offsets.
type blkIdxer struct { type blkIdxer struct {
blockshift int64 blockshift int64
blockmask int64 blockmask int64
+74 -58
View File
@@ -143,121 +143,136 @@ func (d *SPICard) updateCSDCID() (err error) {
} }
// ReadBlock reads to a buffer multiple of 512 bytes from sdcard into dst starting at block `startBlockIdx`. // ReadBlock reads to a buffer multiple of 512 bytes from sdcard into dst starting at block `startBlockIdx`.
func (d *SPICard) ReadBlocks(dst []byte, startBlockIdx int64) error { func (d *SPICard) ReadBlocks(dst []byte, startBlockIdx int64) (int, error) {
numblocks, err := d.checkBounds(startBlockIdx, len(dst)) numblocks, err := d.checkBounds(startBlockIdx, len(dst))
if err != nil { if err != nil {
return err return 0, err
} }
if d.kind != TypeSDHC { if d.kind != TypeSDHC {
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.endTx() defer d.csEnable(false)
if numblocks == 1 { if numblocks == 1 {
_, err := d.card_command(cmdReadSingleBlock, uint32(startBlockIdx)) return d.read_block_single(dst, startBlockIdx)
if err != nil {
return err
}
return d.read_data(dst)
} else if numblocks > 1 { } else if numblocks > 1 {
// TODO: implement multi block transaction reading.
// Rust code is failing here.
blocksize := int(d.blk.size()) blocksize := int(d.blk.size())
_, err = d.card_command(cmdReadMultipleBlock, uint32(startBlockIdx))
if err != nil {
return err
}
for i := 0; i < numblocks; i++ { for i := 0; i < numblocks; i++ {
offset := i * blocksize dataoff := i * blocksize
err = d.read_data(dst[offset : offset+blocksize]) d.csEnable(true)
_, err := d.read_block_single(dst[dataoff:dataoff+blocksize], int64(i)+startBlockIdx)
if err != nil { if err != nil {
return err return dataoff, err
} }
d.csEnable(false)
} }
return nil return len(dst), nil
} }
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")
} }
// WriteBlocks writes to sdcard from a buffer multiple of 512 bytes from src starting at block `startBlockIdx`. // WriteBlocks writes to sdcard from a buffer multiple of 512 bytes from src starting at block `startBlockIdx`.
func (d *SPICard) WriteBlocks(data []byte, startBlockIdx int64) error { func (d *SPICard) WriteBlocks(data []byte, startBlockIdx int64) (int, error) {
numblocks, err := d.checkBounds(startBlockIdx, len(data)) numblocks, err := d.checkBounds(startBlockIdx, len(data))
if err != nil { if err != nil {
return err return 0, err
} }
if d.kind != TypeSDHC { if d.kind != TypeSDHC {
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.endTx() defer d.csEnable(false)
writeTimeout := 2 * d.timeout writeTimeout := 2 * d.timeout
if numblocks == 1 { if numblocks == 1 {
_, err = d.card_command(cmdWriteBlock, uint32(startBlockIdx)) return d.write_block_single(data, startBlockIdx)
if err != nil {
return err
}
err = d.write_data(tokSTART_BLOCK, data)
if err != nil {
return err
}
err = d.wait_not_busy(writeTimeout)
if err != nil {
return err
}
status, err := d.card_command(cmdSendStatus, 0)
if err != nil {
return err
} else if status != 0 {
return makeResponseError(response1(status))
}
status, err = d.receive()
if err != nil {
return err
} else if status != 0 {
return errWrite
}
return nil
} else if numblocks > 1 { } else if numblocks > 1 {
// Start multi block write. // Start multi block write.
blocksize := 1 << d.blk.size() blocksize := int(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 0, err
} }
for i := 0; i < numblocks; i++ { for i := 0; i < numblocks; i++ {
offset := i * blocksize offset := i * blocksize
err = d.wait_not_busy(writeTimeout) err = d.wait_not_busy(writeTimeout)
if err != nil { if err != nil {
return err return 0, err
} }
err = d.write_data(tokWRITE_MULT, data[offset:offset+blocksize]) err = d.write_data(tokWRITE_MULT, data[offset:offset+blocksize])
if err != nil { if err != nil {
return err return 0, err
} }
} }
// Stop the multi write operation. // Stop the multi write operation.
err = d.wait_not_busy(writeTimeout) err = d.wait_not_busy(writeTimeout)
if err != nil { if err != nil {
return err return 0, err
} }
return d.send(tokSTOP_TRAN) err = d.send(tokSTOP_TRAN)
if err != nil {
return 0, err
}
_, err = d.card_command(cmdStopTransmission, 0)
if err != nil {
return 0, err
}
return len(data), nil
} }
panic("unreachable numblocks<=0") panic("unreachable numblocks<=0")
} }
func (d *SPICard) read_block_single(dst []byte, startBlockIdx int64) (int, error) {
_, err := d.card_command(cmdReadSingleBlock, uint32(startBlockIdx))
if err != nil {
return 0, err
}
err = d.read_data(dst)
if err != nil {
return 0, err
}
return len(dst), nil
}
func (d *SPICard) write_block_single(data []byte, startBlockIdx int64) (_ int, err error) {
_, err = d.card_command(cmdWriteBlock, uint32(startBlockIdx))
if err != nil {
return 0, err
}
err = d.write_data(tokSTART_BLOCK, data)
if err != nil {
return 0, err
}
err = d.wait_not_busy(2 * d.timeout)
if err != nil {
return 0, err
}
status, err := d.card_command(cmdSendStatus, 0)
if err != nil {
return 0, err
} else if status != 0 {
return 0, makeResponseError(response1(status))
}
status, err = d.receive()
if err != nil {
return 0, err
} else if status != 0 {
return 0, errWrite
}
return len(data), nil
}
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.NumberOfBlocks() { if startBlockIdx >= d.NumberOfBlocks() {
return 0, errOOB return 0, errOOB
@@ -354,9 +369,10 @@ func (d *SPICard) read_data(data []byte) (err error) {
status, err = d.receive() status, err = d.receive()
if err != nil { if err != nil {
return err return err
} } else if status != 0xff {
if status != 0xff {
break break
} else if tm.expired() {
return errReadTimeout
} }
d.yield() d.yield()
} }