diff --git a/examples/sd/main.go b/examples/sd/main.go index 331ff9f..e80a3c2 100644 --- a/examples/sd/main.go +++ b/examples/sd/main.go @@ -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 +} diff --git a/sd/blockdevice.go b/sd/blockdevice.go index 0003ef0..2891be5 100644 --- a/sd/blockdevice.go +++ b/sd/blockdevice.go @@ -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 diff --git a/sd/rustref.go b/sd/rustref.go index 513088d..3c6880b 100644 --- a/sd/rustref.go +++ b/sd/rustref.go @@ -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() }