Problem between '_ux_device_class_hid_receiver_thread(ULONG hid_instance)' and CDC ACM application

明泉 张 20 Reputation points
2023-09-27T02:33:54.1166667+00:00

Platform: STM32U575AIIQ-EV Board

Modules: USBX, ThreadX, HID Custom, CDC ACM Virtual Com

Purpose: Using stm32u575 as a device to report data to host(PC), receive data from host, and print logs as a virtual COM.

Summary: The functions of reporting data to host, receiving data from host and print logs as a virtual COM have been realized. But if i send a packet from host to device, the virtual COM is killed.

Analysis:

  1. The following code is the function of CDC ACM activation:
VOID USBD_CDC_ACM_Activate(VOID *cdc_acm_instance)
{
  /* USER CODE BEGIN USBD_CDC_ACM_Activate */

  /* Save the CDC instance */
  cdc_acm_user = (UX_SLAVE_CLASS_CDC_ACM*) cdc_acm_instance;

  /* Configure the UART peripheral */
  USBX_APP_UART_Init(&uart_handler);

  /* Get default UART parameters */
  CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_baudrate = uart_handler->Init.BaudRate;

  /* Set the UART data type : only 8bits and 9bits are supported */
  switch (uart_handler->Init.WordLength)
  {
    case UART_WORDLENGTH_8B:
    {
      /* Set UART data bit to 8 */
      CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_data_bit = VCP_WORDLENGTH8;
      break;
    }

    case UART_WORDLENGTH_9B:
    {
      /* Set UART data bit to 9 */
      CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_data_bit = VCP_WORDLENGTH9;
      break;
    }

    default :
    {
      /* By default set UART data bit to 8 */
      CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_data_bit = VCP_WORDLENGTH8;
      break;
    }
  }

  /* Get UART Parity */
  CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_parity = uart_handler->Init.Parity;

  /* Get UART StopBits */
  CDC_VCP_LineCoding.ux_slave_class_cdc_acm_parameter_stop_bit = uart_handler->Init.StopBits;

  /* Set device class_cdc_acm with default parameters */
  if (ux_device_class_cdc_acm_ioctl(cdc_acm_user, UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING,
                                    &CDC_VCP_LineCoding) != UX_SUCCESS)
  {
    Error_Handler();
  }

  /* Receive an amount of data in interrupt mode */
  if (HAL_UART_Receive_IT(uart_handler, (uint8_t *)UserTxBufferFS, 1) != HAL_OK)
  {
    /* Transfer error in reception process */
    Error_Handler();
  }

  /* USER CODE END USBD_CDC_ACM_Activate */

  return;
}

I created 'cdc_acm_user' as the CDC_ACM instance which is the input in other APIs. And using the MDK to observe the program, its memory address is 0x20004AA0.

  1. When host sends a packet to device, after the interrupt handle function the device will enter the thread of '_ux_device_calss_hid_receiver_thread(ULONG hid_instance)', as is shown in following code:
VOID  _ux_device_class_hid_receiver_thread(ULONG hid_instance)
{

UX_SLAVE_CLASS_HID                  *hid;
UX_SLAVE_DEVICE                     *device;
UX_DEVICE_CLASS_HID_RECEIVER        *receiver;
UX_DEVICE_CLASS_HID_RECEIVED_EVENT  *pos;
UCHAR                               *next_pos;
UX_SLAVE_TRANSFER                   *transfer;
UINT                                status;
UCHAR                               *buffer;
ULONG                               temp;

    /* Cast properly the hid instance.  */
    UX_THREAD_EXTENSION_PTR_GET(hid, UX_SLAVE_CLASS_HID, hid_instance)

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

    /* Get receiver instance.  */
    receiver = hid -> ux_device_class_hid_receiver;

    /* This thread runs forever but can be suspended or resumed.  */
    while(1)
    {

        /* Check device state.  */
        if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
        {

            /* We need to suspend ourselves. We will be resumed by the device enumeration module.  */
            _ux_utility_thread_suspend(&receiver -> ux_device_class_hid_receiver_thread);
            continue;
        }

        /* Check if there is buffer available.  */
        pos = receiver -> ux_device_class_hid_receiver_event_save_pos;
        if (pos -> ux_device_class_hid_received_event_length != 0)
        {

            /* Wait before check again.  */
            status = _ux_utility_event_flags_get(
                                &hid -> ux_device_class_hid_event_flags_group,
                                UX_DEVICE_CLASS_HID_RECEIVER_RESTART,
                                UX_OR_CLEAR, &temp, 100);
            if (status != UX_SUCCESS)
            {

                /* Keep checking before a good state.  */
                continue;
            }
        }

        /* Event buffer available, issue request to get data.  */
        transfer = &hid -> ux_device_class_hid_read_endpoint -> ux_slave_endpoint_transfer_request;

        /* Protect read.  */
        _ux_device_mutex_on(&hid -> ux_device_class_hid_read_mutex);

        /* Issue the transfer request.  */
        status = _ux_device_stack_transfer_request(transfer, 
                    receiver -> ux_device_class_hid_receiver_event_buffer_size,
                    receiver -> ux_device_class_hid_receiver_event_buffer_size);

        /* Check status and ignore ZLPs.  */
        if ((status != UX_SUCCESS) ||
            (transfer -> ux_slave_transfer_request_actual_length == 0))
        {
            _ux_device_mutex_off(&hid -> ux_device_class_hid_read_mutex);
            continue;
        }

        /* Save received event data and length.  */
        buffer = (UCHAR *)&pos -> ux_device_class_hid_received_event_data;
        temp = transfer -> ux_slave_transfer_request_actual_length;
        _ux_utility_memory_copy(buffer,
                        transfer -> ux_slave_transfer_request_data_pointer,
                        temp); /* Use case of memcpy is verified. */

        /* Unprotect read.  */
        _ux_device_mutex_off(&hid -> ux_device_class_hid_read_mutex);

        /* Advance the save position.  */
        next_pos = (UCHAR *)pos + receiver -> ux_device_class_hid_receiver_event_buffer_size + sizeof(ULONG);
        if (next_pos >= (UCHAR *)receiver -> ux_device_class_hid_receiver_events_end)
            next_pos = (UCHAR *)receiver -> ux_device_class_hid_receiver_events;
        receiver -> ux_device_class_hid_receiver_event_save_pos = (UX_DEVICE_CLASS_HID_RECEIVED_EVENT *)next_pos;

        /* Save received data length (it's valid now).  */
        pos -> ux_device_class_hid_received_event_length = temp;

        /* Notify application that a event is received.  */
        if (receiver -> ux_device_class_hid_receiver_event_callback)
            receiver -> ux_device_class_hid_receiver_event_callback(hid);
    }
}

In the part of '/* Save received event data and length. */', the address of 'buffer' is 0x20004A90. Therefore, after the execution of '_ux_utility_memory_copy( , , )', the memory of 'cdc_acm_user' is modified at the same time. That's why the CDC_ACM Virtual Com cannot work anymore.

My question is how to solve this problem to avoid the case of memory being modified.

Azure RTOS
Azure RTOS
An Azure embedded development suite including a small but powerful operating system for resource-constrained devices.
333 questions
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.