USB HID Keyboard Device, Interrupts, and NAK

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

USB HID Keyboard Device, Interrupts, and NAK

2,736 Views
koolscooby
Contributor I

Hi Everyone!

 

I have a KDS 3.2.0 project setup for a client with SDK 2.0.

 

The USB descriptor structure is a USB Composite device with one USB HID device. The HID device had one HID report that includes descriptors for two different TLC report IDs. One report is for keyboard (report ID = 1) and one of the reports is for volume (report ID = 2).

 

For the second report ID, I've implemented Consumer Device Control to control volume. On the hardware, there are two buttons: vol up and vol down.

 

(By the way - the USB descriptor, enumeration, and HID report descriptors are all correct and debugged (using a Total Phase USB Debugger.))

 

Here's the report descriptor just for fun:

0x05, 0x01,        // Usage Page (Generic Desktop Ctrls) 0x09, 0x06,        // Usage (Keyboard) 0xA1, 0x01,        // Collection (Application) 0x85, 0x01,        //   Report ID (1) 0x75, 0x01,        //   Report Size (1) 0x95, 0x08,        //   Report Count (8) 0x05, 0x07,        //   Usage Page (Kbrd/Keypad) 0x19, 0xE0,        //   Usage Minimum (0xE0) 0x29, 0xE7,        //   Usage Maximum (0xE7) 0x15, 0x00,        //   Logical Minimum (0) 0x25, 0x01,        //   Logical Maximum (1) 0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x95, 0x01,        //   Report Count (1) 0x75, 0x08,        //   Report Size (8) 0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x95, 0x06,        //   Report Count (6) 0x75, 0x08,        //   Report Size (8) 0x15, 0x00,        //   Logical Minimum (0) 0x25, 0xFF,        //   Logical Maximum (255) 0x05, 0x07,        //   Usage Page (Kbrd/Keypad) 0x19, 0x00,        //   Usage Minimum (0x00) 0x29, 0xFF,        //   Usage Maximum (0xFF) 0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0,              // End Collection 0x05, 0x0C,        // Usage Page (Consumer) 0x09, 0x01,        // Usage (Consumer Control) 0xA1, 0x01,        // Collection (Application) 0x85, 0x02,        //   Report ID (2) 0x05, 0x0C,        //   Usage Page (Consumer) 0x19, 0x00,        //   Usage Minimum (Unassigned) 0x2A, 0xFF, 0x03,  //   Usage Maximum (0x03FF) 0x95, 0x01,        //   Report Count (1) 0x75, 0x10,        //   Report Size (16) 0x15, 0x00,        //   Logical Minimum (0) 0x27, 0xFF, 0x03, 0x00, 0x00,  //   Logical Maximum (1023) 0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0,              // End Collection

 

In order to get expected O/S (Windows, Mac, Linux, etc...) volume control behavior, I need to only send reports when the key is initially pressed - and then send no reports until the key is released.

 

For example:

* user presses volume up key *

[Interrupt] IN 0x02 0xE9 0x00

[Interrupt] NAK

[Interrupt] NAK

...

* user releases volume up key *

[Interrupt] NAK 0x00 0x00

 

In hid_keyboard.c, the example's code structure is setup so that USB_DeviceHidKeyboardCallback gets called back when the interrupt comes from the USB subsystem to transmit a packet for my endpoint. If the device is attached, then USB_DeviceHidKeyboardAction is called. The examples always send data back with USB_DeviceHidSend.

 

If I don't call USB_DeviceHidSend, the device seems to stop communicating with the host altogether. No future events are communicated, (nor is USB_DeviceHidKeyboardCallback ever called again.)

 

I tried calling USB_DeviceHidSend with length = 0, and yielded USB errors (since this is an invalid report based on my report descriptor.)

 

Instead of always sending back DATA, I need to send back a NAK, indicating to the USB Host that nothing has changed. Any USB gurus or experts know if there a way to do this?

 

I did a search through the source code for NAK and didn't find much... and I read through the USB FS reference manual and didn't see much by way of setting any registers that would yield a NAK.

 

My workaround right now is to send a report for the keyboard - report ID = 1. Anyone aware of why this might be a bad idea?

 

Thanks in advance, folks!

Labels (1)
2 Replies

1,837 Views
isaacavila
NXP Employee
NXP Employee

Hello Brad,

As fas as I know, USB module sends the NAK when there is no available data on desired endpoint, in this case, if you want to send NAKs you can try to implement this simple workaround (still need to validate that this is the best way to do it):

  • Send report data when needed.
  • In the moment when you want to send NAKs, you should not call the "USB_DeviceHidSend" API. After this, all incoming interrupt request will be replied by NAKs.
  • When you want to send reports again, you need to call the USB_DeviceHidSend API once. This is necessary in order to restore USB's state machine and USB callback could be called again.

As Reference, I modified the dev_hid_mouse_bm_frdmk64f example to emulate the behaviour that you desire. I am using a switch 3 (PTA4) interrupt to control when NAKs will be sent and same interrupt does restore transfers. I made these modifications:

  • Added two control variables to signal when NAKs will be sent and when reports should be restored.

/* Whether the SW button is pressed */

volatile bool g_bSendNACK = false;

volatile bool g_bStopTransaction = false;

  • In USB_DeviceHidMouseAction, I added:

/* Send mouse report to the host */

    usb_status_t ret = kStatus_USB_Error;

    if (!g_bSendNACK)

    {

        ret = USB_DeviceHidSend(g_UsbDeviceHidMouse.hidHandle, USB_HID_MOUSE_ENDPOINT_IN, g_UsbDeviceHidMouse.buffer,

                                     USB_HID_MOUSE_REPORT_LENGTH);

    }

    else

    {

        g_bStopTransaction = true;

    }

    return ret;

  • In main function, besides pin's initialization is performed, I managed these variables in interrupt handler:

/* Define the init structure for the input switch pin */

    gpio_pin_config_t sw_config = {

        kGPIO_DigitalInput, 0,

    };

    /* Define the init structure for the output LED pin */

    gpio_pin_config_t led_config = {

        kGPIO_DigitalOutput, 0,

    };

    BOARD_InitPins();

    BOARD_BootClockRUN();

    BOARD_InitDebugConsole();

    /* Init input switch GPIO. */

    PORT_SetPinInterruptConfig(PORTA, 4, kPORT_InterruptFallingEdge);

    EnableIRQ(PORTA_IRQn);

    GPIO_PinInit(GPIOA, 4, &sw_config);

    /* Init output LED GPIO. */

    GPIO_PinInit(GPIOB, 22, &led_config);

    USB_DeviceApplicationInit();

    GPIO_ClearPinsOutput(GPIOB, 1U << 22);

    while (1U)

    {

#if USB_DEVICE_CONFIG_USE_TASK

#if defined(USB_DEVICE_CONFIG_EHCI) && (USB_DEVICE_CONFIG_EHCI > 0U)

        USB_DeviceEhciTaskFunction(g_UsbDeviceHidMouse.deviceHandle);

#endif

#if defined(USB_DEVICE_CONFIG_KHCI) && (USB_DEVICE_CONFIG_KHCI > 0U)

        USB_DeviceKhciTaskFunction(g_UsbDeviceHidMouse.deviceHandle);

#endif

#endif

        if (g_bSendNACK)

        {

            GPIO_SetPinsOutput(GPIOB, 1U << 22);

        }

        else

        {

            if (g_bStopTransaction)

            {

                g_bStopTransaction = false;

                USB_DeviceHidSend(g_UsbDeviceHidMouse.hidHandle, USB_HID_MOUSE_ENDPOINT_IN, NULL,

                                                             NULL);

            }

            GPIO_ClearPinsOutput(GPIOB, 1U << 22);

        }

    }

  • And IRQ Handler looks as follows:

void PORTA_IRQHandler(void)

{

    /* Clear external interrupt flag. */

    GPIO_ClearPinsInterruptFlags(GPIOA, 1U << 4);

    /* Change state of button. */

    g_bSendNACK = !g_bSendNACK;

}

This way, I am able to send NAKs when SW3 is pressed and reports are restored everytime that SW3 is pressed again.

NAKs.jpg

I am attaching mouse.c file as well as pin_mux.c for full reference.

Hope this helps!

Regards,

Isaac

1,837 Views
koolscooby
Contributor I

Hi Isaac,

This looks like exactly what I need - thank you so much for the prompt reply. I will try and implement this tomorrow and report back with my findings.

Cheers!
Brad

0 Kudos