IntergatedCircuits / USBDevice

Highly flexible Composite USB Device Library
Apache License 2.0
217 stars 45 forks source link

Linux refuses to mount CDC/VCP Device #16

Closed lukebayes closed 4 years ago

lukebayes commented 4 years ago

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.

benedekkupper commented 4 years ago

Hi! Can you first check if this is not the same issue as #15?

lukebayes commented 4 years ago

That's exactly what it was. Thanks for the pointer!

For anyone else who's coming along later, the fix was to open usb_config.h and set these three variables:

/** @brief Set to 1 if notifications are sent by a CDC-ACM interface.
 * In this case notification EP will be allocated and opened if its address is valid. */
#define USBD_CDC_NOTEP_USED         1

/** @brief Set to 1 if SET_CONTROL_LINE_STATE request is used by a CDC-ACM interface. */
#define USBD_CDC_CONTROL_LINE_USED  1

/** @brief Set to 1 if SEND_BREAK request is used by a CDC-ACM interface. */
#define USBD_CDC_BREAK_SUPPORT      1

And to make sure my console interface configuration in usb_device.c had valid address(es) as in:

    console_if->Config.InEpNum  = 0x81;
    console_if->Config.OutEpNum = 0x01;
    console_if->Config.NotEpNum = 0x82;

Once this is done, the device appears at /dev/ttyACM0 and can be mounted with:

sudo screen /dev/ttyACM0 115000

Thank you!