Native port driver BSoD

I have found a problem in the Device Descriptor defined in the file USBCore.cpp

USB_DeviceDescriptorA has a device class of 2 if CDC is enabled and a device class of 0, if it is not.

The problem is the Device Descriptor for a Composite Device, needs to have a device class of 0 or Windows will not know it is a Composite Device.

I changed the code to use Device Class 0 in either case and recompiled a sketch.

After changing that, I see 3 devices show up when connected to the Native port. I see a CDC device, a HID mouse, and HID Keyboard.

I haven't done any further testing to make sure they all work, but I don't get the BSOD anymore when installing the serial driver.

#ifdef CDC_ENABLED
#define DEVICE_CLASS 0x02
#else
#define DEVICE_CLASS 0x00
#endif

//	DEVICE DESCRIPTOR
const DeviceDescriptor USB_DeviceDescriptor =
	D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);

const DeviceDescriptor USB_DeviceDescriptorA =
	D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);

...

if (USB_DEVICE_DESCRIPTOR_TYPE == t)
	{
		TRACE_CORE(puts("=> USBD_SendDescriptor : USB_DEVICE_DESCRIPTOR_TYPE\r\n");)
		if (setup.wLength >= 8)
		{
			_cdcComposite = 1;
		}
		desc_addr = _cdcComposite ?  (const uint8_t*)&USB_DeviceDescriptorA : (const uint8_t*)&USB_DeviceDescriptor;
        if( *desc_addr > setup.wLength ) {
            desc_length = setup.wLength;
        }
	}

Great! I tried that change of the Descriptor to 0 in "\arduino-1.5.1r2\hardware\arduino\sam\cores\arduino\USB\USBCore.cpp" and tested the KeyboardAndMouseControl example sketch and it worked correctly. (Note: I needed to delete the existing driver and let it reinstall to get the composite USB be recognized)

The question is, I double checked the "\arduino-1.0.2\hardware\arduino\cores\arduino\USBCore.cpp" file for the Leonardo and Micro, but they have the same class 2 definition if CDC_ENABLED? So what is different here that makes it work?

I was able to confirm the Descriptor modification with my other two laptops.
Dell XPS Windows 7 SP1 64-bit and Sony Vaio FW590 Windows 7 SP1 64-bit both installed the ArduinoDue.inf driver correctly now for the Native USB port. I also tested the KeyboardAndMouseController sketch and it works fine.

This modification is just a kludge at the moment. We need to figure out the proper code fix for this issue. Anyone else versed in USB stuff?

hiduino:
The question is, I double checked the "\arduino-1.0.2\hardware\arduino\cores\arduino\USBCore.cpp" file for the Leonardo and Micro, but they have the same class 2 definition if CDC_ENABLED? So what is different here that makes it work?

The reason is works on Leonardo is because of the following differences between Leonardo USBCore.cpp:

	if (USB_DEVICE_DESCRIPTOR_TYPE == t)
	{
		if (setup.wLength == 8)
			_cdcComposite = 1;
		desc_addr = _cdcComposite ?  (const u8*)&USB_DeviceDescriptorA : (const u8*)&USB_DeviceDescriptor;
	}

and Due USBCore.cpp:

	if (USB_DEVICE_DESCRIPTOR_TYPE == t)
	{
		TRACE_CORE(puts("=> USBD_SendDescriptor : USB_DEVICE_DESCRIPTOR_TYPE\r\n");)
		if (setup.wLength >= 8)
		{
			_cdcComposite = 1;
		}
		desc_addr = _cdcComposite ?  (const uint8_t*)&USB_DeviceDescriptorA : (const uint8_t*)&USB_DeviceDescriptor;
                if( *desc_addr > setup.wLength ) {
                    desc_length = setup.wLength;
                }
	}

When I captured a USB protocol trace on my system, setup.wLength was 18 for the Get_Descriptor request.

Therefore, on Leonardo, setup.wLength will not equal 8 and _cdcComposite will not get set to 1. So the function will return USB_DeviceDescriptor, which has the Device Class set to 0.

On Due, setup.wLength will be greater than or equal to 8 and _cdcComposite will get set to 1. So the function will return USB_DeviceDescriptorA , which has the Device Class set to 2.

If the Arduino only wanted to expose a CDC interface, then a Device Descriptor with a Device Class of 2 is correct.
If the Arduino is exposing more than one interface, then it should only return a Device Descriptor with a Device Class of 0. This indicates that each interface will specify its own class information and operate independently.

Great! Thanks for the explanation. That is very helpful in understanding what is going on.

The question is what intent is Arduino meant for this option? I would think everyone will have problems with this out of the factory.

Is setting the Class to 0 the correct action? Or changing the comparison to match the avr code? Or something else?

I am having the same issue on WinXP;
so should I change

#ifdef CDC_ENABLED
#define DEVICE_CLASS 0x02
#else
#define DEVICE_CLASS 0x00
#endif

to

#ifdef CDC_ENABLED
#define DEVICE_CLASS 0x00
#else
#define DEVICE_CLASS 0x00
#endif

in arduino-1.5.1r2\hardware\arduino\sam\cores\arduino\USB\USBCore.cpp?

Yes, that would be the quick fix. That worked for me on various Windows (XP/7 32/64-bit) machines.
I am not sure what the long term fix will be.

answered also here:

Louis,

first thank you, I've seen the forum thread, great work.

This commit was due to the behaviour seen on older macosx (<=10.6.8 ).

We got the following facts:

macosx versions <= 10.6.8 sends a wLenght of 0x12
if we keep the == condition (==0x08) the CDC-Serial is not seen from macosx
changing the condition to >= 0x08 make it working

Note that this happens only with high-speed devices. If you attach a low-speed device macosx
requests a wLength of 0x08. This probably makes Leonardo unaffected.

I'm going to set the condition back to ==, but my question now is: there is a workaround to make it
working also on macos 10.6.8 and older?

I am not sure why there is a need for conditional code to send a different Device Descriptor depending on the length of the request.

If I understand the USB specs properly, the device should return the same Device Descriptor, no matter what the length is requested.

The USB code looks like it is trying to handle three scenarios:

  1. CDC only
  2. HID only
  3. Composite: CDC and HID

If the USB code is configured as CDC only, CDC_ENABLED defined and HID_ENABLED not defined, then it should have a Device Descriptor with Device Class of 2.
If the USB code is configured as HID only, CDC_ENABLED not defined and HID_ENABLED defined, then it should have a Device Descriptor with Device Class of 0.
If the USB code is configured as Composite, CDC_ENABLED defined and HID_ENABLED defined, then it should have a Device Descriptor with Device Class of 0.

I believe that you could simplify the code and accomplish this behavior by making the following changes to the code:

  1. Change the preprocessor macro to set Device Class to 0 if HID is defined, this covers the HID only and Composite cases. Otherwise, set Device Class to 2, this covers the CDC only case.
  2. Eliminate the alternate USB_DeviceDescriptor.
  3. Remove the conditional code based on request length.
#ifdef HID_ENABLED
#define DEVICE_CLASS 0x00
#else
#define DEVICE_CLASS 0x02
#endif

//	DEVICE DESCRIPTOR
const DeviceDescriptor USB_DeviceDescriptor =
	D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);

...

if (USB_DEVICE_DESCRIPTOR_TYPE == t)
	{
		TRACE_CORE(puts("=> USBD_SendDescriptor : USB_DEVICE_DESCRIPTOR_TYPE\r\n");)
		
		desc_addr = (const uint8_t*)&USB_DeviceDescriptor;
                if( *desc_addr > setup.wLength ) {
                    desc_length = setup.wLength;
                }
	}

I haven't fully tested this, but I think this will accomplish what is desired.

The question is why does it work on the most Windows Systems but not on all? What are the differences? There musst something that explains that.

I did some tests, replacing back the if condition to wLentgth==8, and with the full patch provided by Louis David,
here the results:

OS **wLength==8 ** LD patch
Win 7 ultimate (64) OK OK
Linux Ubuntu 11.04 (32) OK OK
MacOS 10.7.2 OK OK
MacOS 10.6.8 No Serial Port No Serial Port

With my Windows box I didn't experienced the BSoD, so probably both patches solves it as already reported in this thread.

For now I stick to what Leonardo do:

can you check if the latest ide-1.5.x solves the BSoD?

I'd like to apply the patch of Louis David, but I don't remember why the condition (with the duplicated descriptor) was introduced, I should clear that before.

Besides that, the question now is: what's the matter with macosx 10.6.8? Has anyone access to such SO and can explain what's happening?

I have attached the Due USB descriptor information captured from USB Prober on OS X 10.8.2.

It would be interesting to compare this information captured on 10.6.8

It would probably be helpful to have the captures from a failing case and a successful case on 10.6.8 to compare.

Arduino Due USB Descriptors.txt (13.1 KB)

Hi, you can find attached a couple of files generated by USB Prober on a Mac OS X 10.6.8, one for each behaviour (the names should be self-explanatory).

The "NoSerial" one is generated when on the Due is running a sketch (just a "SerialUSB.begin(115200);" and nothing else) uploaded with the IDE built from the fabc658a942de659d44f22f196701d04b857f094 commit.

The "SerialOK" one is generated uploading the same sketch with the same IDE, but after restoring the ">=8 patch" so that the serial port appears.

I hope this helps!

USBBusProbe_10.6.8_NoSerial.txt (13 KB)

USBBusProbe_10.6.8_SerialOK.txt (13 KB)

The only difference in the USB Prober captures is the Device Class in the Device Descriptor.

I did some more research and found some interesting posts to the Apple developers lists about a very similar issue:

http://lists.apple.com/archives/usb/2010/May/msg00007.html
http://lists.apple.com/archives/usb/2010/May/msg00009.html

It's not exactly clear which version of OS X is being discussed, but it is probably < 10.6.8.

The posts seem to indicate there were limitations in the way OS X handled USB composite devices with CDC in older versions of OS X.

Based on this info, I don't think it would be worth the effort to try and pursue a solution that would work for all current versions of OS and also 10.6.8.

Thoughts?

I'm afraid that this is the most significant evidence that confirms the hypothesis that Paul Stoffregen made during the test phase of the Due: we are facing a Snow Leopard bug in the CDC driver implementation that has been fixed in Lion only.

At this point, these are my thoughts:

Quick and dirty (temporary) solution: provide a way to implicitly set the device class code at runtime, when needed. In this case the workaround could be activated from within the sketch, from example reading the logic level on an input pin.

Trace the USB communication between the Mac and the Due and look for some kind of fingerprint that can tell us when the host is a Mac OS 10.6.8: this seems unlikely, but why not to try.

Provided that this could be a solution, develop a USB driver for the Due, but, as Louis Davis pointed out, this is hardly worth the effort...

I'll do some test on 2) within the next weekend and let you know.

After some more investigation on this issue, these two messages caught my attention:

http://lists.apple.com/archives/usb/2010/Nov/msg00040.html
http://lists.apple.com/archives/usb/2010/Nov/msg00041.html

Given that:

A) the USB probe output reported in the first message is related to a full speed USB device implementation;

B) at the date of the second message the latest released version of Mac OS X was 10.6.4; version 10.6.5 was released 5 days later (Mac OS X Snow Leopard - Wikipedia);

C) during the test phase of the Due no issue was experienced when the Due was still configured as a full speed device (I'll check this again in the next days, just to be shure...). Only when the high speed configuration is enabled the serial port is not detected (as cmaglie already pointed out);

this is my educated guess: is it possible that something has been fixed in 10.6.8?

In the second message Paul Stoffregen says that (only) mouse and disk appear on OS X (he doesn't mention the CDC-ACM Serial that is detected on Windows and Linux), but we experienced a different behaviour (as described in C) ).

Provided that the device configuration of Paul Stoffregen's demo is different (Miscellaneous/Common Class device), is it possible that a similar configuration can solve the problem? Of course this should be considered only a workaround, but, in my opinion, a better one compared to the 1) mentioned in my previous post.

For years, I and many others tried to convince Apple to add composite device support to their CDC driver.

In November 2010, I finally got through to the right people at Apple. They agreed, but said they didn't have any standards compliant devices for testing. I quickly made a test device based on a Teensy 2.0 and custom code. I tested it carefully on Windows and Linux, and ran it with the USB-IF command verifier tool. I shipped it to Cupertino and hoped for the best.

In early 2011, I emailed their developer. Apparently he fixed the driver in a matter of days, but Apple has a lengthy approval process for releasing new code. You can see a message from late 2010 where they mentioned my device and its descriptors as "will be supported on the Mac in the fairly near future"....

http://lists.apple.com/archives/usb/2010/Nov/msg00040.html

The "fairly near future" turned out to be 10.7 (Lion) released in the summer of 2011.

My understanding is 10.6.x has the old driver, which simply does not support CDC as part of a composite device.

However, if you really want to make this work on 10.6.x, many years ago I did discover a terrible hack that makes it work on Apple's old driver. It violates the USB standards and it not recognized by the Windows driver (at least the XP SP3 driver and Vista pre-SP1 driver), but the Linux driver is able to figure things out and use the device. Publishing code that does not work with Windows is not very practical.... but if anyone really wants that code, just email me directly, paul at pjrc dot com.

So guys, my Arduino DUE at some point is not recognized anymore if connected through Native Port. I am working with Windows 7 64bit.

I have another Arduino DUE and if I connect it through the native port it works.

What should I do?

Thanks for the help,
Fabrizio.

It is recognized again doing this procedure:

1- Press the erase button for 2-3 sec
2-Press the reset button for 8 seconds

I don´t know why, but now It works.

I hope this will help someone,
Fabrizio.