From c3512f4de541db9ce7cccc82b0c874e9d9c01f78 Mon Sep 17 00:00:00 2001 From: fenugrec Date: Thu, 14 Apr 2016 22:17:25 -0400 Subject: [PATCH] tests: gadget0: test for unaligned buffer read/writes. This currently fails on stm32F072, which is expected but not normal. See GH issues #401 , #461 --- tests/gadget-zero/test_gadget0.py | 51 +++++++++++++++++++++++++++++++ tests/gadget-zero/usb-gadget0.c | 43 +++++++++++++++++++++----- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/tests/gadget-zero/test_gadget0.py b/tests/gadget-zero/test_gadget0.py index a27d5f40..616db52d 100644 --- a/tests/gadget-zero/test_gadget0.py +++ b/tests/gadget-zero/test_gadget0.py @@ -318,3 +318,54 @@ class TestControlTransfer_Reads(unittest.TestCase): self.assertEqual(len(q), 10, "In this case, should have gotten wLen back") +class TestUnaligned(unittest.TestCase): + """ + M0 and M0+ cores don't support unaligned memory accesses. These test + how the stack behaves with aligned vs unaligned buffers. + https://github.com/libopencm3/libopencm3/issues/401 + https://github.com/libopencm3/libopencm3/issues/461 + """ + + def setUp(self): + self.dev = usb.core.find(idVendor=0xcafe, idProduct=0xcafe, 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); + self.req = uu.CTRL_OUT | uu.CTRL_TYPE_VENDOR | uu.CTRL_RECIPIENT_INTERFACE + self.intf = self.cfg[(0, 0)] + # heh, kinda gross... + self.ep_out = [ep for ep in self.intf if uu.endpoint_direction(ep.bEndpointAddress) == uu.ENDPOINT_OUT][0] + self.ep_in = [ep for ep in self.intf if uu.endpoint_direction(ep.bEndpointAddress) == uu.ENDPOINT_IN][0] + + def tearDown(self): + uu.dispose_resources(self.dev) + + def set_unaligned(self): + # GZ_REQ_SET_UNALIGNED + x = self.dev.ctrl_transfer(self.req, 4, 0, 0) + + def set_aligned(self): + # GZ_REQ_SET_ALIGNED + x = self.dev.ctrl_transfer(self.req, 3, 0, 0) + + def do_readwrite(self): + """ + transfer garbage data to/from bulk EP; alignment issues will hardfault the target + """ + data = [x for x in range(int(self.ep_out.wMaxPacketSize / 2))] + written = self.dev.write(self.ep_out, data) + self.assertEqual(written, len(data), "Should have written all bytes plz") + + read_size = self.ep_in.wMaxPacketSize * 10 + data = self.dev.read(self.ep_in, read_size) + self.assertEqual(len(data), read_size, "Should have read as much as we asked for") + + def test_aligned(self): + self.set_aligned() + self.do_readwrite() + + def test_unaligned(self): + self.set_unaligned() + self.do_readwrite() diff --git a/tests/gadget-zero/usb-gadget0.c b/tests/gadget-zero/usb-gadget0.c index 9ebbbd37..b6649090 100644 --- a/tests/gadget-zero/usb-gadget0.c +++ b/tests/gadget-zero/usb-gadget0.c @@ -45,6 +45,8 @@ */ #define GZ_REQ_SET_PATTERN 1 #define GZ_REQ_PRODUCE 2 +#define GZ_REQ_SET_ALIGNED 3 +#define GZ_REQ_SET_UNALIGNED 4 #define INTEL_COMPLIANCE_WRITE 0x5b #define INTEL_COMPLIANCE_READ 0x5c @@ -177,42 +179,60 @@ static usbd_device *our_dev; static struct { uint8_t pattern; int pattern_counter; + int test_unaligned; /* If 0 (default), use 16-bit aligned buffers. This should not be declared as bool */ } state = { .pattern = 0, .pattern_counter = 0, + .test_unaligned = 0, }; static void gadget0_ss_out_cb(usbd_device *usbd_dev, uint8_t ep) { (void) ep; + uint16_t x; /* TODO - if you're really keen, perf test this. tiva implies it matters */ /* char buf[64] __attribute__ ((aligned(4))); */ - char buf[BULK_EP_MAXPACKET]; + uint8_t buf[BULK_EP_MAXPACKET + 1] __attribute__ ((aligned(2))); + uint8_t *dest; + trace_send_blocking8(0, 'O'); - uint16_t x = usbd_ep_read_packet(usbd_dev, ep, buf, sizeof(buf)); + if (state.test_unaligned) { + dest = buf + 1; + } else { + dest = buf; + } + x = usbd_ep_read_packet(usbd_dev, ep, dest, BULK_EP_MAXPACKET); trace_send_blocking8(1, x); } static void gadget0_ss_in_cb(usbd_device *usbd_dev, uint8_t ep) { (void) usbd_dev; + uint8_t buf[BULK_EP_MAXPACKET + 1] __attribute__ ((aligned(2))); + uint8_t *src; + trace_send_blocking8(0, 'I'); - uint8_t buf[BULK_EP_MAXPACKET]; + if (state.test_unaligned) { + src = buf + 1; + } else { + src = buf; + } + switch (state.pattern) { case 0: - memset(buf, 0, sizeof(buf)); + memset(src, 0, BULK_EP_MAXPACKET); break; case 1: - for (unsigned i = 0; i < sizeof(buf); i++) { - buf[i] = state.pattern_counter++ % 63; + for (unsigned i = 0; i < BULK_EP_MAXPACKET; i++) { + src[i] = state.pattern_counter++ % 63; } break; } - uint16_t x = usbd_ep_write_packet(usbd_dev, ep, buf, sizeof(buf)); + uint16_t x = usbd_ep_write_packet(usbd_dev, ep, src, BULK_EP_MAXPACKET); /* As we are calling write in the callback, this should never fail */ trace_send_blocking8(2, x); - if (x != sizeof(buf)) { + if (x != BULK_EP_MAXPACKET) { ER_DPRINTF("failed to write?: %d\n", x); } /*assert(x == sizeof(buf));*/ @@ -256,6 +276,12 @@ static int gadget0_control_request(usbd_device *usbd_dev, case INTEL_COMPLIANCE_READ: ER_DPRINTF("unimplemented!"); return USBD_REQ_NOTSUPP; + case GZ_REQ_SET_UNALIGNED: + state.test_unaligned = 1; + return USBD_REQ_HANDLED; + case GZ_REQ_SET_ALIGNED: + state.test_unaligned = 0; + return USBD_REQ_HANDLED; case GZ_REQ_PRODUCE: ER_DPRINTF("fake loopback of %d\n", req->wValue); if (req->wValue > sizeof(usbd_control_buffer)) { @@ -280,6 +306,7 @@ static void gadget0_set_config(usbd_device *usbd_dev, uint16_t wValue) ER_DPRINTF("set cfg %d\n", wValue); switch (wValue) { case GZ_CFG_SOURCESINK: + state.test_unaligned = 0; usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, BULK_EP_MAXPACKET, gadget0_ss_out_cb); usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, BULK_EP_MAXPACKET,