mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
sdcard: add support for spi sdcard driver
This commit is contained in:
@@ -193,12 +193,14 @@ endif
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
|
||||
@md5sum ./build/test.hex
|
||||
|
||||
DRIVERS = $(wildcard */)
|
||||
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
||||
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
|
||||
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
|
||||
pcf8563 mcp2515 servo
|
||||
pcf8563 mcp2515 servo sdcard
|
||||
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
|
||||
|
||||
unit-test:
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"machine"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sdcard"
|
||||
)
|
||||
|
||||
const consoleBufLen = 64
|
||||
const storageBufLen = 1024
|
||||
|
||||
var (
|
||||
debug = false
|
||||
|
||||
input [consoleBufLen]byte
|
||||
store [storageBufLen]byte
|
||||
|
||||
console = machine.Serial
|
||||
|
||||
dev *sdcard.Device
|
||||
|
||||
commands map[string]cmdfunc = map[string]cmdfunc{
|
||||
"": cmdfunc(noop),
|
||||
"help": cmdfunc(help),
|
||||
"dbg": cmdfunc(dbg),
|
||||
"erase": cmdfunc(erase),
|
||||
"lsblk": cmdfunc(lsblk),
|
||||
"write": cmdfunc(write),
|
||||
"xxd": cmdfunc(xxd),
|
||||
}
|
||||
|
||||
his history
|
||||
)
|
||||
|
||||
type history struct {
|
||||
buf [32]string
|
||||
wp int
|
||||
idx int
|
||||
}
|
||||
|
||||
func (h *history) Add(cmd string) {
|
||||
if len(cmd) == 0 {
|
||||
h.idx = h.wp
|
||||
return
|
||||
}
|
||||
|
||||
if h.wp == len(h.buf)-1 {
|
||||
for i := 1; i < len(h.buf); i++ {
|
||||
h.buf[i-1] = h.buf[i]
|
||||
}
|
||||
h.wp--
|
||||
}
|
||||
|
||||
h.buf[h.wp] = cmd
|
||||
h.wp++
|
||||
h.idx = h.wp
|
||||
}
|
||||
|
||||
func (h *history) PeekPrev() string {
|
||||
if h.idx > 0 {
|
||||
h.idx--
|
||||
}
|
||||
return h.buf[h.idx]
|
||||
}
|
||||
|
||||
func (h *history) PeekNext() string {
|
||||
if h.idx < h.wp {
|
||||
h.idx++
|
||||
}
|
||||
return h.buf[h.idx]
|
||||
}
|
||||
|
||||
type cmdfunc func(argv []string)
|
||||
|
||||
const (
|
||||
StateInput = iota
|
||||
StateEscape
|
||||
StateEscBrc
|
||||
StateCSI
|
||||
)
|
||||
|
||||
func RunFor(device *sdcard.Device) {
|
||||
|
||||
dev = device
|
||||
|
||||
prompt()
|
||||
|
||||
var state = StateInput
|
||||
|
||||
for i := 0; ; {
|
||||
if console.Buffered() > 0 {
|
||||
data, _ := console.ReadByte()
|
||||
if debug {
|
||||
fmt.Printf("\rdata: %x, his.idx: %d\r\n\r", data, his.idx)
|
||||
prompt()
|
||||
console.Write(input[:i])
|
||||
}
|
||||
switch state {
|
||||
case StateInput:
|
||||
switch data {
|
||||
case 0x8:
|
||||
fallthrough
|
||||
case 0x7f: // this is probably wrong... works on my machine tho :)
|
||||
// backspace
|
||||
if i > 0 {
|
||||
i -= 1
|
||||
console.Write([]byte{0x8, 0x20, 0x8})
|
||||
}
|
||||
case 13:
|
||||
// return key
|
||||
console.Write([]byte("\r\n"))
|
||||
runCommand(string(input[:i]))
|
||||
his.Add(string(input[:i]))
|
||||
prompt()
|
||||
|
||||
i = 0
|
||||
continue
|
||||
case 27:
|
||||
// escape
|
||||
state = StateEscape
|
||||
default:
|
||||
// anything else, just echo the character if it is printable
|
||||
if strconv.IsPrint(rune(data)) {
|
||||
if i < (consoleBufLen - 1) {
|
||||
console.WriteByte(data)
|
||||
input[i] = data
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
case StateEscape:
|
||||
switch data {
|
||||
case 0x5b:
|
||||
state = StateEscBrc
|
||||
default:
|
||||
state = StateInput
|
||||
}
|
||||
case StateEscBrc:
|
||||
switch data {
|
||||
case 0x41:
|
||||
// up
|
||||
println()
|
||||
prompt()
|
||||
cmd := his.PeekPrev()
|
||||
i = len(cmd)
|
||||
copy(input[:i], []byte(cmd))
|
||||
console.Write(input[:i])
|
||||
state = StateInput
|
||||
case 0x42:
|
||||
//down
|
||||
println()
|
||||
prompt()
|
||||
cmd := his.PeekNext()
|
||||
i = len(cmd)
|
||||
copy(input[:i], []byte(cmd))
|
||||
console.Write(input[:i])
|
||||
state = StateInput
|
||||
default:
|
||||
// TODO: handle escape sequences
|
||||
state = StateInput
|
||||
}
|
||||
default:
|
||||
// TODO: handle escape sequences
|
||||
state = StateInput
|
||||
}
|
||||
} else {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(line string) {
|
||||
argv := strings.SplitN(strings.TrimSpace(line), " ", -1)
|
||||
cmd := argv[0]
|
||||
|
||||
cmdfn, ok := commands[cmd]
|
||||
if !ok {
|
||||
println("unknown command: " + line)
|
||||
return
|
||||
}
|
||||
cmdfn(argv)
|
||||
}
|
||||
|
||||
func noop(argv []string) {}
|
||||
|
||||
func help(argv []string) {
|
||||
fmt.Printf("help\r\n")
|
||||
fmt.Printf("dbg\r\n")
|
||||
fmt.Printf("erase\r\n")
|
||||
fmt.Printf("lsblk\r\n")
|
||||
fmt.Printf("write <hex offset> <bytes>\r\n")
|
||||
fmt.Printf("xxd <start address> <length>\r\n")
|
||||
}
|
||||
|
||||
func dbg(argv []string) {
|
||||
if debug {
|
||||
debug = false
|
||||
println("Console debbuging off")
|
||||
} else {
|
||||
debug = true
|
||||
println("Console debbuging on")
|
||||
}
|
||||
}
|
||||
|
||||
func lsblk(argv []string) {
|
||||
csd := dev.CSD
|
||||
sectors, err := csd.Sectors()
|
||||
if err != nil {
|
||||
fmt.Printf("%s\r\n", err.Error())
|
||||
return
|
||||
}
|
||||
cid := dev.CID
|
||||
|
||||
fmt.Printf(
|
||||
"\r\n-------------------------------------\r\n"+
|
||||
" Device Information: \r\n"+
|
||||
"-------------------------------------\r\n"+
|
||||
" JEDEC ID: %v\r\n"+
|
||||
" Serial: %v\r\n"+
|
||||
" Status 1: %02x\r\n"+
|
||||
" Status 2: %02x\r\n"+
|
||||
" \r\n"+
|
||||
" Max clock speed (MHz): %d\r\n"+
|
||||
" Has Sector Protection: %t\r\n"+
|
||||
" Supports Fast Reads: %t\r\n"+
|
||||
" Supports QSPI Reads: %t\r\n"+
|
||||
" Supports QSPI Write: %t\r\n"+
|
||||
" Write Status Split: %t\r\n"+
|
||||
" Single Status Byte: %t\r\n"+
|
||||
"-Sectors: %d\r\n"+
|
||||
"-Bytes (Sectors * 512) %d\r\n"+
|
||||
"-ManufacturerID %02X\r\n"+
|
||||
"-OEMApplicationID %04X\r\n"+
|
||||
"-ProductName %s\r\n"+
|
||||
"-ProductVersion %s\r\n"+
|
||||
"-ProductSerialNumber %08X\r\n"+
|
||||
"-ManufacturingYear %02X\r\n"+
|
||||
"-ManufacturingMonth %02X\r\n"+
|
||||
"-Always1 %d\r\n"+
|
||||
"-CRC %02X\r\n"+
|
||||
"-------------------------------------\r\n\r\n",
|
||||
"attrs.JedecID", // attrs.JedecID,
|
||||
cid.ProductSerialNumber, // serialNumber1,
|
||||
0, // status1,
|
||||
0, // status2,
|
||||
csd.TRAN_SPEED, // attrs.MaxClockSpeedMHz,
|
||||
false, // attrs.HasSectorProtection,
|
||||
false, // attrs.SupportsFastRead,
|
||||
false, // attrs.SupportsQSPI,
|
||||
false, // attrs.SupportsQSPIWrites,
|
||||
false, // attrs.WriteStatusSplit,
|
||||
false, // attrs.SingleStatusByte,
|
||||
sectors,
|
||||
csd.Size(),
|
||||
cid.ManufacturerID,
|
||||
cid.OEMApplicationID,
|
||||
cid.ProductName,
|
||||
cid.ProductVersion,
|
||||
cid.ProductSerialNumber,
|
||||
cid.ManufacturingYear,
|
||||
cid.ManufacturingMonth,
|
||||
cid.Always1,
|
||||
cid.CRC,
|
||||
)
|
||||
}
|
||||
|
||||
func erase(argv []string) {
|
||||
fmt.Printf("erase - not impl\r\n")
|
||||
}
|
||||
|
||||
var writeBuf [256]byte
|
||||
|
||||
func write(argv []string) {
|
||||
if len(argv) < 3 {
|
||||
println("usage: write <hex offset> <bytes>")
|
||||
return
|
||||
}
|
||||
var err error
|
||||
var addr uint64 = 0x0
|
||||
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
|
||||
println("Invalid address: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
buf := writeBuf[:0]
|
||||
for i := 0; i < len(argv[2]); i += 2 {
|
||||
var b uint64
|
||||
if b, err = strconv.ParseUint(argv[2][i:i+2], 16, 8); err != nil {
|
||||
println("Invalid bytes: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
buf = append(buf, byte(b))
|
||||
}
|
||||
|
||||
if _, err = dev.WriteAt(buf, int64(addr)); err != nil {
|
||||
println("Write error: " + err.Error() + "\r\n")
|
||||
}
|
||||
}
|
||||
|
||||
func xxd(argv []string) {
|
||||
var err error
|
||||
var addr uint64 = 0x0
|
||||
var size int = 64
|
||||
switch len(argv) {
|
||||
case 3:
|
||||
if size, err = strconv.Atoi(argv[2]); err != nil {
|
||||
println("Invalid size argument: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
if size > storageBufLen || size < 1 {
|
||||
fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
|
||||
println("Invalid address: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
// no args supplied, so nothing to do here, just use the defaults
|
||||
default:
|
||||
println("usage: xxd <hex address, ex: 0xA0> <size of hexdump in bytes>\r\n")
|
||||
return
|
||||
}
|
||||
buf := store[0:size]
|
||||
_, err = dev.ReadAt(buf, int64(addr))
|
||||
if err != nil {
|
||||
fmt.Printf("xxd err : %s\r\n", err.Error())
|
||||
}
|
||||
xxdfprint(os.Stdout, uint32(addr), buf)
|
||||
}
|
||||
|
||||
func xxdfprint(w io.Writer, offset uint32, b []byte) {
|
||||
var l int
|
||||
var buf16 = make([]byte, 16)
|
||||
for i, c := 0, len(b); i < c; i += 16 {
|
||||
l = i + 16
|
||||
if l >= c {
|
||||
l = c
|
||||
}
|
||||
fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l])
|
||||
for j, n := 0, l-i; j < 16; j++ {
|
||||
if j >= n || !strconv.IsPrint(rune(b[i+j])) || b[i+j] >= 0xFE {
|
||||
buf16[j] = '.'
|
||||
} else {
|
||||
buf16[j] = b[i+j]
|
||||
}
|
||||
}
|
||||
console.Write(buf16)
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
func prompt() {
|
||||
print("==> ")
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// +build feather_m4 feather_m4_can
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
spi.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 24000000,
|
||||
LSBFirst: false,
|
||||
Mode: 0, // phase=0, polarity=0
|
||||
})
|
||||
|
||||
csPin = machine.D10
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// +build atsamd21
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
spi.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 24000000,
|
||||
LSBFirst: false,
|
||||
Mode: 0, // phase=0, polarity=0
|
||||
})
|
||||
|
||||
csPin = machine.D2
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sdcard"
|
||||
)
|
||||
|
||||
var (
|
||||
spi machine.SPI
|
||||
csPin machine.Pin
|
||||
ledPin machine.Pin
|
||||
)
|
||||
|
||||
func main() {
|
||||
waitSerial()
|
||||
fmt.Printf("sdcard console\r\n")
|
||||
|
||||
led := ledPin
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
sd := sdcard.New(spi, csPin)
|
||||
err := sd.Configure()
|
||||
if err != nil {
|
||||
fmt.Printf("%s\r\n", err.Error())
|
||||
for {
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
go RunFor(&sd)
|
||||
|
||||
for {
|
||||
led.High()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
led.Low()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
spi.Configure(machine.SPIConfig{
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Frequency: 24000000,
|
||||
LSBFirst: false,
|
||||
Mode: 0, // phase=0, polarity=0
|
||||
})
|
||||
|
||||
csPin = machine.D32 // SD_CS
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI2
|
||||
spi.Configure(machine.SPIConfig{
|
||||
SCK: machine.SCK2,
|
||||
SDO: machine.SDO2,
|
||||
SDI: machine.SDI2,
|
||||
Frequency: 24000000,
|
||||
LSBFirst: false,
|
||||
Mode: 0, // phase=0, polarity=0
|
||||
})
|
||||
|
||||
csPin = machine.SS2
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package sdcard
|
||||
|
||||
import "fmt"
|
||||
|
||||
type CID struct {
|
||||
//// byte 0
|
||||
ManufacturerID byte
|
||||
//uint8_t mid; // Manufacturer ID
|
||||
//// byte 1-2
|
||||
OEMApplicationID uint16
|
||||
//char oid[2]; // OEM/Application ID
|
||||
//// byte 3-7
|
||||
ProductName string
|
||||
//char pnm[5]; // Product name
|
||||
//// byte 8
|
||||
ProductVersion string
|
||||
//unsigned prv_m : 4; // Product revision n.m
|
||||
//unsigned prv_n : 4;
|
||||
//// byte 9-12
|
||||
ProductSerialNumber uint32
|
||||
//uint32_t psn; // Product serial number
|
||||
//// byte 13
|
||||
ManufacturingYear byte
|
||||
ManufacturingMonth byte
|
||||
//unsigned mdt_year_high : 4; // Manufacturing date
|
||||
//unsigned reserved : 4;
|
||||
//// byte 14
|
||||
//unsigned mdt_month : 4;
|
||||
//unsigned mdt_year_low : 4;
|
||||
//// byte 15
|
||||
Always1 byte
|
||||
//unsigned always1 : 1;
|
||||
CRC byte
|
||||
//unsigned crc : 7;
|
||||
}
|
||||
|
||||
func NewCID(buf []byte) *CID {
|
||||
return &CID{
|
||||
ManufacturerID: buf[0],
|
||||
OEMApplicationID: (uint16(buf[0]) << 8) | uint16(buf[1]),
|
||||
ProductName: string(buf[3:8]),
|
||||
ProductVersion: fmt.Sprintf("%d.%d", (buf[8]&0xF0)>>4, buf[8]&0x0F),
|
||||
ProductSerialNumber: (uint32(buf[9]) << 24) | (uint32(buf[10]) << 16) | (uint32(buf[11]) << 8) | uint32(buf[12]),
|
||||
ManufacturingYear: (buf[13] & 0xF0) | (buf[14] & 0x0F),
|
||||
ManufacturingMonth: (buf[14] & 0xF0) >> 4,
|
||||
Always1: (buf[15] & 0x80) >> 7,
|
||||
CRC: buf[15] & 0x7F,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package sdcard
|
||||
|
||||
const (
|
||||
CMD0_GO_IDLE_STATE = 0
|
||||
CMD1_SEND_OP_CND = 1
|
||||
CMD2_ALL_SEND_CID = 2
|
||||
CMD3_SEND_RELATIVE_ADDR = 3
|
||||
CMD4_SET_DSR = 4
|
||||
CMD6_SWITCH_FUNC = 6
|
||||
CMD7_SELECT_DESELECT_CARD = 7
|
||||
CMD8_SEND_IF_COND = 8
|
||||
CMD9_SEND_CSD = 9
|
||||
CMD10_SEND_CID = 10
|
||||
CMD12_STOP_TRANSMISSION = 12
|
||||
CMD13_SEND_STATUS = 13
|
||||
CMD15_GO_INACTIVE_STATE = 15
|
||||
CMD16_SET_BLOCKLEN = 16
|
||||
CMD17_READ_SINGLE_BLOCK = 17
|
||||
CMD18_READ_MULTIPLE_BLOCK = 18
|
||||
CMD24_WRITE_BLOCK = 24
|
||||
CMD25_WRITE_MULTIPLE_BLOCK = 25
|
||||
CMD27_PROGRAM_CSD = 27
|
||||
CMD28_SET_WRITE_PROT = 28
|
||||
CMD29_CLR_WRITE_PROT = 29
|
||||
CMD30_SEND_WRITE_PROT = 30
|
||||
CMD32_ERASE_WR_BLK_START_ADDR = 32
|
||||
CMD33_ERASE_WR_BLK_END_ADDR = 33
|
||||
CMD38_ERASE = 38
|
||||
CMD42_LOCK_UNLOCK = 42
|
||||
CMD55_APP_CMD = 55
|
||||
CMD56_GEN_CMD = 56
|
||||
CMD58_READ_OCR = 58
|
||||
CMD59_CRC_ON_OFF = 59
|
||||
ACMD6_SET_BUS_WIDTH = 6
|
||||
ACMD13_SD_STATUS = 13
|
||||
ACMD22_SEND_NUM_WR_BLOCKS = 22
|
||||
ACMD23_SET_WR_BLK_ERASE_COUNT = 23
|
||||
ACMD41_SD_APP_OP_COND = 41
|
||||
ACMD42_SET_CLR_CARD_DETECT = 42
|
||||
ACMD51_SEND_SCR = 51
|
||||
ACMD18_SECURE_READ_MULTI_BLOCK = 18
|
||||
ACMD25_SECURE_WRITE_MULTI_BLOCK = 25
|
||||
ACMD26_SECURE_WRITE_MKB = 26
|
||||
ACMD38_SECURE_ERASE = 38
|
||||
ACMD43_GET_MKB = 43
|
||||
ACMD44_GET_MID = 44
|
||||
ACMD45_SET_CER_RN1 = 45
|
||||
ACMD46_SET_CER_RN2 = 46
|
||||
ACMD47_SET_CER_RES2 = 47
|
||||
ACMD48_SET_CER_RES1 = 48
|
||||
ACMD49_CHANGE_SECURE_AREA = 49
|
||||
)
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package sdcard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CSD struct {
|
||||
CSD_STRUCTURE byte // 2 R [127:126] 0x01 : CSD Structure
|
||||
TAAC byte // 8 R [119:112] 0x0E : Data Read Access-Time-1
|
||||
NSAC byte // 8 R [111:104] 0x00 : Data Read Access-Time-2 in CLK Cycles (NSAC*100)
|
||||
TRAN_SPEED byte // 8 R [103:96] 0x5A : Max. Data Transfer Rate
|
||||
CCC uint16 // 12 R [95:84] 0x5B5 : Card Command Classes
|
||||
READ_BL_LEN byte // 4 R [83:80] 0x09 : Max. Read Data Block Length
|
||||
READ_BL_PARTIAL byte // 1 R [79:79] 0x00 : Partial Blocks for Read Allowed
|
||||
WRITE_BLK_MISALIGN byte // 1 R [78:78] 0x00 : Write Block Misalignment
|
||||
READ_BLK_MISALIGN byte // 1 R [77:77] 0x00 : Read Block Misalignment
|
||||
DSR_IMP byte // 1 R [76:76] 0x00 : DSR Implemented
|
||||
C_SIZE uint32 // 22 R [69:48] 0xXXXXXX : Device Size
|
||||
ERASE_BLK_EN byte // 1 R [46:46] 0x01 : Erase Single Block Enable
|
||||
SECTOR_SIZE byte // 7 R [45:39] 0x7F : Erase Sector Size
|
||||
WP_GRP_SIZE byte // 7 R [38:32] 0x00 : Write Protect Group Size
|
||||
WP_GRP_ENABLE byte // 1 R [31:31] 0x00 : Write Protect Group Enable
|
||||
R2W_FACTOR byte // 3 R [28:26] 0x02 : Write Speed Factor
|
||||
WRITE_BL_LEN byte // 4 R [25:22] 0x09 : Max. Write data Block Length
|
||||
WRITE_BL_PARTIAL byte // 1 R [21:21] 0x00 : Partial Blocks for Write Allowed
|
||||
FILE_FORMAT_GRP byte // 1 R [15:15] 0x00 : File Format Group
|
||||
COPY byte // 1 RW [14:14] 0x00 :Copy Flag
|
||||
PERM_WRITE_PROTECT byte // 1 RW [13:13] 0x00 : Permanent Write Protection
|
||||
TMP_WRITE_PROTECT byte // 1 RW [12:12] 0x00 : Temporary Write Protection
|
||||
FILE_FORMAT byte // 2 R [11:10] 0x00 : File Format
|
||||
CRC byte // 7 RW [7:1] 0xXX : CRC
|
||||
}
|
||||
|
||||
func NewCSD(buf []byte) *CSD {
|
||||
return &CSD{
|
||||
CSD_STRUCTURE: (buf[0] & 0xC0) >> 6,
|
||||
TAAC: buf[1],
|
||||
NSAC: buf[2],
|
||||
TRAN_SPEED: buf[3],
|
||||
CCC: uint16(buf[4])<<4 | uint16(buf[5])>>4,
|
||||
READ_BL_LEN: buf[5] & 0x0F,
|
||||
READ_BL_PARTIAL: (buf[6] & 0x80) >> 7,
|
||||
WRITE_BLK_MISALIGN: (buf[6] & 0x40) >> 6,
|
||||
READ_BLK_MISALIGN: (buf[6] & 0x20) >> 5,
|
||||
DSR_IMP: (buf[6] & 0x10) >> 4,
|
||||
C_SIZE: uint32(buf[7]&0x3F)<<16 | uint32(buf[8])<<8 | uint32(buf[9]),
|
||||
ERASE_BLK_EN: (buf[10] & 0x40) >> 6,
|
||||
SECTOR_SIZE: (buf[10]&0x3F)<<1 | (buf[11]&0x80)>>7,
|
||||
WP_GRP_SIZE: buf[11] & 0x7F,
|
||||
WP_GRP_ENABLE: (buf[12] & 0x80) >> 7,
|
||||
R2W_FACTOR: (buf[12] & 0x1C) >> 2,
|
||||
WRITE_BL_LEN: (buf[12]&0x03)<<2 | (buf[13]&0xC0)>>6,
|
||||
WRITE_BL_PARTIAL: (buf[13] & 0x20) >> 5,
|
||||
FILE_FORMAT_GRP: (buf[14] & 0x80) >> 7,
|
||||
COPY: (buf[14] & 0x40) >> 6,
|
||||
PERM_WRITE_PROTECT: (buf[14] & 0x20) >> 5,
|
||||
TMP_WRITE_PROTECT: (buf[14] & 0x10) >> 4,
|
||||
FILE_FORMAT: (buf[14] & 0x0C) >> 2,
|
||||
CRC: (buf[15] & 0xFE) >> 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CSD) Dump() {
|
||||
fmt.Printf("CSD_STRUCTURE: %X\r\n", c.CSD_STRUCTURE)
|
||||
fmt.Printf("TAAC: %X\r\n", c.TAAC)
|
||||
fmt.Printf("NSAC: %X\r\n", c.NSAC)
|
||||
fmt.Printf("TRAN_SPEED: %X\r\n", c.TRAN_SPEED)
|
||||
fmt.Printf("CCC: %X\r\n", c.CCC)
|
||||
fmt.Printf("READ_BL_LEN: %X\r\n", c.READ_BL_LEN)
|
||||
fmt.Printf("READ_BL_PARTIAL: %X\r\n", c.READ_BL_PARTIAL)
|
||||
fmt.Printf("WRITE_BLK_MISALIGN: %X\r\n", c.WRITE_BLK_MISALIGN)
|
||||
fmt.Printf("READ_BLK_MISALIGN: %X\r\n", c.READ_BLK_MISALIGN)
|
||||
fmt.Printf("DSR_IMP: %X\r\n", c.DSR_IMP)
|
||||
fmt.Printf("C_SIZE: %X\r\n", c.C_SIZE)
|
||||
fmt.Printf("ERASE_BLK_EN: %X\r\n", c.ERASE_BLK_EN)
|
||||
fmt.Printf("SECTOR_SIZE: %X\r\n", c.SECTOR_SIZE)
|
||||
fmt.Printf("WP_GRP_SIZE: %X\r\n", c.WP_GRP_SIZE)
|
||||
fmt.Printf("WP_GRP_ENABLE: %X\r\n", c.WP_GRP_ENABLE)
|
||||
fmt.Printf("R2W_FACTOR: %X\r\n", c.R2W_FACTOR)
|
||||
fmt.Printf("WRITE_BL_LEN: %X\r\n", c.WRITE_BL_LEN)
|
||||
fmt.Printf("WRITE_BL_PARTIAL: %X\r\n", c.WRITE_BL_PARTIAL)
|
||||
fmt.Printf("FILE_FORMAT_GRP: %X\r\n", c.FILE_FORMAT_GRP)
|
||||
fmt.Printf("COPY: %X\r\n", c.COPY)
|
||||
fmt.Printf("PERM_WRITE_PROTECT: %X\r\n", c.PERM_WRITE_PROTECT)
|
||||
fmt.Printf("TMP_WRITE_PROTECT: %X\r\n", c.TMP_WRITE_PROTECT)
|
||||
fmt.Printf("FILE_FORMAT: %X\r\n", c.FILE_FORMAT)
|
||||
fmt.Printf("CRC: %X\r\n", c.CRC)
|
||||
}
|
||||
|
||||
func (c *CSD) Sectors() (int64, error) {
|
||||
sectors := int64(0)
|
||||
if c.CSD_STRUCTURE == 0x01 {
|
||||
// CSD version 2.0
|
||||
sectors = (int64(c.C_SIZE) + 1) * 1024
|
||||
} else if c.CSD_STRUCTURE == 0x00 {
|
||||
// CSD version 1.0 (old, <=2GB)
|
||||
return 0, fmt.Errorf("CSD format version 1.0 is not supported")
|
||||
} else {
|
||||
return 0, fmt.Errorf("unknown CSD format")
|
||||
}
|
||||
return sectors, nil
|
||||
}
|
||||
|
||||
func (c *CSD) Size() uint64 {
|
||||
return uint64(c.C_SIZE) * 512 * 1024
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
package sdcard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
_CMD_TIMEOUT = 100
|
||||
|
||||
_R1_IDLE_STATE = 1 << 0
|
||||
_R1_ERASE_RESET = 1 << 1
|
||||
_R1_ILLEGAL_COMMAND = 1 << 2
|
||||
_R1_COM_CRC_ERROR = 1 << 3
|
||||
_R1_ERASE_SEQUENCE_ERROR = 1 << 4
|
||||
_R1_ADDRESS_ERROR = 1 << 5
|
||||
_R1_PARAMETER_ERROR = 1 << 6
|
||||
|
||||
// card types
|
||||
SD_CARD_TYPE_SD1 = 1 // Standard capacity V1 SD card
|
||||
SD_CARD_TYPE_SD2 = 2 // Standard capacity V2 SD card
|
||||
SD_CARD_TYPE_SDHC = 3 // High Capacity SD card
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus machine.SPI
|
||||
cs machine.Pin
|
||||
cmdbuf []byte
|
||||
dummybuf []byte
|
||||
tokenbuf []byte
|
||||
sdCardType byte
|
||||
CID *CID
|
||||
CSD *CSD
|
||||
}
|
||||
|
||||
func New(b machine.SPI, cs machine.Pin) Device {
|
||||
return Device{
|
||||
bus: b,
|
||||
cs: cs,
|
||||
cmdbuf: make([]byte, 6),
|
||||
dummybuf: make([]byte, 512),
|
||||
tokenbuf: make([]byte, 1),
|
||||
sdCardType: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) Configure() error {
|
||||
return d.initCard()
|
||||
}
|
||||
|
||||
func (d *Device) initCard() error {
|
||||
// set pin modes
|
||||
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.cs.High()
|
||||
|
||||
// clock card at least 100 cycles with cs high
|
||||
for i := 0; i < 16; i++ {
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
}
|
||||
|
||||
d.cs.Low()
|
||||
|
||||
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
|
||||
ok := false
|
||||
for i := 0; i < 2000; i++ {
|
||||
// Wait up to 2 seconds to be the same as the Arduino
|
||||
if d.cmd(CMD0_GO_IDLE_STATE, 0, 0x95) == _R1_IDLE_STATE {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("no SD card")
|
||||
}
|
||||
|
||||
// CMD8: determine card version
|
||||
r := d.cmd(CMD8_SEND_IF_COND, 0x01AA, 0x87)
|
||||
if (r & _R1_ILLEGAL_COMMAND) == _R1_ILLEGAL_COMMAND {
|
||||
d.sdCardType = SD_CARD_TYPE_SD1
|
||||
return fmt.Errorf("init_card_v1 not impl\r\n")
|
||||
} else {
|
||||
// r7 response
|
||||
status := byte(0)
|
||||
for i := 0; i < 3; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if (status & 0x0F) != 0x01 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD8 %02X", status)
|
||||
}
|
||||
|
||||
for i := 3; i < 4; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if status != 0xAA {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD8 %02X", status)
|
||||
}
|
||||
d.sdCardType = SD_CARD_TYPE_SD2
|
||||
}
|
||||
|
||||
// initialize card and send host supports SDHC if SD2
|
||||
arg := uint32(0)
|
||||
if d.sdCardType == SD_CARD_TYPE_SD2 {
|
||||
arg = 0x40000000
|
||||
}
|
||||
|
||||
// check for timeout
|
||||
ok = false
|
||||
for i := 0; i < 2000; i++ {
|
||||
if d.acmd(ACMD41_SD_APP_OP_COND, arg) == 0 {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("SD_CARD_ERROR_ACMD41")
|
||||
}
|
||||
|
||||
// if SD2 read OCR register to check for SDHC card
|
||||
if d.sdCardType == SD_CARD_TYPE_SD2 {
|
||||
if d.cmd(CMD58_READ_OCR, 0, 0) != 0 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD58")
|
||||
}
|
||||
|
||||
status, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (status & 0xC0) == 0xC0 {
|
||||
d.sdCardType = SD_CARD_TYPE_SDHC
|
||||
}
|
||||
// discard rest of ocr - contains allowed voltage range
|
||||
for i := 1; i < 4; i++ {
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
}
|
||||
}
|
||||
|
||||
var buf [16]byte
|
||||
// read CID
|
||||
err := d.ReadCID(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.CID = NewCID(buf[:])
|
||||
|
||||
// read CSD
|
||||
err = d.ReadCSD(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.CSD = NewCSD(buf[:])
|
||||
|
||||
d.cs.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Device) acmd(cmd byte, arg uint32) byte {
|
||||
d.cmd(CMD55_APP_CMD, 0, 0)
|
||||
return d.cmd(cmd, arg, 0)
|
||||
}
|
||||
|
||||
func (d Device) cmd(cmd byte, arg uint32, crc byte) byte {
|
||||
d.cs.Low()
|
||||
|
||||
if cmd != 12 {
|
||||
d.waitNotBusy(300)
|
||||
}
|
||||
|
||||
// create and send the command
|
||||
buf := d.cmdbuf
|
||||
buf[0] = 0x40 | cmd
|
||||
buf[1] = byte(arg >> 24)
|
||||
buf[2] = byte(arg >> 16)
|
||||
buf[3] = byte(arg >> 8)
|
||||
buf[4] = byte(arg)
|
||||
buf[5] = crc
|
||||
d.bus.Tx(buf, nil)
|
||||
|
||||
if cmd == 12 {
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
}
|
||||
|
||||
// wait for the response (response[7] == 0)
|
||||
for i := 0; i < 0xFFFF; i++ {
|
||||
d.bus.Tx([]byte{0xFF}, d.tokenbuf)
|
||||
response := d.tokenbuf[0]
|
||||
if (response & 0x80) == 0 {
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
//// timeout
|
||||
d.cs.High()
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
return 0xFF // -1
|
||||
}
|
||||
|
||||
func (d Device) waitNotBusy(timeoutMs int) error {
|
||||
for i := 0; i < timeoutMs; i++ {
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r == 0xFF {
|
||||
return nil
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Device) waitStartBlock() error {
|
||||
status := byte(0xFF)
|
||||
|
||||
for i := 0; i < 3000; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
d.cs.High()
|
||||
return err
|
||||
}
|
||||
if status != 0xFF {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
}
|
||||
|
||||
if status != 254 {
|
||||
d.cs.High()
|
||||
return fmt.Errorf("SD_CARD_START_BLOCK")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadCSD reads the CSD using CMD9.
|
||||
func (d Device) ReadCSD(csd []byte) error {
|
||||
return d.readRegister(CMD9_SEND_CSD, csd)
|
||||
}
|
||||
|
||||
// ReadCID reads the CID using CMD10
|
||||
func (d Device) ReadCID(csd []byte) error {
|
||||
return d.readRegister(CMD10_SEND_CID, csd)
|
||||
}
|
||||
|
||||
func (d Device) readRegister(cmd uint8, dst []byte) error {
|
||||
if d.cmd(cmd, 0, 0) != 0 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_READ_REG")
|
||||
}
|
||||
if err := d.waitStartBlock(); err != nil {
|
||||
return err
|
||||
}
|
||||
// transfer data
|
||||
for i := uint16(0); i < 16; i++ {
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst[i] = r
|
||||
}
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.cs.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadData reads 512 bytes from sdcard into dst.
|
||||
func (d Device) ReadData(block uint32, dst []byte) error {
|
||||
if len(dst) < 512 {
|
||||
return fmt.Errorf("len(dst) must be greater than or equal to 512")
|
||||
}
|
||||
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD17_READ_SINGLE_BLOCK, block, 0) != 0 {
|
||||
return fmt.Errorf("CMD17 error")
|
||||
}
|
||||
if err := d.waitStartBlock(); err != nil {
|
||||
return fmt.Errorf("waitStartBlock()")
|
||||
}
|
||||
|
||||
err := d.bus.Tx(nil, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip CRC (2byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// TODO: probably not necessary
|
||||
d.cs.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMultiStart starts the continuous write mode using CMD25.
|
||||
func (d Device) WriteMultiStart(block uint32) error {
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD25_WRITE_MULTIPLE_BLOCK, block, 0) != 0 {
|
||||
return fmt.Errorf("CMD25 error")
|
||||
}
|
||||
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMulti performs continuous writing. It is necessary to call
|
||||
// WriteMultiStart() in prior.
|
||||
func (d Device) WriteMulti(buf []byte) error {
|
||||
// send Data Token for CMD25
|
||||
d.bus.Transfer(byte(0xFC))
|
||||
|
||||
for i := 0; i < 512; i++ {
|
||||
_, err := d.bus.Transfer(buf[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// send dummy CRC (2 byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// Data Resp.
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (r & 0x1F) != 0x05 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE")
|
||||
}
|
||||
|
||||
// wait no busy
|
||||
err = d.waitNotBusy(600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMultiStop exits the continuous write mode.
|
||||
func (d Device) WriteMultiStop() error {
|
||||
defer d.cs.High()
|
||||
|
||||
// Stop Tran token for CMD25
|
||||
d.bus.Transfer(0xFD)
|
||||
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
err := d.waitNotBusy(600)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteData writes 512 bytes from dst to sdcard.
|
||||
func (d Device) WriteData(block uint32, src []byte) error {
|
||||
if len(src) < 512 {
|
||||
return fmt.Errorf("len(src) must be greater than or equal to 512")
|
||||
}
|
||||
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD24_WRITE_BLOCK, block, 0) != 0 {
|
||||
return fmt.Errorf("CMD24 error")
|
||||
}
|
||||
|
||||
// wait 1 byte?
|
||||
token := byte(0xFE)
|
||||
d.bus.Transfer(token)
|
||||
|
||||
err := d.bus.Tx(src[:512], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send dummy CRC (2 byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// Data Resp.
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (r & 0x1F) != 0x05 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE")
|
||||
}
|
||||
|
||||
// wait no busy
|
||||
err = d.waitNotBusy(600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
|
||||
}
|
||||
|
||||
// TODO: probably not necessary
|
||||
d.cs.High()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadAt reads the given number of bytes from the sdcard.
|
||||
func (dev *Device) ReadAt(buf []byte, addr int64) (int, error) {
|
||||
block := uint64(addr)
|
||||
// use address if not SDHC card
|
||||
if dev.sdCardType == SD_CARD_TYPE_SDHC {
|
||||
block >>= 9
|
||||
}
|
||||
|
||||
idx := uint32(0)
|
||||
|
||||
start := uint32(addr % 512)
|
||||
end := uint32(0)
|
||||
remain := uint32(len(buf))
|
||||
|
||||
// If data starts in the middle
|
||||
if 0 < start {
|
||||
if start+remain <= 512 {
|
||||
end = start + remain
|
||||
} else {
|
||||
end = 512
|
||||
}
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// If more than 512 bytes left
|
||||
for 512 <= remain {
|
||||
start = 0
|
||||
end = 512
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// Read to the end
|
||||
if 0 < remain {
|
||||
start = 0
|
||||
end = remain
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
return int(idx), nil
|
||||
}
|
||||
|
||||
// WriteAt writes the given number of bytes to sdcard.
|
||||
func (dev *Device) WriteAt(buf []byte, addr int64) (n int, err error) {
|
||||
block := uint64(addr)
|
||||
// use address if not SDHC card
|
||||
if dev.sdCardType == SD_CARD_TYPE_SDHC {
|
||||
block >>= 9
|
||||
}
|
||||
|
||||
idx := uint32(0)
|
||||
|
||||
start := uint32(addr % 512)
|
||||
end := uint32(0)
|
||||
remain := uint32(len(buf))
|
||||
|
||||
// If data starts in the middle
|
||||
if 0 < start {
|
||||
if start+remain <= 512 {
|
||||
end = start + remain
|
||||
} else {
|
||||
end = 512
|
||||
}
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(dev.dummybuf[start:end], buf[idx:])
|
||||
|
||||
err = dev.WriteData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// If more than 512 bytes left
|
||||
for 512 <= remain {
|
||||
start = 0
|
||||
end = 512
|
||||
|
||||
err := dev.WriteData(uint32(block), buf[idx:idx+512])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// Write to the end
|
||||
if 0 < remain {
|
||||
start = 0
|
||||
end = remain
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(dev.dummybuf[start:end], buf[idx:])
|
||||
|
||||
err = dev.WriteData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
return int(idx), nil
|
||||
}
|
||||
|
||||
// Size returns the number of bytes in this sdcard.
|
||||
func (dev *Device) Size() int64 {
|
||||
return int64(dev.CSD.Size())
|
||||
}
|
||||
|
||||
// WriteBlockSize returns the block size in which data can be written to
|
||||
// memory.
|
||||
func (dev *Device) WriteBlockSize() int64 {
|
||||
return 512
|
||||
}
|
||||
|
||||
// EraseBlockSize returns the smallest erasable area on this sdcard in bytes.
|
||||
func (dev *Device) EraseBlockSize() int64 {
|
||||
return 512
|
||||
}
|
||||
|
||||
// EraseBlocks erases the given number of blocks.
|
||||
func (dev *Device) EraseBlocks(start, len int64) error {
|
||||
dev.WriteMultiStart(uint32(start))
|
||||
|
||||
for i := range dev.dummybuf {
|
||||
dev.dummybuf[i] = 0
|
||||
}
|
||||
|
||||
for i := 0; i < int(len); i++ {
|
||||
dev.WriteMulti(dev.dummybuf)
|
||||
}
|
||||
|
||||
dev.WriteMultiStop()
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user