eclipse-threadx / usbx

Eclipse ThreadX - USBX is a high-performance USB host, device, and on-the-go (OTG) embedded stack, that is fully integrated with Eclipse ThreadX RTOS
https://github.com/eclipse-threadx/rtos-docs/blob/main/rtos-docs/usbx/index.md
MIT License
148 stars 88 forks source link

Host stack HID support questions #109

Closed sw2mrc closed 10 months ago

sw2mrc commented 1 year ago

Greetings!

I'm using stm32f7, and have few questions about using usbx host stack with HID devices, and would appreciate your response.

  1. When a composite HID device with two or more HID interfaces (say kbd and mouse) connects to stm32f7 host, only first interface gets recognized and configured, not the second interface. What api sequence needs to be used to enumerate all the interfaces and configure them?

  2. Is there usbx host side api to retrieve all the descriptors that host stack queried and received, such as device, configuration, and HID report descriptors?

  3. I'm able to receive events such as key press/release, mouse position etc, however, is there hostside api to retrieve the HID report sent by the device "as-is"?

Thanks in advance!

xiaocq2001 commented 1 year ago
  1. Each HID interface should be enumerated as a HID class instance, you can get notified on HID instance creation in registered host change callback. Another way is to get the registered class (ux_host_stack_class_get) and get instances from the class (ux_host_stack_class_instance_get).
  2. The descriptors are retrieved for enumeration, there is no public to read them, but there is an API to read string descriptor, as ux_host_stack_device_string_get.
  3. Register the HID class, do not register client but register report callback (ux_host_class_hid_report_callback_register), and start periodic report polling (ux_host_class_hid_periodic_report_start), you can handle report in callback.
sw2mrc commented 1 year ago

Regarding 2: since there is no public api, are there any structures I can look at to come up with a private api if need be?

Regarding the other two items, I will test them in couple weeks and update the thread.

cheers.

sw2mrc commented 12 months ago
3. Register the HID class, do not register client but register report callback (ux_host_class_hid_report_callback_register), and start periodic report polling (ux_host_class_hid_periodic_report_start), you can handle report in callback.

I was able to get this working with HID mouse where the report callback gets triggered, however it looks like the callback keeps getting triggered even when mouse is stationary and is not being used.. If the mouse is not being used, then there are only NAKs from the device, so I wonder why the report callback is being called continuously? Sometimes the reports seem to die down but I'm unable to see a pattern of when it would stop or runs continuously.

Overall, I'm looking for your guidance on which approach results in less MCU overhead when number of HID devices increase to say 4-7 devices? 1) a HID client thread that keeps polling for the mouse pointer/button/scroll wheel position continuously or 2) HID report callback invocation?

Thank you in advance..

xiaocq2001 commented 12 months ago

however it looks like the callback keeps getting triggered even when mouse is stationary and is not being used.

If you have a USB tracer, please check to confirm that your mouse stops sending reports, if it sends reports in specific rate maybe you can set infinity idle rate with ux_host_class_hid_idle_set() (ref here), if interrupt happens even there is no data, maybe the host controller driver (HCD) and STM32 HAL need to be checked.

sw2mrc commented 11 months ago
  1. Each HID interface should be enumerated as a HID class instance, you can get notified on HID instance creation in registered host change callback. Another way is to get the registered class (ux_host_stack_class_get) and get instances from the class (ux_host_stack_class_instance_get).

I've not been able to add support for a composite HID device with stm32/usbx host. Here are some details

  1. Mouse-only HID device and Keyboard-only HID device works fine under usbx host
  2. Composite Keyboard and Touchpad device (Rii x1) works fine with PC host, but under stm32/usbx host, only keyboard works, and touchpad doesn't work at all -- bottons, cursor pos, etc have no effect
  3. In host_event_callback, there is usual sequence of events (HID_insert, USB_insert, and Device_connect) for both keyboard and mouse interfaces
  4. Both keyboard and mouse are registered as HID clients and each have a separate user threads to poll keyboard and mouse status reports, and the polling happens only after device configure state is set in host_event_callback
  5. Since keyboard works, i tried just forcing the kbd thread to sleep to see if mouse only actions can help retrieve the mouse state, but it doesn't work either.
  6. Added a mutex to serialize simultaneous access in keyboard and mouse threads but it didn't help
  7. Code does not crash

Please advise. Thank you in advance!

xiaocq2001 commented 11 months ago

Composite Keyboard and Touchpad device (Rii x1) works fine with PC host, but under stm32/usbx host, only keyboard works, and touchpad doesn't work at all -- bottons, cursor pos, etc have no effect

Maybe keyboard and touchpad use different interfaces, the usage of touchpad does not match any existing registered client page and usage, so the touchpad is not recognized as a HID client. May you share the descriptors for the composite device to check?

Both keyboard and mouse are registered as HID clients and each have a separate user threads to poll keyboard and mouse status reports, and the polling happens only after device configure state is set in host_event_callback Since keyboard works, i tried just forcing the kbd thread to sleep to see if mouse only actions can help retrieve the mouse state, but it doesn't work either. Added a mutex to serialize simultaneous access in keyboard and mouse threads but it didn't help

Not sure if the "mouse" here is real mouse or the touchpad, a touchpad may report itself a touchpad instead of a mouse, in this case there is no client driver matching and client instance is not available.

If it's confirmed the connected device is composed of HID keyboard interface and HID mouse interface, the HID class is registered, and HID mouse and keyboard clients are registered, the keyboard and mouse client instances should be recognized.

Note currently only first page/usage in report descriptor is checked to find client, If the device uses complex report structure, the other page/usage may not work.

sw2mrc commented 11 months ago

Maybe keyboard and touchpad use different interfaces, the usage of touchpad does not match any existing registered client page and usage, so the touchpad is not recognized as a HID client. May you share the descriptors for the composite device to check?

Attached is the descriptors from usbview.

If it's confirmed the connected device is composed of HID keyboard interface and HID mouse interface, the HID class is registered, and HID mouse and keyboard clients are registered, the keyboard and mouse client instances should be recognized.

it would be sufficient if usbx supported boot interface.. I was unable to find if usbx supports it or not.. rii-x1-combo-kbd-mse.txt

sw2mrc commented 11 months ago

@xiaocq2001 any updates on the response i posted couple weeks ago? thank you.

xiaocq2001 commented 11 months ago

Sorry for late reply.

There are configuration descriptors in attached file, but do you have HID report descriptor? The contents in report descriptor are used to recognize clients and define report layouts.

For "boot interface" do you mean boot protocol devices? Unfortunately boot protocol is not enabled by default, and there is no API to switch to boot protocol for now.

sw2mrc commented 11 months ago

HID report descriptor captured from wireshark attached.

For "boot interface" do you mean boot protocol devices? Unfortunately boot protocol is not enabled by default, and there is no API to switch to boot protocol for now.

Is there a plan for it to be enabled? it would be a very useful feature if usbx supported it..

hid-report-desc-rii-combo-kbd-mse.txt

xiaocq2001 commented 11 months ago

I checked the stack by simulate composite device, there seems no problem on the two interfaces operations. Maybe you can check if there is issue on STM HAL or DCD part on enabling two interrupt pipes.

sw2mrc commented 11 months ago

I checked the stack by simulate composite device, there seems no problem on the two interfaces operations. Maybe you can check if there is issue on STM HAL or DCD part on enabling two interrupt pipes.

Thank you for the confirmation.

  1. Could you please share the test stub so i can see if there are any issues from my side before I reach out to ST?
  2. If sharing code is not possible, could you confirm if both input reports from mouse and keyboard need to be processed from a single thread or can they be in two separate threads? My code processes input reports from two separate threads and since it is just one device instance but two interfaces, i was wondering if there is a data race issue elsewhere as two threads in my code use mutex

Regards..

xiaocq2001 commented 11 months ago

Testing code is as following, but I'm afraid you can not use that directly, since it requires simulator implement.

/* This test is designed to test simple usage of the composite (mouse + keyboard) class.  */

#include "ux_api.h"
#include "ux_device_class_hid.h"
#include "ux_host_class_hid.h"

#include "ux_host_class_hid_keyboard.h"
#include "ux_host_class_hid_mouse.h"

#include "ux_test_utility_sim.h"
#include "ux_test_hcd_sim_host.h"

#define     UX_DEMO_STACK_SIZE  1024
#define     UX_DEMO_MEMORY_SIZE (96*1024)

#define     ARRAY_COUNT(array)  (sizeof(array)/sizeof(array[0]))
#define     LSB(x)              (x & 0xff)
#define     MSB(x)              ((x & 0xff00) >> 8)

static void tx_demo_thread_host_simulation_entry(ULONG);
static void tx_demo_thread_device_simulation_entry(ULONG);

static UINT demo_thread_hid_keyboard_callback(UX_SLAVE_CLASS_HID *, UX_SLAVE_CLASS_HID_EVENT *);
static UINT demo_thread_hid_mouse_callback(UX_SLAVE_CLASS_HID *, UX_SLAVE_CLASS_HID_EVENT *);

static UCHAR                        usbx_memory[UX_DEMO_MEMORY_SIZE + (UX_DEMO_STACK_SIZE * 2)];
static TX_THREAD                    tx_demo_thread_host_simulation;
static TX_THREAD                    tx_demo_thread_device_simulation;

static UX_HOST_CLASS_HID_KEYBOARD   *hid_keyboard;
static UX_HOST_CLASS_HID_MOUSE      *hid_mouse;

static UX_SLAVE_CLASS_HID_PARAMETER hid_parameter;

TX_EVENT_FLAGS_GROUP                ev_flags;
#define EV_KEYBOARD_RECEIVED        0x01u
#define EV_MOUSE_RECEIVED           0x02u

/* REF: Composite Keyboard and Touchpad device (Rii x1)  */

static UCHAR hid_keyboard_report[] = {
    0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
    0x09, 0x06,        // Usage (Keyboard)
    0xA1, 0x01,        // Collection (Application)
    0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
    0x19, 0xE0,        //   Usage Minimum (0xE0)
    0x29, 0xE7,        //   Usage Maximum (0xE7)
    0x15, 0x00,        //   Logical Minimum (0)
    0x25, 0x01,        //   Logical Maximum (1)
    0x75, 0x01,        //   Report Size (1)
    0x95, 0x08,        //   Report Count (8)
    0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x95, 0x01,        //   Report Count (1)
    0x75, 0x08,        //   Report Size (8)
    0x81, 0x03,        //   Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x95, 0x05,        //   Report Count (5)
    0x75, 0x01,        //   Report Size (1)
    0x05, 0x08,        //   Usage Page (LEDs)
    0x19, 0x01,        //   Usage Minimum (Num Lock)
    0x29, 0x05,        //   Usage Maximum (Kana)
    0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x95, 0x01,        //   Report Count (1)
    0x75, 0x03,        //   Report Size (3)
    0x91, 0x03,        //   Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
    0x95, 0x06,        //   Report Count (6)
    0x75, 0x08,        //   Report Size (8)
    0x15, 0x00,        //   Logical Minimum (0)
    0x26, 0xFF, 0x00,  //   Logical Maximum (255)
    0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
    0x19, 0x00,        //   Usage Minimum (0x00)
    0x29, 0xFF,        //   Usage Maximum (0xFF)
    0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,              // End Collection
};
#define HID_KEYBOARD_REPORT_LENGTH (sizeof(hid_keyboard_report)/sizeof(hid_keyboard_report[0]))
static UCHAR hid_mouse_report[] = {
    0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
    0x09, 0x02,        // Usage (Mouse)
    0xA1, 0x01,        // Collection (Application)
    0x85, 0x01,        //   Report ID (1)
    0x09, 0x01,        //   Usage (Pointer)
    0xA1, 0x00,        //   Collection (Physical)
    0x05, 0x09,        //     Usage Page (Button)
    0x19, 0x01,        //     Usage Minimum (0x01)
    0x29, 0x05,        //     Usage Maximum (0x05)
    0x15, 0x00,        //     Logical Minimum (0)
    0x25, 0x01,        //     Logical Maximum (1)
    0x95, 0x05,        //     Report Count (5)
    0x75, 0x01,        //     Report Size (1)
    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x95, 0x01,        //     Report Count (1)
    0x75, 0x03,        //     Report Size (3)
    0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
    0x09, 0x30,        //     Usage (X)
    0x09, 0x31,        //     Usage (Y)
    0x09, 0x38,        //     Usage (Wheel)
    0x15, 0x81,        //     Logical Minimum (-127)
    0x25, 0x7F,        //     Logical Maximum (127)
    0x75, 0x08,        //     Report Size (8)
    0x95, 0x03,        //     Report Count (3)
    0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
    0x95, 0x03,        //     Report Count (3)
    0x75, 0x08,        //     Report Size (8)
    0x81, 0x01,        //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,              //   End Collection
    0xC0,              // End Collection
    0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
    0x09, 0x80,        // Usage (Sys Control)
    0xA1, 0x01,        // Collection (Application)
    0x85, 0x02,        //   Report ID (2)
    0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
    0x19, 0x81,        //   Usage Minimum (Sys Power Down)
    0x29, 0x83,        //   Usage Maximum (Sys Wake Up)
    0x15, 0x00,        //   Logical Minimum (0)
    0x25, 0x01,        //   Logical Maximum (1)
    0x95, 0x03,        //   Report Count (3)
    0x75, 0x01,        //   Report Size (1)
    0x81, 0x06,        //   Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
    0x95, 0x01,        //   Report Count (1)
    0x75, 0x05,        //   Report Size (5)
    0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,              // End Collection
    0x05, 0x0C,        // Usage Page (Consumer)
    0x09, 0x01,        // Usage (Consumer Control)
    0xA1, 0x01,        // Collection (Application)
    0x85, 0x03,        //   Report ID (3)
    0x19, 0x00,        //   Usage Minimum (Unassigned)
    0x2A, 0x3C, 0x02,  //   Usage Maximum (AC Format)
    0x15, 0x00,        //   Logical Minimum (0)
    0x26, 0x3C, 0x02,  //   Logical Maximum (572)
    0x95, 0x01,        //   Report Count (1)
    0x75, 0x10,        //   Report Size (16)
    0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
    0xC0,              // End Collection
};
#define HID_MOUSE_REPORT_LENGTH (sizeof(hid_mouse_report)/sizeof(hid_mouse_report[0]))
static UCHAR device_framework_full_speed[] = {

    /* Device descriptor */
        0x12, 0x01,
        0x00, 0x02,         /* bcdUSB */
        0x00, 0x00, 0x00,   /* Class/Sub/Protocol */
        0x40,               /* bMaxPacketSize0 */
        0x97, 0x19,         /* idVendor */
        0x33, 0x24,         /* idProduct */
        0x06, 0x01,         /* bcdDevice */
        0x00, 0x00, 0x00,   /* iManufacturer/Product/SerialNumber */
        0x01,

    /* Configuration descriptor */
        0x09, 0x02,
        0x3B, 0x00,         /* wTotalLength */
        0x02,               /* bNumInterfaces */
        0x01,               /* bConfigurationValue */
        0x00,               /* iConfiguration */
        0xA0,               /* bmAttributes */
        0x32,               /* MaxPower */

    /* Interface descriptor */
        0x09, 0x04,
        0x00, 0x00,         /* bInterfaceNumber/bAlternateSetting */
        0x01,               /* bNumEndpoints */
        0x03, 0x01, 0x01,   /* Class/Sub/Protocol(1-keyboard) */
        0x00,               /* iInterface */

    /* HID descriptor */
        0x09, 0x21,
        0x11, 0x01,         /* bcdHID */
        0x00,               /* bCountryCode */
        0x01,               /* bNumDescriptors */
        0x22, LSB(HID_KEYBOARD_REPORT_LENGTH), MSB(HID_KEYBOARD_REPORT_LENGTH),

    /* Endpoint descriptor (Interrupt) */
        0x07, 0x05,
        0x81,               /* bEndpointAddress */
        0x03,               /* bmAttributes */
        0x00, 0x40,         /* wMaxPacketSize */
        0x0A,               /* bInterval */

    /* Interface descriptor */
        0x09, 0x04,
        0x01, 0x00,         /* bInterfaceNumber/bAlternateSetting */
        0x01,               /* bNumEndpoints */
        0x03, 0x01, 0x02,   /* Class/Sub/Protocol(2-mouse) */
        0x00,               /* iInterface */

    /* HID descriptor */
        0x09, 0x21,
        0x11, 0x01,         /* bcdHID */
        0x00,               /* bCountryCode */
        0x01,               /* bNumDescriptors */
        0x22, LSB(HID_MOUSE_REPORT_LENGTH), MSB(HID_MOUSE_REPORT_LENGTH),

    /* Endpoint descriptor (Interrupt) */
        0x07, 0x05,
        0x82,               /* bEndpointAddress */
        0x03,               /* bmAttributes */
        0x00, 0x40,         /* wMaxPacketSize */
        0x01,               /* bInterval */
    };
#define DEVICE_FRAMEWORK_LENGTH_FULL_SPEED  sizeof(device_framework_full_speed)
#define device_framework_high_speed         UX_NULL
#define DEVICE_FRAMEWORK_LENGTH_HIGH_SPEED  0

    /* String Device Framework :
     Byte 0 and 1 : Word containing the language ID : 0x0904 for US
     Byte 2       : Byte containing the index of the descriptor
     Byte 3       : Byte containing the length of the descriptor string
    */

#define STRING_FRAMEWORK_LENGTH 40
static UCHAR string_framework[] = {

    /* Manufacturer string descriptor : Index 1 */
        0x09, 0x04, 0x01, 0x0c,
        0x45, 0x78, 0x70, 0x72,0x65, 0x73, 0x20, 0x4c,
        0x6f, 0x67, 0x69, 0x63,

    /* Product string descriptor : Index 2 */
        0x09, 0x04, 0x02, 0x0c,
        0x55, 0x53, 0x42, 0x20, 0x4b, 0x65, 0x79, 0x62,
        0x6f, 0x61, 0x72, 0x64,

    /* Serial Number string descriptor : Index 3 */
        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. */
#define LANGUAGE_ID_FRAMEWORK_LENGTH 2
static UCHAR language_id_framework[] = {

    /* English. */
        0x09, 0x04
    };

static VOID error_callback(UINT system_level, UINT system_context, UINT error_code)
{

    /* Failed test.  */
    printf("Error on line %d, system_level: %d, system_context: %d, error code: %d\n", __LINE__, system_level, system_context, error_code);
    test_control_return(1);
}

/* Define what the initial system looks like.  */

#ifdef CTEST
void test_application_define(void *first_unused_memory)
#else
void usbx_hid_composite_basic_test_application_define(void *first_unused_memory)
#endif
{

UINT status;
CHAR *stack_pointer;
CHAR *memory_pointer;

    /* Inform user.  */
    printf("Running HID Composite (Rii) Basic Functionality Test................ ");
#if !UX_TEST_MULTI_IFC_ON                                                   || \
    (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH < 116)
    printf("SKIP SUCCESS!\n");
    test_control_return(0);
    return;
#endif

    /* Initialize the free memory pointer */
    stack_pointer = (CHAR *) usbx_memory;
    memory_pointer = stack_pointer + (UX_DEMO_STACK_SIZE * 2);

    /* Initialize USBX. Memory */
    status = ux_system_initialize(memory_pointer, UX_DEMO_MEMORY_SIZE, UX_NULL,0);

    /* Check for error.  */
    if (status != UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* Register the error callback. */
    _ux_utility_error_callback_register(error_callback);

    /* The code below is required for installing the host portion of USBX */
    status =  ux_host_stack_initialize(UX_NULL);
    if (status != UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    status =  ux_host_stack_class_register(_ux_system_host_class_hid_name, ux_host_class_hid_entry);
    if (status != UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* Register the HID client(s).  */
    status = ux_host_class_hid_client_register(_ux_system_host_class_hid_client_mouse_name, ux_host_class_hid_mouse_entry);
    status = ux_host_class_hid_client_register(_ux_system_host_class_hid_client_keyboard_name, ux_host_class_hid_keyboard_entry);
    if (status != UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* The code below is required for installing the device portion of USBX. No call back for
       device status change in this example. */
    status =  ux_device_stack_initialize(device_framework_high_speed, DEVICE_FRAMEWORK_LENGTH_HIGH_SPEED,
                                       device_framework_full_speed, DEVICE_FRAMEWORK_LENGTH_FULL_SPEED,
                                       string_framework, STRING_FRAMEWORK_LENGTH,
                                       language_id_framework, LANGUAGE_ID_FRAMEWORK_LENGTH,UX_NULL);
    if(status!=UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* Initialize the hid class parameters for a keyboard.  */
    hid_parameter.ux_device_class_hid_parameter_report_address = hid_keyboard_report;
    hid_parameter.ux_device_class_hid_parameter_report_length  = HID_KEYBOARD_REPORT_LENGTH;
    hid_parameter.ux_device_class_hid_parameter_callback       = demo_thread_hid_keyboard_callback;
    /* Initilize the device hid class. The class is connected with interface 0 */
    status =  ux_device_stack_class_register(_ux_system_slave_class_hid_name, ux_device_class_hid_entry,
                                             1, 0, (VOID *)&hid_parameter);

    /* Initialize the hid class parameters for a mouse.  */
    hid_parameter.ux_device_class_hid_parameter_report_address = hid_mouse_report;
    hid_parameter.ux_device_class_hid_parameter_report_length  = HID_MOUSE_REPORT_LENGTH;
    hid_parameter.ux_device_class_hid_parameter_callback       = demo_thread_hid_mouse_callback;
    /* Initilize the device hid class. The class is connected with interface 1 */
    status |= ux_device_stack_class_register(_ux_system_slave_class_hid_name, ux_device_class_hid_entry,
                                             1, 1, (VOID *)&hid_parameter);

    /* Check status.  */
    if(status!=UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* Initialize the simulated device controller.  */
    status =  _ux_dcd_sim_slave_initialize();

    /* Check for error.  */
    if (status != UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* Register all the USB host controllers available in this system */
    status =  ux_host_stack_hcd_register(_ux_system_host_hcd_simulator_name, ux_hcd_sim_host_initialize,0,0);

    /* Check for error.  */
    if (status != UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* Create event flags group.  */
    status = tx_event_flags_create(&ev_flags, "test flags");
    UX_TEST_CHECK_SUCCESS(status);

    /* Create the main host simulation thread.  */
    status =  tx_thread_create(&tx_demo_thread_host_simulation, "tx demo host simulation", tx_demo_thread_host_simulation_entry, 0,
            stack_pointer, UX_DEMO_STACK_SIZE,
            20, 20, 1, TX_AUTO_START);

    /* Check for error.  */
    if (status != TX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* Create the main demo thread.  */
    status =  tx_thread_create(&tx_demo_thread_device_simulation, "tx demo slave simulation", tx_demo_thread_device_simulation_entry, 0,
            stack_pointer + UX_DEMO_STACK_SIZE, UX_DEMO_STACK_SIZE,
            20, 20, 1, TX_AUTO_START);

    /* Check for error.  */
    if (status != TX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

}

static UINT  sleep_break_on_removed(VOID)
{

UINT                status;
UX_HOST_CLASS       *class_inst;
UX_HOST_CLASS_HID   *hid_inst;

    /* Find the main HID container. */
    status =  ux_host_stack_class_get(_ux_system_host_class_hid_name, &class_inst);
    if (status == UX_SUCCESS)
    {

        /* Find the instance. */
        status =  ux_host_stack_class_instance_get(class_inst, 0, (void **) &hid_inst);
        if (status != UX_SUCCESS)
            return UX_TRUE;
    }

    return UX_FALSE;
}

static UINT demo_class_hids_get(void)
{

UINT                        status;
UX_HOST_CLASS               *class_inst;
UX_HOST_CLASS_HID           *hid_inst;
UX_HOST_CLASS_HID_CLIENT    *hid_client_inst;

    /* Wait for enum thread to complete. */
    ux_test_wait_for_enum_thread_completion();

    /* Find the main HID container */
    status = ux_host_stack_class_get(_ux_system_host_class_hid_name, &class_inst);
    if (status != UX_SUCCESS)
        return(status);

    /* We get the first instance of the hid device */
    status = ux_host_stack_class_instance_get(class_inst, 0, (void **)&hid_inst);
    if (status != UX_SUCCESS)
        return(status);
    if(hid_inst -> ux_host_class_hid_state != UX_HOST_CLASS_INSTANCE_LIVE)
        return(UX_ERROR);
    /* Get client.  */
    hid_client_inst = hid_inst -> ux_host_class_hid_client;
    /* Get keyboard.  */
    hid_keyboard = (UX_HOST_CLASS_HID_KEYBOARD*)hid_client_inst -> ux_host_class_hid_client_local_instance;

    /* We get the 2nd instance of the hid device */
    status = ux_host_stack_class_instance_get(class_inst, 1, (void **)&hid_inst);
    if (status != UX_SUCCESS)
        return(status);
    if(hid_inst -> ux_host_class_hid_state != UX_HOST_CLASS_INSTANCE_LIVE)
        return(UX_ERROR);
    /* Get client.  */
    hid_client_inst = hid_inst -> ux_host_class_hid_client;
    /* Get mouse.  */
    hid_mouse = (UX_HOST_CLASS_HID_MOUSE*)hid_client_inst -> ux_host_class_hid_client_local_instance;

    return(UX_SUCCESS);
}

static void  tx_demo_thread_host_simulation_entry(ULONG arg)
{

UINT    status;
UINT    max_num_loops;
UINT    num_loops;
ULONG   num_attempts;
ULONG   max_attempts;
SLONG   cur_mouse_wheel_movement;
SLONG   next_mouse_wheel_movement;
SLONG   cur_mouse_x_position;
SLONG   cur_mouse_y_position;
SLONG   next_mouse_x_position;
SLONG   next_mouse_y_position;
ULONG   cur_mouse_buttons;
UCHAR   next_mouse_buttons;
ULONG   key_value, key_state, next_key;

    /* Initilize max loop value.  */
    max_num_loops = 16;

    /* Find the HID class */
    status =  demo_class_hids_get();
    if (status != UX_SUCCESS)
    {

        printf("Error on line %d\n", __LINE__);
        test_control_return(1);
    }

    /* Set number of successful loops to execute. */
    num_loops = 0;
    max_num_loops = 16;

    /* Set number of fails. */
    num_attempts = 0;
    max_attempts = 3*max_num_loops;

    /* Set initial mouse button values. */
    next_mouse_buttons = 0x01;

    /* Set initial mouse position values. */
    next_mouse_x_position = 1;
    next_mouse_y_position = 1;

    /* Set initial mouse wheel value. */
    next_mouse_wheel_movement = 1;

    /* Set initial keyboard key value. */
    next_key = 'a';

    while (num_loops++ != max_num_loops)
    {

        /* Check keyboard.  */
        status = ux_host_class_hid_keyboard_key_get(hid_keyboard, &key_value, &key_state);
        if (status == UX_SUCCESS)
        {
            UX_TEST_ASSERT(key_value == next_key);

            /* Signal to device to continue.  */
            tx_event_flags_set(&ev_flags, EV_KEYBOARD_RECEIVED, TX_OR);

            /* Next key. */
            if (next_key >= 'z')
                next_key = 'a';
            else
                next_key++;
        }

        /* Check mouse.  */
        ux_host_class_hid_mouse_buttons_get(hid_mouse, &cur_mouse_buttons);
        if (cur_mouse_buttons == next_mouse_buttons)
        {
            ux_host_class_hid_mouse_position_get(hid_mouse, &cur_mouse_x_position, &cur_mouse_y_position);
            ux_host_class_hid_mouse_wheel_get(hid_mouse, &cur_mouse_wheel_movement);

            /* Are these the expected values? */
            UX_TEST_ASSERT(cur_mouse_buttons == next_mouse_buttons &&
                        cur_mouse_x_position == next_mouse_x_position &&
                        cur_mouse_y_position == next_mouse_y_position &&
                        cur_mouse_wheel_movement == next_mouse_wheel_movement);

            /* Signal to device to continue.  */
            tx_event_flags_set(&ev_flags, EV_MOUSE_RECEIVED, TX_OR);

            /* Increment values. */
            next_mouse_buttons = 0x07 & (next_mouse_buttons + 1);
            next_mouse_x_position++;
            next_mouse_y_position++;
            next_mouse_wheel_movement++;
        }
        tx_thread_sleep(10);
    }

    /* Simulate detach. */
    ux_test_hcd_sim_host_disconnect();
    ux_test_breakable_sleep(50, sleep_break_on_removed);

    /* Now disconnect the device.  */
    _ux_device_stack_disconnect();

    /* And deinitialize the class.  */
    status =  ux_device_stack_class_unregister(_ux_system_slave_class_hid_name, ux_device_class_hid_entry);

    /* Deinitialize the device side of usbx.  */
    _ux_device_stack_uninitialize();

    /* And finally the usbx system resources.  */
    _ux_system_uninitialize();

    /* Successful test.  */
    printf("SUCCESS!\n");
    test_control_return(0);
}

static UINT demo_thread_hid_keyboard_callback(UX_SLAVE_CLASS_HID *class, UX_SLAVE_CLASS_HID_EVENT *event)
{
    return(UX_SUCCESS);
}
static UINT demo_thread_hid_mouse_callback(UX_SLAVE_CLASS_HID *class, UX_SLAVE_CLASS_HID_EVENT *event)
{
    return(UX_SUCCESS);
}

static void  tx_demo_thread_device_simulation_entry(ULONG arg)
{

UX_SLAVE_DEVICE                 *device;
UX_SLAVE_INTERFACE              *interface;
UX_SLAVE_CLASS_HID              *hid;
UX_SLAVE_CLASS_HID_EVENT        hid_event;
ULONG                           flags;
UCHAR                           mouse_buttons;
UCHAR                           key;

    /* Get the pointer to the device.  */
    device =  &_ux_system_slave -> ux_system_slave_device;

    while (1)
    {

        /* Is the device configured ? */
        while (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
        {
#if defined(UX_DEVICE_STANDALONE)
            ux_system_tasks_run();
            tx_thread_relinquish();
#else
            /* Then wait.  */
            tx_thread_sleep(10);
#endif
        }
        flags = EV_KEYBOARD_RECEIVED | EV_MOUSE_RECEIVED;
        mouse_buttons = 1;
        key = 0x04;

        /* Until the device stays configured.  */
        while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED)
        {
#if defined(UX_DEVICE_STANDALONE)
            ux_system_tasks_run();
            tx_thread_relinquish();
#endif

            /* Get the interface.  We use the first interface, this is a simple device.  */
            interface =  device -> ux_slave_device_first_interface;

            if (flags & EV_KEYBOARD_RECEIVED)
            {
                /* IFC0 for keyboard.  */
                hid = interface -> ux_slave_interface_class_instance;

                /* There is no report ID.  */

                /* Then insert a key into the keyboard event.  Length is fixed to 8.  */
                hid_event.ux_device_class_hid_event_length = 8;
                ux_utility_memory_set(hid_event.ux_device_class_hid_event_buffer, 0, 8);
                /* First byte is a modifier byte.  */
                /* Second byte is reserved. */
                /* The 6 next bytes are keys. We only have one key here.  */
                hid_event.ux_device_class_hid_event_buffer[2] = key;
                /* Set the keyboard event.  */
                ux_device_class_hid_event_set(hid, &hid_event);

                /* Next event has the key depressed.  */
                hid_event.ux_device_class_hid_event_buffer[2] = 0;
                /* Set the keyboard event.  */
                ux_device_class_hid_event_set(hid, &hid_event);

                /* Are we at the end of alphabet ?  */
                if (key != (0x04 + 26))
                    key++;
                else
                    key = 0x04;
            }
            if (flags & EV_MOUSE_RECEIVED)
            {
                /* IFC1 for mouse.  */
                interface = interface -> ux_slave_interface_next_interface;
                hid = interface -> ux_slave_interface_class_instance;

                /* Reset event buffer, 8 bytes.  */
                hid_event.ux_device_class_hid_event_length = 8;
                ux_utility_memory_set(hid_event.ux_device_class_hid_event_buffer, 0, 8);
                /* Report ID.  */
                hid_event.ux_device_class_hid_event_buffer[0] = 1;
                /* Change the buttons.  */
                hid_event.ux_device_class_hid_event_buffer[1] = mouse_buttons ++;
                /* Change the x, y, and wheel positions. These are relative values.  */
                hid_event.ux_device_class_hid_event_buffer[2] = 1;
                hid_event.ux_device_class_hid_event_buffer[3] = 1;
                hid_event.ux_device_class_hid_event_buffer[4] = 1;

                /* Set the mouse event.  */
                ux_device_class_hid_event_set(hid, &hid_event);
            }

            /* Wait for host to receive.  */
            tx_event_flags_get(&ev_flags, EV_MOUSE_RECEIVED | EV_KEYBOARD_RECEIVED,
                                        TX_OR_CLEAR, &flags, TX_WAIT_FOREVER);

        }
    }
}

The APIs to get input reports from mouse and keyboard are actually not blocking, so they can be processed in single thread (in my test code ux_host_class_hid_keyboard_key_get is used to check if there is key ready in key queue, ux_host_class_hid_mouse_buttons_get, ux_host_class_hid_mouse_position_get and ux_host_class_hid_mouse_wheel_get are used to get current state for the mouse).

sw2mrc commented 11 months ago

@xiaocq2001 thanks for the details. I will update this thread (or close the issue) in a week. cheers.