diff --git a/circuitpython_typing/__init__.py b/circuitpython_typing/__init__.py index 5a56cec..a826ae2 100644 --- a/circuitpython_typing/__init__.py +++ b/circuitpython_typing/__init__.py @@ -24,6 +24,7 @@ __all__ = [ "Alarm", "AudioSample", + "BlockDevice", "ByteStream", "FrameBuffer", "ReadableBuffer", @@ -72,6 +73,58 @@ def write(self, buf: ReadableBuffer) -> Optional[int]: """Write the bytes in ``buf`` to the stream.""" +class BlockDevice(Protocol): + """Protocol for block device objects to enable a device to support + CircuitPython filesystems. Classes which implement this protocol + include `storage.VfsFat`. + """ + + def readblocks(self, block_num: int, buf: bytearray) -> None: + """Read aligned, multiples of blocks. Starting at + the block given by the index ``block_num``, read blocks + from the device into ``buf`` (an array of bytes). The number + of blocks to read is given by the length of ``buf``, + which will be a multiple of the block size. + """ + + def writeblocks(self, block_num: int, buf: bytearray) -> None: + """Write aligned, multiples of blocks, and require that + the blocks that are written to be first erased (if necessary) + by this method. Starting at the block given by the index + ``block_num``, write blocks from ``buf`` (an array of bytes) to the + device. The number of blocks to write is given by the length + of ``buf``, which will be a multiple of the block size. + """ + + def ioctl(self, operation: int, arg: Optional[int] = None) -> Optional[int]: + """Control the block device and query its parameters. The operation to + perform is given by ``operation`` which is one of the following integers: + + * 1 - initialise the device (``arg`` is unused) + * 2 - shutdown the device (``arg`` is unused) + * 3 - sync the device (``arg`` is unused) + * 4 - get a count of the number of blocks, should return an integer (``arg`` is unused) + * 5 - get the number of bytes in a block, should return an integer, + or ``None`` in which case the default value of 512 is used (``arg`` is unused) + * 6 - erase a block, arg is the block number to erase + + As a minimum ``ioctl(4, ...)`` must be intercepted; for littlefs ``ioctl(6, ...)`` + must also be intercepted. The need for others is hardware dependent. + + Prior to any call to ``writeblocks(block, ...)`` littlefs issues ``ioctl(6, block)``. + This enables a device driver to erase the block prior to a write if the hardware + requires it. Alternatively a driver might intercept ``ioctl(6, block)`` and return 0 + (success). In this case the driver assumes responsibility for detecting the need + for erasure. + + Unless otherwise stated ``ioctl(operation, arg)`` can return ``None``. Consequently an + implementation can ignore unused values of ``operation``. Where ``operation`` is + intercepted, the return value for operations 4 and 5 are as detailed above. Other + operations should return 0 on success and non-zero for failure, with the value returned + being an ``OSError`` errno code. + """ + + # These types may not be in adafruit-blinka, so use the string form instead of a resolved name. AudioSample: TypeAlias = Union[