mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-04 06:57:48 +00:00
working BlockDevice
This commit is contained in:
+60
-9
@@ -48,14 +48,65 @@ func main() {
|
||||
cid := sdcard.CID()
|
||||
fmt.Printf("name=%s\ncsd=\n%s\n", cid.ProductName(), csd.String())
|
||||
|
||||
var buf [512]byte
|
||||
for i := 0; i < 11; i += 1 {
|
||||
err = sdcard.ReadBlocks(buf[:], int64(i))
|
||||
if err != nil {
|
||||
println("err reading block", i, ":", err.Error())
|
||||
continue
|
||||
}
|
||||
expectCRC := sd.CRC16(buf[:])
|
||||
fmt.Printf("block %d theircrc=%#x ourcrc=%#x:\n\t%#x\n", i, sdcard.LastReadCRC(), expectCRC, buf[:])
|
||||
const placeholderEraseSectorSize = 512
|
||||
bd, err := sd.NewBlockDevice(sdcard, int(csd.ReadBlockLen()), csd.NumberOfBlocks(), placeholderEraseSectorSize)
|
||||
if err != nil {
|
||||
panic("block device creation:" + err.Error())
|
||||
}
|
||||
var mc MemChecker
|
||||
|
||||
ok, badBlkIdx, err := mc.MemCheck(bd, 2, 100)
|
||||
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
@@ -18,10 +18,10 @@ var _ io.WriterAt = (*BlockDevice)(nil)
|
||||
type Card interface {
|
||||
// WriteBlocks writes the given data to the card, starting at the given block index.
|
||||
// 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.
|
||||
// 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(startSectorIdx, numSectors int64) error
|
||||
}
|
||||
@@ -59,13 +59,12 @@ func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
if off < 0 {
|
||||
return 0, errNegativeOffset
|
||||
}
|
||||
blockSize := len(bd.blockbuf)
|
||||
|
||||
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 {
|
||||
if _, err = bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil {
|
||||
return n, err
|
||||
}
|
||||
n += copy(p, bd.blockbuf[blockOff:])
|
||||
@@ -73,22 +72,22 @@ func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
blockIdx++
|
||||
}
|
||||
|
||||
remaining := len(p) - n
|
||||
if remaining >= blockSize {
|
||||
fullBlocksToRead := bd.blk.idx(int64(len(p)))
|
||||
if fullBlocksToRead > 0 {
|
||||
// 1 or more full blocks case.
|
||||
endOffset := remaining - int(bd.blk.off(int64(remaining)))
|
||||
err = bd.card.ReadBlocks(p[:endOffset], blockIdx)
|
||||
endOffset := fullBlocksToRead * bd.blk.size()
|
||||
ngot, err := bd.card.ReadBlocks(p[:endOffset], blockIdx)
|
||||
if err != nil {
|
||||
return n, err
|
||||
return n + ngot, err
|
||||
}
|
||||
p = p[endOffset:]
|
||||
n += endOffset
|
||||
blockIdx += int64(endOffset / blockSize)
|
||||
n += ngot
|
||||
blockIdx += fullBlocksToRead
|
||||
}
|
||||
|
||||
if len(p) > 0 {
|
||||
// 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
|
||||
}
|
||||
n += copy(p, bd.blockbuf)
|
||||
@@ -101,51 +100,66 @@ func (bd *BlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
||||
if off < 0 {
|
||||
return 0, errNegativeOffset
|
||||
}
|
||||
blockSize := len(bd.blockbuf)
|
||||
|
||||
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 {
|
||||
if _, err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil {
|
||||
return n, err
|
||||
}
|
||||
n += copy(bd.blockbuf[blockOff:], p)
|
||||
if err := bd.card.WriteBlocks(bd.blockbuf, blockIdx); err != nil {
|
||||
nexpect := copy(bd.blockbuf[blockOff:], p)
|
||||
ngot, err := bd.card.WriteBlocks(bd.blockbuf, blockIdx)
|
||||
if err != nil {
|
||||
return n, err
|
||||
} else if ngot != len(bd.blockbuf) {
|
||||
return n, io.ErrShortWrite
|
||||
}
|
||||
p = p[n:]
|
||||
n += nexpect
|
||||
p = p[nexpect:]
|
||||
blockIdx++
|
||||
}
|
||||
|
||||
remaining := len(p) - n
|
||||
if remaining >= blockSize {
|
||||
fullBlocksToWrite := bd.blk.idx(int64(len(p)))
|
||||
if fullBlocksToWrite > 0 {
|
||||
// 1 or more full blocks case.
|
||||
endOffset := remaining - int(bd.blk.off(int64(remaining)))
|
||||
err = bd.card.WriteBlocks(p[:endOffset], blockIdx)
|
||||
endOffset := fullBlocksToWrite * bd.blk.size()
|
||||
ngot, err := bd.card.WriteBlocks(p[:endOffset], blockIdx)
|
||||
n += ngot
|
||||
if err != nil {
|
||||
return n, err
|
||||
} else if ngot != int(endOffset) {
|
||||
return n, io.ErrShortWrite
|
||||
}
|
||||
p = p[endOffset:]
|
||||
n += endOffset
|
||||
blockIdx += int64(endOffset / blockSize)
|
||||
p = p[ngot:]
|
||||
blockIdx += fullBlocksToWrite
|
||||
}
|
||||
|
||||
if len(p) > 0 {
|
||||
// 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
|
||||
}
|
||||
n += copy(bd.blockbuf, p)
|
||||
if err := bd.card.WriteBlocks(bd.blockbuf, blockIdx); err != nil {
|
||||
copy(bd.blockbuf, p)
|
||||
ngot, err := bd.card.WriteBlocks(bd.blockbuf, blockIdx)
|
||||
if err != nil {
|
||||
return n, err
|
||||
} else if ngot != len(bd.blockbuf) {
|
||||
return n, io.ErrShortWrite
|
||||
}
|
||||
n += len(p)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Size returns the number of bytes in this block device.
|
||||
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
|
||||
@@ -163,7 +177,7 @@ func (bd *BlockDevice) EraseBlockSize() int64 {
|
||||
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 {
|
||||
blockshift int64
|
||||
blockmask int64
|
||||
|
||||
+74
-58
@@ -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`.
|
||||
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))
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
if d.kind != TypeSDHC {
|
||||
startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards.
|
||||
}
|
||||
|
||||
d.csEnable(true)
|
||||
defer d.endTx()
|
||||
defer d.csEnable(false)
|
||||
|
||||
if numblocks == 1 {
|
||||
_, err := d.card_command(cmdReadSingleBlock, uint32(startBlockIdx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.read_data(dst)
|
||||
return d.read_block_single(dst, startBlockIdx)
|
||||
|
||||
} else if numblocks > 1 {
|
||||
// TODO: implement multi block transaction reading.
|
||||
// Rust code is failing here.
|
||||
blocksize := int(d.blk.size())
|
||||
_, err = d.card_command(cmdReadMultipleBlock, uint32(startBlockIdx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < numblocks; i++ {
|
||||
offset := i * blocksize
|
||||
err = d.read_data(dst[offset : offset+blocksize])
|
||||
dataoff := i * blocksize
|
||||
d.csEnable(true)
|
||||
_, err := d.read_block_single(dst[dataoff:dataoff+blocksize], int64(i)+startBlockIdx)
|
||||
if err != nil {
|
||||
return err
|
||||
return dataoff, err
|
||||
}
|
||||
d.csEnable(false)
|
||||
}
|
||||
return nil
|
||||
return len(dst), 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")
|
||||
}
|
||||
|
||||
// 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))
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
if d.kind != TypeSDHC {
|
||||
startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards.
|
||||
}
|
||||
d.csEnable(true)
|
||||
defer d.endTx()
|
||||
defer d.csEnable(false)
|
||||
|
||||
writeTimeout := 2 * d.timeout
|
||||
if numblocks == 1 {
|
||||
_, err = d.card_command(cmdWriteBlock, uint32(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
|
||||
return d.write_block_single(data, startBlockIdx)
|
||||
|
||||
} else if numblocks > 1 {
|
||||
// Start multi block write.
|
||||
blocksize := 1 << d.blk.size()
|
||||
blocksize := int(d.blk.size())
|
||||
_, err = d.card_command(cmdWriteMultipleBlock, uint32(startBlockIdx))
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for i := 0; i < numblocks; i++ {
|
||||
offset := i * blocksize
|
||||
err = d.wait_not_busy(writeTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
err = d.write_data(tokWRITE_MULT, data[offset:offset+blocksize])
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
// Stop the multi write operation.
|
||||
err = d.wait_not_busy(writeTimeout)
|
||||
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")
|
||||
}
|
||||
|
||||
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) {
|
||||
if startBlockIdx >= d.NumberOfBlocks() {
|
||||
return 0, errOOB
|
||||
@@ -354,9 +369,10 @@ func (d *SPICard) read_data(data []byte) (err error) {
|
||||
status, err = d.receive()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != 0xff {
|
||||
} else if status != 0xff {
|
||||
break
|
||||
} else if tm.expired() {
|
||||
return errReadTimeout
|
||||
}
|
||||
d.yield()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user