This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

HID keyboard example - What does the buffer do and why it needs to be dequeued?

Hi, this question could be ridiculous question due to my lack of data structure knowledge.

As you see in the HID keyboard example (SDK 7.2), there are several buffers.

typedef struct hid_key_buffer
{
     uint8_t    data_offset;       /**< Max Data that can be buffered for all entries */
     uint8_t    data_len;          /**< Total length of data */
     uint8_t    * p_data;          /**< Scanned key pattern */
     ble_hids_t * p_instance;      /**< Identifies peer and service instance */
} buffer_entry_t;                  /** Abstracts buffer element */


#define MAX_BUFFER_ENTRIES               5      /**< Number of elements that can be enqueued */

typedef struct                                  /** Circular buffer list */
{
     buffer_entry_t buffer[MAX_BUFFER_ENTRIES]; /**< Maximum number of entries that can enqueued in the list */
     uint8_t        rp;                         /**< Index to the read location */
     uint8_t        wp;                         /**< Index to write location */
     uint8_t        count;                      /**< Number of elements in the list */
} buffer_list_t;

static uint8_t m_sample_key_press_scan_str[] ={
     0x0b, 0x08, 0x0f, 0x0f, 0x12, 0x28 }; 
     // each keys are h, e, l, l, o, Return

After sending keys (which is h, e, l, l, and o),

the program goes to on_ble_evt function since BLE_EVT_TX_COMPLETE has occurred.

static void on_ble_evt(ble_evt_t * p_ble_evt){ // I omitted other parts

     switch (p_ble_evt->header.evt_id){
 
          case BLE_EVT_TX_COMPLETE:
                  // Send next key event
                  (void) buffer_dequeue(true);
                  break;
     }
}


#define BUFFER_LIST_FULL()\    /** Provide status of data list is full or not */
        ((MAX_BUFFER_ENTRIES == buffer_list.count - 1) ? true : false)

#define BUFFER_LIST_EMPTY()\   /** Provides status of buffer list is empty or not */
        ((0 == buffer_list.count) ? true : false)

#define BUFFER_ELEMENT_INIT(i)\
         do                                                                                        \
         {                                                                                         \
             buffer_list.buffer[(i)].p_data = NULL;                                                \
          } while (0)

static uint32_t buffer_dequeue(bool tx_flag)
{
   buffer_entry_t * p_element;
   uint32_t         err_code = NRF_SUCCESS;
   uint16_t         actual_len;

   if (BUFFER_LIST_EMPTY()) 
   {
       err_code = NRF_ERROR_NOT_FOUND;
   }
   else
   {
       bool remove_element = true;

       p_element = &buffer_list.buffer[(buffer_list.rp)];

       if (tx_flag)
       {
           err_code = send_key_scan_press_release(p_element->p_instance,
                                                  p_element->p_data,
                                                  p_element->data_len,
                                                  p_element->data_offset,
                                                  &actual_len);
           // An additional notification is needed for release of all keys, therefore check
           // is for actual_len <= element->data_len and not actual_len < element->data_len
           if ((err_code == BLE_ERROR_NO_TX_BUFFERS) && (actual_len <= p_element->data_len))
           {
               // Transmission could not be completed, do not remove the entry, adjust next data to
               // be transmitted
               p_element->data_offset = actual_len;
               remove_element         = false;
           }
       }

       if (remove_element)
       {
           BUFFER_ELEMENT_INIT(buffer_list.rp);

           buffer_list.rp++;
           buffer_list.count--;

           if (buffer_list.rp == MAX_BUFFER_ENTRIES)
           {
               buffer_list.rp = 0;
           }
       }
   }

   return err_code;
}

Comparing with the HID mouse example, the mouse example did not had any buffers at all.

So my questions are,

  • Why did the keyboard example implemented the circular buffer?

What does these buffers do?

(I hope this can help me to find the reason why the buffer needs dequeued

after every BLE_EVT_TX_COMPLETE event.)

  • I want to manipulated the keyboard example.

If I don't dequeue every BLE_EVT_TX_COMPLETE event, does the HID key sending fails?

-Regards, Mango922

  • Hi Mango,

    This is not a ridiculous question.

    The buffer functionality an complex solution to obtain something simple; loop through the string "hello" in a HID appropriate format. I have a reply to an older case here where I have explained a bit about this: devzone.nordicsemi.com/.../

    If you do not dequeue in BLE_EVT_TX_COMPLETE (or BLE_GAP_EVT_DISCONNECT) you will go into the scenario where you do not flush your buffer. The next time you send a key, you will then be stuck at the first element, which is 'h', until you fill up your queue and the application stops working properly.

    If you are looking for a solution to base your keyboard matrix upon, I would recommend looking at our nRFready Desktop 2 solution.

    Cheers, Håkon

  • Thanks for the Desktop 2 solution. I'm afraid I understanded some parts incorrectly.

    • Does the original HID keyboard example sends "hello" all at once when I press a button? In my case, I receive one character per press. (h -> e -> l -> l -> o -> h -> e -> ...)

    • You mean the dequeue function's purpose is not to dequeue the buffers after buffer_enqueue was called? Does the dequeue function always has to be called at BLE_EVT_TX_COMPLETE regardless of buffer_enqueue was called or not?

    Sorry to add more questions. I just want to implement a simple keyboard sending one key per button click.

  • Pardon me Håkon.

    In short I changed the code like this.

    At button_event_handler, when I press a button,

    I send one key. (For example, I send only 'h' at each press.)

    static uint8_t my_key = 0x0b;
    
    keys_send(1,& my_key);
    

    After that I send 0 mask at BLE_EVT_TX_COMPLETE event and I did not call buffer_dequeue.

    The code worked. Is this okay?

  • If you try to send another char, it will then still send '0x0b' instead of your intended char, as it has not been de-queued from the buffer. It will still work in the sense that the program runs, but it would not behave as it should :-)

  • Hmm... Acutally, it does behave strange after I disconnect the device. I was facing that issue quite long. Anyway thanks for your detailed answer. I'll write the issue later or so.

Related