Tests: USB: Check ep buffer is released by abort

Validate that endpoint buffer is not used after a transfer has been
aborted.
pull/9768/head
Filip Jagodzinski 2018-06-29 18:05:36 +02:00 committed by Russ Butler
parent 36d4c355a9
commit 1f8bf46f2e
4 changed files with 107 additions and 10 deletions

View File

@ -56,6 +56,7 @@ VENDOR_TEST_CTRL_OUT_STATUS_DELAY = 8
VENDOR_TEST_CTRL_IN_SIZES = 9
VENDOR_TEST_CTRL_OUT_SIZES = 10
VENDOR_TEST_READ_START = 11
VENDOR_TEST_ABORT_BUFF_CHECK = 12
VENDOR_TEST_UNSUPPORTED_REQUEST = 32
REQUEST_GET_STATUS = 0
@ -1062,6 +1063,16 @@ def request_endpoint_read_start(dev, ep):
dev.ctrl_transfer(**ctrl_kwargs)
def request_abort_buff_check(dev, ep):
ctrl_kwargs = {
'bmRequestType': build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, CTRL_RECIPIENT_ENDPOINT),
'bRequest': VENDOR_TEST_ABORT_BUFF_CHECK,
'wValue': 0,
'wIndex': ep.bEndpointAddress,
'data_or_wLength': 1}
return bool(dev.ctrl_transfer(**ctrl_kwargs)[0])
USB_ERROR_FMT = str('Got {0!r} while testing endpoints '
'{1.bEndpointAddress:#04x}({1.wMaxPacketSize:02}) and '
'{2.bEndpointAddress:#04x}({2.wMaxPacketSize:02}) with '
@ -1304,9 +1315,16 @@ def ep_test_abort(dev, log, verbose=False):
Given a USB device with multiple OUT/IN endpoint pairs
When a device aborts an in progress data transfer
Then no more data is transmitted
and endpoint buffer is correctly released on the device end
"""
NUM_PACKETS_UNTIL_ABORT = 2
NUM_PACKETS_AFTER_ABORT = 8
# If the host ever receives a payload with any byte set to this value,
# the device does not handle abort operation correctly. The buffer
# passed to aborted operation must not be used after call to abort().
FORBIDDEN_PAYLOAD_VALUE = NUM_PACKETS_AFTER_ABORT + 1
cfg = dev.get_active_configuration()
for intf in cfg:
log('interface {}, alt {} -- '.format(intf.bInterfaceNumber, intf.bAlternateSetting), end='')
@ -1336,6 +1354,11 @@ def ep_test_abort(dev, log, verbose=False):
payload_in.extend(packet)
except usb.core.USBError as err:
break
if FORBIDDEN_PAYLOAD_VALUE in payload_in:
raise_unconditionally(
lineno(), 'Endpoint buffer not released when aborting the '
'write operation on endpoint {0.bEndpointAddress:#04x}.'
.format(ep_in))
if verbose:
log('The size of data successfully received from endpoint {0.bEndpointAddress:#04x}: {1} B.'
.format(ep_in, len(payload_in)))
@ -1353,13 +1376,24 @@ def ep_test_abort(dev, log, verbose=False):
log('Testing aborting an in progress transfer for OUT endpoints.')
for ep_out in (bulk_out, interrupt_out):
payload_size = (NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) * ep_out.wMaxPacketSize
payload_out = array.array('B', (0x01 for _ in range(ep_out.wMaxPacketSize)))
num_bytes_written = 0
while num_bytes_written < payload_size:
payload_out = array.array('B', (num_bytes_written/ep_out.wMaxPacketSize
for _ in range(ep_out.wMaxPacketSize)))
try:
num_bytes_written += ep_out.write(payload_out)
except usb.core.USBError:
break
try:
ep_buff_correct = request_abort_buff_check(dev, ep_out)
except (usb.core.USBError, IndexError, TypeError) as err:
raise_unconditionally(
lineno(), 'Unable to verify endpoint buffer content ({!r}).'.format(err))
if not ep_buff_correct:
raise_unconditionally(
lineno(), 'Endpoint buffer not released when aborting the '
'read operation on endpoint {0.bEndpointAddress:#04x}.'
.format(ep_out))
if verbose:
log('The size of data successfully sent to endpoint {0.bEndpointAddress:#04x}: {1} B.'
.format(ep_out, num_bytes_written))

View File

@ -24,12 +24,20 @@
#define NUM_PACKETS_UNTIL_ABORT 2
#define NUM_PACKETS_AFTER_ABORT 8
#define EP_ABORT_BUFF_VALUE 0xff
#define VENDOR_TEST_CTRL_IN 1
#define VENDOR_TEST_CTRL_OUT 2
#define VENDOR_TEST_CTRL_IN_SIZES 9
#define VENDOR_TEST_CTRL_OUT_SIZES 10
#define VENDOR_TEST_READ_START 11
/* If the host ever receives a payload with any byte set to this value,
* the device does not handle abort operation correctly. The buffer
* passed to aborted operation must not be used after call to abort().
*/
#define FORBIDDEN_PAYLOAD_VALUE (NUM_PACKETS_AFTER_ABORT + 1)
#define VENDOR_TEST_CTRL_IN 1
#define VENDOR_TEST_CTRL_OUT 2
#define VENDOR_TEST_CTRL_IN_SIZES 9
#define VENDOR_TEST_CTRL_OUT_SIZES 10
#define VENDOR_TEST_READ_START 11
#define VENDOR_TEST_ABORT_BUFF_CHECK 12
#define EVENT_READY (1 << 0)
@ -236,6 +244,12 @@ void USBEndpointTester::callback_request(const setup_packet_t *setup)
case VENDOR_TEST_READ_START:
result = (_request_read_start(setup)) ? Success : Failure;
break;
case VENDOR_TEST_ABORT_BUFF_CHECK:
result = Send;
ctrl_buf[0] = _request_abort_buff_check(setup);
data = ctrl_buf;
size = 1;
break;
default:
result = PassThrough;
break;
@ -273,20 +287,50 @@ bool USBEndpointTester::_request_read_start(const setup_packet_t *setup)
if (setup->bmRequestType.Recipient != ENDPOINT_RECIPIENT) {
return false;
}
size_t ep_index = NUM_ENDPOINTS + 1;
size_t ep_index = NUM_ENDPOINTS;
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
if (_endpoints[i] == setup->wIndex) {
ep_index = i;
break;
}
}
if (ep_index > NUM_ENDPOINTS) {
if (ep_index == NUM_ENDPOINTS) {
return false;
}
if (_endpoint_buffs[ep_index] == NULL) {
return false;
}
endpoint_abort(_endpoints[ep_index]);
return read_start(_endpoints[ep_index], _endpoint_buffs[ep_index], (*_endpoint_configs)[ep_index].max_packet);
}
bool USBEndpointTester::_request_abort_buff_check(const setup_packet_t *setup)
{
assert_locked();
if (setup->bmRequestType.Recipient != ENDPOINT_RECIPIENT) {
return false;
}
size_t ep_index = NUM_ENDPOINTS;
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
if (_endpoints[i] == setup->wIndex) {
ep_index = i;
break;
}
}
if (ep_index == NUM_ENDPOINTS) {
return false;
}
if (_endpoint_buffs[ep_index] == NULL) {
return false;
}
for (size_t i = 0; i < (*_endpoint_configs)[ep_index].max_packet; i++) {
if (_endpoint_buffs[ep_index][i] != EP_ABORT_BUFF_VALUE) {
return false;
}
}
return true;
}
void USBEndpointTester::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
{
if (aborted) {
@ -309,6 +353,9 @@ void USBEndpointTester::callback_request_xfer_done(const setup_packet_t *setup,
case VENDOR_TEST_CTRL_IN_SIZES:
result = true;
break;
case VENDOR_TEST_ABORT_BUFF_CHECK:
result = true;
break;
default:
result = false;
break;
@ -723,7 +770,6 @@ void USBEndpointTester::_cb_bulk_out()
{
_cnt_cb_bulk_out++;
uint32_t rx_size = read_finish(_endpoints[EP_BULK_OUT]);
if (_abort_transfer_test == false) {
// Send data back to host using the IN endpoint.
memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet);
@ -732,6 +778,10 @@ void USBEndpointTester::_cb_bulk_out()
} else {
// Abort the transfer if enough data was received.
_num_packets_bulk_out_abort++;
if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) {
// Set every byte of the buffer to a known value.
memset(_endpoint_buffs[EP_BULK_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_BULK_OUT].max_packet);
}
read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet);
if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_BULK_OUT]);
@ -743,7 +793,6 @@ void USBEndpointTester::_cb_bulk_in()
{
_cnt_cb_bulk_in++;
write_finish(_endpoints[EP_BULK_IN]);
if (_abort_transfer_test == false) {
// Receive more data from the host using the OUT endpoint.
read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet);
@ -757,6 +806,10 @@ void USBEndpointTester::_cb_bulk_in()
write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet);
if (_num_packets_bulk_in_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_BULK_IN]);
// Verify that buffer given in write_start is not used after the
// call to endpoint_abort(), by changing the buffer contents.
// The test will fail if the host receives new buffer content.
memset(_endpoint_buffs[EP_BULK_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_BULK_IN].max_packet);
}
}
}
@ -773,6 +826,10 @@ void USBEndpointTester::_cb_int_out()
} else {
// Abort the transfer if enough data was received.
_num_packets_int_out_abort++;
if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) {
// Set every byte of the buffer to a known value.
memset(_endpoint_buffs[EP_INT_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_INT_OUT].max_packet);
}
read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet);
if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_INT_OUT]);
@ -797,6 +854,10 @@ void USBEndpointTester::_cb_int_in()
write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet);
if (_num_packets_int_in_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_INT_IN]);
// Verify that buffer given in write_start is not used after the
// call to endpoint_abort(), by changing the buffer contents.
// The test will fail if the host receives new buffer content.
memset(_endpoint_buffs[EP_INT_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_INT_IN].max_packet);
}
}
}

View File

@ -104,6 +104,7 @@ protected:
private:
const char *get_desc_string(const uint8_t *desc);
bool _request_read_start(const setup_packet_t *setup);
bool _request_abort_buff_check(const setup_packet_t *setup);
};
#endif

View File

@ -355,6 +355,7 @@ void ep_test_parallel_transfers_ctrl()
* Given a USB device with multiple OUT/IN endpoint pairs
* When a device aborts an in progress data transfer
* Then no more data is transmitted
* and endpoint buffer is correctly released on the device end
*/
void ep_test_abort()
{