Code Monkey home page Code Monkey logo

usbdevice's Introduction

Composite USB Device library

This project implements a platform-independent, highly flexible USB Device software framework, which allows you to create a full-feature USB 2.0 device firmware with multiple independent interfaces.

Features

  • Effective compliance to USB 2.0 specification
  • Interfaces are independent of the device and can be added or removed in runtime
  • Interface classes support multiple instantiation
  • All USB descriptors are created internally (no need for user definition)
  • Code size optimized for resource-constrained systems
  • Platform-independent stack
  • A console interface template provides zero-effort implementation for standard I/O through a CDC serial port

Supported device classes

  • Communications Device Class (CDC - ACM) specification version 1.10
  • Network Control Model (CDC - NCM) specification version 1.0
  • Human Interface Device Class (HID) specification version 1.11 - with helper macros for report definition
  • Mass Storage Class Bulk-Only Transport (MSC - BOT) revision 1.0 with transparent SCSI command set
  • Device Firmware Upgrade Class (DFU) specification version 1.1 (or DFU STMicroelectronics Extension (DFUSE) 1.1A using USBD_DFU_ST_EXTENSION compile switch)

Contents

The project consists of the followings:

  • The USB 2.0 device framework is located in the Device folder.
  • Common USB classes are implemented as part of the project, under the Class folder.
  • The Templates folder contains usbd_config.h configuration file and various example files.
  • The Doc folder contains a prepared doxyfile for Doxygen documentation generation.

Platform support

Currently the following hardware platforms are supported:

Basis of operation

The interface implementations are completely separated from the USB device control. Each of them should use its class-specific API from usbd_<class>.h. There are only two steps to mount an interface to a device:

  1. Setting the interface's endpoint addresses;
  2. Cross-referencing the interface and the device with a USBD_<CLASS>_MountInterface() call.

The interfaces are added to the device configuration in the order of the mount calls. It is possible to change the active interfaces during runtime by unmounting all and mounting the new ones. The only requirement is that the device has to be logically disconnected from the host when it is done.

The device control of the library is limited to the global state management using the public API in usbd.h. The bulk of the device operation is servicing the device peripheral events:

  • USB Reset signal on bus -> USBD_ResetCallback()
  • USB control pipe setup request received -> USBD_SetupCallback()
  • USB endpoint data transfer completed -> USBD_EpInCallback() or USBD_EpOutCallback()

The USBD handles are used as a shared management structure for both this stack and the peripheral driver. Any additional fields that the peripheral driver requires can be defined in the driver-specific usbd_pd_def.h header, while the usbd_types.h shall be included by the driver.

Example Projects

A virtual network with a single lwIP server (DNS, DHCP, HTTP) is presented by the device (as a network adapter). Composite USB device demonstrating the CDC-NCM function usage and a reduced DFU interface to enter ROM bootloader.

A generic USB device bootloader firmware for STM32 controllers. USB device with a single DFU interface which is mountable on both the bootloader's and the application's device stack.

A debug serial port with selectable output power and battery charging. Composite USB device with one CDC (serial port), two HID interfaces (onboard sensors and power management) and the bootloader's DFU interface.

A CAN bus gateway which uses a custom protocol over a USB serial port emulation. Composite USB device with CDC-ACM function and the bootloader's DFU interface.

How to contribute

This project is free to use for everyone as long as the license terms are met. Any found defects or suggestions should be reported as a GitHub issue. Improvements in the form of pull requests are also welcome.

Authors

usbdevice's People

Contributors

benedekkupper avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

usbdevice's Issues

CDC and HID don't seem to coexist properly (STM32F072)

I got all my CDC issues fixed, so now I am moving on to the HID portion of my code. I have the hid device defined (it is a simple 2 axis joystick, the report desc is 57 bytes), but when I mount it and the CDC interface Linux can't properly detect the HID interface. If I comment out the mounting of the CDC interface the HID interface gets detected fine (I've not tried to send data yet).

Any suggestions on what I may have done wrong? I can post a gist of the HID code if you need, it is fairly short.

Unfortunately on this hardware I don't have access to the JTAG/SWD interface so I can't attach a debugger. :(

Linux refuses to mount CDC/VCP Device

First, thank you for such a beautifully factored and clean library. I can't express how grateful I am for these resources!

I have an STM32L412Cx as a USB device that uses a VCP/CDC interface to receive commands from a connected tty (or more commonly, a custom .NET application we're also authoring).

Some of these commands cause the device to take some action (e.g., transmit an IR code), while others will cause the device to present itself to a host computer as a keyboard, mouse or joystick.

I've been fighting with the HAL libraries for some weeks before discovering your gorgeous library.

One difference I've noticed, is that with the STMCUBE-generated USB CDC library, Linux recognizes the CDC device and mounts it at /dev/ttyACM0 (or /dev/ttyUSB0, I can't remember which at the moment). But when I use the USBDevice library, this does not happen, even if I use STMicro's Vendor and Product IDs.

The current configuration does get picked up by Windows and it is reachable using a PowerShell (or C#) with no issues.

FWIW, here is my usb_device.c file:

#include "usb_device.h"

// CDC console interface
extern USBD_CDC_IfHandleType *const console_if;

// @brief USB device handle
USBD_HandleType hUsbDevice, *const UsbDevice = &hUsbDevice;

const USBD_DescriptionType hdev_cfg = {
    .Vendor = {
        .Name           = "STMicroelectronics",
        .ID             = 0x0483,
    },
    .Product = {
        .Name           = "STM32 Virtual ComPort",
        .ID             = 0x5740,
        .Version.bcd    = 0x0100,
    },
    .Config = {
        .Name           = "STM32 Virtual ComPort config",
        .MaxCurrent_mA  = 100,
        .RemoteWakeup   = 0,
        .SelfPowered    = 0,
    },
}, *const dev_cfg = &hdev_cfg;

void UsbDevice_Init(void) {
        // All fields of Config have to be properly set up
        console_if->Config.InEpNum  = 0x81;
        console_if->Config.OutEpNum = 0x01;
        console_if->Config.NotEpNum = 0x82;
        // I've seen this value elsewhere, not sure which is correct?
        // console_if->Config.NotEpNum = 0x8F;

        // Mount the CDC interface
        USBD_CDC_MountInterface(console_if, UsbDevice);

        // Initialize the device
        USBD_Init(UsbDevice, dev_cfg);

        // After charger detection the device connection can be made
        USBD_Connect(UsbDevice);
}

/**
 * @brief Shuts down the USB peripheral.
 */
void UsbDevice_Deinit(void)
{
    USBD_Deinit(UsbDevice);
}

Here is my main.c file:

#include "real_main.h"

void SystemClock_Config(void);

uint8_t incr = 0;
char message[25];

int main(void) {
        HAL_Init();
        SystemClock_Config();
        __HAL_RCC_PWR_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        HAL_PWREx_EnableVddUSB();
        HAL_USBD_Setup();
        UsbDevice_Init();

        while (1);
}

void SystemClock_Config(void) {
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;

  RCC_OscInitStruct.OscillatorType      = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState            = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSIClockRange       = RCC_MSIRANGE_11;
  RCC_OscInitStruct.PLL.PLLState        = RCC_PLL_OFF;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /* Select MSI output as USB clock source */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_MSI;
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
}

void Error_Handler(void) {
        while(1);
}

#ifdef USE_FULL_ASSERT
void assert_failed(char *file, uint32_t line)
{ 
        printf("Wrong parameters value: file %s on line %d\r\n", file, line);
}
#endif // USE_FULL_ASSERT

Here is what /var/log/syslog report when unplugging/plugging in the USB connector:

Mar 16 13:04:58 beefcake kernel: [71359.855514] usb 3-4.2: USB disconnect, device number 100
Mar 16 13:05:01 beefcake kernel: [71362.382370] usb 3-4.2: new full-speed USB device number 101 using xhci_hcd
Mar 16 13:05:01 beefcake kernel: [71362.514684] usb 3-4.2: New USB device found, idVendor=0483, idProduct=5740, bcdDevice= 1.00
Mar 16 13:05:01 beefcake kernel: [71362.514687] usb 3-4.2: New USB device strings: Mfr=16, Product=32, SerialNumber=0
Mar 16 13:05:01 beefcake kernel: [71362.514688] usb 3-4.2: Product: STM32 Virtual ComPort
Mar 16 13:05:01 beefcake kernel: [71362.514689] usb 3-4.2: Manufacturer: STMicroelectronics
Mar 16 13:05:01 beefcake kernel: [71362.522715] cdc_acm: probe of 3-4.2:1.0 failed with error -22
Mar 16 13:05:01 beefcake mtp-probe: checking bus 3, device 101: "/sys/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:07:00.3/usb3/3-4/3-4.2"
Mar 16 13:05:01 beefcake mtp-probe: bus: 3, device: 101 was not an MTP device
Mar 16 13:05:01 beefcake mtp-probe: checking bus 3, device 101: "/sys/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:07:00.3/usb3/3-4/3-4.2"
Mar 16 13:05:01 beefcake mtp-probe: bus: 3, device: 101 was not an MTP device

I strongly suspect my problem is in the "101 was not an MTP device," but I'm not sure what to do about it, as I'm not trying to use "Media Transfer Protocol."

The device does show up when I run lsusb

$ lsusb | grep STMicro
Bus 003 Device 101: ID 0483:5740 STMicroelectronics Virtual COM Port

Any tips, pointers or feedback would be enormously appreciated.

Build warning in usbd_dfu.c, line 601

../Vendor/USBDevice/Class/DFU/usbd_dfu.c:601:45: error: passing argument 2 of 'USBD_CtrlSendData' discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers]

On line 110 of the same file, simply remove the const portion of the declaration.

Question: PC device using USB

My company is starting to work on a USB printer simulator that will connect to a PC via USB.

The simulator will be hosted in a PC, using Windows 10.

Do you think your library would be appropriate for our project? I mean both technically and legally. This device will be used exclusively by the company for testing our product so we will not make a direct profit from it.

Thanks

Building a dependent project with warnings enabled (-Wall), throws warnings in library

Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c: In function 'SCSI_ReadCapacity10':                                                                                                                                                                
Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c:192:7: warning: unused variable 'cmd' [-Wunused-variable]                                                                                                                                          
     }*cmd = (void*)itf->CBW.CB;                                                                                                                                                                                                              
       ^~~                                                                                                                                                                                                                                    
Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c: In function 'SCSI_StartStopUnit':                                                                                                                                                                 
Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c:396:7: warning: unused variable 'cmd' [-Wunused-variable]                                                                                                                                          
     }*cmd = (void*)itf->CBW.CB;                                                                                                                                                                                                              
       ^~~                                                                                                                                                                                                                                    
Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c: In function 'SCSI_PreventAllowMediumRemoval':                                                                                                                                                     
Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c:420:7: warning: unused variable 'cmd' [-Wunused-variable]                                                                                                                                          
     }*cmd = (void*)itf->CBW.CB;                                                                                                                                                                                                              
       ^~~                                                                                                                                                                                                                                    
Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c: In function 'SCSI_TestUnitReady':                                                                                                                                                                 
Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c:436:7: warning: unused variable 'cmd' [-Wunused-variable]                                                                                                                                          
     }*cmd = (void*)itf->CBW.CB;                                                                                                                                                                                                              
       ^~~                                   

SetReport callback data is shifted

I'm noticing in the SetReport callback, when using Report ID's, the data in dev->CtrlData appears to be shifted by 1 place.

Using report ID 0x44 and sending payload 0x55 0xAA 0x55 0xAA 0x55 0xAA 0x55 0xAA, it shows the following contents for dev->CtrlData in hid_dataStage

dev->CtrlData |  uint8_t [256]
  | [0] | 0x22 '"' | uint8_t
  | [1] | 0x44 'D' | uint8_t
  | [2] | 0x55 'U' | uint8_t
  | [3] | 0xaa 'ª' | uint8_t
  | [4] | 0x55 'U' | uint8_t
  | [5] | 0xaa 'ª' | uint8_t
  | [6] | 0x55 'U' | uint8_t
  | [7] | 0xaa 'ª' | uint8_t
  | [8] | 0x55 'U' | uint8_t
  | [9] | 0xaa 'ª' | uint8_t

CtrlData[0] seems to contain some data perhaps related to setup value?

When not using report id's, the data starts at CtrlData[0].

HID output endpoint is opened with wrong maximum packet size

Looking at usbd_hid.c, line 295:
mps = HID_APP(itf)->Report->Input.MaxSize;
With very high confidence this should be Output.MaxSize as it is used for opening the output endpoint.
Our device is one of those special cases with long output reports and relatively small input reports, so we were bound to discover this one. :)

MSC configuration

Hello,

Could you please help me how to configure an MSC device properly?

I hope you may have something which may be used as example configuration showing basic requirements of device setup.
I think I miss some configuration to define or doing it completely wrong as my MCU generates HardFault.

So, if you may have something which can be used for proper device initialization, I would be happy.

Thank you your help in advance.

Hotplugging results in failed enumeration/invalid device descriptor

I built a device that is sometimes powered through USB and sometimes externally. When powered over USB this issue is never observed, but when the device is already running and then I plug in the USB cable, about 1/3 times Windows fails to enumerate the device with message "invalid device descriptor".
Is there anything special we should do to support hotplugging?

Fix build warning (error) in usbd_msc_scsi.c

C/usbd_msc_scsi.d" -MT"Dist/obj/USBDevice/Class/MSC/usbd_msc_scsi.o" --specs=nano.specs -mthumb -o "Dist/obj/USBDevice/Class/MSC/usbd_msc_scsi.o"                          
Vendor/USBDevice/Class/MSC/usbd_msc_scsi.c:62:35: error: excess elements in array initializer [-Werror]
   62 |     .SupportedPages     = { 0x00, 0x80 },                                    
      |                                   ^~~~                                 
compilation terminated due to -Wfatal-errors. 

Line 62 of usbd_msc_scsi.c initializes the collection with 2 entries, but the declaration on line 42 only supports a single entry.

Please either truncate the number of initialized values, or increase the available entries in the declaration.

Enumeration fails with gcc optimizations enabled

I've adopted this library on a STM32F042 project and ran into an issue where the device will not enumerate on the host because of a problem related to the device descriptor:
Windows has stopped this device because it has reported problems. (Code 43) A request for the USB device descriptor failed.

Tracing the code that is executed, the device only gets one GetDescriptor request with value 0x01 for the device descriptor itself. Config or string descriptors are never requested.

When all gcc optimizations are disabled, the device does enumerate just fine and all my interfaces and endpoints function perfectly. GCC invoked with -O0, -Os, -O2 or -O3 results in proper enumeration. With fewer optimizations -O1 or -Og enumeration fails.

CDC bulk in transfer stalls for specific data lengths

I have noticed in the latest USBDevice library the USB FS CDC IN endpoint seems to freeze when sending a packet of a multiple of 64 bytes: UsbDevice_SEND_CDC(buffer, 64) for example.

My USB knowledge is not the greatest but it seems to be related to this stackoverflow issue: https://stackoverflow.com/questions/41195559/usb-cdc-bulk-in-endpoint-freeze

Following the advice of that thread and sending a zero length packet indeed rectifies the issue on my device (stm32f722 using USBDevice4Cube wrapper library).

Note that I am using Termite 3.4 on the receiving side (100ms polling), and Line coding parameters are 115200 baud, 1 stop bit, 8 data bits, no parity.

Typo: USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION

In Device/usbd_desc.c l.275
case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION:
Shall be renamed into:
case USB_DESC_TYPE_OTHER_SPEED_CONFIG:
To match the USB_DescriptorType enumeration from usb_types.h

HID InData callback

Is there any particular reason why the HID doesn't have an InData callback?

Unicode strings not well supported

As I know they were only added to C18 standard, many embedded compiler way older, like C99,
but would be nice if it was fully C89 for portability.

problem in usb_std.h

USB_STRING_DESC(s)
uint16_t wString[]; 

e.g.
static const struct usb_string_descriptor manuf_desc_en = USB_STRING_DESC("Open source USB stack for STM32");
static const struct usb_string_descriptor prod_desc_en  = USB_STRING_DESC("CDC Loopback demo");

USB HID OUT endpoint trouble

I'm having issues with getting a USB HID device with an OUT endpoint to work. My goal here is to use the USB HID protocol to provide a generic communication channel to a device with which I can then control the device from a PC. HID was chosen because it doesn't require drivers on any operating system, and many libraries for the PC side are available.

Environment:
Chip used: STM32F407VG (in 3.3V mode with 32.768kHz + 25MHz external oscillators)
Programming via SWD programming header with a STLink3 (official, not a clone)
Used current master branch (2021-06-22) of both STM32_XPD and USBDevice repositories

I've observed the following behavior with STM32_XPD + USBDevice:

  • device enumerates correctly to the operating system
  • send 64 bytes via HID to device: appears to work (send appears to be successful), but interrupt handle on device is not triggered (checked via gdb via STLink)
  • receive 64 bytes via HID from device: timeout (because the handler is not executed and no report is ever sent back -- if reports are sent back from the main loop manually, the can be received by the application)
  • I can repeat that as often as I like (same behavior)
  • however: send less than 64 bytes via HID to device (e.g. 63 bytes): handler is executed, but second "data" argument of SetReport handler is set to NULL (seen in debugger), so garbage is sent back (but something is sent back)
  • after the send with less than 64 bytes the USB stack on the device appears to have gone into some undefined state and nothing appears to work correctly anymore and I have to reset the device to perform further USB operations (the device itself is not hung totally, if I add a button and some LEDs and some simple logic, those will still work -- and the debugger just tells me that the USB interrupt isn't called anymore for some reason.

The following code was used for setup:

mcu_hwconfig.h:

#ifndef MCU_HWCONFIG_H_
#define MCU_HWCONFIG_H_

#include <xpd_usb.h>
#include <xpd_gpio.h>

#ifdef __cplusplus
extern "C"
{
#endif

#define USB_DP_PIN          PA12
#define USB_DM_PIN          PA11
#define USB_VBUS_PIN        PA9

extern void SystemClock_Config(void);
extern void HwConfig_USB_Bind(void);
extern void HwConfig_USB_Init();

#ifdef __cplusplus
}
#endif

#endif /* MCU_HWCONFIG_H_ */

mcu_hwconfig.c:

#include <xpd_rcc.h>
#include <xpd_nvic.h>

#include <usbd.h>
#include <usbd_dfu.h>
#include <usbd_hid.h>

#include "mcu_hwconfig.h"

#include <string.h>

static USB_HandleType h_usb_handle;
USB_HandleType *const g_usb_handle = &h_usb_handle;

static const RCC_PLL_InitType pllconf = {
    .State = ENABLE,
    .Source = HSE,
    .M = HSE_VALUE_Hz / 1000000,
    .N = 336,
    .P = 4,         /* 1 * 336 / 4 = PLLP =  84000000 -> SYSCLK */
    .Q = 7          /* 1 * 336 / 7 = PLLQ =  48000000 -> USB */
};

static const GPIO_InitType usb_pinconf =
{
    .Mode = GPIO_MODE_ALTERNATE,
    .Pull = GPIO_PULL_FLOAT,
    .Output.Type  = GPIO_OUTPUT_PUSHPULL,
    .Output.Speed = VERY_HIGH,
    .AlternateMap = GPIO_OTG_FS_AF10
};

/* System clocks configuration */
void SystemClock_Config(void)
{
    RCC_eHSE_Config(OSC_ON);
    RCC_ePLL_Config(&pllconf);

    /* System clocks configuration */
    RCC_eHCLK_Config(PLL, CLK_DIV1, 3);

    RCC_vPCLK1_Config(CLK_DIV2);
    RCC_vPCLK2_Config(CLK_DIV1);
}

void HwConfig_USB_Bind(void)
{
    GPIO_vInitPin(USB_DM_PIN, &usb_pinconf);
    GPIO_vInitPin(USB_DP_PIN, &usb_pinconf);
    USB_INST2HANDLE(g_usb_handle, USB_OTG_FS);
}

/** @brief USB device configuration */
const USBD_DescriptionType hdev_cfg = {
    .Vendor = {
        .Name           = "Sample Vendor https://www.example.com/",
        .ID             = 5824,
    },
    .Product = {
        .Name           = "HID Test Device",
        .ID             = 1503,
        .Version.bcd    = 0x0100,
    },
#if (USBD_SERIAL_BCD_SIZE > 0)
    .SerialNumber       = (USBD_SerialNumberType*)DEVICE_ID_REG,
#endif
    .Config = {
        .Name           = "HID Test Device",
        .MaxCurrent_mA  = 500,
        .RemoteWakeup   = 0,
        .SelfPowered    = 0,
    },
}, *const dev_cfg = &hdev_cfg;

static void HID_GetReport(void* itf, USBD_HID_ReportType type, uint8_t reportId);
static void HID_SetReport(void* itf, USBD_HID_ReportType type, uint8_t* data, uint16_t length);

__alignment(USBD_DATA_ALIGNMENT)
static const uint8_t HID_Report[] __align(USBD_DATA_ALIGNMENT) = {
    0x06, 0x00, 0xFF,
    0x0A, 0x00, 0xFF,
    0xA1, 0x01,
    0x15, 0x00,
    0x25, 0xFF,
    0x75, 0x08,
    0x95, 0x40,
    0x09, 0x01,
    0x81, 0x00,
    0x09, 0x02,
    0x91, 0x00,
    0x09, 0x03,
    0xB1, 0x02,
    0xC0
};

static const USBD_HID_ReportConfigType HID_ReportConfig = {
        .Desc = HID_Report,
        .DescLength = sizeof(HID_Report),
        .MaxId = 0,
        .Input.MaxSize = 64,
        .Input.Interval_ms = 5,
        .Output.MaxSize = 64,
        .Output.Interval_ms = 5,
};

static USBD_HID_AppType hid_app = {
    .Name = "HID Test Device",
    .SetReport = &HID_SetReport,
    .GetReport = &HID_GetReport,
    .Report = &HID_ReportConfig,
};

static USBD_HID_IfHandleType h_hid_handle = {
    .App = &hid_app,
    .Config.InEpNum = 0x81,
    .Config.OutEpNum = 0x01,
};
static USBD_HID_IfHandleType *const g_hid_handle = &h_hid_handle;

void HwConfig_USB_Init(void)
{
    NVIC_SetPriorityConfig(OTG_FS_IRQn, 1, 1);
    NVIC_SetPriorityConfig(OTG_FS_WKUP_IRQn, 1, 2);

    USBD_Init(g_usb_handle, dev_cfg);
    USBD_HID_MountInterface(g_hid_handle, g_usb_handle);

    NVIC_EnableIRQ(OTG_FS_IRQn);

    USBD_Connect(g_usb_handle);
}

void OTG_FS_IRQHandler(void)
{
    USB_vIRQHandler(g_usb_handle);
}




void HID_GetReport(void* itf, USBD_HID_ReportType type, uint8_t reportId)
{
    (void) itf;
    (void) type;
    (void) reportId;
}

void HID_SetReport(void* itf, USBD_HID_ReportType type, uint8_t* data, uint16_t length)
{
    uint8_t buffer[64];

    if (type == HID_REPORT_OUTPUT) {
        if (length > 64)
            length = 64;
        // Reverse the data, send it back
        for (int i = 0; i < length; ++i)
            buffer[length - i - 1] = data[i];
        USBD_HID_ReportIn(g_hid_handle, buffer, length);
    }
}

main.c:

#include <usbd_dfu.h>
#include <usbd_hid.h>
#include <usbd.h>

#include <xpd_flash.h>
#include <xpd_rcc.h>
#include <xpd_utils.h>
#include <xpd_gpio.h>

#include "mcu_hwconfig.h"

#include <string.h>

int main(void)
{
    /* Reset hardware to default state */
    XPD_vDeinit();
    RCC_vDeinit();

    // Basic initialization
    XPD_vInit();
    FLASH_vPrefetchBuffer(ENABLE);

    // Configure pinout
    HwConfig_USB_Bind();

    // Configure system clock
    SystemClock_Config();

    // Configure USB
    HwConfig_USB_Init();

    while (1)
        XPD_vDelay_ms(1000);
    
    return 0;
}

On the computer side, the following code via HIDAPI:

#ifdef WIN32
#include <windows.h>
#endif
#include <hidapi.h>

#include <iostream>
#include <cstddef>
#include <cstdlib>
#include <cstring>

int main()
{
    hid_device *handle;

    // Initialize the hidapi library
    (void) hid_init();

    // Open the device using the VID, PID,
    // and optionally the Serial number.
    handle = hid_open(0x16c0, 0x05df, NULL);

    // Write some data
    uint8_t buf[65];
    ssize_t len;
    memset(buf, 0, sizeof(buf));
    std::cout << "Sending: ";
    // hid_write requires the report id in the first byte,
    // since we don't use report ids, set it to 0
    // (only 64 bytes will be transferred to the HID device)
    buf[0] = 0;
    for (int i = 0; i < 64; ++i) {
        buf[i + 1] = rand();
        if (i > 0)
                std::cout << ' ';
        std::cout << int(buf[i + 1]);
    }
    std::cout << std::endl;
    len = hid_write(handle, buf, 65);
    std::cout << "Write result: " << len << std::endl;

        // Read some data
    std::cout << "Reading data..." << std::endl;
        len = hid_read(handle, buf, 64);
    std::cout << "Read result: " << len << std::endl;

    std::cout << "Received: ";
    for (int i = 0; i < len; ++i) {
        if (i > 0)
            std::cout << ' ';
        std::cout << int(buf[i]);
    }
    std::cout << std::endl;

    // Close device
    hid_close(handle);

    // Finalize the hidapi library
    (void) hid_exit();

    return 0;
}

For comparison, I created a very simple project with STM32CubeIDE and used the ST-integrated library instead of XPD and this one, and there I can get the USB HID communication to work. (The ST library has other drawbacks, which is why I attempted to use this one, because I liked the design a lot more.)

I've used the debugger via the STLink device to attempt to get more information, and step through parts of the XPD and/or USBDevice code, but I don't understand the code well enough to easily see what's wrong. (From a plain reading of the code that is triggered by the USB interrupt it seems correct to me at first glance.)

So it appears to me that either I'm using USBDevice wrong, in which case it would be great to know how, or there's a bug there, and I'm not quite sure how to find it.

If necessary I can provide more information, such as a tarball with the complete code, or USB traces on Linux/Windows, or gdb expressions taken at certain places, but the bug report is long enough as-is, so I'll wait for responses first.

Many thanks in advance!

IAR __packed needs to be before struct and union keywords

usbd_desc.c, usbd_desc,h usb_types.h, and the source/header files for device classes (e.g. usbd_cdc.c and usbd_cdc.h) specify typedefs and declare instances of packed structs and unions as follows:

typedef struct { ... } __packed TYPE_NAME
struct { ... } __packed INSTANCE_NAME
and so forth.

Based on empirical testing with the console_if.c class, USBDevice, and USBDevice4Cube, it appears IAR does not handle this syntax as expected. In particular, the configuration descriptor bytes that are sent during enumeration are malformed, causing the host to retry a few times and then reject the device.

This document from IAR gives the impression that __packed struct and __packed union would be the appropriate syntax. This is further given credence by the defines in cmsis_iccarm.h of #define __PACKED_STRUCT __packed struct and #define __PACKED UNION __packed union

Moving __packed in front of struct and union in the files mentioned above appeared to fix the issue for IAR and the console_if.c example. No testing was done on what impact, if any, this has on other compilers/toolchains, as such this is only an issue listing and not a pull request.

Windows 10 complains on Composite device configuration

Errors are:

          ===>Device Descriptor<===
bLength:                           0x12
bDescriptorType:                   0x01
bcdUSB:                          0x0201
bDeviceClass:                      0x00
*!*ERROR: device class should be Multi-interface Function 0xEF
          When IAD descriptor is used
bDeviceSubClass:                   0x00
*!*ERROR: device SubClass should be USB Common Sub Class 2
          When IAD descriptor is used
bDeviceProtocol:                   0x00
*!*ERROR: device Protocol should be USB IAD Protocol 1
          When IAD descriptor is used
bMaxPacketSize0:                   0x40 = (64) Bytes
idVendor:                        0x0483 = STMicroelectronics
idProduct:                       0xA367
bcdDevice:                       0x0104
iManufacturer:                     0x10
     English (United States)  "Eight Amps"
iProduct:                          0x20
     English (United States)  "Aspen"
iSerialNumber:                     0x00
bNumConfigurations:                0x01

Attached a log output from USBView.exe when connecting a composite device:

USBView-Aspen.txt

Error suppressed after editing Device/usbd_desc.c with the following diff:

diff --git a/Device/usbd_desc.c b/Device/usbd_desc.c
index 9d8b82c..b6ab6ea 100644
--- a/Device/usbd_desc.c
+++ b/Device/usbd_desc.c
@@ -42,9 +42,9 @@ static const USB_DeviceDescType usbd_deviceDesc = {
     .bLength            = sizeof(USB_DeviceDescType),
     .bDescriptorType    = USB_DESC_TYPE_DEVICE,
     .bcdUSB             = USBD_SPEC_BCD,
-    .bDeviceClass       = 0x00,
-    .bDeviceSubClass    = 0x00,
-    .bDeviceProtocol    = 0x00,
+    .bDeviceClass       = 0xEF,
+    .bDeviceSubClass    = 0x02,
+    .bDeviceProtocol    = 0x01,
     .bMaxPacketSize     = USBD_EP0_MAX_PACKET_SIZE,
     .idVendor           = 0xFFFF,
     .idProduct          = 0xFFFF,

I'm pretty sure that's not a desirable change, but providing here as troubleshooting input...

Any tips appreciated.

Add support for Windows OS descriptors

There's a particularly annoying issue with Windows, that it doesn't map any driver to unknown USB functions, instead of simply falling back to the default WinUSB driver (which allows raw USB access, leaving everything to higher level). According to my findings, this could be circumvented by adding Windows OS descriptors to the device, which could direct the OS to map WinUSB driver to the desired USB device functions.

Resources:
https://github.com/pbatard/libwdi/wiki/WCID-Devices
http://janaxelson.com/winusb.htm
https://techcommunity.microsoft.com/t5/microsoft-usb-blog/how-to-install-winusb-sys-without-a-custom-inf/ba-p/270769
https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors

CDC bulk transfer stalls when USBD_CDC_Transmit called too often

Dear Benedek,

I am connecting my STM32F407VET6 microcontroller to a PC running Debian Linux.
On the controller, I have a relatively huge ring buffer where I put data for sending through USB.

Initially, I had a check of data count in ring buffer in every "round" and if so, I have called USBD_CDC_Transmit and if it was returned USBD_E_OK, I thought it was sent.
Otherwise, USBD_CDC_Transmit is called again in the next round.

When I write to my buffer too often, I am experiencing the communication halt until I reconnect the USB device (device may remain powered).

My solution calls USBD_CDC_Transmit with a buffer directly pointing to my ring buffer position which will not be rewritten while it still working on it (it is large).
When the data to be sent is greater than the remaining space till the end of my ring buffer memory, then USBD_CDC_Transmit is called only till the end of the buffer and in the very next round, it is called from the beginning of my ring buffer till the remaining data count.

I think this was the point when I have experienced most of my communication halts, so when I called transmit routine, then the very next time (so just when it became not busy) called it again to send remaining data.

If I print to my buffer and call USBD_CDC_Transmit in every round, it halts even sooner.

Now I put a 50ms delay of calling transmit routine and now it seems communication does not halt.
Later: it halted after maybe an hour. When I have called it in every 20ms, it was halted maybe in 10-20 minutes. If I set it to be called in every "1 ms" then it halts after a few seconds.

However, I see USBD_CDC_Transmit has USBD_E_OK result only when (ep->State == USB_EP_STATE_IDLE) and (ep->Type == USB_EP_TYPE_ISOCHRONOUS).
If the result is not USBD_E_OK, my routine does nothing but tries to send data in the next call of it. Also USBD_CDC_Transmit (USBD_EpSend) does not do anything this case.

...but if ep->State equals to USB_EP_STATE_IDLE, it should be ready for sending another data, but it seems it is not always ready yet.

Maybe it is something like a corner case so it is not necessarily a bug in USBDevice itself.
Do you have any ideas of a potential solution?

I mean it would be good if the communication could keep working somehow even if USBD_CDC_Transmit is called more often.

As of the Wireshark capture below, I see device requests an interrupt even when host stops receiving new data, then host sends the interrupt request (which is as should), but data is not arriving from device.

I have also checked the software still calls USBD_CDC_Transmit which returns BUSY as ep->State is USB_EP_STATE_DATA, my data count in buffer is increasing, but it seems state machine does not go out of USB_EP_STATE_DATA.


This is my older message:

I have done a Wireshark capture.
When I open the port at package 95, it sends out the internal buffer.
From package 153, it sends only the counter ("Counter: 770").
At package 10151, I see my last received counter value ("Counter: 1093").
I don't know why I see the same data again at 10153, however I also saw it earlier like at package 9427.

So after that, I have not receiving any data, but I see "URB_INTERRUPT in" messages from package 10189 until I close opened port maybe near at package 10451.

So it seems to be unrelated to 64 byte-specific problem.

Anyway, in capture, I see "URB Interrupt in" messages maybe in every 242 messages, so device sends it to host then host sends it to device and a messages comes right after in the next package.
However, when the connection freezes, I see the same "URB Interrupt in" ping-pont but without the message.

So it seems to me the message interrupt comes to host but message is not arriving.

ttyacm.pcapng.gz

ncm:Out in itf

Hi,I'm a newbie,I want to use ncm in my project, but I don't know why there are two Out page in itf?

HardFault from USB_prvReadPMA

I have a very simple interface with IN and OUT endpoints, no report id's. As soon as I send some data from the host to this interface, due to accessing a nullptr in pxEP->Transfer.Data.

I'll try to list as much relevant info as possible at the moment of failure:

  • USB_prvReadPMA() called with pucDstBuf = 0x00000000, usPmaAddress = 0x00a8, usDataCount = 0x0008
  • usEpId = 0x02; that's correct OUT EP for this interface
  • pxEP is properly set, but its TRANSFER structure might not be:
    ** Transfer | {Data = 0x00000000, Length = 0x0000, ...}
    ** MaxPacketSize | 0x0008
    ** Type | USB_EP_TYPE_INTERRUPT
    ** State | USB_EP_STATE_IDLE
    ** IfNum | 0x01 '\x01'
    ** RegId | 0x02 '\x02'

Report descriptor and interface definitions below.

__ALIGN_BEGIN static const uint8_t hid_rpt_cfg[] __ALIGN_END = {
	0x06,	0x00,	0xFF,            // Usage Page = 0xFF00 (Vendor Defined Page 1)
	0x09,	0x01,                    // USAGE (Vendor Usage 1)
	0xa1,	0x01,                    // COLLECTION (Application)
	0x05,	0x01,                    //   USAGE_PAGE (Generic Desktop)
	0x15,	0x00,                    //     LOGICAL_MINIMUM (0)
	0x26,	0xff,	0x00,            //     LOGICAL_MAXIMUM (255)

	// write config command
	0x09,	0x00,                    //     USAGE (Undefined)
	0x95,	CFG_CMD_REPORT_SIZE,     //     REPORT_COUNT
	0x29,	CFG_CMD_REPORT_SIZE,     //     USAGE_MAXIMUM
	0x19,	0x01,                    //     USAGE_MINIMUM
	0x75,	0x08,                    //     REPORT_SIZE (8)
	0x91,	0x00,                    //     OUTPUT (Data,Ary,Abs)
	0x29,	CFG_CMD_REPORT_SIZE,     //     USAGE_MAXIMUM
	0x19,	0x01,                    //     USAGE_MINIMUM
	0x81,	0x00,                    //     INPUT (Data,Ary,Abs)

	0xc0                             // END_COLLECTION   
};

const USBD_HID_AppType cfgApp = {
	.Name = "sample config if",
	.Report = {
		.Desc = hid_rpt_cfg,
		.Length = sizeof(hid_rpt_cfg),
		.IDs = 0, // no report ids
	},
	.SetReport = Configurator_SetReport,
	.GetReport = Configurator_GetReport,
};


USBD_HID_IfHandleType hconfigurator_if = {
	.App = &cfgApp,
	.Config.InEp.Size = CFG_CMD_REPORT_SIZE,
	.Config.InEp.Interval = 20,
	.Config.OutEp.Size = CFG_CMD_REPORT_SIZE,
	.Config.OutEp.Interval = 20,
}, *const configurator_if = &hconfigurator_if;

CDC enumeration fails in Linux

When I plug my device into a Windows PC, it enumerates and all is happy. Unfortunately when I plug this same device into a PC running Ubuntu Linux 18.01 or a Beagle Board Black running Debian, I get this error:

cdc_acm: probe of 1-1:1.0 failed with error -22

Usually it's Windows that doesn't play nicely! Any thoughts?

HID Keyboard valid interrupt endpoint address and report format?

As mentioned in another post, I'm working on getting a Composite USB device to represent itself as a CDC device to receive configuration commands, that will then cause it to also present as an HID keyboard, mouse or joystick.

As a caveat, I have lots of experience (> 2 decades) with a variety of programming languages, but C is not one of them, so I'm quite sure there are simple problems in what I'm doing here.

While attempting to get the keyboard HID device configured, I'm getting errors that look like they're related to my configuration of the keyboard keyboard interrupt address. Unfortunately, I can't seem to figure out what I'm doing wrong.

I found this document, which indicates the endpoint address format:

0x81, // bEndpointAddress
This item describes the address and data flow direction of the endpoint.
Bits 0 through 3 define the endpoint’s address, and bit 7 describes the data
flow direction, with 1 meaning IN and 0 meaning OUT. The item in this
descriptor defines an “IN” endpoint with Endpoint Address 1.

I suspect there's a problem in the fact that I'm not generating nor returning a keyboard report, but I can't be certain because I can't seem to find an example of a valid keyboard report and I don't know how to author one.

Here is my usb_device.c configuration:

#include "usb_device.h"

extern USBD_CDC_IfHandleType *const console_if;

// Keyboard HID interface
USBD_HID_IfHandleType _keyboard_if = {
        .App = &keyboard_app,
        .Base.AltCount = 1,
}, *const keyboard_if = &_keyboard_if;

// @brief USB device handle
USBD_HandleType hUsbDevice, *const UsbDevice = &hUsbDevice;

// @brief USB device configuration
const USBD_DescriptionType hdev_cfg = {
    .Vendor = {
        .Name           = "Eight Amps (8A)",
        .ID             = USBD_VID,
    },
    .Product = {
        .Name           = "Switchy & Infrareddy",
        .ID             = USBD_PID,
        .Version.bcd    = 0x0100 | HW_REV,
    },
#if (USBD_SERIAL_BCD_SIZE > 0)
    .SerialNumber       = (USBD_SerialNumberType*)DEVICE_ID_REG,
#endif
    .Config = {
        .Name           = "Switchy & Infrareddy",
        .MaxCurrent_mA  = 100,
        .RemoteWakeup   = 0,
        .SelfPowered    = 0,
    },
}, *const dev_cfg = &hdev_cfg;

void UsbDevice_Init(void) {
        KeyboardInit();

        // NOTE(lbayes): Made up this address, what value should it be?
        keyboard_if->Config.InEpNum = 0x89;
        // Mount the Keyboard interface
        USBD_HID_MountInterface(keyboard_if, UsbDevice);

        // All fields of Config have to be properly set up
        console_if->Config.InEpNum  = 0x81;
        console_if->Config.OutEpNum = 0x01;
        console_if->Config.NotEpNum = 0x8F;
        // console_if->Config.NotEpNum = 0x82;

        // Mount the CDC interface
        USBD_CDC_MountInterface(console_if, UsbDevice);

        // Initialize the device
        USBD_Init(UsbDevice, dev_cfg);

        // After charger detection the device connection can be made
        USBD_Connect(UsbDevice);
}

void UsbDevice_Deinit(void)
{
    USBD_Deinit(UsbDevice);
}

Here is my /var/log/syslog output when the device boots:

ar 16 13:30:16 beefcake kernel: [72877.892814] usb 3-4.2: USB disconnect, device number 101
Mar 16 13:30:21 beefcake kernel: [72882.978477] usb 3-4.2: new full-speed USB device number 102 using xhci_hcd
Mar 16 13:30:21 beefcake kernel: [72883.107182] usb 3-4.2: New USB device found, idVendor=0483, idProduct=0002, bcdDevice= 1.01
Mar 16 13:30:21 beefcake kernel: [72883.107185] usb 3-4.2: New USB device strings: Mfr=16, Product=32, SerialNumber=0
Mar 16 13:30:21 beefcake kernel: [72883.107186] usb 3-4.2: Product: Switchy & Infrareddy
Mar 16 13:30:21 beefcake kernel: [72883.107187] usb 3-4.2: Manufacturer: Eight Amps (8A)
Mar 16 13:30:21 beefcake kernel: [72883.115083] usbhid 3-4.2:1.0: couldn't find an input interrupt endpoint
Mar 16 13:30:21 beefcake kernel: [72883.118076] cdc_acm: probe of 3-4.2:1.1 failed with error -22
Mar 16 13:30:21 beefcake mtp-probe: checking bus 3, device 102: "/sys/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:07:00.3/usb3/3-4/3-4.2"
Mar 16 13:30:21 beefcake mtp-probe: bus: 3, device: 102 was not an MTP device
Mar 16 13:30:21 beefcake mtp-probe: checking bus 3, device 102: "/sys/devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:07:00.3/usb3/3-4/3-4.2"
Mar 16 13:30:21 beefcake mtp-probe: bus: 3, device: 102 was not an MTP device

I suspect my problem is highlighted by the line with, usbhid 3-4.2:1.0: couldn't find an input interrupt endpoint.

When I run sudo lshw -c input, I get the following entry (others removed):

$ sudo lshw -c input

  *-usb:1 UNCLAIMED
       description: Keyboard
       product: Switchy & Infrareddy
       vendor: Eight Amps (8A)
       physical id: 2
       bus info: usb@3:4.2
       version: 1.01
       capabilities: usb-2.01
       configuration: maxpower=100mA speed=12Mbit/s

Here is my keyboard_if.c file:

#include "keyboard_if.h"

void SetKeyboardReport(void* itf, USBD_HID_ReportType type, uint8_t * data, uint16_t length) {
        // What to do here?
}

void GetKeyboardReport(void* itf, USBD_HID_ReportType type, uint8_t reportId) {
        // What to do here?
}

void KeyboardInit(void) {
}

const USBD_HID_AppType keyboard_app = {
        .Name      = "Eight Amps (8A), Switchy",
        .SetReport = SetKeyboardReport,
        .GetReport = GetKeyboardReport,
};

Any tips or pointers are greatly appreciated and thanks again for such a great library.

Wrong reference assigned to altSel

In usbd_if.c l.126 there is an extra &: we try to assign an uint16_t* to an uint8_t
uint8_t altSel = (uint8_t)&dev->Setup.Value;
IMHO it should be:
uint8_t altSel = (uint8_t)(0xFF & dev->Setup.Value);

buffer overflow in USBDevice

bug 1 - buffer overflow in USBD_CtrlReceiveData

path

Device/usbd_ctrl.c

code

 
USBD_ReturnType USBD_CtrlReceiveData(USBD_HandleType *dev, void *data)
{
    USBD_ReturnType retval = USBD_E_ERROR;

    /* Sanity check */
    if (dev->EP.OUT[0].State == USB_EP_STATE_SETUP)
    {
        uint16_t len = dev->Setup.Length;  // recv from other USB device.

        dev->EP.OUT[0].State = USB_EP_STATE_DATA;
        USBD_PD_EpReceive(dev, 0x00, (uint8_t*)data, len);

        retval = USBD_E_OK;
    }
    return retval;
}

dev->Setup.Length is recv from other USB device, if dev->Setup.Length > the size of data, it will overflow.

Device enumeration fails for composite device of NCM and two HID

We are observing that Windows and iOS both fail to enumerate our device. Windows reports a “Device not migrated due to partial or ambiguous match” message.
Initialization is given as:
/* All fields of Config have to be properly set up /
hid_if.Config.InEpNum = 0x83;
hid_if.Config.OutEpNum = 0x03;
hid_if.App = &hid_app;
keyboard_if.Config.InEpNum = 0x84;
keyboard_if.Config.OutEpNum = 0x04;
keyboard_if.App = &keyboard_app;
ncm_usb_if->Config.InEpNum = 0x81;
ncm_usb_if->Config.OutEpNum = 0x01;
ncm_usb_if->Config.NotEpNum = 0x82;
/
Mount the interfaces to the device */
USBD_NCM_MountInterface(ncm_usb_if, UsbDevice1);
USBD_HID_MountInterface(&hid_if, UsbDevice1);
USBD_HID_MountInterface(&keyboard_if, UsbDevice1);

And here is my configuration:
#define USBD_MAX_IF_COUNT 8
#define USBD_EP0_BUFFER_SIZE 512
#define USBD_HS_SUPPORT 0
#define USBD_SERIAL_BCD_SIZE 0
#define USBD_MS_OS_DESC_VERSION 2
#define USBD_CDC_NOTEP_USED 0
#define USBD_CDC_CONTROL_LINE_USED 0
#define USBD_CDC_BREAK_SUPPORT 0
#define USBD_DFU_ALTSETTINGS 0
#define USBD_DFU_ST_EXTENSION 0
#define USBD_HID_ALTSETTINGS 0
#define USBD_HID_OUT_SUPPORT 1
#define USBD_HID_REPORT_STRINGS 0

It is important to note that as soon as we drop any of the three components, the device is successfully enumerated. Only with all three present do we encounter this problem.
Could you please advise?

console_if.c won't transmit on STM32F072 (CubeMX)

For some reason I can't get anything to send with console_if.c. I get the device detected and it shows up as ttyACM0 on my linux system, but when I use minicom to read it I don't see any output from the device. In my main.c in the while loop I am calling _write() directly so I would expect to see something but nothing is coming through.

Not sure what I am doing wrong, probably something simple, do you have any suggestions?

Thanks for any help,
Mike

USB CDC & main program hangs when port is not opened or cable is replugged - problem/solution

Dear Benedek,

First of all, thank you for developing and sharing your project, I think it is a much better solution compared to STM32 original implementation.

I am using an STM32F407 microcontroller using Eclipse and STM32 HAL implementation.
Thanks to descriptions, I have successfully managed to compile and run USBDevice in a test project.

As a starting point, I have used VCP_IF interface from your project and simplified it a bit.

My host computer is running Linux.

The main program calls transmit function in every millisecond and sends a message using VCP interface to host computer.

In that case, STM32 "hangs" after calling USBD_CDC_Transmit() function after a few times if the port is not open or the cable was reconnected.

About STM32 hangs... it is actually running, but it constantly calls USB_vIRQHandler() routine. If I unplug then replug the cable, the main program starts to run again till a call of USBD_CDC_Transmit() function occurs, then it hangs again by continuously calling USB_vIRQHandler() function.
I don't know the exact reason of the loop, but calling USBD_Disconnect() then USBD_Connect() function solves the situation, communication works again.

So, to solve it, when (USBD_HandleType).Suspend callback occurs, I am calling USBD_Disconnect() and USBD_Connect() once so system calls it every time I disconnect USB cable (anyway, it is usually also fired up on cable reconnection). Currently, a communication delay of 1000ms is also applied here.

It solves the problem which happens after cable reconnection, but STM32 still halts if I wait too long to open the serial port after plugged in USB cable.

At the time of Close callback (in USBD_CDC_AppType) is called, I have also added an option for the software to discard calling USBD_CDC_Transmit() till Open callback not called or at least for 100ms.

It helped a bit, but STM32 still hangs if it wants to send data before the serial port was opened at the host computer.

If I open the port and close it at host computer in time, STM32 does not hang and I can reopen serial port again.

So, I have started to find a way to determine if the port was opened after Close/Open callbacks fired up.

I have found a similar problem here.
Based on that, I have modified usbd_cdc.c and included a callback at "CDC_REQ_SET_CONTROL_LINE_STATE" stage.
In VCP function, the software continues to discard calling USBD_CDC_Transmit() function after Close callback happens while ControlLineState callback is not fired up (it happens when I open serial ttyACM interface).

After this modification, STM32 seems to run well even after disconnect/connect the cable and also if the serial port was not opened. If I open it, my messages are displayed well.

My modified callback structure is below:

`/** @brief CDC application structure /
typedef struct
{
const char
Name; /*!< String description of the application */

void (*Open)        (void* itf,
                     USBD_CDC_LineCodingType* coding);  /*!< Open port */

void (*Close)       (void* itf);        /*!< Close port */

void (*Received)    (void* itf,
                     uint8_t* data,
                     uint16_t length);  /*!< Received data available */

void (*Transmitted) (void* itf,
                     uint8_t* data,
                     uint16_t length);  /*!< Transmission of data completed */

void (*ControlLineState)	(void* itf);        /*!< Control line state changed */

#if (USBD_CDC_BREAK_SUPPORT == 1)
void (Break) (void itf,
uint16_t len_ms); /*!< Interface interaction through the control channel /
#endif /
USBD_CDC_BREAK_SUPPORT */
}USBD_CDC_AppType;
`

My modified cdc_setupStage function is below:
`/**

  • @brief Performs the interface-specific setup request handling.
  • @param itf: reference of the CDC interface
  • @return OK if the setup request is accepted, INVALID otherwise
    */

/** @brief USB setup request */
static USBD_ReturnType cdc_setupStage(USBD_CDC_IfHandleType *itf)
{
USBD_ReturnType retval = USBD_E_INVALID;
USBD_HandleType *dev = itf->Base.Device;

switch (dev->Setup.RequestType.Type)
{
    case USB_REQ_TYPE_CLASS:
    {
        switch (dev->Setup.Request)
        {
            case CDC_REQ_SET_LINE_CODING:
                /* Reset the data interface */
                cdc_deinit(itf);

                retval = USBD_CtrlReceiveData(dev, &itf->LineCoding);
                break;

            case CDC_REQ_GET_LINE_CODING:
                retval = USBD_CtrlSendData(dev,
                        &itf->LineCoding, sizeof(itf->LineCoding));
                break;

            case CDC_REQ_SET_CONTROL_LINE_STATE:
		USBD_SAFE_CALLBACK(CDC_APP(itf)->ControlLineState, itf);

                retval = USBD_E_OK;
                break;

#if (USBD_CDC_BREAK_SUPPORT == 1)
case CDC_REQ_SEND_BREAK:
/* Simply pass the request with wValue /
if (CDC_APP(itf)->Break != NULL)
{
CDC_APP(itf)->Break(itf, dev->Setup.Value);
retval = USBD_E_OK;
}
break;
#endif /
USBD_CDC_BREAK_SUPPORT */

            default:
                break;
        }
        break;
    }

    default:
        break;
}

return retval;

}
`
Another small modification of the code was in USBD_CDC_Notify function, where "notice->Header->Length" should be "notice->Header.Length":

`#if (USBD_CDC_NOTEP_USED == 1)
/**

  • @brief Sends a device notification to the host.

  • @param itf: reference of the CDC interface

  • @param notice: pointer to the notification message to send

  • @return BUSY if the previous transfer is still ongoing, OK if successful
    */
    USBD_ReturnType USBD_CDC_Notify(USBD_CDC_IfHandleType *itf, USBD_CDC_NotifyMessageType *notice)
    {
    USBD_ReturnType retval = USBD_E_ERROR;

    if (itf->Config.NotEpNum < USBD_MAX_EP_COUNT)
    {
    uint16_t length = sizeof(notice->Header) + notice->Header.Length;
    retval = USBD_EpSend(itf->Base.Device, itf->Config.NotEpNum, notice, length);
    }

    return retval;
    }
    #endif`

Could you please update your code to include this (or similar) callback function?

Thank you in advance and thank you again for your work.

Best Regards,
Zsolt

Cannot differentiate between HID report types

In hid_dataStage(...) the (optional) report id is extracted and copied from the dev->Setup.Value LSB into the packet payload.
The MSB in dev->Setup.Value indicates whether this is a SetReport (0x02) or SetFeature (0x03). There seems to be no way to differentiate between the two in the SetReport callback.
I believe a similar mechanism is used for GetReport (0x01) and GetFeature (0x03) on the other endpoint.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.