From aecced4fd90afdd2b0e73855b56a31c57bc02c60 Mon Sep 17 00:00:00 2001 From: Marcus Mendenhall Date: Thu, 21 Dec 2023 10:11:53 -0500 Subject: [PATCH] drivers/sdcard/sdcard.py: Fixes per PR discussion. Merged crc7.py into sdcard.py, fixed many little things per suggestions. Removed hardwired crcs from cmd, always recompute. Signed-off-by: Marcus Mendenhall --- micropython/drivers/storage/sdcard/crc16.py | 111 +++++ .../drivers/storage/sdcard/manifest.py | 2 +- .../storage/sdcard/sd_card_spi_test.py | 101 +++++ micropython/drivers/storage/sdcard/sdcard.py | 378 ++++++++++-------- 4 files changed, 421 insertions(+), 171 deletions(-) create mode 100644 micropython/drivers/storage/sdcard/crc16.py create mode 100644 micropython/drivers/storage/sdcard/sd_card_spi_test.py diff --git a/micropython/drivers/storage/sdcard/crc16.py b/micropython/drivers/storage/sdcard/crc16.py new file mode 100644 index 000000000..7ad799561 --- /dev/null +++ b/micropython/drivers/storage/sdcard/crc16.py @@ -0,0 +1,111 @@ +import micropython +from uctypes import addressof + +# ruff: noqa: F821 - @asm_thumb and @viper decorator adds names to function scope + +# https://electronics.stackexchange.com/questions/321304/how-to-use-the-data-crc-of-sd-cards-in-spi-mode +# for sd bit mode +import array + +_sd_crc16_table = array.array("H", (0 for _ in range(256))) +# /* Generate CRC16 table */ +# for (byt = 0U; byt < 256U; byt ++){ +# crc = byt << 8; +# for (bit = 0U; bit < 8U; bit ++){ +# crc <<= 1; +# if ((crc & 0x10000U) != 0U){ crc ^= 0x1021U; } +# } +# sd_crc16_table[byt] = (crc & 0xFFFFU); +# } +for byt in range(256): + crc = byt << 8 + for bit in range(8): + crc = crc << 1 + if (crc & 0x10000) != 0: + crc ^= 0x1021 + _sd_crc16_table[byt] = crc & 0xFFFF + + +# /* Running CRC16 calculation for a byte. */ +# static unsigned int sd_crc16_byte(unsigned int crcval, unsigned int byte) +# { +# return (sd_crc16_table[(byte ^ (crcval >> 8)) & 0xFFU] ^ (crcval << 8)) & 0xFFFFU; +# } + + +@micropython.viper +def crc16_viper(crc: int, data) -> int: + dp = ptr8(addressof(data)) + tp = ptr16(addressof(_sd_crc16_table)) + nn = int(len(data)) + idx = 0 + while idx < nn: + crc = ((crc << 8) & 0xFFFF) ^ tp[((crc >> 8) ^ dp[idx]) & 0xFF] + idx += 1 + return crc + + +try: # if we have asm_thumb, this goes faster + + @micropython.asm_thumb + def _crc_loop_16(r0, r1, r2, r3) -> int: + # r0 is data address + # r1 is table address + # r2 is CRC + # r3 is count + mov(r4, 0) + mvn(r4, r4) # all ones now + mov(r7, 16) + lsr(r4, r7) # R4 = half-word of ones + mov(r5, 0xFF) # used for byte masking + label(loop) + mov(r6, r2) # copy current CRC + mov(r7, 8) + lsr(r6, r7) # crc >> 8 + ldrb(r7, [r0, 0]) # fetch new byte dp[idx] + add(r0, 1) # push to next byte address + eor(r6, r7) # r6 = (crc>>8) ^ dp[idx] + and_(r6, r5) # mask byte ( (crc>>8) ^ dp[idx]) & 0xff + add(r6, r6, r6) # double for table offset + add(r6, r6, r1) # table data address + ldrh(r6, [r6, 0]) # fetch table syndrome + mov(r7, 8) + lsl(r2, r7) # CRC << 8 + and_(r2, r4) # (crc << 8) & 0xffff) + eor(r2, r6) # new CRC + sub(r3, 1) + bne(loop) + mov(r0, r2) + + @micropython.viper + def crc16(crc: int, data) -> int: + return int( + _crc_loop_16( + int(addressof(data)), + int(addressof(_sd_crc16_table)), + crc, + int(len(data)), + ) + ) + +except: + # wrapper to allow the pure-python implementation to be accessed by the right name if asm_thumb doesn't work + @micropython.viper + def crc16(crc: int, data) -> int: + return int(crc16_viper(crc, data)) + + +# def test_speed(): +# data = b"\xaa"*1024 +# import time +# crc = 0 +# start = time.ticks_us() +# for i in range(1024): +# crc = crc16(crc, data) +# print("asm crc speed = ", f"{crc:08x}", 2**20 / (time.ticks_diff(time.ticks_us(), start) / 1e6), "bytes/s") +# +# crc = 0 +# start = time.ticks_us() +# for i in range(1024): +# crc = crc16_viper(crc, data) +# print("py crc speed = ", f"{crc:08x}", 2**20 / (time.ticks_diff(time.ticks_us(), start) / 1e6), "bytes/s") diff --git a/micropython/drivers/storage/sdcard/manifest.py b/micropython/drivers/storage/sdcard/manifest.py index cb4647eeb..2589343d4 100644 --- a/micropython/drivers/storage/sdcard/manifest.py +++ b/micropython/drivers/storage/sdcard/manifest.py @@ -1,3 +1,3 @@ -metadata(description="SDCard block device driver.", version="0.1.0") +metadata(description="SDCard block device driver.", version="0.2.0") module("sdcard.py", opt=3) diff --git a/micropython/drivers/storage/sdcard/sd_card_spi_test.py b/micropython/drivers/storage/sdcard/sd_card_spi_test.py new file mode 100644 index 000000000..1df2a32cd --- /dev/null +++ b/micropython/drivers/storage/sdcard/sd_card_spi_test.py @@ -0,0 +1,101 @@ +# SD card setup + +from machine import SPI, Pin, freq + +freq(120_000_000) +print("freq:", freq()) + +import os +from sdcard import SDCard +import time + +# the bus spi1 on these pins on my test card + +# I have cs on GP13 for this one +spi = SPI(1, 24_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12)) +spi.init() # Ensure right baudrate + +from crc16 import crc16 + +sd = SDCard(spi=spi, cs=Pin(13, Pin.OUT), baudrate=24_000_000, crc16_function=None) +vfs = os.VfsFat(sd) +os.mount(vfs, "/fc") + + +def sdtest(): + print("Filesystem check") + print(os.listdir("/fc")) + + line = "abcdefghijklmnopqrstuvwxyz\n" + lines = line * 200 # 5400 chars + short = "1234567890\n" + + fn = "/fc/rats.txt" + print() + print("Multiple block read/write") + loops = 1000 + t0 = time.ticks_ms() + with open(fn, "w") as f: + n = f.write(lines) + print(n, "bytes written") + n = f.write(short) + print(n, "bytes written") + for i in range(loops): + n = f.write(lines) + + nbytes = loops * len(lines) + len(lines) + len(short) + rate = 1000 * nbytes / time.ticks_diff(time.ticks_ms(), t0) + print(nbytes, "bytes written at ", rate / 1e6, "MB/s") + + stat = os.stat(fn) + filesize = stat[6] + total = 0 + t0 = time.ticks_ms() + + readbuf = bytearray(8192) + import uctypes + + with open(fn, "rb") as f: + f.readinto(readbuf) + big_readback = readbuf[: len(lines)] # check a big chunk of data + + with open(fn, "rb") as f: + while (count := f.readinto(readbuf)) != 0: + total += count + + rate = 1000 * total / time.ticks_diff(time.ticks_ms(), t0) + + print("final file size", filesize, "expected", nbytes, "read", total, "rate=", rate / 1e6) + + fn = "/fc/rats1.txt" + print() + print("Single block read/write") + with open(fn, "w") as f: + n = f.write(short) # one block + print(n, "bytes written") + + with open(fn, "r") as f: + result2 = f.read() + print(len(result2), "bytes read") + + print() + print("Verifying data read back") + success = True + if result2 == short: + print("Small file Pass") + else: + print("Small file Fail") + success = False + if big_readback == lines: + print("Big read Pass") + else: + print("Big readFail") + success = False + + print() + print("Tests", "passed" if success else "failed") + + +sdtest() + +os.umount("/fc") diff --git a/micropython/drivers/storage/sdcard/sdcard.py b/micropython/drivers/storage/sdcard/sdcard.py index c9c991f59..00633cc84 100644 --- a/micropython/drivers/storage/sdcard/sdcard.py +++ b/micropython/drivers/storage/sdcard/sdcard.py @@ -1,57 +1,96 @@ -""" -MicroPython driver for SD cards using SPI bus. +# +# MicroPython driver for SD cards using SPI bus. +# +# Requires an SPI bus and a CS pin. Provides readblocks and writeblocks +# methods so the device can be mounted as a filesystem. +# +# Example usage on pyboard: +# +# import pyb, sdcard, os +# sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5) +# pyb.mount(sd, '/sd2') +# os.listdir('/') +# +# Example usage on ESP8266: +# +# import machine, sdcard, os +# sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15)) +# os.mount(sd, '/sd') +# os.listdir('/') +# +# Note about the crc_function: +# this is crc(seed: int, buf: buffer) -> int +# If no crc16 function is provided, CRCs are not computed on data transfers. +# If a crc16 is provided, the CRC function of the SD card is enabled, +# and data transfers both ways are protected by it +# + +import micropython +from micropython import const +import time +import uctypes +from errno import ETIMEDOUT, EIO, ENODEV, EINVAL -Requires an SPI bus and a CS pin. Provides readblocks and writeblocks -methods so the device can be mounted as a filesystem. +crc7_be_syndrome_table = ( + b"\x00\x12$6HZl~\x90\x82\xb4\xa6\xd8\xca\xfc\xee2 \x16\x04zh^L\xa2\xb0\x86\x94\xea\xf8" + b"\xce\xdcdv@R,>\x08\x1a\xf4\xe6\xd0\xc2\xbc\xae\x98\x8aVDr`\x1e\x0c:(\xc6\xd4\xe2\xf0" + b"\x8e\x9c\xaa\xb8\xc8\xda\xec\xfe\x80\x92\xa4\xb6XJ|n\x10\x024&\xfa\xe8\xde\xcc\xb2\xa0" + b'\x96\x84jxN\\"0\x06\x14\xac\xbe\x88\x9a\xe4\xf6\xc0\xd2<.\x18\ntfPB\x9e\x8c\xba\xa8' + b"\xd6\xc4\xf2\xe0\x0e\x1c*8FTbp\x82\x90\xa6\xb4\xca\xd8\xee\xfc\x12\x006$ZH~l\xb0\xa2" + b"\x94\x86\xf8\xea\xdc\xce 2\x04\x16hzL^\xe6\xf4\xc2\xd0\xae\xbc\x8a\x98vdR@>,\x1a\x08" + b"\xd4\xc6\xf0\xe2\x9c\x8e\xb8\xaaDV`r\x0c\x1e(:JXn|\x02\x10&4\xda\xc8\xfe\xec\x92\x80" + b'\xb6\xa4xj\\N0"\x14\x06\xe8\xfa\xcc\xde\xa0\xb2\x84\x96.<\n\x18ftBP\xbe\xac\x9a\x88\xf6' + b"\xe4\xd2\xc0\x1c\x0e8*TFpb\x8c\x9e\xa8\xba\xc4\xd6\xe0\xf2" +) -Example usage on pyboard: - import pyb, sdcard, os - sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5) - pyb.mount(sd, '/sd2') - os.listdir('/') +def crc7(buf) -> int: + crc = 0 + for b in buf: + crc = crc7_be_syndrome_table[crc ^ b] + return crc -Example usage on ESP8266: - import machine, sdcard, os - sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15)) - os.mount(sd, '/sd') - os.listdir('/') +def gb(bigval, b0, bn): + # get numbered bits from a buf_to_int from, for example, the CSD + return (bigval >> b0) & ((1 << (1 + bn - b0)) - 1) -""" -from micropython import const -import time - - -_CMD_TIMEOUT = const(100) +_CMD_TIMEOUT = const(50) _R1_IDLE_STATE = const(1 << 0) # R1_ERASE_RESET = const(1 << 1) _R1_ILLEGAL_COMMAND = const(1 << 2) -# R1_COM_CRC_ERROR = const(1 << 3) +_R1_COM_CRC_ERROR = const(1 << 3) # R1_ERASE_SEQUENCE_ERROR = const(1 << 4) # R1_ADDRESS_ERROR = const(1 << 5) # R1_PARAMETER_ERROR = const(1 << 6) _TOKEN_CMD25 = const(0xFC) _TOKEN_STOP_TRAN = const(0xFD) _TOKEN_DATA = const(0xFE) +_HCS_BIT = const(1 << 30) # for ACMD41 class SDCard: - def __init__(self, spi, cs, baudrate=1320000): + def __init__(self, spi, cs, baudrate=1320000, crc16_function=None): self.spi = spi self.cs = cs self.cmdbuf = bytearray(6) - self.dummybuf = bytearray(512) + self.cmdbuf5 = memoryview(self.cmdbuf)[:5] # for crc7 generation self.tokenbuf = bytearray(1) - for i in range(512): - self.dummybuf[i] = 0xFF - self.dummybuf_memoryview = memoryview(self.dummybuf) - + self.crcbuf = bytearray(2) + self.crc16 = None # during init # initialise the card self.init_card(baudrate) + self.check_crcs(crc16_function) # now set it up + + def check_crcs(self, crc16_function): + self.crc16 = crc16_function + result = self.cmd( + 59, 1 if crc16_function else 0, release=True + ) # send CRC enable/disable command + return result def init_spi(self, baudrate): try: @@ -63,6 +102,9 @@ def init_spi(self, baudrate): # on pyboard self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) + def _spiff(self): + self.spi.write(b"\xff") + def init_card(self, baudrate): # init CS pin self.cs.init(self.cs.OUT, value=1) @@ -70,82 +112,74 @@ def init_card(self, baudrate): # init SPI bus; use low data rate for initialisation self.init_spi(100000) - # clock card at least 100 cycles with cs high - for i in range(16): - self.spi.write(b"\xff") + # clock card at least 100 cycles with cs high (16 bytes = 128 cycles) + # use explicit string here for small memory footprint + self.spi.write(b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff") # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) for _ in range(5): if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE: break else: - raise OSError("no SD card") + raise OSError(ENODEV, "no SD card") # CMD8: determine card version - r = self.cmd(8, 0x01AA, 0x87, 4) - if r == _R1_IDLE_STATE: - self.init_card_v2() - elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): - self.init_card_v1() - else: - raise OSError("couldn't determine SD card version") + r = self.cmd(8, 0x01AA, 4) # probe version + v2 = r == _R1_IDLE_STATE + v1 = r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND) + + if not (v1 or v2): + raise OSError(EIO, "couldn't determine SD card version") + arg41 = _HCS_BIT if v2 else 0 # we support high capacity, on v2 cards + for i in range(_CMD_TIMEOUT): # loop on acmd41 to get + self.cmd(55, 0) + if (r := self.cmd(41, arg41)) == 0: + break + time.sleep_ms(5) + if r != 0: + raise OSError(ETIMEDOUT, "card type", "v2" if v2 else "v1") # get the number of sectors # CMD9: response R2 (R1 byte + 16-byte block read) - if self.cmd(9, 0, 0, 0, False) != 0: - raise OSError("no response from SD card") + if self.cmd(9, 0, 0, False) != 0: + raise OSError(EIO, "no CSD response") csd = bytearray(16) self.readinto(csd) - if csd[0] & 0xC0 == 0x40: # CSD version 2.0 - self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 - elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB) - c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6 - c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7 - read_bl_len = csd[5] & 0b1111 + self.CSD = csd_int = int.from_bytes( + csd, "big" + ) # convert 16-byte CSD to a giant integer for bit extraction + _gb = gb # just for local binding + # use bit numbers from SD card spec v9.0.0, table 5.3.2 + vers = _gb(csd_int, 126, 127) + if vers == 1: # CSD version 2.0 + self.sectors = (_gb(csd_int, 48, 69) + 1) * 1024 + self.cdv = 1 + elif vers == 0x00: # CSD version 1.0 (old, <=2GB) + c_size = _gb(csd_int, 62, 73) + c_size_mult = _gb(csd_int, 47, 49) + read_bl_len = _gb(csd_int, 80, 83) capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len) self.sectors = capacity // 512 + self.cdv = 512 # converts bytes to sectors else: - raise OSError("SD card CSD format not supported") + raise OSError(EIO, "CSD format unknown") # print('sectors', self.sectors) # CMD16: set block length to 512 bytes - if self.cmd(16, 512, 0) != 0: - raise OSError("can't set 512 block size") + if self.cmd(16, 512) != 0: + raise OSError(EIO, "can't set 512 block size") # set to high data rate now that it's initialised self.init_spi(baudrate) - def init_card_v1(self): - for i in range(_CMD_TIMEOUT): - time.sleep_ms(50) - self.cmd(55, 0, 0) - if self.cmd(41, 0, 0) == 0: - # SDSC card, uses byte addressing in read/write/erase commands - self.cdv = 512 - # print("[SDCard] v1 card") - return - raise OSError("timeout waiting for v1 card") - - def init_card_v2(self): - for i in range(_CMD_TIMEOUT): - time.sleep_ms(50) - self.cmd(58, 0, 0, 4) - self.cmd(55, 0, 0) - if self.cmd(41, 0x40000000, 0) == 0: - self.cmd(58, 0, 0, -4) # 4-byte response, negative means keep the first byte - ocr = self.tokenbuf[0] # get first byte of response, which is OCR - if not ocr & 0x40: - # SDSC card, uses byte addressing in read/write/erase commands - self.cdv = 512 - else: - # SDHC/SDXC card, uses block addressing in read/write/erase commands - self.cdv = 1 - # print("[SDCard] v2 card") - return - raise OSError("timeout waiting for v2 card") - - def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): - self.cs(0) + def cmd(self, cmd, arg, final=0, release=True, skip1=False): + cs = self.cs # prebind + w = self.spi.write + r = self.spi.readinto + tb = self.tokenbuf + spiff = self._spiff + + cs(0) # select chip # create and send the command buf = self.cmdbuf @@ -154,150 +188,154 @@ def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): buf[2] = arg >> 16 buf[3] = arg >> 8 buf[4] = arg - buf[5] = crc - self.spi.write(buf) + buf[5] = crc7(self.cmdbuf5) | 1 + w(buf) if skip1: - self.spi.readinto(self.tokenbuf, 0xFF) + r(tb, 0xFF) # wait for the response (response[7] == 0) for i in range(_CMD_TIMEOUT): - self.spi.readinto(self.tokenbuf, 0xFF) - response = self.tokenbuf[0] + r(tb, 0xFF) + response = tb[0] + # print(f"response: {response:02x}") + if not (response & 0x80): # this could be a big-endian integer that we are getting here # if final<0 then store the first byte to tokenbuf and discard the rest + if response & _R1_COM_CRC_ERROR: + cs(1) + spiff() + raise OSError(EIO, f"CRC err on cmd: {cmd:02d}") if final < 0: - self.spi.readinto(self.tokenbuf, 0xFF) + r(tb, 0xFF) final = -1 - final for j in range(final): - self.spi.write(b"\xff") + spiff() if release: - self.cs(1) - self.spi.write(b"\xff") + cs(1) + spiff() return response + else: + if i > (_CMD_TIMEOUT // 2): + time.sleep_ms(1) # very slow response, give it time # timeout - self.cs(1) - self.spi.write(b"\xff") - return -1 + cs(1) + spiff() + raise OSError(ETIMEDOUT, "command:", cmd, "arg:", arg) def readinto(self, buf): - self.cs(0) + cs = self.cs + spiff = self._spiff + + cs(0) # read until start byte (0xff) for i in range(_CMD_TIMEOUT): self.spi.readinto(self.tokenbuf, 0xFF) if self.tokenbuf[0] == _TOKEN_DATA: break - time.sleep_ms(1) + if i > _CMD_TIMEOUT // 2: + time.sleep_ms(1) # if response is slow, wait longer + else: - self.cs(1) - raise OSError("timeout waiting for response") + cs(1) + raise OSError(ETIMEDOUT, "read timeout") - # read data - mv = self.dummybuf_memoryview - if len(buf) != len(mv): - mv = mv[: len(buf)] - self.spi.write_readinto(mv, buf) + self.spi.readinto(buf, 0xFF) # read checksum - self.spi.write(b"\xff") - self.spi.write(b"\xff") + ck = self.spi.read(2, 0xFF) + if self.crc16: + crc = self.crc16(self.crc16(0, buf), ck) + if crc != 0: + raise OSError(EIO, f"bad data CRC: {crc:04x}") - self.cs(1) - self.spi.write(b"\xff") + cs(1) + spiff() def write(self, token, buf): - self.cs(0) + cs = self.cs + spiff = self._spiff + r = self.spi.read + w = self.spi.write - # send: start of block, data, checksum - self.spi.read(1, token) - self.spi.write(buf) - self.spi.write(b"\xff") - self.spi.write(b"\xff") + cs(0) - # check the response - if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05: - self.cs(1) - self.spi.write(b"\xff") - return + # send: start of block, data, checksum + r(1, token) + w(buf) + if self.crc16: + crc = self.crc16(0, buf) + self.crcbuf[0] = crc >> 8 + self.crcbuf[1] = crc & 0xFF + w(self.crcbuf) # write checksum + else: + w(b"\xff\xff") + # check the response + if ((r(1, 0xFF)[0]) & 0x1F) != 0x05: + cs(1) + spiff() + raise OSError(EIO, "write fail") # wait for write to finish - while self.spi.read(1, 0xFF)[0] == 0: + while (r(1, 0xFF)[0]) == 0: pass - self.cs(1) - self.spi.write(b"\xff") + cs(1) + spiff() def write_token(self, token): self.cs(0) self.spi.read(1, token) - self.spi.write(b"\xff") + self._spiff() # wait for write to finish while self.spi.read(1, 0xFF)[0] == 0x00: pass self.cs(1) - self.spi.write(b"\xff") + self._spiff() + + @staticmethod + def blocks(buf): + nblocks, err = divmod(len(buf), 512) + if not nblocks or err: + raise OSError(EINVAL, "Buffer length is invalid") + return nblocks def readblocks(self, block_num, buf): # workaround for shared bus, required for (at least) some Kingston # devices, ensure MOSI is high before starting transaction - self.spi.write(b"\xff") + self._spiff() + nblocks = self.blocks(buf) - nblocks = len(buf) // 512 - assert nblocks and not len(buf) % 512, "Buffer length is invalid" - if nblocks == 1: - # CMD17: set read address for single block - if self.cmd(17, block_num * self.cdv, 0, release=False) != 0: - # release the card - self.cs(1) - raise OSError(5) # EIO - # receive the data and release card - self.readinto(buf) - else: - # CMD18: set read address for multiple blocks - if self.cmd(18, block_num * self.cdv, 0, release=False) != 0: - # release the card - self.cs(1) - raise OSError(5) # EIO - offset = 0 - mv = memoryview(buf) - while nblocks: - # receive the data and release card - self.readinto(mv[offset : offset + 512]) - offset += 512 - nblocks -= 1 - if self.cmd(12, 0, 0xFF, skip1=True): - raise OSError(5) # EIO + # CMD18: set read address for multiple blocks + if self.cmd(18, block_num * self.cdv, release=False) != 0: + # release the card + self.cs(1) + raise OSError(EIO) # EIO + mv = memoryview(buf) + for offset in range(0, nblocks * 512, 512): + self.readinto(mv[offset : offset + 512]) + + if self.cmd(12, 0, skip1=True): + raise OSError(EIO) # EIO def writeblocks(self, block_num, buf): # workaround for shared bus, required for (at least) some Kingston # devices, ensure MOSI is high before starting transaction - self.spi.write(b"\xff") - - nblocks, err = divmod(len(buf), 512) - assert nblocks and not err, "Buffer length is invalid" - if nblocks == 1: - # CMD24: set write address for single block - if self.cmd(24, block_num * self.cdv, 0) != 0: - raise OSError(5) # EIO - - # send the data - self.write(_TOKEN_DATA, buf) - else: - # CMD25: set write address for first block - if self.cmd(25, block_num * self.cdv, 0) != 0: - raise OSError(5) # EIO - # send the data - offset = 0 - mv = memoryview(buf) - while nblocks: - self.write(_TOKEN_CMD25, mv[offset : offset + 512]) - offset += 512 - nblocks -= 1 - self.write_token(_TOKEN_STOP_TRAN) + self._spiff() + nblocks = self.blocks(buf) + + # CMD25: set write address for first block + if self.cmd(25, block_num * self.cdv) != 0: + raise OSError(EIO) # EIO` + # send the data + mv = memoryview(buf) + for offset in range(0, nblocks * 512, 512): + self.write(_TOKEN_CMD25, mv[offset : offset + 512]) + self.write_token(_TOKEN_STOP_TRAN) def ioctl(self, op, arg): if op == 4: # get number of blocks pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy