LUFA 2.0 API

I’ve come to realise that the current design of the LUFA API was a bad idea in many respects. Most people were confused by the simple “Scheduler” included in the demos, which was just a nice way (IMHO, of course) of thinking about the multitude of functions normally called in a loop within a project’s main function. Many people took a look at all the class boilerplate code required to implement each USB class in the demos and spat the dummy – I’m looking at you, Arduino programmers – and were turned off using the library.

As such, now that I’ve realised the library’s shortcomings, I’m moving towards correcting them. That means removing the psuedo-scheduler from the library, as well as making generic class drivers for both host and device mode inside the library that can be referenced by the user code if desired. Of course, this will not change the underlying API, and those wanting to implement their own classes (like in the case of the DFU bootloader) or use the low level library API will not be affected. However, this will mean that the focus in the demos will be on the demo’s functionality, and not all the USB code around it to get it to work.
Contrast the existing Keyboard device demo from LUFA 090510, with the current in-work version using the HID class driver I’m developing:

#include "Keyboard.h"

USB_ClassInfo_HID_t Keyboard_HID_Interface =
    {
        .InterfaceNumber         = 0,

        .ReportINEndpointNumber  = KEYBOARD_EPNUM,
        .ReportINEndpointSize    = KEYBOARD_EPSIZE,

        .ReportOUTEndpointNumber = KEYBOARD_LEDS_EPNUM,
        .ReportOUTEndpointSize   = KEYBOARD_EPSIZE,

        .IdleCount               = 500,
    };

int main(void)
{
    SetupHardware();

    LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);

    for (;;)
    {
        USB_HID_USBTask(&Keyboard_HID_Interface);
        USB_USBTask();
    }
}

void SetupHardware()
{
    /* Disable watchdog if enabled by bootloader/fuses */
    MCUSR &= ~(1 << WDRF);
    wdt_disable();

    /* Disable clock division */
    clock_prescale_set(clock_div_1);

    /* Hardware Initialization */
    Joystick_Init();
    LEDs_Init();
    Buttons_Init();
    USB_Init();
}

void EVENT_USB_Connect(void)
{
    LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING);
}

void EVENT_USB_Disconnect(void)
{
    LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
}

void EVENT_USB_ConfigurationChanged(void)
{
    LEDs_SetAllLEDs(LEDMASK_USB_READY);

    if (!(USB_HID_ConfigureEndpoints(&Keyboard_HID_Interface)))
      LEDs_SetAllLEDs(LEDMASK_USB_ERROR);
}

void EVENT_USB_UnhandledControlPacket(void)
{
    USB_HID_ProcessControlPacket(&Keyboard_HID_Interface);
}

void EVENT_USB_StartOfFrame(void)
{
    USB_HID_RegisterStartOfFrame(&Keyboard_HID_Interface);
}

uint16_t CALLBACK_USB_HID_CreateNextHIDReport(USB_ClassInfo_HID_t* HIDInterfaceInfo, void* ReportData)
{
    USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;

    uint8_t JoyStatus_LCL    = Joystick_GetStatus();
    uint8_t ButtonStatus_LCL = Buttons_GetStatus();

    if (JoyStatus_LCL & JOY_UP)
      KeyboardReport->KeyCode[0] = 0x04; // A
    else if (JoyStatus_LCL & JOY_DOWN)
      KeyboardReport->KeyCode[0] = 0x05; // B

    if (JoyStatus_LCL & JOY_LEFT)
      KeyboardReport->KeyCode[0] = 0x06; // C
    else if (JoyStatus_LCL & JOY_RIGHT)
      KeyboardReport->KeyCode[0] = 0x07; // D

    if (JoyStatus_LCL & JOY_PRESS)
      KeyboardReport->KeyCode[0] = 0x08; // E

    if (ButtonStatus_LCL & BUTTONS_BUTTON1)
      KeyboardReport->KeyCode[0] = 0x09; // F

    return sizeof(USB_KeyboardReport_Data_t);
}

void CALLBACK_USB_HID_ProcessReceivedHIDReport(USB_ClassInfo_HID_t* HIDInterfaceInfo, void* ReportData, uint16_t ReportSize)
{
    uint8_t  LEDMask   = LEDS_NO_LEDS;
    uint8_t* LEDReport = (uint8_t*)ReportData;

    if (*LEDReport & 0x01) // NUM Lock
      LEDMask |= LEDS_LED1;

    if (*LEDReport & 0x02) // CAPS Lock
      LEDMask |= LEDS_LED3;

    if (*LEDReport & 0x04) // SCROLL Lock
      LEDMask |= LEDS_LED4;

    LEDs_SetAllLEDs(LEDMask);
}

Yep, that’s the whole code, other than the supporting header files and descriptors. As you can see, it all revolves around the new HID class driver, which implements all the backend needed (between the user application and the LUFA library) for the HID USB class. You create a new global of each class driver’s type (for multiple instances of the same class, you make multiple instances of the class driver’s state structure), fill in the required values to match your device’s descriptors, and then link the LUFA events to the class driver functions. Each class driver can also define its own custom events and callbacks for the user application, which is the case here; the HID driver calls the CreateNextHIDReport() and ProcessReceivedHIDReport() functions when reports are sent and received.

This new system means more robust applications, as all the work is in the library itself; as the library is updated, so are all the class drivers. The best part is that it makes it trivial to create compound devices of several interfaces, both of the same class and of multiple different classes.

These changes will be visible in the next release of LUFA, scheduled in the next week or two.

 

Comments: 6

Leave a reply »

 
 
 

The problem is that when you started this over a year ago you didnt know what you know now. This is compounded by the fact that many of us have begun to incorporate your library in our own products. As you learn you change and this is good but when what your working on becomes the foundations of others work then the changes you make undermine the structures built on top of them.

The fact that so much has been built on your previous iteration attests to the fact that it wast a bad idea or even a bad design (I use the schedular regularly to do pulses and persistance of vision)

When you consider radical changes such as those above please consider the many of us who will have to completly rewrite our codebases.

Don.

 

Don,

Wise words. However, this new API layer is an effort to *stop* people having to rewrite their code, as the APIs per-class should be very stable. As of the next version, the low level APIs will also be very stable, so you and others can look forward to much less in the migration notes with each passing version – the recent API changes were made out of necessity (both to fix some architectural problems, as well as to make the final low-level API naming consistent).

As I say, the API will be stable from this point – barring possible tiny changes – as it’s now feature-rich and proven to work. However, the new class APIs will *NOT* invalidate use code with respect to the low-level APIs, they compliment them and are just a convenience. Users can (and will, if they need a class implementation not covered in the library) still use the same old APIs they have up until now, with the exception of the “scheduler”. The scheduler was an awful idea from the start, but those with legacy code can still just copy it out of previous versions into their application folder, and it will work just fine.

The goal of this next upcoming release will be:

1) Finalised, consistent low-level APIs, so that future library upgrades will be painless (possible compatibility layers to be included with future versions as needed)
2) All demos converted to use the new class-drivers, to simplify them as much as possible. Focus in demos will be on the use of the abstraction layers and the actual demo functionality, rather than all the library boilerplate per-class. Low level APIs will still be demonstrated through the bootloaders which will not use the class drivers to save space

– Dean

 

Ok so if I am hearing you correctly I should work on my current code in the April release expecting that the Api will not change and then evaluate the new apis as they are appropriate.

If so that will work well. I appreciate the great ammount of work that you have put into this library.

Don.

 

Don,

That’s correct, other than a few minor changes relating to removing much of the function (event, etc.) abstraction macros, which are all visible in the SVN. Take the current SVN revision to be indicative of the final low level APIs, as they are now in lockdown. Commits from now until the next release will be on the new optional class abstraction APIs and not on the low level library components.

– Dean

 

Make sure you don’t remove the examples of “low level” API usage from (all of) the practical demos. Perhaps a separate directory for the demos as they were before, even if it means you have to keep testing them for regressions.

 

Jeff,

That’s probably a good idea. I can’t even begin to imagine the amount of work its going to be maintaining all this…

– Dean

 

Leave a Reply

 
(will not be published)
 
 
Comment
 
 

 

Vital Stats

  • 35 Years Old
  • Australian
  • Lover of embedded systems
  • Firmware engineer
  • Self-Proclaimed Geek

Latest Blog Posts

RSS