From 42e43515c682c58e968a134683d160b4a6a50206 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Wed, 2 May 2018 23:20:44 +0000 Subject: [PATCH] usb gadget0: ctrl write/read loopback tests This is based on linux's gadget0 intel loopback tests, and also github pr: https://github.com/libopencm3/libopencm3/pull/592 Note that this captures the currently broken control loopback issues on dwc_otg devices. See https://github.com/libopencm3/libopencm3/issues/873 and all linked issues. Current status is passing on f3, f0, and failing on f4. --- tests/gadget-zero/test_gadget0.py | 38 +++++++++++++++++++++++++++++++ tests/gadget-zero/usb-gadget0.c | 30 +++++++++++++++++++++--- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/tests/gadget-zero/test_gadget0.py b/tests/gadget-zero/test_gadget0.py index c27c10f7..064545f8 100644 --- a/tests/gadget-zero/test_gadget0.py +++ b/tests/gadget-zero/test_gadget0.py @@ -3,6 +3,7 @@ import datetime import random import usb.core import usb.util as uu +import random import sys import unittest @@ -25,6 +26,8 @@ GZ_REQ_SET_ALIGNED=3 GZ_REQ_SET_UNALIGNED=4 GZ_REQ_WRITE_LOOPBACK_BUFFER=10 GZ_REQ_READ_LOOPBACK_BUFFER=11 +GZ_REQ_INTEL_WRITE=0x5b +GZ_REQ_INTEL_READ=0x5c class find_by_serial(object): def __init__(self, serial): @@ -83,6 +86,41 @@ class TestGadget0(unittest.TestCase): # Note, this might not be as portable as we'd like. self.assertIn("Pipe", e.strerror) +class TestIntelCompliance(unittest.TestCase): + """ + Part of intel's usb 2.0 compliance is writing and reading back control transfers + """ + def setUp(self): + self.dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID, custom_match=find_by_serial(DUT_SERIAL)) + self.assertIsNotNone(self.dev, "Couldn't find locm3 gadget0 device") + + self.cfg = uu.find_descriptor(self.dev, bConfigurationValue=2) + self.assertIsNotNone(self.cfg, "Config 2 should exist") + self.dev.set_configuration(self.cfg) + + def tearDown(self): + uu.dispose_resources(self.dev) + + def inner_t(self, mylen): + data = [random.randrange(255) for x in range(mylen)] + written = self.dev.ctrl_transfer(uu.CTRL_OUT | uu.CTRL_RECIPIENT_INTERFACE | uu.CTRL_TYPE_VENDOR, GZ_REQ_INTEL_WRITE, 0, 0, data) + self.assertEqual(written, len(data), "Should have written all bytes plz") + # now. in _theory_ I should be able to make a bulk transfer here and have it not "interfere" + # fixme - try this out? + read = self.dev.ctrl_transfer(uu.CTRL_IN | uu.CTRL_RECIPIENT_INTERFACE | uu.CTRL_TYPE_VENDOR, GZ_REQ_INTEL_READ, 0, 0, mylen) + self.assertEqual(mylen, len(read)) + expected = array.array('B', [x for x in data]) + self.assertEqual(expected, read, "should have read back what we wrote") + + def test_ctrl_loopbacks(self): + self.inner_t(0) + self.inner_t(10) + self.inner_t(63) + self.inner_t(64) + self.inner_t(65) + self.inner_t(140) + self.inner_t(183) + class TestConfigSourceSink(unittest.TestCase): """ diff --git a/tests/gadget-zero/usb-gadget0.c b/tests/gadget-zero/usb-gadget0.c index d4d271bd..6e981436 100644 --- a/tests/gadget-zero/usb-gadget0.c +++ b/tests/gadget-zero/usb-gadget0.c @@ -280,7 +280,6 @@ static enum usbd_request_return_codes gadget0_control_request(usbd_device *usbd_ (void) usbd_dev; (void) complete; (void) buf; - (void) len; ER_DPRINTF("ctrl breq: %x, bmRT: %x, windex :%x, wlen: %x, wval :%x\n", req->bRequest, req->bmRequestType, req->wIndex, req->wLength, req->wValue); @@ -292,9 +291,31 @@ static enum usbd_request_return_codes gadget0_control_request(usbd_device *usbd_ state.pattern = req->wValue; return USBD_REQ_HANDLED; case INTEL_COMPLIANCE_WRITE: + /* accept correctly formed ctrl writes */ + if (req->bmRequestType != (USB_REQ_TYPE_VENDOR|USB_REQ_TYPE_INTERFACE)) { + return USBD_REQ_NOTSUPP; + } + if (req->wValue || req->wIndex) { + return USBD_REQ_NOTSUPP; + } + if (req->wLength > sizeof(usbd_control_buffer)) { + return USBD_REQ_NOTSUPP; + } + /* ok, mark it as accepted. */ + return USBD_REQ_HANDLED; case INTEL_COMPLIANCE_READ: - ER_DPRINTF("unimplemented!"); - return USBD_REQ_NOTSUPP; + if (req->bmRequestType != (USB_REQ_TYPE_IN|USB_REQ_TYPE_VENDOR|USB_REQ_TYPE_INTERFACE)) { + return USBD_REQ_NOTSUPP; + } + if (req->wValue || req->wIndex) { + return USBD_REQ_NOTSUPP; + } + if (req->wLength > sizeof(usbd_control_buffer)) { + return USBD_REQ_NOTSUPP; + } + /* ok, return what they left there earlier */ + *len = req->wLength; + return USBD_REQ_HANDLED; case GZ_REQ_SET_UNALIGNED: state.test_unaligned = 1; return USBD_REQ_HANDLED; @@ -316,6 +337,9 @@ static enum usbd_request_return_codes gadget0_control_request(usbd_device *usbd_ *len = req->wValue; } return USBD_REQ_HANDLED; + default: + ER_DPRINTF("Unhandled request!\n"); + return USBD_REQ_NOTSUPP; } return USBD_REQ_NEXT_CALLBACK; }