Skip to content

Commit

Permalink
Merge pull request #115 from antoinevg/antoinevg/fix-facedancer-zlp-bug
Browse files Browse the repository at this point in the history
Fix ZLP bug on CONTROL IN transfers
  • Loading branch information
antoinevg authored Sep 16, 2024
2 parents 7314d29 + b0f4855 commit b4defce
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 16 deletions.
5 changes: 4 additions & 1 deletion facedancer/backends/MAXUSBApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

from ..core import FacedancerApp

class MAXUSBApp(FacedancerApp):
from .base import FacedancerBackend


class MAXUSBApp(FacedancerApp, FacedancerBackend):
app_name = "MAXUSB"

reg_ep0_fifo = 0x00
Expand Down
15 changes: 15 additions & 0 deletions facedancer/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ def read_from_endpoint(self, endpoint_number: int) -> bytes:
raise NotImplementedError


def send_on_control_endpoint(self, endpoint_number: int, in_request: USBControlRequest, data: bytes, blocking: bool=True):
"""
Sends a collection of USB data in response to a IN control request by the host.
Args:
endpoint_number : The number of the IN endpoint on which data should be sent.
in_request : The control request being responded to.
data : The data to be sent.
blocking : If true, this function should wait for the transfer to complete.
"""
# Truncate data to requested length and forward to `send_on_endpoint()` for backends
# that do not need to support this method.
return self.send_on_endpoint(endpoint_number, data[:in_request.length], blocking)


def send_on_endpoint(self, endpoint_number: int, data: bytes, blocking: bool=True):
"""
Sends a collection of USB data on a given endpoint.
Expand Down
4 changes: 3 additions & 1 deletion facedancer/backends/greatdancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@

from ..logging import log

from .base import FacedancerBackend

class GreatDancerApp(FacedancerApp):

class GreatDancerApp(FacedancerApp, FacedancerBackend):
"""
Backend for using GreatFET devices as Facedancers.
"""
Expand Down
22 changes: 18 additions & 4 deletions facedancer/backends/moondancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ..core import *
from ..device import USBDevice
from ..configuration import USBConfiguration
from ..request import USBControlRequest
from ..types import DeviceSpeed, USBDirection

from ..logging import log
Expand Down Expand Up @@ -312,8 +313,23 @@ def read_from_endpoint(self, endpoint_number: int) -> bytes:
return data


# TODO greatdancer does blocking=True by default, should we too given that everthing else sets it to False
# by default?
def send_on_control_endpoint(self, endpoint_number: int, in_request: USBControlRequest, data: bytes, blocking: bool=True):
"""
Sends a collection of USB data in response to a IN control request by the host.
Args:
endpoint_number : The number of the IN endpoint on which data should be sent.
requested_length : The number of bytes requested by the host.
data : The data to be sent.
blocking : If true, this function should wait for the transfer to complete.
"""
requested_length = in_request.length
self.api.write_control_endpoint(endpoint_number, requested_length, blocking, bytes(data))

log.debug(f"moondancer.send_on_control_endpoint({endpoint_number}, {requested_length}, {len(data)}, {blocking})")
log.trace(f" moondancer.api.write_control_endpoint({endpoint_number}, {requested_length}, {blocking}, {len(data)})")


def send_on_endpoint(self, endpoint_number: int, data: bytes, blocking: bool=True):
"""
Sends a collection of USB data on a given endpoint.
Expand All @@ -324,8 +340,6 @@ def send_on_endpoint(self, endpoint_number: int, data: bytes, blocking: bool=Tru
blocking : If true, this function will wait for the transfer to complete.
"""

# TODO possibly wait until endpoint is ready to send?

self.api.write_endpoint(endpoint_number, blocking, bytes(data))

log.debug(f"moondancer.send_on_endpoint({endpoint_number}, {len(data)}, {blocking})")
Expand Down
23 changes: 17 additions & 6 deletions facedancer/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,21 @@ def clear_halt(self, endpoint_number: int, direction: USBDirection):
self.backend.clear_halt(endpoint_number, direction)


def control_send(self, endpoint_number: int, in_request: USBControlRequest, data: bytes, *, blocking: bool = False):
""" Queues sending data on the provided control endpoint in
response to a IN control request.
Args:
endpoint_number : The endpoint number to send data upon.
in_request : The control request being responded to.
data : The data to send.
blocking : If provided and true, this function will block
until the backend indicates the send is complete.
"""
self.backend.send_on_control_endpoint(endpoint_number, in_request, data, blocking=blocking)


def send(self, endpoint_number: int, data: bytes, *, blocking: bool = False):
""" Queues sending data on the IN endpoint with the provided number.
Expand All @@ -267,13 +282,9 @@ def send(self, endpoint_number: int, data: bytes, *, blocking: bool = False):
until the backend indicates the send is complete.
"""

# EP0 is special, as it conceptually belongs to the whole device, as it's used
# for control requests and configuration. We'll handle it directly, here.
#
# All of our backends currently automatically handle packetization and ZLPs for
# the control endpoint, so we'll skip packetizing it (which would generate spurious ZLPs).
if endpoint_number == 0:
self.backend.send_on_endpoint(0, data, blocking=blocking)
# TODO upgrade to an exception with the release of Facedancer 3.1.0
log.warning("Please use USBDevice::control_send() for control transfers")
elif self.configuration:
endpoint = self.configuration.get_endpoint(endpoint_number, USBDirection.IN)
endpoint.send(data, blocking=blocking)
Expand Down
2 changes: 1 addition & 1 deletion facedancer/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def get_device(self):
return self.parent.get_device()


def send(self, data: bytes, *, blocking: bool =False):
def send(self, data: bytes, *, blocking: bool = False):
""" Sends data on this endpoint. Valid only for IN endpoints.
Args:
Expand Down
2 changes: 1 addition & 1 deletion facedancer/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def _proxy_in_control_request(self, request: USBControlRequest):
self.backend.stall_endpoint(0, USBDirection.IN)
else:
# TODO: support control endpoints other than 0
self.send(0, data)
self.control_send(0, request, data)


def _proxy_out_control_request(self, request: USBControlRequest):
Expand Down
4 changes: 2 additions & 2 deletions facedancer/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def from_raw_bytes(cls, raw_bytes: bytes, *, device = None):

def reply(self, data: bytes):
""" Replies to the given request with a given set of bytes. """
self.device.send(endpoint_number=0, data=data)
self.device.control_send(endpoint_number=0, in_request=self, data=data)


def acknowledge(self, *, blocking: bool = False):
Expand All @@ -260,7 +260,7 @@ def acknowledge(self, *, blocking: bool = False):
Args:
blocking : If true, the relevant control request will complete before returning.
"""
self.device.send(endpoint_number=0, data=b"", blocking=blocking)
self.device.control_send(endpoint_number=0, in_request=self, data=b"", blocking=blocking)


def ack(self, *, blocking: bool = False):
Expand Down

0 comments on commit b4defce

Please sign in to comment.