usbx/test/regression/usbx_ux_test_hub.h
CQ Xiao 6ed7092b77
Add regression tests. (#126)
Add regression tests (auto triggered on PR, manually triggered in forked branch).
2023-11-28 15:50:39 +08:00

633 lines
21 KiB
C

#include <stdio.h>
#include "tx_api.h"
#include "ux_api.h"
#include "ux_system.h"
#include "ux_utility.h"
#include "ux_device_stack.h"
#include "ux_host_class_hub.h"
#include "ux_host_class_dpump.h"
#include "ux_test_dcd_sim_slave.h"
#include "ux_test_hcd_sim_host.h"
#include "ux_test_utility_sim.h"
#include "ux_test.h"
#include "ux_test_actions.h"
#include "ux_device_class_dummy.h"
#include "ux_dcd_sim_slave.h"
#include "ux_device_class_dummy_hub.h"
/* Define local constants. */
#define UX_TEST_HUB_PORT_STATUS_FULL_SPEED 0x0000
#define UX_DEMO_STACK_SIZE (4*1024)
#define UX_USBX_MEMORY_SIZE (128*1024)
/* Define structs. */
typedef struct DEVICE_INIT_DATA_STRUCT
{
UCHAR *framework;
ULONG framework_length;
UCHAR *hub_descriptor;
ULONG hub_descriptor_length;
UCHAR dont_enumerate;
} DEVICE_INIT_DATA;
/* Host */
static TX_THREAD g_thread_host;
static UCHAR g_thread_stack_host[UX_DEMO_STACK_SIZE];
static UX_HOST_CLASS *g_class_driver_host;
static UX_HOST_CLASS_HUB *g_hub_host;
static UX_HOST_CLASS_HUB *g_hub_host_from_system_change_function;
static UX_HOST_CLASS_DPUMP *g_dpump_host;
static UX_HOST_CLASS_DPUMP *g_dpump_host_from_system_change_function;
static TX_THREAD g_thread_cmd;
static UCHAR g_thread_cmd_stack[UX_DEMO_STACK_SIZE];
static ULONG g_thread_cmd_cmd = 0;
#define CMD_SET_AND_SEND_PORT_EVENT_1 0 /* 1 byte port 1 change. */
#define CMD_SET_AND_SEND_PORT_EVENT_2 1 /* 2 bytes port 1 change. */
#define CMD_SET_AND_SEND_PORT_EVENT_3 2 /* 2 bytes device change | port 1 change. */
#define UX_TEST_HOST_CHANGE_LOG_N 16
static struct {
ULONG event;
UX_HOST_CLASS *class;
VOID *instance;
} g_host_change_logs[UX_TEST_HOST_CHANGE_LOG_N];
static ULONG g_host_change_count;
/* Device */
static TX_THREAD g_thread_device;
static UCHAR g_thread_stack_device[UX_DEMO_STACK_SIZE];
static UX_DEVICE_CLASS_HUB *g_hub_device;
static UX_DEVICE_CLASS_HUB_PARAMS g_hub_device_parameter;
static UCHAR g_device_can_go;
/* Define local prototypes and definitions. */
static void thread_entry_host(ULONG arg);
static void thread_entry_device(ULONG arg);
static void thread_entry_cmd(ULONG arg);
static void post_init_host();
static void post_init_device();
/* Define device framework. */
static unsigned char default_device_framework[] = {
/* Device Descriptor */
0x12, /* bLength */
0x01, /* bDescriptorType */
0x00, 0x02, /* bcdUSB */
0x09, /* bDeviceClass - Hub */
0x00, /* bDeviceSubClass */
0x01, /* bDeviceProtocol */
0x40, /* bMaxPacketSize0 */
0x24, 0x04, /* idVendor */
0x12, 0x24, /* idProduct */
0xb2, 0x0b, /* bcdDevice */
0x00, /* iManufacturer */
0x00, /* iProduct */
0x00, /* iSerialNumber */
0x01, /* bNumConfigurations */
/* Configuration Descriptor */
0x09, /* bLength */
0x02, /* bDescriptorType */
0x19, 0x00, /* wTotalLength */
0x01, /* bNumInterfaces */
0x01, /* bConfigurationValue */
0x00, /* iConfiguration */
0xe0, /* bmAttributes - Self-powered */
0x01, /* bMaxPower */
/* Interface Descriptor */
0x09, /* bLength */
0x04, /* bDescriptorType */
0x00, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x01, /* bNumEndpoints */
0x09, /* bInterfaceClass - Hub */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00, /* iInterface */
/* Endpoint Descriptor */
0x07, /* bLength */
0x05, /* bDescriptorType */
0x81, /* bEndpointAddress */
0x03, /* bmAttributes - Interrupt */
0x02, 0x00, /* wMaxPacketSize */
0x0c, /* bInterval */
};
static unsigned char default_hub_descriptor[] = {
/* Hub Descriptor */
0x09, /* bLength */
0x29, /* bDescriptorType */
0x02, /* bNbrPorts */
0x09, 0x00, /* wHubCharacteristics */
0x32, /* bPwrOn2PwrGood */
0x01, /* bHubContrCurrent */
0x00, /* DeviceRemovable */
0xff, /* PortPwrCtrlMask */
};
static unsigned char string_framework[] = {
/* Manufacturer string descriptor : Index 1 - "Express Logic" */
0x09, 0x04, 0x01, 0x0c,
0x45, 0x78, 0x70, 0x72,0x65, 0x73, 0x20, 0x4c,
0x6f, 0x67, 0x69, 0x63,
/* Product string descriptor : Index 2 - "EL Composite device" */
0x09, 0x04, 0x02, 0x13,
0x45, 0x4c, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x6f,
0x73, 0x69, 0x74, 0x65, 0x20, 0x64, 0x65, 0x76,
0x69, 0x63, 0x65,
/* Serial Number string descriptor : Index 3 - "0001" */
0x09, 0x04, 0x03, 0x04,
0x30, 0x30, 0x30, 0x31
};
/* Multiple languages are supported on the device, to add
a language besides english, the unicode language code must
be appended to the language_id_framework array and the length
adjusted accordingly. */
static unsigned char language_id_framework[] = {
/* English. */
0x09, 0x04
};
static UCHAR dpump_framework[] = {
/* Device descriptor */
0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40,
0x0a, 0x07, 0x25, 0x40, 0x01, 0x00, 0x01, 0x02,
0x03, 0x01,
/* Device qualifier descriptor */
0x0a, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40,
0x01, 0x00,
/* Configuration descriptor */
0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0xc0,
0x32,
/* Interface descriptor */
0x09, 0x04, 0x00, 0x00, 0x02, 0x99, 0x99, 0x99,
0x00,
/* Endpoint descriptor (Bulk Out) */
0x07, 0x05, 0x01, 0x02, 0x00, 0x02, 0x00,
/* Endpoint descriptor (Bulk In) */
0x07, 0x05, 0x82, 0x02, 0x00, 0x02, 0x00
};
/* This function is invoked by the simulator when the control transfer is meant
for a device on the hub. */
static UINT hub_control_request_handler(UX_SLAVE_TRANSFER *transfer_request)
{
UX_SLAVE_DCD *dcd;
UX_SLAVE_DEVICE *device;
ULONG request_type;
ULONG request;
ULONG request_value;
ULONG request_index;
ULONG request_length;
UINT status = UX_ERROR;
UCHAR *original_framework;
ULONG original_framework_length;
/* Get the pointer to the DCD. */
dcd = &_ux_system_slave -> ux_system_slave_dcd;
/* Get the pointer to the device. */
device = &_ux_system_slave -> ux_system_slave_device;
/* Ensure that the Setup request has been received correctly. */
if (transfer_request -> ux_slave_transfer_request_completion_code == UX_SUCCESS)
{
/* Seems so far, the Setup request is valid. Extract all fields of
the request. */
request_type = *transfer_request -> ux_slave_transfer_request_setup;
request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST);
request_value = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE);
request_index = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_INDEX);
request_length = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH);
/* Filter for GET_DESCRIPTOR/SET_DESCRIPTOR commands. If the descriptor to be returned is not a standard descriptor,
treat the command as a CLASS command. */
if ((request == UX_GET_DESCRIPTOR || request == UX_SET_DESCRIPTOR) && (((request_value >> 8) & UX_REQUEST_TYPE) != UX_REQUEST_TYPE_STANDARD))
{
/* This request is to be handled by the class layer. */
request_type &= (UINT)~UX_REQUEST_TYPE;
request_type |= UX_REQUEST_TYPE_CLASS;
}
/* Ensure it's not vendor. */
UX_TEST_ASSERT((request_type & UX_REQUEST_TYPE) != UX_REQUEST_TYPE_VENDOR);
/* We don't support any class commands right now. */
UX_TEST_ASSERT((request_type & UX_REQUEST_TYPE) != UX_REQUEST_TYPE_CLASS);
/* Here we proceed only the standard request we know of at the device level. */
switch (request)
{
case UX_GET_STATUS:
UX_TEST_ASSERT(0);
break;
case UX_CLEAR_FEATURE:
UX_TEST_ASSERT(0);
break;
case UX_SET_FEATURE:
UX_TEST_ASSERT(0);
break;
case UX_SET_ADDRESS:
/* Don't do anything. */
break;
case UX_GET_DESCRIPTOR:
/* We need to trick USBX into sending a different framework than
the hub's. */
original_framework = _ux_system_slave->ux_system_slave_device_framework;
original_framework_length = _ux_system_slave->ux_system_slave_device_framework_length;
/* USBX sends looks here for the framework, so just change it do
our liking. */
_ux_system_slave->ux_system_slave_device_framework = dpump_framework;
_ux_system_slave->ux_system_slave_device_framework_length = sizeof(dpump_framework);
/* Kindly ask USBX to send our descriptor. */
UX_TEST_CHECK_SUCCESS(_ux_device_stack_descriptor_send(request_value, request_index, request_length));
_ux_system_slave->ux_system_slave_device_framework = original_framework;
_ux_system_slave->ux_system_slave_device_framework_length = original_framework_length;
break;
case UX_SET_DESCRIPTOR:
UX_TEST_ASSERT(0);
break;
case UX_GET_CONFIGURATION:
UX_TEST_ASSERT(0);
break;
case UX_SET_CONFIGURATION:
/* Do nothing. */
break;
case UX_GET_INTERFACE:
UX_TEST_ASSERT(0);
break;
case UX_SET_INTERFACE:
/* Do nothing. */
break;
case UX_SYNCH_FRAME:
UX_TEST_ASSERT(0);
break;
default:
UX_TEST_ASSERT(0);
return(UX_ERROR);
}
return(UX_SUCCESS);
}
return(UX_ERROR);
}
static UINT class_dpump_get(void)
{
UX_HOST_CLASS *class;
/* Find the main dpump container. */
UX_TEST_CHECK_SUCCESS(ux_host_stack_class_get(_ux_system_host_class_dpump_name, &class));
/* Get the class. */
UX_TEST_CHECK_SUCCESS(ux_test_host_stack_class_instance_get(class, 0, (void **) &g_dpump_host));
/* We still need to wait for the dpump status to be live. */
UX_TEST_CHECK_SUCCESS(ux_test_wait_for_value_uint(&g_dpump_host -> ux_host_class_dpump_state, UX_HOST_CLASS_INSTANCE_LIVE));
/* In virtually all cases, we want the enumeration thread to be finished. */
ux_test_wait_for_enum_thread_completion();
/* Return success. */
return(UX_SUCCESS);
}
/* Leave all the fields null. */
static DEVICE_INIT_DATA default_device_init_data;
/* Define what the initial system looks like. */
static void initialize_hub_with_device_init_data(void *first_unused_memory, DEVICE_INIT_DATA *device_init_data)
{
UCHAR *memory_pointer = (UCHAR *)first_unused_memory;
stepinfo("\n");
if (device_init_data->framework == UX_NULL)
{
device_init_data->framework = default_device_framework;
device_init_data->framework_length = sizeof(default_device_framework);
}
if (device_init_data->hub_descriptor == UX_NULL)
{
device_init_data->hub_descriptor = default_hub_descriptor;
device_init_data->hub_descriptor_length = sizeof(default_hub_descriptor);
}
/* Initialize USBX Memory. */
ux_system_initialize(memory_pointer, UX_USBX_MEMORY_SIZE, UX_NULL, 0);
memory_pointer += UX_USBX_MEMORY_SIZE;
/* It looks weird if this doesn't have a comment! */
ux_utility_error_callback_register(ux_test_error_callback);
/* Create the host thread. */
UX_TEST_CHECK_SUCCESS(tx_thread_create(&g_thread_host, "host thread", thread_entry_host, (ULONG)(ALIGN_TYPE)device_init_data,
g_thread_stack_host, UX_DEMO_STACK_SIZE,
30, 30, 1, TX_DONT_START));
UX_THREAD_EXTENSION_PTR_SET(&g_thread_host, device_init_data)
tx_thread_resume(&g_thread_host);
/* Create the slave thread. */
UX_TEST_CHECK_SUCCESS(tx_thread_create(&g_thread_device, "device thread", thread_entry_device, (ULONG)(ALIGN_TYPE)device_init_data,
g_thread_stack_device, UX_DEMO_STACK_SIZE,
30, 30, 1, TX_DONT_START));
UX_THREAD_EXTENSION_PTR_SET(&g_thread_device, device_init_data)
tx_thread_resume(&g_thread_device);
/* Create the command thread. */
UX_TEST_CHECK_SUCCESS(tx_thread_create(&g_thread_cmd, "cmd thread", thread_entry_cmd, 0,
g_thread_cmd_stack, UX_DEMO_STACK_SIZE,
30, 30, 1, TX_DONT_START));
}
static void initialize_hub(void *first_unused_memory)
{
initialize_hub_with_device_init_data(first_unused_memory, &default_device_init_data);
}
static UINT class_hub_get(void)
{
UX_HOST_CLASS *class;
/* Find the main dpump container. */
UX_TEST_CHECK_SUCCESS(ux_host_stack_class_get(_ux_system_host_class_hub_name, &class));
/* Get the class. */
UX_TEST_CHECK_SUCCESS(ux_test_host_stack_class_instance_get(class, 0, (void **) &g_hub_host));
/* We still need to wait for the dpump status to be live. */
UX_TEST_CHECK_SUCCESS(ux_test_wait_for_value_uint(&g_hub_host -> ux_host_class_hub_state, UX_HOST_CLASS_INSTANCE_LIVE));
/* Return success. */
return(UX_SUCCESS);
}
static VOID device_hub_instance_activate(VOID *instance)
{
g_hub_device = (UX_DEVICE_CLASS_HUB *)instance;
}
static VOID device_hub_instance_deactivate(VOID *instance)
{
g_hub_device = (UX_DEVICE_CLASS_HUB *)UX_NULL;
}
static UINT system_change_function(ULONG event, UX_HOST_CLASS *class, VOID *instance)
{
if (g_host_change_count < UX_TEST_HOST_CHANGE_LOG_N)
{
g_host_change_logs[g_host_change_count].event = event;
g_host_change_logs[g_host_change_count].class = class;
g_host_change_logs[g_host_change_count].instance = instance;
g_host_change_count ++;
}
if (class == UX_NULL)
return(UX_SUCCESS);
if (!memcmp(class->ux_host_class_name, _ux_system_host_class_hub_name, strlen(_ux_system_host_class_hub_name)))
{
if (event == UX_DEVICE_INSERTION)
{
g_hub_host_from_system_change_function = instance;
}
else if (event == UX_DEVICE_REMOVAL)
{
g_hub_host_from_system_change_function = UX_NULL;
}
}
else if (!memcmp(class->ux_host_class_name, _ux_system_host_class_dpump_name, strlen(_ux_system_host_class_dpump_name)))
{
if (event == UX_DEVICE_INSERTION)
{
g_dpump_host_from_system_change_function = instance;
}
else if (event == UX_DEVICE_REMOVAL)
{
g_dpump_host_from_system_change_function = UX_NULL;
}
}
return(UX_SUCCESS);
}
static void thread_entry_cmd(ULONG arg)
{
ULONG temp;
while(1)
{
if (g_hub_device)
{
switch(g_thread_cmd_cmd)
{
case CMD_SET_AND_SEND_PORT_EVENT_1:
_ux_device_class_hub_notify_change(g_hub_device, 1, 1);
break;
case CMD_SET_AND_SEND_PORT_EVENT_2:
_ux_device_class_hub_notify_change(g_hub_device, 1, 2);
break;
case CMD_SET_AND_SEND_PORT_EVENT_3:
temp = 0x3u;
_ux_device_class_hub_notify_changes(g_hub_device, (UCHAR*)&temp, 2);
break;
default:
break;
}
}
tx_thread_suspend(&g_thread_cmd);
}
}
static VOID set_and_send_port_event(UINT port_status, UINT port_change)
{
/* Setup the approriate fields in the hub instance. */
g_hub_device->port_status = port_status;
g_hub_device->port_change = port_change;
g_thread_cmd_cmd = CMD_SET_AND_SEND_PORT_EVENT_1;
tx_thread_resume(&g_thread_cmd);
}
static VOID connect_device_to_hub_speed(UINT speed)
{
set_and_send_port_event(speed | UX_HOST_CLASS_HUB_PORT_STATUS_CONNECTION | UX_HOST_CLASS_HUB_PORT_STATUS_POWER,
UX_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION);
}
static VOID connect_device_to_hub()
{
connect_device_to_hub_speed(UX_HOST_CLASS_HUB_PORT_STATUS_HIGH_SPEED);
}
static VOID connect_device_to_hub_short_with_hub()
{
/* Setup the approriate fields in the hub instance. */
g_hub_device->port_status = UX_HOST_CLASS_HUB_PORT_STATUS_CONNECTION | UX_HOST_CLASS_HUB_PORT_STATUS_POWER;
g_hub_device->port_change = UX_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION;
g_thread_cmd_cmd = CMD_SET_AND_SEND_PORT_EVENT_3;
tx_thread_resume(&g_thread_cmd);
}
static VOID disconnect_device_from_hub()
{
/* Setup the approriate fields in the hub instance. */
g_hub_device->port_status = 0; /* Make sure the connection bit is off. */
g_hub_device->port_change = UX_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION;
g_thread_cmd_cmd = CMD_SET_AND_SEND_PORT_EVENT_1;
tx_thread_resume(&g_thread_cmd);
}
static void thread_entry_host(ULONG device_init_data_ptr)
{
UX_DCD_SIM_SLAVE *dcd_sim_slave;
DEVICE_INIT_DATA *device_init_data;
UX_THREAD_EXTENSION_PTR_GET(device_init_data, DEVICE_INIT_DATA, device_init_data_ptr)
/* The code below is required for installing the device portion of USBX.
In this demo, DFU is possible and we have a call back for state change. */
UX_TEST_CHECK_SUCCESS(ux_device_stack_initialize(device_init_data->framework, device_init_data->framework_length,
device_init_data->framework, device_init_data->framework_length,
string_framework, sizeof(string_framework),
language_id_framework, sizeof(language_id_framework),
UX_NULL));
g_hub_device_parameter.instance_activate = device_hub_instance_activate;
g_hub_device_parameter.instance_deactivate = device_hub_instance_deactivate;
g_hub_device_parameter.descriptor = device_init_data->hub_descriptor;
g_hub_device_parameter.descriptor_length = device_init_data->hub_descriptor_length;
/* Initialize the device hub class. */
UX_TEST_CHECK_SUCCESS(ux_device_stack_class_register(_ux_device_class_hub_name, _ux_device_class_hub_entry, 1, 0, &g_hub_device_parameter));
/* The code below is required for installing the host portion of USBX. */
UX_TEST_CHECK_SUCCESS(ux_host_stack_initialize(system_change_function));
/* Register hub class. */
UX_TEST_CHECK_SUCCESS(ux_host_stack_class_register(_ux_system_host_class_hub_name, ux_host_class_hub_entry));
#if UX_MAX_CLASS_DRIVER > 1
/* Register dpump class. */
UX_TEST_CHECK_SUCCESS(ux_host_stack_class_register(_ux_system_host_class_dpump_name, ux_host_class_dpump_entry));
#endif
/* Initialize the simulated device controller. */
UX_TEST_CHECK_SUCCESS(_ux_test_dcd_sim_slave_initialize());
/* Get the DCD sim slave. */
dcd_sim_slave = (UX_DCD_SIM_SLAVE *)_ux_system_slave->ux_system_slave_dcd.ux_slave_dcd_controller_hardware;
/* Set the hub control request handler. */
dcd_sim_slave->ux_dcd_sim_slave_dcd_control_request_process_hub = hub_control_request_handler;
if (!device_init_data->dont_enumerate)
{
/* Register all the USB host controllers available in this system. */
UX_TEST_CHECK_SUCCESS(ux_host_stack_hcd_register(_ux_system_host_hcd_simulator_name, _ux_test_hcd_sim_host_initialize, 0, 0));
/* Sim HCD init will put() the enum thread semaphore. */
ux_test_wait_for_enum_thread_completion();
/* Get the hub instance. */
class_hub_get();
}
/* Inform device. */
g_device_can_go = 1;
/* Call application. */
post_init_host();
/* Disconnect. */
ux_test_disconnect_slave_and_host_wait_for_enum_completion();
UX_TEST_ASSERT(ux_test_check_actions_empty() == UX_TRUE);
/* And finally the usbx system resources. */
_ux_system_uninitialize();
/* Successful test. */
printf("SUCCESS!\n");
test_control_return(0);
}
static void thread_entry_device(ULONG input)
{
/* Wait for host to give us the go ahead. */
UX_TEST_CHECK_SUCCESS(ux_test_wait_for_value_uchar(&g_device_can_go, 1));
/* Call the application. */
post_init_device();
}