LUFA hid / hub issues

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,
I've got a strange problem with a project under development which involves an at90usb1287 running as a generic hid device thanks to lufa 100219. The overall device also has two video capture dongles, both of which are to be plugged into a hub along with the atmel.

Sometimes it all runs just fine, but other times, when a video capture is unplugged and replugged into the hub it kicks the hid off. By this I mean it disappears from device manager (xp) and the atmel needs to be unplugged /replugged to get the hid going again. I've largely fixed this by adding a call to USB_ResetInterface() after a rtos delay triggered by either EVENT_USB_Device_Disconnect or EVENT_USB_Device_Suspend, which seems to do a good job at getting it working again, as well as changing the control handling to interrupt to remove some delay issues I had earlier (-D INTERRUPT_CONTROL_ENDPOINT).
At other times the replugging of a video causes a usb suspend&resume of the hid that the hid windows software we've got sees as a reset of the device. And then some other times the replug of video has no visible effect on the hid.

Now the kicker, sometimes when I do a unplug/replug of the capture, I get the hid disappear from device manager and the USB Device Not Recognized popup, every time this happens I pause the debugger (jtagice mkii) on the atmel and it's it's stuck in an infinite loop, in Template_Endpoint_Control_W.c, lines 33-35, in the loop:

while (!(Endpoint_IsOUTReceived()))
{
	if (USB_DeviceState == DEVICE_STATE_Unattached)
		 return ENDPOINT_RWCSTREAM_DeviceDisconnected;	
}

In theory unplugging the device should trigger DEVICE_STATE_Unattached and get out of this loop, but somehow the runtime has got here with global interrupts disabled, so the USB interrupt never gets run to update that flag.

By manually setting DEVICE_STATE_Unattached and stepping I get out and see the function exits to line 308 of DevChapter9.c, in USB_Device_GetDescriptor.

Other than that I don't know how it's got to this point with interrupts disabled. Either way, once it's in this loop unplugging/replugging usb doesn't help, it needs a power cycle of the atmel to wake it up. Or, I manually enabled the global interrupt and replugged the usb and it started fine then too.

Any suggestions would be much appreciated, as this is one of the last niggling issues I can't seem to fix.

Regardless of this issue, thanks so much for the LUFA libraries, they are worlds above any of the USB libraries/references I've seen from any chip manufacturer, and I've used a few.

Cheers,
Andrew

Electronics Design and other funky stuff -
alelec Engineering
http://www.alelec.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interesting. LUFA doesn't disable global interrupts once it has enabled them as part of the USB_Init() process - which is no longer the case as of the next release - so I'd look at your application code. Is there a point in your code where you disable global interrupts by calling sei()?

That might explain the hub issues as well - if the hub is sending a bus suspension request to the AVR and you've disabled interrupts globally, the AVR will not be able to enter and exit bus suspended mode correctly.

Also, you've made me realize that the stream functions don't have an early-abort if the bus is suspended during a transfer, which isn't a good thing. I'll add that into the next release.

- Dean :twisted:

PS: Glad you like my library!

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Dean,
Thanks for that, I just spent the last hour or so going through the code trying to find the global interrupt clearing part, and did find a couple of places it did it, usually only for brief periods of time while it did state saving, but it was there nonetheless.
I inherited this code from a friend, who's primarily a software programmer rather than firmware, and there's a bit to it that I'm not completely familiar with, including a small custom rtos which my friend had integrated lufa into when he did the initial port from atmel to your stack.

Getting rid of the other clearing bits didn't stop the problem, although it has seemed to make the usb a little more stable.
I've just realised where it's coming from, by forcing it out of the loop and stepping out far enough. It's all stemming down from USB_USBTask(); being run from the interrupt via INTERRUPT_CONTROL_ENDPOINT, the interrupts are automatically disabled as soon as it gets into the control interrupt. It appears to be getting stuck during the enumeration process, when the computer is requesting descriptors, it must be when unplug a video board, occasionally resetting the hid in the process, if I replug the video board while hid's being re-enumerated at the wrong point and interrupt the re-enumeration, it dies.

At this stage I guess I can either speed up the main loop enough to ensure the control usb can be run not from interrupts again, or just ensure the power sequencing in the device is set up right that it won't get a replug event in such a way to trigger this issue. Either way I'll keep working at it.

Thanks for the pointer,
Andrew

Electronics Design and other funky stuff -
alelec Engineering
http://www.alelec.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It's all stemming down from USB_USBTask(); being run from the interrupt via INTERRUPT_CONTROL_ENDPOINT, the interrupts are automatically disabled as soon as it gets into the control interrupt.

Oooh, that's a nasty condition I didn't think of. Off the top of my head it might be best for me to change the control endpoint ISR to disable the control endpoint interrupt, re-enable global interrupts, run USB_USBTask() and then reverse the procedure.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try this. Inside LUFA/Drivers/USB/HighLevel/USBInterrupt.c, replace the ISR at the bottom with this modified version:

#if defined(INTERRUPT_CONTROL_ENDPOINT) && defined(USB_CAN_BE_DEVICE)
ISR(USB_COM_vect, ISR_BLOCK)
{
        uint8_t PrevSelectedEndpoint = Endpoint_GetCurrentEndpoint();

	USB_INT_Disable(USB_INT_RXSTPI);
	sei();
        USB_USBTask();
	USB_INT_Enable(USB_INT_RXSTPI);

        USB_INT_Clear(USB_INT_RXSTPI);
        
        Endpoint_SelectEndpoint(PrevSelectedEndpoint);
}
#endif

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Dean,
I had actually tried something similar, although I just moved the clear line above usbtask and ran sei between them, hadn't thought to explicitly disable the interrupt as a better way to avoid re-entrancy.

My effort didn't work, pc said device not recognized, but now thinking back, I think I made the code changes to get control back in interrupt, and forgot to enable it in the makefile. Either that or my effort didn't work because of reentering the same interrupt infinitely.

Your code on the other hand works great, enumerates quickly and seems stable, I haven't seen the same lock-up as yet.

I had gone back to running usb-task in runtime, calling it multiple times in between a couple of other things in main loop seems to have fixed the latency issue.
Even with that working though, there's been a couple of occasions (after video replugs) I've been stuck in the same infinite loop in the same place - non-interrupt means global interrupt is on, and it still got stuck waiting. In this situation an unplug and replug of atmel is fine, gets it going again. I'd prefer control on interrupt, so I'll stick with your new block of code for now and see if I can get another lockup.

On another front, I've found a physical reason or trigger for all this happening, the video converters take a _big_ spike of power about 5-10 seconds after they're plugged in, must be as it fires up the analog circuitry or something. Either way, the power rail dips and it seems to be this power spike causing resets on the atmel, it often gets the vbus line low enough to fire an vbuserr interrupt. Sometimes this reset is fine and the hid comes back up, sometimes it errors.

It's probably a case of this happening at the wrong time while the atmel is waiting for a reply from the pc that it gets stuck. Maybe the "while (!(Endpoint_IsOUTReceived())) {}" loop I'm getting stuck in needs a timeout period or something, after which it resets the usb because something is obviously not right with the pc communication. Either way, I'm going to get the power situation sorted as well which should mean I never get to this situation.

Thanks for the code,
Andrew

Electronics Design and other funky stuff -
alelec Engineering
http://www.alelec.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well the changes so far seem to have pretty well fixed the enumeration problems, and I've confirmed that the replugging of video boards only resets the hid when a spike it created on the atmel's vbus line (It's self powered, so usb 5V goes straight to vbus). I added an bit of a filter onto the vref line and now no more resets on video replugs.

I've got a new issue however, we've got a custom client program for testing this, and sending a ping constantly (otherwise known as our request version number report) it works for an indeterminate amount of time, before the hid suddenly stops responding, usually after 3-10 minutes of ~3 reports per second.

When this happens the HID is still in device manager, and it still comes up as USB_DeviceState = DEVICE_STATE_Configured mode just fine on the debug. Shutting down and restarting the HID ping software does nothing to restart communication, I never get replies from the atmel to any requests. A reset gets it going again fine.

It's hard to diagnose, being so intermittent. Can you suggest anything to look at to find what might have stopped it responding? I though HIDInterfaceInfo might have been useful, as once when it stopped working this looked different to normal, but other times it looks fine.

There's a chance it might still be latency in running HID_Device_USBTask(), as there are a number of things running in the rtos. I'll try running this function from a separate timer interrupt, can you suggest a suitable rate I should run it at (10-20ms?)?
And/or even better, is there any flag or register I can check (or windows diagnostic software) that would indicate a latency as being a problem?

Thanks again,
Andrew

update: I've narrowed the dropouts to not being a problem with the lufa code at all, it's in our packet handling code and more specifically the rtos tasker, it's not fixed yet but I'm on the right track. I've still had an occasional case of stuck in the original infinite loop in Template_Endpoint_Control_W.c which seems most likely triggered by voltage spikes on the usb still, but I think I'm close to having all the USB working great.

Electronics Design and other funky stuff -
alelec Engineering
http://www.alelec.net

Last Edited: Wed. May 5, 2010 - 01:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Man, everyone seems to be having LUFA issues at the same time - and all in the week I need to finish a critical assignment. Murphy's law at work :P.

When the device stops responding, do you still get the HID class driver callbacks to create a new HID report? Are you returning true or false to the callback (force or do not force send) and what is the value of the HIDINTERFACE.State.IdleCount attribute when the lockup occurs?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey Dean,
Sorry for the difficulties! if you saw the update I just chucked up, my dropouts are rtos based, not lufa. It was because I am still getting CALLBACK_HID_Device_ProcessHIDReport that I managed to trace the issue all the way to our code :oops:. All I have left is the occasional lockup in the Template_Endpoint_Control_W which appears to be during re-enumeration, and dodgy hardware triggered. I'll bash that one out myself for a while if I can get it to happen reliably enough to diagnose it.
On the subject of CALLBACK_HID_Device_CreateHIDReport, we do return false, so it wont be forcing a send by the sound of it. The problem in our packet handling code means that we never try to send the reply to the received data, because it never gets through the handling code far enough to know it needs to send anything.

Sorry about the distractions ;-) and a big thanks for the help.
Andrew

Electronics Design and other funky stuff -
alelec Engineering
http://www.alelec.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No problem - it's heartening when I'm able to fix bug and help people solve their problems with LUFA; it seems everyone's got some problem or other with it this week with can be a bit depressing when you're the developer.

Returning false to the callback function just tells the driver to only send the report if the contents has changed from the last transmission, or the idle timeout has expired. This is how things are supposed to work, however there are some instances (like when the joystick is held down in the mouse demo) when you actually do want to send repeated packets, since they each mean something different to the host.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hah, please don't get depressed over it all - as I mentioned at the start, I wholeheartedly believe your library far exceeds every manufacturer usb library/examples I've ever worked with, and this includes atmel, ti(luminary), silabs, nxp and cypress. This is both in features and documentation, you've done a commendable effort.

Cheers!
Andrew

Electronics Design and other funky stuff -
alelec Engineering
http://www.alelec.net