Skip to content
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

Add support for alternate interface settings #122

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
29 changes: 15 additions & 14 deletions facedancer/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,22 @@ def from_binary_descriptor(cls, data):
descriptor_type, total_length, num_interfaces, index, string_index, \
attributes, max_power = struct.unpack_from('<xBHBBBBB', data[0:length])

# Extract the subordinate descriptors, and parse them.
interfaces = cls._parse_subordinate_descriptors(data[length:total_length])

# TODO _parse_subordinate_descriptors should handle this
interfaces = {interface.number:interface for interface in interfaces}

return cls(
configuration = cls(
number=index,
configuration_string=string_index,
max_power=max_power,
self_powered=(attributes >> 6) & 1,
supports_remote_wakeup=(attributes >> 5) & 1,
interfaces=interfaces,
)

# Extract the subordinate descriptors, and parse them.
interfaces = cls._parse_subordinate_descriptors(data[length:total_length])

for interface in interfaces:
configuration.add_interface(interface)

return configuration


@classmethod
def _parse_subordinate_descriptors(cls, data):
Expand Down Expand Up @@ -144,7 +145,7 @@ def get_device(self):

def add_interface(self, interface: USBInterface):
""" Adds an interface to the configuration. """
self.interfaces[interface.number] = interface
self.interfaces[interface.get_identifier()] = interface
interface.parent = self


Expand All @@ -157,7 +158,7 @@ def get_endpoint(self, number: int, direction: USBDirection) -> USBEndpoint:
"""

# Search each of our interfaces for the relevant endpoint.
for interface in self.interfaces.values():
for interface in self.active_interfaces.values():
endpoint = interface.get_endpoint(number, direction)
if endpoint is not None:
return endpoint
Expand All @@ -184,7 +185,7 @@ def handle_data_received(self, endpoint: USBEndpoint, data: bytes):
data : The raw bytes received on the relevant endpoint.
"""

for interface in self.interfaces.values():
for interface in self.active_interfaces.values():
if interface.has_endpoint(endpoint.number, direction=USBDirection.OUT):
interface.handle_data_received(endpoint, data)
return
Expand All @@ -204,7 +205,7 @@ def handle_data_requested(self, endpoint: USBEndpoint):
endpoint : The endpoint on which the host requested data.
"""

for interface in self.interfaces.values():
for interface in self.active_interfaces.values():
if interface.has_endpoint(endpoint.number, direction=USBDirection.IN):
interface.handle_data_requested(endpoint)
return
Expand All @@ -223,7 +224,7 @@ def handle_buffer_empty(self, endpoint: USBEndpoint):
This function is called only once per buffer.
"""

for interface in self.interfaces.values():
for interface in self.active_interfaces.values():
if interface.has_endpoint(endpoint.number, direction=USBDirection.IN):
interface.handle_buffer_empty(endpoint)
return
Expand All @@ -246,7 +247,7 @@ def get_descriptor(self) -> bytes:
# FIXME: use construct

# All all subordinate descriptors together to create a big subordinate descriptor.
interfaces = sorted(self.interfaces.values(), key=lambda item: item.number)
interfaces = sorted(self.interfaces.values(), key=lambda item: item.get_identifier())
for interface in interfaces:
interface_descriptors += interface.get_descriptor()

Expand Down
36 changes: 27 additions & 9 deletions facedancer/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,15 @@ def handle_set_configuration_request(self, request):
else:
try:
self.configuration = self.configurations[request.value]

# On a configuration change, all interfaces revert
# to alternate setting 0.
self.configuration.active_interfaces = {
interface.number: interface
for interface in self.configuration.get_interfaces()
if interface.alternate == 0
}

request.acknowledge()
except KeyError:
request.stall()
Expand All @@ -902,11 +911,13 @@ def handle_get_interface_request(self, request):
""" Handle GET_INTERFACE requests; per USB2 [9.4.4] """
log.debug("received GET_INTERFACE request")

# TODO: support alternate interfaces.
# Since we don't support alternate interfaces [yet], we'll always
# indicate use of interface zero.
if self.configuration and (request.index_low in self.configuration.interfaces):
request.reply(b'\x00')
if self.configuration:
try:
number = request.index_low
interface = self.configuration.active_interfaces[number]
request.reply(bytes([interface.alternate]))
except KeyError:
request.stall()
else:
request.stall()

Expand All @@ -917,10 +928,17 @@ def handle_set_interface_request(self, request):
""" Handle SET_INTERFACE requests; per USB2 [9.4.10] """
log.debug(f"f{self.name} received SET_INTERFACE request")

# We don't support alternate interface settings; so ACK
# alternate setting zero, and stall all others.
if request.value == 0:
request.acknowledge()
if self.configuration:
try:
# Find this alternate setting and switch to it.
number = request.index_low
alternate = request.value
identifier = (number, alternate)
interface = self.configuration.interfaces[identifier]
self.configuration.active_interfaces[number] = interface
request.acknowledge()
except KeyError:
request.stall()
else:
request.stall()

Expand Down
31 changes: 23 additions & 8 deletions facedancer/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,17 +243,23 @@ def get_descriptor(self) -> bytes:
# Alternate interface support.
#

# TODO: support these!

@standard_request_handler(number=USBStandardRequests.SET_INTERFACE)
@to_this_interface
def handle_set_interface_request(self, request: USBControlRequest):
""" Handle SET_INTERFACE requests; per USB2 [9.4.10] """
log.debug(f"f{self.name} received SET_INTERFACE request")
# We don't support alternate interface settings; so ACK
# alternate setting zero, and stall all others.
if request.value == 0:
request.acknowledge()

if self.parent:
try:
# Find this alternate setting and switch to it.
number = request.index_low
alternate = request.value
identifier = (number, alternate)
interface = self.parent.interfaces[identifier]
self.parent.active_interfaces[number] = interface
request.acknowledge()
except KeyError:
request.stall()
else:
request.stall()

Expand All @@ -262,8 +268,17 @@ def handle_set_interface_request(self, request: USBControlRequest):
# Automatic instantiation support.
#

def get_identifier(self) -> int:
return self.number
def get_identifier(self) -> (int, int):
return (self.number, self.alternate)


# Although we identify interfaces by (number, alternate), this helper
# is called from the request handling code, where we only want to
# match by interface number. The correct alternate interface should have
# been selected earlier in the request handling process.
def matches_identifier(self, other: int) -> bool:
return (other == self.number)


#
# Request handler functions.
Expand Down