mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 14:33:52 +01:00
tests/usbus: Add automated test for usbus init
This commit is contained in:
parent
5b477918b6
commit
e3123f352e
23
tests/usbus/Makefile
Normal file
23
tests/usbus/Makefile
Normal file
@ -0,0 +1,23 @@
|
||||
include ../Makefile.tests_common
|
||||
USEMODULE += embunit
|
||||
USEMODULE += usbus
|
||||
FEATURES_PROVIDED += periph_usbdev
|
||||
|
||||
# USB device vendor and product ID
|
||||
DEFAULT_VID = 1209
|
||||
DEFAULT_PID = 0001
|
||||
USB_VID ?= $(DEFAULT_VID)
|
||||
USB_PID ?= $(DEFAULT_PID)
|
||||
|
||||
CFLAGS += -DUSB_CONFIG_VID=0x$(USB_VID) -DUSB_CONFIG_PID=0x$(USB_PID)
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
.PHONY: usb_id_check
|
||||
usb_id_check:
|
||||
@if [ $(USB_VID) = $(DEFAULT_VID) ] || [ $(USB_PID) = $(DEFAULT_PID) ] ; then \
|
||||
$(COLOR_ECHO) "$(COLOR_RED)Private testing pid.codes USB VID/PID used!, do not use it outside of test environments!$(COLOR_RESET)" 1>&2 ; \
|
||||
$(COLOR_ECHO) "$(COLOR_RED)MUST NOT be used on any device redistributed, sold or manufactured, VID/PID is not unique!$(COLOR_RESET)" 1>&2 ; \
|
||||
fi
|
||||
|
||||
all: | usb_id_check
|
||||
354
tests/usbus/main.c
Normal file
354
tests/usbus/main.c
Normal file
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Tests USB interaction with USBUS
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "periph/usbdev.h"
|
||||
#include "embUnit.h"
|
||||
|
||||
#include "usbdev_mock.h"
|
||||
#include "usb/descriptor.h"
|
||||
#include "usb/usbus.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief Test case states
|
||||
*/
|
||||
typedef enum {
|
||||
TESTPHASE_START, /**< Initial start */
|
||||
TESTPHASE_RESET, /**< Signal USB RESET */
|
||||
TESTPHASE_REQ_DEV_INIT, /**< Request the first part of the device descriptor */
|
||||
TESTPHASE_RESET2, /**< Second reset */
|
||||
TESTPHASE_SET_ADDRESS, /**< Configure Address */
|
||||
TESTPHASE_REQ_DEV_FULL, /**< Request the full device descriptor */
|
||||
TESTPHASE_FINAL, /**< Testing done */
|
||||
} testphase_t;
|
||||
|
||||
/**
|
||||
* @brief Test control request phases
|
||||
*/
|
||||
typedef enum {
|
||||
TEST_REQ_PHASE_IDLE,
|
||||
TEST_REQ_PHASE_START_GET,
|
||||
TEST_REQ_PHASE_START_SET,
|
||||
TEST_REQ_PHASE_OUTDATA,
|
||||
TEST_REQ_PHASE_INDATA,
|
||||
TEST_REQ_PHASE_OUTACK,
|
||||
TEST_REQ_PHASE_INACK,
|
||||
} test_req_phase_t;
|
||||
|
||||
testphase_t phase = TESTPHASE_START;
|
||||
test_req_phase_t req_phase = TEST_REQ_PHASE_IDLE;
|
||||
|
||||
static usbus_t usbus;
|
||||
static char _stack[USBUS_STACKSIZE];
|
||||
|
||||
static mutex_t waiter = MUTEX_INIT;
|
||||
|
||||
static char *_state2str(test_req_phase_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case TEST_REQ_PHASE_IDLE:
|
||||
return "idle";
|
||||
case TEST_REQ_PHASE_START_GET:
|
||||
return "start get";
|
||||
case TEST_REQ_PHASE_START_SET:
|
||||
return "start set";
|
||||
case TEST_REQ_PHASE_OUTDATA:
|
||||
return "OUT data";
|
||||
case TEST_REQ_PHASE_INDATA:
|
||||
return "IN data";
|
||||
case TEST_REQ_PHASE_OUTACK:
|
||||
return "OUT Ack";
|
||||
case TEST_REQ_PHASE_INACK:
|
||||
return "IN Ack";
|
||||
}
|
||||
return "unknown control request state";
|
||||
}
|
||||
|
||||
static void _build_conf_req(uint8_t *buf, size_t reqlen)
|
||||
{
|
||||
usb_setup_t *setup = (usb_setup_t *)buf;
|
||||
|
||||
memset(setup, 0, sizeof(usb_setup_t));
|
||||
setup->type = USB_SETUP_REQUEST_DEVICE2HOST;
|
||||
setup->request = USB_SETUP_REQ_GET_DESCRIPTOR;
|
||||
setup->value = USB_TYPE_DESCRIPTOR_DEVICE << 8;
|
||||
setup->length = reqlen;
|
||||
}
|
||||
|
||||
static void _build_set_addr(uint8_t *buf)
|
||||
{
|
||||
usb_setup_t *setup = (usb_setup_t *)buf;
|
||||
|
||||
memset(setup, 0, sizeof(usb_setup_t));
|
||||
setup->request = USB_SETUP_REQ_SET_ADDRESS;
|
||||
setup->value = 42;
|
||||
}
|
||||
|
||||
static void _start_setup_get(usbdev_mock_t *dev)
|
||||
{
|
||||
req_phase = TEST_REQ_PHASE_START_GET;
|
||||
dev->in[0].available = 0;
|
||||
/* Trigger the OUT request at the USBUS stack */
|
||||
dev->usbdev.epcb(&dev->out[0].ep, USBDEV_EVENT_ESR);
|
||||
}
|
||||
|
||||
static void _start_setup_set(usbdev_mock_t *dev)
|
||||
{
|
||||
req_phase = TEST_REQ_PHASE_START_SET;
|
||||
dev->in[0].available = 0;
|
||||
dev->usbdev.epcb(&dev->out[0].ep, USBDEV_EVENT_ESR);
|
||||
}
|
||||
|
||||
/* Validates the first 8 bytes of the device descriptor */
|
||||
static void _validate_device_desc_init(usb_descriptor_device_t *desc)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT(sizeof(usb_descriptor_device_t), desc->length);
|
||||
TEST_ASSERT_EQUAL_INT(USB_TYPE_DESCRIPTOR_DEVICE, desc->type);
|
||||
}
|
||||
|
||||
static void _test_sequence(usbdev_mock_t *dev)
|
||||
{
|
||||
testphase_t next_phase = TESTPHASE_FINAL;
|
||||
|
||||
/* Global sequence control */
|
||||
/* Called as part of the ESR handling */
|
||||
switch (phase) {
|
||||
case TESTPHASE_START:
|
||||
next_phase = TESTPHASE_RESET;
|
||||
/* Signal reset condition */
|
||||
DEBUG("[test]: Signalling USB reset condition\n");
|
||||
dev->usbdev.cb(&dev->usbdev, USBDEV_EVENT_RESET);
|
||||
dev->usbdev.cb(&dev->usbdev, USBDEV_EVENT_ESR);
|
||||
break;
|
||||
case TESTPHASE_RESET:
|
||||
next_phase = TESTPHASE_REQ_DEV_INIT;
|
||||
DEBUG("[test]: Requesting device descriptor\n");
|
||||
_build_conf_req(dev->out[0].ep.buf, 8); /* initial config request */
|
||||
dev->req_len = 8;
|
||||
dev->out[0].state = EP_STATE_DATA_AVAILABLE;
|
||||
dev->out[0].available = 8;
|
||||
_start_setup_get(dev);
|
||||
break;
|
||||
case TESTPHASE_REQ_DEV_INIT:
|
||||
next_phase = TESTPHASE_RESET2;
|
||||
DEBUG("[test]: validating device descriptor\n");
|
||||
TEST_ASSERT_EQUAL_INT(dev->in[0].available, 8);
|
||||
_validate_device_desc_init(
|
||||
(usb_descriptor_device_t *)dev->in[0].buf_start);
|
||||
/* Reset device */
|
||||
DEBUG("[test]: Signalling second USB reset condition\n");
|
||||
dev->usbdev.cb(&dev->usbdev, USBDEV_EVENT_RESET);
|
||||
dev->usbdev.cb(&dev->usbdev, USBDEV_EVENT_ESR);
|
||||
break;
|
||||
case TESTPHASE_RESET2:
|
||||
next_phase = TESTPHASE_SET_ADDRESS;
|
||||
DEBUG("[test]: Set USB address\n");
|
||||
_build_set_addr(dev->out[0].ep.buf);
|
||||
dev->req_len = 0;
|
||||
dev->out[0].state = EP_STATE_DATA_AVAILABLE;
|
||||
dev->out[0].available = 8;
|
||||
_start_setup_set(dev);
|
||||
break;
|
||||
case TESTPHASE_SET_ADDRESS:
|
||||
next_phase = TESTPHASE_REQ_DEV_FULL;
|
||||
DEBUG("[test]: Requesting full device descriptor\n");
|
||||
_build_conf_req(dev->out[0].ep.buf,
|
||||
sizeof(usb_descriptor_device_t)); /* initial config request */
|
||||
dev->req_len = sizeof(usb_descriptor_device_t);
|
||||
dev->out[0].state = EP_STATE_DATA_AVAILABLE;
|
||||
dev->out[0].available = sizeof(usb_setup_t);
|
||||
_start_setup_get(dev);
|
||||
break;
|
||||
case TESTPHASE_REQ_DEV_FULL:
|
||||
TEST_ASSERT_EQUAL_INT(dev->in[0].available,
|
||||
sizeof(usb_descriptor_device_t));
|
||||
DEBUG("[test]: Validating full descriptor\n");
|
||||
_validate_device_desc_init(
|
||||
(usb_descriptor_device_t *)dev->in[0].buf_start);
|
||||
|
||||
next_phase = TESTPHASE_FINAL;
|
||||
dev->usbdev.cb(&dev->usbdev, USBDEV_EVENT_ESR);
|
||||
break;
|
||||
|
||||
case TESTPHASE_FINAL:
|
||||
/* Testing done */
|
||||
mutex_unlock(&waiter);
|
||||
break;
|
||||
default:
|
||||
DEBUG("[test]: Unhandled situation in test script\n");
|
||||
}
|
||||
phase = next_phase;
|
||||
}
|
||||
|
||||
/* Validates the ready call after submitting a setup request */
|
||||
static void _validate_get_req_start(usbdev_mock_ep_t *ep)
|
||||
{
|
||||
DEBUG("[validation]: get request validation\n");
|
||||
|
||||
/* We should get an IN request with the data */
|
||||
TEST_ASSERT_EQUAL_INT(ep->ep.dir, USB_EP_DIR_IN);
|
||||
req_phase = TEST_REQ_PHASE_INDATA;
|
||||
}
|
||||
|
||||
static void _validate_out_ack(usbdev_mock_t *dev, usbdev_mock_ep_t *ep)
|
||||
{
|
||||
DEBUG("[validation]: OUT ACK validation\n");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(ep->ep.dir, USB_EP_DIR_OUT);
|
||||
dev->usbdev.epcb(&dev->out[0].ep, USBDEV_EVENT_ESR);
|
||||
req_phase = TEST_REQ_PHASE_IDLE;
|
||||
}
|
||||
|
||||
static void _validate_in_ack(usbdev_mock_t *dev, usbdev_mock_ep_t *ep,
|
||||
size_t len)
|
||||
{
|
||||
DEBUG("[validation]: IN ACK validation\n");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(ep->ep.dir, USB_EP_DIR_IN);
|
||||
TEST_ASSERT_EQUAL_INT(len, 0);
|
||||
dev->usbdev.epcb(&dev->in[0].ep, USBDEV_EVENT_ESR);
|
||||
req_phase = TEST_REQ_PHASE_IDLE;
|
||||
}
|
||||
|
||||
static void _validate_set_req_start(usbdev_mock_t *dev, usbdev_mock_ep_t *ep,
|
||||
size_t len)
|
||||
{
|
||||
DEBUG("[validation]: set request validation\n");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(dev->out[0].available, 8);
|
||||
/* We should get an IN request with the data */
|
||||
TEST_ASSERT_EQUAL_INT(ep->ep.dir, USB_EP_DIR_IN);
|
||||
if (dev->req_len == 0) {
|
||||
DEBUG("[validation]: No payload, performing IN ACK validation\n");
|
||||
/* No data phase, this `ready` is the OUT ACK */
|
||||
_validate_in_ack(dev, ep, len);
|
||||
}
|
||||
else {
|
||||
req_phase = TEST_REQ_PHASE_OUTDATA;
|
||||
}
|
||||
}
|
||||
|
||||
static void _handle_data(usbdev_mock_t *dev, usbdev_mock_ep_t *ep, size_t len)
|
||||
{
|
||||
if (ep->ep.dir == USB_EP_DIR_IN) {
|
||||
/* Zero length IN packet is not valid here */
|
||||
DEBUG("[data]: Handling IN data from stack: %u\n", (unsigned)len);
|
||||
TEST_ASSERT(len != 0);
|
||||
dev->usbdev.epcb(&dev->in[0].ep, USBDEV_EVENT_ESR);
|
||||
if (ep->available == dev->req_len) {
|
||||
DEBUG("[data]: Full data received from stack\n");
|
||||
req_phase = TEST_REQ_PHASE_OUTACK;
|
||||
/* Reset buffer ptr to the start */
|
||||
ep->ep.buf = ep->buf_start;
|
||||
}
|
||||
else {
|
||||
DEBUG("[data]: Expecting more data from stack: %u/%u\n",
|
||||
(unsigned)ep->available,
|
||||
(unsigned)dev->req_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _ep_esr_validation(usbdev_mock_t *dev, usbdev_mock_ep_t *ep)
|
||||
{
|
||||
DEBUG("[ep esr]: Data available for stack: %u\n", ep->available);
|
||||
if (req_phase == TEST_REQ_PHASE_IDLE) {
|
||||
DEBUG("[ep esr]: Done with the request\n");
|
||||
/* signal USBDEV_EVENT_ESR to call _test_sequence */
|
||||
dev->usbdev.cb(&dev->usbdev, USBDEV_EVENT_ESR);
|
||||
}
|
||||
ep->ep.dev->epcb(&ep->ep, USBDEV_EVENT_TR_COMPLETE);
|
||||
}
|
||||
|
||||
/* Handles Control endpoint validation from the usbdev side. *
|
||||
* Called as part of the endpoint ready function. */
|
||||
static void _ep0_validation(usbdev_mock_t *dev, usbdev_mock_ep_t *ep,
|
||||
size_t len)
|
||||
{
|
||||
DEBUG("[validation]: req state: %s\n", _state2str(req_phase));
|
||||
/* Validate direction and content for the current state */
|
||||
switch (req_phase) {
|
||||
case TEST_REQ_PHASE_START_GET:
|
||||
_validate_get_req_start(ep);
|
||||
_handle_data(dev, ep, len);
|
||||
break;
|
||||
case TEST_REQ_PHASE_START_SET:
|
||||
_validate_set_req_start(dev, ep, len);
|
||||
break;
|
||||
case TEST_REQ_PHASE_OUTACK:
|
||||
_validate_out_ack(dev, ep);
|
||||
break;
|
||||
case TEST_REQ_PHASE_INACK:
|
||||
_validate_in_ack(dev, ep, len);
|
||||
break;
|
||||
default:
|
||||
/* Nothing is validated at the moment for the cases of:
|
||||
* - TEST_REQ_PHASE_IDLE
|
||||
* - TEST_REQ_PHASE_INDATA
|
||||
* - TEST_REQ_PHASE_OUTDATA
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void _setup(void)
|
||||
{
|
||||
mutex_lock(&waiter);
|
||||
}
|
||||
|
||||
static void _test(void)
|
||||
{
|
||||
_setup();
|
||||
/* Initialize basic usbus struct, don't start the thread yet */
|
||||
usbdev_t *usbdev = usbdev_get_ctx(0);
|
||||
usbus_init(&usbus, usbdev);
|
||||
usbdev_mock_setup(&_test_sequence, &_ep_esr_validation, &_ep0_validation);
|
||||
|
||||
usbus_create(_stack, USBUS_STACKSIZE, USBUS_PRIO, USBUS_TNAME, &usbus);
|
||||
|
||||
/* Try to lock to block until the test thread has finished */
|
||||
mutex_lock(&waiter);
|
||||
}
|
||||
|
||||
static Test *tests_usbus(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(_test),
|
||||
};
|
||||
EMB_UNIT_TESTCALLER(tests, NULL, NULL, fixtures);
|
||||
|
||||
return (Test *)&tests;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TESTS_START();
|
||||
TESTS_RUN(tests_usbus());
|
||||
TESTS_END();
|
||||
|
||||
return 0;
|
||||
}
|
||||
22
tests/usbus/tests/01-run.py
Executable file
22
tests/usbus/tests/01-run.py
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
# Copyright (C) 2016 Takuo Yonezawa <Yonezawa-T2@mail.dnp.co.jp>
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.expect(r"OK \(1 tests\)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.path.append(os.path.join(os.environ['RIOTBASE'],
|
||||
'dist/tools/testrunner'))
|
||||
from testrunner import run
|
||||
sys.exit(run(testfunc))
|
||||
226
tests/usbus/usbdev_mock.c
Normal file
226
tests/usbus/usbdev_mock.c
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "embUnit.h"
|
||||
#include "periph/usbdev.h"
|
||||
#include "usbdev_mock.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static usbdev_mock_t usbdev_mock;
|
||||
static uint8_t _in_buf[256]; /* "host" in */
|
||||
static uint8_t _out_buf[64]; /* "host" out */
|
||||
|
||||
static const usbdev_driver_t testdriver;
|
||||
|
||||
static usbdev_mock_t *_ep2dev(usbdev_ep_t *ep)
|
||||
{
|
||||
return (usbdev_mock_t *)ep->dev;
|
||||
}
|
||||
|
||||
void usbdev_init_lowlevel(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
usbdev_t *usbdev_get_ctx(unsigned num)
|
||||
{
|
||||
(void)num;
|
||||
return &usbdev_mock.usbdev;
|
||||
}
|
||||
|
||||
void usbdev_mock_setup(usbdev_mock_esr_cb_t esr_cb,
|
||||
usbdev_mock_ep_esr_cb_t ep_esr_cb,
|
||||
usbdev_mock_ready_cb_t ready_cb)
|
||||
{
|
||||
memset(&usbdev_mock, 0, sizeof(usbdev_mock));
|
||||
usbdev_mock.usbdev.driver = &testdriver;
|
||||
|
||||
usbdev_mock.esr_cb = esr_cb;
|
||||
usbdev_mock.ep_esr_cb = ep_esr_cb;
|
||||
usbdev_mock.ready_cb = ready_cb;
|
||||
}
|
||||
|
||||
static void _init(usbdev_t *usbdev)
|
||||
{
|
||||
usbdev_mock_t *dev = (usbdev_mock_t *)usbdev;
|
||||
|
||||
/* Throw first event to start the test sequence */
|
||||
dev->usbdev.cb(usbdev, USBDEV_EVENT_ESR);
|
||||
}
|
||||
|
||||
usbdev_ep_t *_new_ep(usbdev_t *dev, usb_ep_type_t type, usb_ep_dir_t dir,
|
||||
size_t buf_len)
|
||||
{
|
||||
usbdev_mock_t *testdev = (usbdev_mock_t *)dev;
|
||||
usbdev_mock_ep_t *res = NULL;
|
||||
|
||||
if (type == USB_EP_TYPE_CONTROL) {
|
||||
if (dir == USB_EP_DIR_OUT) {
|
||||
|
||||
res = &testdev->out[0];
|
||||
res->ep.num = 0;
|
||||
res->ep.buf = _out_buf;
|
||||
}
|
||||
else {
|
||||
res = &testdev->in[0];
|
||||
res->ep.num = 0;
|
||||
res->ep.buf = _in_buf;
|
||||
}
|
||||
}
|
||||
if (res) {
|
||||
res->buf_start = res->ep.buf;
|
||||
res->state = EP_STATE_READY;
|
||||
res->available = 0;
|
||||
res->ep.len = buf_len;
|
||||
res->ep.dir = dir;
|
||||
res->ep.type = type;
|
||||
res->ep.dev = dev;
|
||||
}
|
||||
return &res->ep;
|
||||
}
|
||||
|
||||
int _get(usbdev_t *usbdev, usbopt_t opt,
|
||||
void *value, size_t max_len)
|
||||
{
|
||||
(void)usbdev;
|
||||
(void)opt;
|
||||
(void)value;
|
||||
(void)max_len;
|
||||
DEBUG("[mock]: Unhandled get call: 0x%x\n", opt);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int _set(usbdev_t *usbdev, usbopt_t opt,
|
||||
const void *value, size_t value_len)
|
||||
{
|
||||
usbdev_mock_t *testdev = (usbdev_mock_t *)usbdev;
|
||||
|
||||
(void)value_len;
|
||||
int res = -ENOTSUP;
|
||||
switch (opt) {
|
||||
case USBOPT_ADDRESS:
|
||||
testdev->config_addr = *(uint8_t *)value;
|
||||
res = sizeof(uint8_t);
|
||||
break;
|
||||
case USBOPT_ATTACH:
|
||||
assert(value_len == sizeof(usbopt_enable_t));
|
||||
res = sizeof(usbopt_enable_t);
|
||||
break;
|
||||
default:
|
||||
DEBUG("[mock]: Unhandled set call: 0x%x\n", opt);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _esr(usbdev_t *dev)
|
||||
{
|
||||
usbdev_mock_t *usbdev_mock = (usbdev_mock_t *)dev;
|
||||
|
||||
usbdev_mock->esr_cb(usbdev_mock);
|
||||
}
|
||||
|
||||
void _ep_init(usbdev_ep_t *ep)
|
||||
{
|
||||
(void)ep;
|
||||
}
|
||||
|
||||
int _ep_get(usbdev_ep_t *ep, usbopt_ep_t opt,
|
||||
void *value, size_t max_len)
|
||||
{
|
||||
usbdev_mock_ep_t *testep = (usbdev_mock_ep_t *)ep;
|
||||
|
||||
(void)max_len;
|
||||
switch (opt) {
|
||||
case USBOPT_EP_AVAILABLE:
|
||||
*((size_t *)value) = testep->available;
|
||||
return sizeof(size_t);
|
||||
default:
|
||||
DEBUG("[mock]: Unhandled endpoint get call: 0x%x\n", opt);
|
||||
break;
|
||||
}
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int _ep_set(usbdev_ep_t *ep, usbopt_ep_t opt,
|
||||
const void *value, size_t value_len)
|
||||
{
|
||||
(void)ep;
|
||||
(void)value;
|
||||
(void)value_len;
|
||||
int res = -ENOTSUP;
|
||||
switch (opt) {
|
||||
case USBOPT_EP_ENABLE:
|
||||
res = sizeof(usbopt_enable_t);
|
||||
break;
|
||||
case USBOPT_EP_STALL:
|
||||
res = sizeof(usbopt_enable_t);
|
||||
break;
|
||||
default:
|
||||
DEBUG("[mock]: Unhandled ep set call: 0x%x\n", opt);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void _ep_esr(usbdev_ep_t *ep)
|
||||
{
|
||||
usbdev_mock_t *dev = _ep2dev(ep);
|
||||
|
||||
DEBUG("[mock]: ESR EP %u, dir %s\n",
|
||||
ep->num, ep->dir == USB_EP_DIR_OUT ? "out" : "in");
|
||||
usbdev_mock_ep_t *mock_ep = (usbdev_mock_ep_t *)ep;
|
||||
if (mock_ep->state == EP_STATE_DATA_AVAILABLE) {
|
||||
dev->ep_esr_cb(dev, mock_ep);
|
||||
mock_ep->state = EP_STATE_READY;
|
||||
}
|
||||
}
|
||||
|
||||
int _ready(usbdev_ep_t *ep, size_t len)
|
||||
{
|
||||
DEBUG("[mock]: Readying EP %u, dir %s, len %u\n",
|
||||
ep->num, ep->dir == USB_EP_DIR_OUT ? "out" : "in", (unsigned)len);
|
||||
if (ep->num == 0) {
|
||||
usbdev_mock_t *usbdev_mock = _ep2dev(ep);
|
||||
usbdev_mock_ep_t *mock_ep = (usbdev_mock_ep_t *)ep;
|
||||
|
||||
mock_ep->available = len;
|
||||
mock_ep->ep.buf += len;
|
||||
mock_ep->state = EP_STATE_DATA_AVAILABLE;
|
||||
|
||||
usbdev_mock->ready_cb(usbdev_mock, (usbdev_mock_ep_t *)ep, len);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const usbdev_driver_t testdriver = {
|
||||
.init = _init,
|
||||
.new_ep = _new_ep,
|
||||
.get = _get,
|
||||
.set = _set,
|
||||
.esr = _esr,
|
||||
.ep_init = _ep_init,
|
||||
.ep_get = _ep_get,
|
||||
.ep_set = _ep_set,
|
||||
.ep_esr = _ep_esr,
|
||||
.ready = _ready,
|
||||
};
|
||||
117
tests/usbus/usbdev_mock.h
Normal file
117
tests/usbus/usbdev_mock.h
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Koen Zandberg <koen@bergzand.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
/**
|
||||
* @defgroup tests_usbdev_mock USBdev mockup device
|
||||
* @ingroup tests
|
||||
* @brief USBdev mockup device for testing
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*/
|
||||
|
||||
#ifndef USBDEV_MOCK_H
|
||||
#define USBDEV_MOCK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of IN and OUT endpoints available in the mock usbdev device
|
||||
*/
|
||||
#define USBDEV_MOCK_NUM_EP (1)
|
||||
|
||||
/**
|
||||
* @name usbdev mock device endpoint states
|
||||
*/
|
||||
typedef enum {
|
||||
EP_STATE_READY, /**< Endpoint is ready/idle */
|
||||
EP_STATE_DATA_AVAILABLE, /**< Endpoint has data */
|
||||
} usbdev_mock_ep_state_t;
|
||||
|
||||
/**
|
||||
* @brief usbdev mock device endpoint
|
||||
*/
|
||||
typedef struct {
|
||||
usbdev_ep_t ep; /**< Generic endpoint struct */
|
||||
usbdev_mock_ep_state_t state; /**< Endpoint state */
|
||||
size_t available; /**< Bytes available in the buffer */
|
||||
uint8_t *buf_start; /**< Start location of the buffer */
|
||||
} usbdev_mock_ep_t;
|
||||
|
||||
/**
|
||||
* @brief usbdev mock device forward declaration
|
||||
*/
|
||||
typedef struct usbdev_mock usbdev_mock_t;
|
||||
|
||||
/**
|
||||
* @brief usbdev mock device callback for esr event checking
|
||||
*
|
||||
* @param dev usbdev mock device descriptor
|
||||
*/
|
||||
typedef void (*usbdev_mock_esr_cb_t)(usbdev_mock_t *dev);
|
||||
|
||||
/**
|
||||
* @brief usbdev mock device callback for endpoint event checking
|
||||
*
|
||||
* @param dev usbdev mock device descriptor
|
||||
* @param ep usbdev mock device endpoint
|
||||
*/
|
||||
typedef void (*usbdev_mock_ep_esr_cb_t)(usbdev_mock_t *dev,
|
||||
usbdev_mock_ep_t *ep);
|
||||
|
||||
/**
|
||||
* @brief usbdev mock device callback for endpoint ready call checking
|
||||
*
|
||||
* @param dev usbdev mock device descriptor
|
||||
* @param ep usbdev mock device endpoint
|
||||
* @param len length supplied to the ready call
|
||||
*/
|
||||
typedef void (*usbdev_mock_ready_cb_t)(usbdev_mock_t *dev,
|
||||
usbdev_mock_ep_t *ep,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* @brief usbdev mock device
|
||||
*/
|
||||
struct usbdev_mock {
|
||||
usbdev_t usbdev; /**< Generic usbdev device */
|
||||
usbdev_mock_ep_t in[USBDEV_MOCK_NUM_EP]; /**< IN endpoints */
|
||||
usbdev_mock_ep_t out[USBDEV_MOCK_NUM_EP]; /**< OUT endpoints */
|
||||
uint8_t config_addr; /**< Configured address */
|
||||
size_t req_len; /**< Length of the current
|
||||
* request */
|
||||
usbdev_mock_esr_cb_t esr_cb; /**< ESR callback for
|
||||
* validation */
|
||||
usbdev_mock_ep_esr_cb_t ep_esr_cb; /**< Endpoint ESR cb for
|
||||
* validation */
|
||||
usbdev_mock_ready_cb_t ready_cb; /**< Endpoint ready cb for
|
||||
* validation */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief usbdev mock device setup function
|
||||
*
|
||||
* @param esr_cb ESR callback
|
||||
* @param ep_esr_cb Endpoint ESR callback
|
||||
* @param ready_cb Endpoint ready callback
|
||||
*/
|
||||
void usbdev_mock_setup(usbdev_mock_esr_cb_t esr_cb,
|
||||
usbdev_mock_ep_esr_cb_t ep_esr_cb,
|
||||
usbdev_mock_ready_cb_t ready_cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* USBDEV_MOCK_H */
|
||||
/** @} */
|
||||
Loading…
x
Reference in New Issue
Block a user