Skip to content

Linted #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 46 additions & 13 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Dependencies
=============
This driver depends on:

* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
* `Adafruit CircuitPython 9.0.0 and later <https://github.com/adafruit/circuitpython>`_

Please ensure all dependencies are available on the CircuitPython filesystem.
This is easily achieved by downloading
Expand All @@ -37,18 +37,8 @@ or individual libraries can be installed using
`circup <https://github.com/adafruit/circup>`_.



.. todo:: Describe the Adafruit product this library works with. For PCBs, you can also add the
image from the assets folder in the PCB's GitHub repo.

`Purchase one from the Adafruit shop <http://www.adafruit.com/products/>`_

Installing from PyPI
=====================
.. note:: This library is not available on PyPI yet. Install documentation is included
as a standard element. Stay tuned for PyPI availability!

.. todo:: Remove the above note if PyPI version is/will be available at time of release.

On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
PyPI <https://pypi.org/project/adafruit-circuitpython-usb-host-mass-storage/>`_.
Expand Down Expand Up @@ -99,8 +89,51 @@ Or the following command to update an existing version:
Usage Example
=============

.. todo:: Add a quick, simple example. It and other examples should live in the
examples folder and be included in docs/examples.rst.
Print basic information about a device and its first (and usually only) configuration.

.. code-block:: python

import usb.core
import os
import storage
import time

from adafruit_usb_host_descriptors import *

DIR_IN = 0x80

while True:
print("searching for devices")
for device in usb.core.find(find_all=True):
print("pid", hex(device.idProduct))
print("vid", hex(device.idVendor))
print("man", device.manufacturer)
print("product", device.product)
print("serial", device.serial_number)
print("config[0]:")
config_descriptor = get_configuration_descriptor(device, 0)

i = 0
while i < len(config_descriptor):
descriptor_len = config_descriptor[i]
descriptor_type = config_descriptor[i + 1]
if descriptor_type == DESC_CONFIGURATION:
config_value = config_descriptor[i + 5]
print(f" value {config_value:d}")
elif descriptor_type == DESC_INTERFACE:
interface_number = config_descriptor[i + 2]
interface_class = config_descriptor[i + 5]
interface_subclass = config_descriptor[i + 6]
print(f" interface[{interface_number:d}] class {interface_class:02x} subclass {interface_subclass:02x}")
elif descriptor_type == DESC_ENDPOINT:
endpoint_address = config_descriptor[i + 2]
if endpoint_address & DIR_IN:
print(f" IN {endpoint_address:02x}")
else:
print(f" OUT {endpoint_address:02x}")
i += descriptor_len
print()
time.sleep(5)

Documentation
=============
Expand Down
96 changes: 74 additions & 22 deletions adafruit_usb_host_mass_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@

import struct
import time
from typing import Optional
import usb.core
from micropython import const
from adafruit_usb_host_descriptors import *
import adafruit_usb_host_descriptors

__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_USB_Host_Mass_Storage.git"
__repo__ = (
"https://github.com/adafruit/Adafruit_CircuitPython_USB_Host_Mass_Storage.git"
)

# USB defines
_DIR_OUT = const(0x00)
Expand All @@ -32,27 +35,39 @@
_MSC_REQ_GET_GET_MAX_LUN = const(254)

# SCSI commands
_SCSI_CMD_TEST_UNIT_READY = const(0x00)
"""The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation."""
_SCSI_CMD_INQUIRY = const(0x12)
_SCSI_CMD_TEST_UNIT_READY = const(0x00)
"""The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data
(read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not
perform a self-test operation."""
_SCSI_CMD_INQUIRY = const(0x12)
"""The SCSI Inquiry command is used to obtain basic information from a target device."""
_SCSI_CMD_READ_CAPACITY_10 = const(0x25)
"""The SCSI Read Capacity command is used to obtain data capacity information from a target device."""
_SCSI_CMD_REQUEST_SENSE = const(0x03)
"""The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device."""
_SCSI_CMD_READ_10 = const(0x28)
"""The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer."""
_SCSI_CMD_WRITE_10 = const(0x2A)
"""The WRITE (10) command requests that the device server transfer the specified logical block(s) from the data-out buffer and write them."""
_SCSI_CMD_READ_CAPACITY_10 = const(0x25)
"""The SCSI Read Capacity command is used to obtain data capacity information from a target
device."""
_SCSI_CMD_REQUEST_SENSE = const(0x03)
"""The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is
used to obtain sense data -- status/error information -- from a target device."""
_SCSI_CMD_READ_10 = const(0x28)
"""The READ (10) command requests that the device server read the specified logical block(s) and
transfer them to the data-in buffer."""
_SCSI_CMD_WRITE_10 = const(0x2A)
"""The WRITE (10) command requests that the device server transfer the specified logical block(s)
from the data-out buffer and write them."""


class USBMassStorage:
"""CircuitPython BlockDevice backed by a USB mass storage device (aka thumb drive)."""

def __init__(self, device: usb.core.Device, lun=0):
config_descriptor = get_configuration_descriptor(device, 0)
config_descriptor = adafruit_usb_host_descriptors.get_configuration_descriptor(
device, 0
)

self.in_ep = 0
self.out_ep = 0

self.sector_count = None
self.block_size = None
# Look over each descriptor for mass storage interface and then the two
# endpoints.
in_msc_interface = False
Expand All @@ -62,16 +77,19 @@ def __init__(self, device: usb.core.Device, lun=0):
while i < len(config_descriptor):
descriptor_len = config_descriptor[i]
descriptor_type = config_descriptor[i + 1]
if descriptor_type == DESC_CONFIGURATION:
if descriptor_type == adafruit_usb_host_descriptors.DESC_CONFIGURATION:
config_value = config_descriptor[i + 5]
elif descriptor_type == DESC_INTERFACE:
elif descriptor_type == adafruit_usb_host_descriptors.DESC_INTERFACE:
interface_number = config_descriptor[i + 2]
interface_class = config_descriptor[i + 5]
interface_subclass = config_descriptor[i + 6]
in_msc_interface = interface_class == 8 and interface_subclass == 6
if in_msc_interface:
msc_interface = interface_number
elif descriptor_type == DESC_ENDPOINT and in_msc_interface:
elif (
descriptor_type == adafruit_usb_host_descriptors.DESC_ENDPOINT
and in_msc_interface
):
endpoint_address = config_descriptor[i + 2]
if endpoint_address & _DIR_IN:
self.in_ep = endpoint_address
Expand All @@ -89,7 +107,13 @@ def __init__(self, device: usb.core.Device, lun=0):
# Get the max lun.
max_lun = bytearray(1)
try:
device.ctrl_transfer(_REQ_RCPT_INTERFACE | _REQ_TYPE_CLASS | _DIR_IN, _MSC_REQ_GET_GET_MAX_LUN, 0, msc_interface, max_lun)
device.ctrl_transfer(
_REQ_RCPT_INTERFACE | _REQ_TYPE_CLASS | _DIR_IN,
_MSC_REQ_GET_GET_MAX_LUN,
0,
msc_interface,
max_lun,
)
max_lun = max_lun[0] + 1
except usb.core.USBError:
# Stall means 0.
Expand All @@ -108,8 +132,9 @@ def __init__(self, device: usb.core.Device, lun=0):
self._wait_for_ready()

def _scsi_command(self, direction, command, data) -> None:
"""Do a SCSI command over USB. Reads or writes to data depending on direction."""
struct.pack_into("<IBxB", self.cbw, 8, len(data), direction, len(command))
self.cbw[15:15+len(command)] = command
self.cbw[15 : 15 + len(command)] = command
# Write out the command.
self.device.write(self.out_ep, self.cbw)
# Depending on the direction, read or write the data.
Expand All @@ -123,6 +148,7 @@ def _scsi_command(self, direction, command, data) -> None:
self.device.read(self.in_ep, self.csw)

def _wait_for_ready(self, tries=100):
"""Waits for the device to be ready."""
status = 12
self.csw[status] = 1
test_ready = bytearray(6)
Expand All @@ -145,36 +171,62 @@ def _wait_for_ready(self, tries=100):
raise RuntimeError("Out of tries")

def _inquire(self) -> None:
"""Run inquiry command"""
response = bytearray(36)
command = bytearray(6)
command[0] = _SCSI_CMD_INQUIRY
command[4] = len(response)
self._scsi_command(_DIR_IN, command, response)

def _read_capacity(self) -> None:
"""Read the device's capacity and store it in the object"""
command = bytearray(10)
command[0] = _SCSI_CMD_READ_CAPACITY_10
response = bytearray(8)

self._scsi_command(_DIR_IN, command, response)

self.sector_count, self.block_size = struct.unpack(">II", response)
self.sector_count += 1 # Response has the last valid number. Count is one greater.
self.sector_count += (
1 # Response has the last valid number. Count is one greater.
)

def readblocks(self, block_num: int, buf: bytearray) -> None:
"""Read data from block_num into buf"""
command = bytearray(10)
struct.pack_into(">BBIxH", command, 0, _SCSI_CMD_READ_10, self.lun, block_num, len(buf) // 512)
struct.pack_into(
">BBIxH",
command,
0,
_SCSI_CMD_READ_10,
self.lun,
block_num,
len(buf) // 512,
)

self._scsi_command(_DIR_IN, command, buf)

def writeblocks(self, block_num: int, buf: bytearray) -> None:
"""Write data to block_num from buf"""
command = bytearray(10)
struct.pack_into(">BBIxH", command, 0, _SCSI_CMD_WRITE_10, self.lun, block_num, len(buf) // 512)
struct.pack_into(
">BBIxH",
command,
0,
_SCSI_CMD_WRITE_10,
self.lun,
block_num,
len(buf) // 512,
)

self._scsi_command(_DIR_OUT, command, buf)

def ioctl(self, operation: int, arg: Optional[int] = None) -> Optional[int]:
"""Perform an IOCTL operation"""
# This is a standard interface so we need to take arg even though we ignore it.
# pylint: disable=unused-argument
if operation == _MP_BLOCKDEV_IOCTL_BLOCK_COUNT:
if not self.sector_count:
self._read_capacity()
return self.sector_count
return None
2 changes: 1 addition & 1 deletion docs/examples.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Simple test
------------

Ensure your device works with this simple test.
Mount a USB mass storage device to ``/usb_device`` in CircuitPython.

.. literalinclude:: ../examples/usb_host_mass_storage_simpletest.py
:caption: examples/usb_host_mass_storage_simpletest.py
Expand Down
6 changes: 1 addition & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,10 @@ Table of Contents
.. toctree::
:caption: Tutorials

.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave
the toctree above for use later.

.. toctree::
:caption: Related Products

.. todo:: Add any product links here. If there are none, then simply delete this todo and leave
the toctree above for use later.
Adafruit Feather RP2040 USB Host <https://www.adafruit.com/product/5723>

.. toctree::
:caption: Other Links
Expand Down
4 changes: 2 additions & 2 deletions examples/usb_host_mass_storage_simpletest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
#
# SPDX-License-Identifier: Unlicense

import usb.core
import time
import os
import storage
import time
import usb.core

import adafruit_usb_host_mass_storage

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
# SPDX-License-Identifier: MIT

Adafruit-Blinka
adafruit-circuitpython-usb-host-descriptors