NordicSemiconductor / pc-nrfconnect-programmer

Programmer app for nRF Connect for Desktop
Other
64 stars 26 forks source link

DFU trigger interface not recognized in composite device. #116

Open markrages opened 5 years ago

markrages commented 5 years ago

Background

Here is a hex file compiled from https://github.com/markrages/blackmagic/tree/nRF52840/src/platforms/nRF52840 : blackmagic_example.zip (hexfile zipped for Gihub reasons -- this is not a DFU package zip.)

The firmware presents two CDC_ACM serial ports and the DFU Trigger interface as a USB composite device. It is compiled for nRF52840 Dongle (PCA10059)

When I load the hex file with nRF Connect, it works, and allows me to debug other targets using the nRF52840 Dongle.

Bug description After the file is loaded, the trigger interface is not recognized by nRF Connect Programmer:

Screenshot from 2019-03-27 13-34-25

What I would expect

nRF Connect Programmer should see the DFU trigger interface and not require the reset button to be pressed on the nRF52840 Dongle.

More information

  1. The trigger interface is there.

Partial lsusb -v output:

      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        4
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      1 
      bInterfaceProtocol      1 
      iInterface              0 
      ** UNRECOGNIZED:  09 21 09 00 00 00 00 10 01
  1. nrfutil dfu works. The usbdfu target in https://github.com/markrages/blackmagic/blob/nRF52840/Makefile will reload the firmware over DFU, without requiring to press the reset button on the Dongle.
markrages commented 5 years ago

SWD debugging connections

20190325_161716

bencefr commented 5 years ago

Hi @markrages ,

I am not up to date with the SDK, but I don't see that _nrf_dfu_trigger_usbinit() call which should happen right after _app_usbdinit(). Also change _NRF_DFU_TRIGGER_USB_INTERFACENUM to 0, just to be sure.

The nRFConnect ecosystem is strict in its expectation about the USB VID/PID and interface order for a good reason (mostly because of windows drivers...) and this is not a concern for the SDK examples.

If this doesn't work, please send the complete lsusb -v output.

markrages commented 5 years ago

@bencefr OK, I did that, but no change.

When I click on the device it puts this message in the error log: Screenshot from 2019-03-28 00-51-31

I am using the PID/VID unchanged from the cdc_acm demo: Bus 001 Device 037: ID 1915:520f Nordic Semiconductor ASA

Here is the output of lsusb -v -d 1915:: lsusb_output.txt

bencefr commented 5 years ago

Please also try to change the USB product ID to: #define APP_USBD_PID 0xC00A in _sdkconfig.h. I'm not convinced this will work, I need a bit more time to dig deeper.

Have you also used this package or its rules? https://github.com/NordicSemiconductor/nrf-udev

bencefr commented 5 years ago

@markrages , I also found that you need to set the _CDCACM*INTERFACE numbers to 1 higher in _nrfmain.c, since the 0th interface is now the trigger.

With this setup it works for me, however I still find the behaviour of Programmer app a bit of hit and miss, at least you can reload and try again.

markrages commented 5 years ago

@bencefr

I made the following change. Is this what you are suggesting?

--- a/src/platforms/nRF52840/nrf_main.c
+++ b/src/platforms/nRF52840/nrf_main.c
@@ -112,17 +112,17 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
 static void cdc_acm_user_ev_handler2(app_usbd_class_inst_t const * p_inst,
                                      app_usbd_cdc_acm_user_event_t event);

-#define CDC_ACM_COMM_INTERFACE  0
+#define CDC_ACM_COMM_INTERFACE  1
 #define CDC_ACM_COMM_EPIN       NRF_DRV_USBD_EPIN2

-#define CDC_ACM_DATA_INTERFACE  1
+#define CDC_ACM_DATA_INTERFACE  2
 #define CDC_ACM_DATA_EPIN       NRF_DRV_USBD_EPIN1
 #define CDC_ACM_DATA_EPOUT      NRF_DRV_USBD_EPOUT1

-#define CDC_ACM2_COMM_INTERFACE  2
+#define CDC_ACM2_COMM_INTERFACE  3
 #define CDC_ACM2_COMM_EPIN       NRF_DRV_USBD_EPIN3

-#define CDC_ACM2_DATA_INTERFACE  3
+#define CDC_ACM2_DATA_INTERFACE  4
 #define CDC_ACM2_DATA_EPIN       NRF_DRV_USBD_EPIN4
 #define CDC_ACM2_DATA_EPOUT      NRF_DRV_USBD_EPOUT2

This change breaks the one of the serial ports.

dmesg compains:

[3021576.680919] usb 1-1.6.7.6: config 1 has 4 interfaces, different from the descriptor's value: 5
[3021576.680923] usb 1-1.6.7.6: config 1 has no interface number 0
[3021576.680930] usb 1-1.6.7.6: Duplicate descriptor for config 1 interface 4 altsetting 0, skipping
[3021576.681960] usb 1-1.6.7.6: New USB device found, idVendor=1915, idProduct=c00a
[3021576.681965] usb 1-1.6.7.6: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[3021576.681968] usb 1-1.6.7.6: Product: nRF52 USB CDC Demo
[3021576.681971] usb 1-1.6.7.6: Manufacturer: Nordic Semiconductor
[3021576.681973] usb 1-1.6.7.6: SerialNumber: EC4938BFC6DB
[3021576.683251] cdc_acm 1-1.6.7.6:1.1: ttyACM3: USB ACM device
[3021576.684351] cdc_acm: probe of 1-1.6.7.6:1.3 failed with error -22

And it's not working in nRF Connect: Screenshot from 2019-03-28 09-14-10

(nRF Connect thinks it changed to the bootloader, but it didn't. Observed with LED and dmesg)

markrages commented 5 years ago

Ah, in sdk_config.h:

#define NRF_DFU_TRIGGER_USB_INTERFACE_NUM 4

So if I increase the CDC_ACM*INTERFACE numbers, I should set this to 0.

This fixes the error I noted above.

Now nRF Connect Programmer finds the device, but has an error after programming it.

Screenshot from 2019-03-28 09-23-38

Here is the output of lsusb -v -d 1915:: lsusb_output.txt

markrages commented 5 years ago

According to dmesg timestamps, it takes one second to reconnect, and an additional 200 ms to finish enumerating the serial ports. Do you know the duration of the timeout referred to in the nRF Connect error log?

[3022394.484111] usb 1-1.6.7.6: USB disconnect, device number 54
[3022395.495431] usb 1-1.6.7.6: new full-speed USB device number 55 using ehci-pci
[3022395.627396] usb 1-1.6.7.6: New USB device found, idVendor=1915, idProduct=c00a
[3022395.627400] usb 1-1.6.7.6: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[3022395.627403] usb 1-1.6.7.6: Product: nRF52 USB CDC Demo
[3022395.627406] usb 1-1.6.7.6: Manufacturer: Nordic Semiconductor
[3022395.627408] usb 1-1.6.7.6: SerialNumber: EC4938BFC6DB
[3022395.628558] cdc_acm 1-1.6.7.6:1.1: ttyACM3: USB ACM device
[3022395.629540] cdc_acm 1-1.6.7.6:1.3: ttyACM4: USB ACM device
markrages commented 5 years ago

One more data point:

If I comment out this line:

 //    ret = app_usbd_class_append(class_cdc_acm2);

Then the composite device is just one serial port and the DFU trigger interface, and it works 100% reliably in nRF Connect.

It also works if I comment the other class_append. It appears that having two serial ports is the issue.

markrages commented 5 years ago

OK, not quite 100%. The following sequence triggers an error:

  1. Load the one-serial-port firmware with nrfutil dfu.
  2. Open nRF Connect Programmer
  3. Select device
  4. Add hex file (same one)
  5. Write
  6. Reset
  7. Select device

This gives error 2019-03-28T17:53:58.751Z ERROR Error while probing usb device at bus.address 1.97: Can't close device with a pending request. Please check your udev rules concerning permissions for USB devices, see https://github.com/NordicSemiconductor/nrf-udev

Full log: 2019-03-28T17_52_22.136Z-log.txt

Screenshot from 2019-03-28 11-50-12

bencefr commented 5 years ago

I have found a few more things in the firmware code that should be changed for the 59 device:

In _sdkconfig.h please change the power configuration:

#define APP_USBD_CONFIG_SELF_POWERED 0

This changes the lsusb output Self powered to Bus powered.

In _nrfmain.c add these functions:

void enable_reset()
{
    if (((NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) != (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos)) ||
        ((NRF_UICR->PSELRESET[1] & UICR_PSELRESET_CONNECT_Msk) != (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos)))
    {
        NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
        NRF_UICR->PSELRESET[0] = 18;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
        NRF_UICR->PSELRESET[1] = 18;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
        NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
        NVIC_SystemReset();
    }
}

/**
 * Function for configuring UICR_REGOUT0 register
 * to set GPIO output voltage to 3.0V.
 */
static void gpio_output_voltage_setup(void)
{
    // Configure UICR_REGOUT0 register only if it is set to default value.
    if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) ==
        (UICR_REGOUT0_VOUT_DEFAULT << UICR_REGOUT0_VOUT_Pos))
    {
        NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}

        NRF_UICR->REGOUT0 = (NRF_UICR->REGOUT0 & ~((uint32_t)UICR_REGOUT0_VOUT_Msk)) |
                            (UICR_REGOUT0_VOUT_3V0 << UICR_REGOUT0_VOUT_Pos);

        NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
        while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}

        // System reset is needed to update UICR registers.
        NVIC_SystemReset();
    }
}

And invoke them just before _nrf_drv_clockinit() like this:

    if (NRF_POWER->MAINREGSTATUS &
            (POWER_MAINREGSTATUS_MAINREGSTATUS_High << POWER_MAINREGSTATUS_MAINREGSTATUS_Pos))
    {
        gpio_output_voltage_setup();
    }
    enable_reset();

    ret = nrf_drv_clock_init();

Now you need to know that I am not an embedded developer, so I don't understand these functions. Once upon a time I hunted these in order to make the RSSI reference application work with the dongle, which has the firmware source here: https://github.com/NordicSemiconductor/pc-nrfconnect-rssi/tree/master/fw/src/rssi_cdc_acm

I know that on Windows modified code works quite well, however it does produce the LIBUSB error after resetting to application mode, since the Windows driver bundled by nRFConnect maps only 1 serialport to libusb, so the 2nd will trigger this issue. I don't understand though why is this an issue on Linux since it doesn't need a driver.

I also suggest to use the dongle in the RSSI app, which will load the RSSI fw on it, and you can compare the expected libusb output.

markrages commented 5 years ago

gpio_output_voltage_setup() is in $SDK/components/boards/boards.c which gets called in init_bsp() in this code.

I believe the voltage is good because the dongle would not work as a debugger otherwise: it has no other power source.

enable_reset() is in SystemReset in $SDK/modules/nrfx/mdk/system_nrf52840.c which is called from startup code in $SDK/modules/nrfx/mdk/gcc_startup_nrf52840.S. (It is contingent on CONFIG_GPIO_AS_PINRESET which is defined in the Makefile.)

I believe the reset is good because nrfutil dfu has no problems updating this device.

markrages commented 5 years ago

Updated APP_USBD_CONFIG_SELF_POWERED, thank you.

markrages commented 5 years ago

the Windows driver bundled by nRFConnect maps only 1 serialport to libusb, so the 2nd will trigger this issue.

Can you point me to some documentation for this? I am not very familiar with libusb.

bencefr commented 5 years ago

Libusb is a cross platform library that provides a generic access to USB devices. Since nRFConnect aims to be cross platform, it was a natural choice to use. On Linux and macOS it works out of the box, but on Windows it requires libusb enabled filter drivers, and because of the nature of Windows drivers every device with a specific VID/PID and interface layout needs to have some .inf file that tells the OS which driver to use. Therefore nRFConnect is expecting 1915:521F devices to be nordic bootloader mode and 1915:C00A devices to be have DFU trigger and CDC ACM interfaces in this order. I don't think this is well documented, I will raise question this in the team. Although nothing prevents anyone to write a new driver for any kind of USB configuration, it's just very tedious work.

bencefr commented 5 years ago

I consider this issue solved, feel free to reopen if needed.

markrages commented 5 years ago

The issue is not solved.

Triggering still doesn't work when there are two serial ports.

(I am not allowed to reopen this issue)

bencefr commented 5 years ago

@markrages , do you use Linux in a virtual box/vmware or something similar?

markrages commented 5 years ago

No, just plain Linux.

bencefr commented 5 years ago

Since I am not able to reproduce the issue with or without your firmware, I must ask further details. Is there anything in the setup that might effect timing?

Do you use USB hub or chain of hubs? If yes, do you have better results if the dongle is directly connected to the computer and/or to a different hub?

The enumeration by nRFConnect depends on both USB and serialport enumeration and I recall seeing the latter not reporting serial numbers in some cases. This might be due to timing. I don't think the default timeout (10s) is the issue, but maybe a race between the two enumerations, and the logic is implemented here: https://github.com/NordicSemiconductor/nrf-device-setup-js/blob/master/src/setupDevice.js#L121

I just installed a clean Ubuntu 18.04 LTS on a fairly low spec and old notebook. By dmesg device reconnection on this setup happens in less than 500 ms, and even seems to be faster via a powered hub.

Also for the record, please describe your system/distribution/versions...

markrages commented 5 years ago

I have tried two systems.

System #1 is a Lenovo T450s laptop. There are no hubs or other USB devices. The dongle is plugged into the side.

lsb_release -a shows:

Distributor ID: Ubuntu
Description:    Ubuntu 16.04.6 LTS
Release:    16.04
Codename:   xenial

hwinfo.t450s.txt

System #2 is a Dell desktop of 2014 vintage. The dongle is plugged into a USB hub with many other devices.

lsb_release -a shows:

Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:    18.04
Codename:   bionic

hwinfo.dell.txt

gksrb4 commented 4 years ago

I have faced this problem too.

My project are started from usb cli example.

I have succeed to display DFU Trigger and CDC ACM in device manager. image

image

but, nrfutil and nrf-programmer failed to trigger dfu.

error: LIBUSB_ERROR_NOT_SUPPORTED image

My sdk_config.h file, what I'm missing?

#ifndef APP_USBD_VID
#define APP_USBD_VID 0x1915
#endif

#ifndef APP_USBD_PID
#define APP_USBD_PID 0xC00A
#endif

#ifndef APP_USBD_NRF_DFU_TRIGGER_ENABLED
#define APP_USBD_NRF_DFU_TRIGGER_ENABLED 1
#endif

#ifndef NRF_DFU_TRIGGER_USB_USB_SHARED
#define NRF_DFU_TRIGGER_USB_USB_SHARED 1
#endif

#ifndef NRF_DFU_TRIGGER_USB_INTERFACE_NUM
#define NRF_DFU_TRIGGER_USB_INTERFACE_NUM 0
#endif

#ifndef NRF_CLI_CDC_ACM_COMM_INTERFACE
#define NRF_CLI_CDC_ACM_COMM_INTERFACE 1
#endif

#ifndef NRF_CLI_CDC_ACM_DATA_INTERFACE
#define NRF_CLI_CDC_ACM_DATA_INTERFACE 2
#endif

#ifndef APP_USBD_CONFIG_SELF_POWERED
#define APP_USBD_CONFIG_SELF_POWERED 0
#endif

#ifndef APP_USBD_CONFIG_MAX_POWER
#define APP_USBD_CONFIG_MAX_POWER 100
#endif

#ifndef APP_USBD_CONFIG_POWER_EVENTS_PROCESS
#define APP_USBD_CONFIG_POWER_EVENTS_PROCESS 1
#endif

#ifndef APP_USBD_CONFIG_EVENT_QUEUE_ENABLE
#define APP_USBD_CONFIG_EVENT_QUEUE_ENABLE 0
#endif

and add functions in main.c

if (NRF_POWER->MAINREGSTATUS &
            (POWER_MAINREGSTATUS_MAINREGSTATUS_High << POWER_MAINREGSTATUS_MAINREGSTATUS_Pos))
    {
        gpio_output_voltage_setup();
    }
    enable_reset();

I found a solution. but, I can't understand why it works. Is there anyone who tell me about this story?

https://devzone.nordicsemi.com/f/nordic-q-a/62174/usb-dfu-trigger-library-problem?ReplySortBy=Votes&ReplySortOrder=Descending