From e52b91ec5ffbc9b98293c7095c3098fc1064cb1c Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Wed, 9 Jan 2019 10:53:21 +0100 Subject: [PATCH 1/3] Read Bulk-IN data during abort_bulk_in sequence The USBTMC specification requires that pending Bulk-IN data are read during the abort_bulk_in sequence. Without this change, several tested USBTMC devices become fully unresponsive after a read timeout. With this change, the device responds correctly to subsequent commands after a timeout. --- usbtmc/usbtmc.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/usbtmc/usbtmc.py b/usbtmc/usbtmc.py index 93b7d55..ff54d8c 100644 --- a/usbtmc/usbtmc.py +++ b/usbtmc/usbtmc.py @@ -264,6 +264,11 @@ def __init__(self, *args, **kwargs): self.timeout = 5.0 + # Separate timeout for abort sequences. It is reasonable to wait + # longer during an abort sequence, to avoid leaving the device + # in an unusable state. + self.abort_timeout = 5.0 + self.bulk_in_ep = None self.bulk_out_ep = None self.interrupt_in_ep = None @@ -869,6 +874,8 @@ def _abort_bulk_out(self, btag=None): if btag is None: btag = self.last_btag + abort_timeout_ms = int(1000 * self.abort_timeout) + # Send INITIATE_ABORT_BULK_OUT b = self.device.ctrl_transfer( bmRequestType=usb.util.build_request_type(usb.util.CTRL_IN, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_ENDPOINT), @@ -876,7 +883,7 @@ def _abort_bulk_out(self, btag=None): wValue=btag, wIndex=self.bulk_out_ep.bEndpointAddress, data_or_wLength=0x0002, - timeout=self._timeout_ms + timeout=abort_timeout_ms ) if (b[0] == USBTMC_STATUS_SUCCESS): # Initiate abort bulk out succeeded, wait for completion @@ -888,7 +895,7 @@ def _abort_bulk_out(self, btag=None): wValue=0x0000, wIndex=self.bulk_out_ep.bEndpointAddress, data_or_wLength=0x0008, - timeout=self._timeout_ms + timeout=abort_timeout_ms ) time.sleep(0.1) if (b[0] != USBTMC_STATUS_PENDING): @@ -906,6 +913,8 @@ def _abort_bulk_in(self, btag=None): if btag is None: btag = self.last_btag + abort_timeout_ms = int(1000 * self.abort_timeout) + # Send INITIATE_ABORT_BULK_IN b = self.device.ctrl_transfer( bmRequestType=usb.util.build_request_type(usb.util.CTRL_IN, usb.util.CTRL_TYPE_CLASS, usb.util.CTRL_RECIPIENT_ENDPOINT), @@ -913,9 +922,21 @@ def _abort_bulk_in(self, btag=None): wValue=btag, wIndex=self.bulk_in_ep.bEndpointAddress, data_or_wLength=0x0002, - timeout=self._timeout_ms + timeout=abort_timeout_ms ) + if (b[0] == USBTMC_STATUS_SUCCESS): + # Read remaining data from bulk in endpoint. + # This is a required step before the abort request can complete. + # + # See USBTMC v1.00 4.2.1.4: + # "The host should continue reading from the Bulk-IN endpoint + # until a short packet is received." + # USBTMC v1.00 4.2.1.5: + # "The host should not send CHECK_ABORT_BULK_IN_STATUS until + # a short Bulk-IN packet has been received." + resp = self.bulk_in_ep.read(self.max_transfer_size, timeout=abort_timeout_ms) + # Initiate abort bulk in succeeded, wait for completion while True: # Check status @@ -925,7 +946,7 @@ def _abort_bulk_in(self, btag=None): wValue=0x0000, wIndex=self.bulk_in_ep.bEndpointAddress, data_or_wLength=0x0008, - timeout=self._timeout_ms + timeout=abort_timeout_ms ) time.sleep(0.1) if (b[0] != USBTMC_STATUS_PENDING): From 3ae28c58cfe95da65484be490ca2bc221240ed96 Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Wed, 9 Jan 2019 11:10:04 +0100 Subject: [PATCH 2/3] Clear Bulk-OUT endpoint after abort_bulk_out The USBTMC specification requires that the Bulk-OUT endpoint is stalled by the device during abort_bulk_out, then explicitly cleared by the host. --- usbtmc/usbtmc.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/usbtmc/usbtmc.py b/usbtmc/usbtmc.py index ff54d8c..1df5e9e 100644 --- a/usbtmc/usbtmc.py +++ b/usbtmc/usbtmc.py @@ -900,9 +900,12 @@ def _abort_bulk_out(self, btag=None): time.sleep(0.1) if (b[0] != USBTMC_STATUS_PENDING): break - else: - # no transfer in progress; nothing to do - pass + if (b[0] == USBTMC_STATUS_SUCCESS): + # Abort request completed. Clear endpoint. + # See USBTMC v1.00 4.2.1.3: + # "The host must send a CLEAR_FEATURE control endpoint + # request to clear the Bulk-OUT halt." + self.bulk_out_ep.clear_halt() def _abort_bulk_in(self, btag=None): "Abort bulk in" From 15343d4457b5551252a4d381a8b2dcdbd629ec58 Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Wed, 9 Jan 2019 11:11:30 +0100 Subject: [PATCH 3/3] Ignore EOM flag when device sends partial data The USBTMC specificiation requires that the host ignore the EOM flag unless the device sends a complete data transfer. --- usbtmc/usbtmc.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/usbtmc/usbtmc.py b/usbtmc/usbtmc.py index 1df5e9e..608b9f3 100644 --- a/usbtmc/usbtmc.py +++ b/usbtmc/usbtmc.py @@ -709,7 +709,14 @@ def read_raw(self, num=-1): else: eom = False else: - eom = transfer_attributes & 1 + # Only consider EOM flag when transfer_size bytes received. + # See USBTMC v1.00 3.3.1.1: + # "The host must ignore EOM if the device does not + # send TransferSize message data bytes." + if len(data) >= transfer_size: + eom = (transfer_attributes & 1) != 0 + else: + eom = False read_data += data # Advantest devices never signal EOI and may only send one read packet