Cannot find the correct handle to subscribe to in a HOGP object

Hello!

This is the third time I'm asking something about the same project, but I'm running into problems one after the other.

The current problem is this - the hogp object I have saved of a BLE keyboard I'm trying to read the input of has 6 HID handles.
I'm not sure which handle to subscribe to or if even bt_hogp_rep_subscribe() is the correct funciton to call. 

I've included the whole program and my prj.conf befow, and also shared  the relevant functions as code snippets.

/* --- Header --- */

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>

// The main lib itself includes
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>

#include <bluetooth/scan.h>
#include <bluetooth/gatt_dm.h>
#include <bluetooth/services/hogp.h>

#define TARGET_DEVICE_NAME "Flow84@Lofree"

struct bt_conn *target_conn;
struct bt_hogp target_hogp;
struct bt_hogp_rep_info *target_rep = NULL;

LOG_MODULE_REGISTER(ska_logging, LOG_LEVEL_INF);

/* --- End Header --- */

/* --- BLE Connection Handling --- */

static void scan_filter_match(struct bt_scan_device_info *device_info, struct bt_scan_filter_match *filter_match, bool connectable);
static void scan_filter_no_match(struct bt_scan_device_info *device_info, bool connectable);
static void connecting_error(struct bt_scan_device_info *device_info);
static void connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn);

static void exch_mtu_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params);
static void conn_connected(struct bt_conn *conn, uint8_t err);
static void disconnected_cb(struct bt_conn *conn, uint8_t reason)  { LOG_INF("Disconnected, reason: %d", reason); }

BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match, connecting_error, connecting);

static struct bt_conn_cb conn_callbacks = 
{
    .connected    = conn_connected,
    .disconnected = disconnected_cb,
};

static const struct bt_le_scan_param scan_params =
{
    .type     = BT_LE_SCAN_TYPE_ACTIVE,
    .options  = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
    .interval = BT_GAP_SCAN_FAST_INTERVAL,
    .window   = BT_GAP_SCAN_FAST_WINDOW,
};

static const struct bt_scan_init_param init_params = 
{
    .scan_param      = &scan_params,
    .connect_if_match = true,
    .conn_param      = NULL,
};

/* --- End BLE Connection Handling --- */

/* --- GATT Discovery Functions --- */

static void dm_completed(struct bt_gatt_dm *dm, void *context);
static void dm_error(struct bt_conn *conn, int err, void *context);
static void dm_service_not_found(struct bt_conn *conn, void *context);

static struct bt_gatt_dm_cb dm_cbs =
{
    .completed = dm_completed,
    .error_found = dm_error,
    .service_not_found = dm_service_not_found,
};

/* --- End GATT Discovery Functions */

/* --- HOGP Handling --- */

static void hogp_ready(struct bt_hogp *hogp);
static void hogp_init_error(struct bt_hogp *hogp, int error);
static void hogp_pm_update(struct bt_hogp *hogp);

static uint8_t hogp_read_cb(struct bt_hogp *hogp, struct bt_hogp_rep_info *rep, uint8_t err, const uint8_t *data);

static struct bt_hogp_init_params hogp_params =
{
    .ready_cb = hogp_ready,
    .prep_error_cb = hogp_init_error,
    .pm_update_cb = hogp_pm_update,
};



/* --- End HOGP Handling --- */

int main(void)
{
    int err;

    err = bt_enable(NULL);
    if(err) { LOG_INF("Error initializing Bluetooth stack."); return 1; }

    bt_hogp_init(&target_hogp, &hogp_params);

    bt_conn_cb_register(&conn_callbacks);

    // Note: The filtering needs to be defined after the scan object has been initialized
    bt_scan_init(&init_params);
    bt_scan_cb_register(&scan_cb);
    bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_HIDS);
    bt_scan_filter_enable(BT_SCAN_UUID_FILTER, true);

    bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);

    return 0;
}

static void scan_filter_match(struct bt_scan_device_info *device_info, struct bt_scan_filter_match *filter_match, bool connectable)
{
    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(device_info->recv_info->addr, addr_str, sizeof(addr_str));

    if(filter_match->uuid.match) { LOG_INF("HID Device found at addr %s with RSSI of %d db. The device is %s", addr_str, device_info->recv_info->rssi, connectable ? "connectable." : "not connectable"); }
}

static void scan_filter_no_match(struct bt_scan_device_info *device_info, bool connectable)
{
    // char addr_str[BT_ADDR_LE_STR_LEN];
    // bt_addr_le_to_str(device_info->recv_info->addr, addr_str, sizeof(addr_str));

    // LOG_INF("Non-HID device found at address %s with RSSI of %d db", addr_str, device_info->recv_info->rssi);
}

static void connecting_error(struct bt_scan_device_info *device_info) 
{ 
    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(device_info->recv_info->addr, addr_str, sizeof(addr_str));

    LOG_ERR("Error connecting to target device at address %s.", addr_str); 
    return;
}

// I'm doing quite a bit in the connecting function, but it's the only logical point to start most of the opetations
static void connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn)
{
    int err;

    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(device_info->recv_info->addr, addr_str, sizeof(addr_str));

    LOG_INF("Target device found at address %s, connecting...", addr_str);

    if(!conn) { LOG_ERR("Received NULL pointer for connection object, aborting."); return; }

    target_conn = bt_conn_ref(conn);
    
    LOG_INF("Connected! Connection information saved in the global variable. Now starting GATT discovery");

    // err = bt_gatt_dm_start(conn, BT_UUID_HIDS, &dm_cbs, NULL);
}

static void exch_mtu_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
{
    if (err) {
        LOG_ERR("MTU exchange failed (%u)", err);
        return;
    }
    LOG_INF("MTU exchanged, starting GATT discovery");
    bt_gatt_dm_start(conn, BT_UUID_HIDS, &dm_cbs, NULL);
}

static void conn_connected(struct bt_conn *conn, uint8_t err)
{
    if (err) {
        LOG_ERR("Connection failed (%u)", err);
        return;
    }
    LOG_INF("Connection ready, exchanging MTU");
    static struct bt_gatt_exchange_params exch = { .func = exch_mtu_cb };
    bt_gatt_exchange_mtu(conn, &exch);
}


static void dm_completed(struct bt_gatt_dm *dm, void *context) 
{ 
    LOG_INF("HID Service discovery complete!");

    int err = bt_hogp_handles_assign(dm, &target_hogp);
    if (err) {
        LOG_ERR("Failed to assign HOGP handles: %d", err);
        bt_gatt_dm_data_release(dm);
        return;
    }

    bt_gatt_dm_data_print(dm);
    // const struct bt_hogp_rep_info *rep = NULL;

    bt_gatt_dm_data_release(dm);
}

static void dm_error(struct bt_conn *conn, int err, void *context) { LOG_INF("Error when discovering HID service: %d", err); }

static void dm_service_not_found(struct bt_conn *conn, void *context) { LOG_INF("HID service not found."); }

static void hogp_ready(struct bt_hogp *hogp) 
{ 
    LOG_INF("The HOG profile has been created successfully. Now attempting to subscribe...");
    
    struct bt_hogp_rep_info *rep = bt_hogp_rep_find(hogp, BT_HIDS_REPORT_TYPE_INPUT, 1);

    size_t hid = bt_hogp_rep_count(hogp);

    LOG_INF("There are %zu HID reports in the HOGP object.", hid);
    
    int err = bt_hogp_rep_subscribe(hogp, rep, hogp_read_cb);
    if (err) { LOG_INF("There was an error subscribing to the hogp read report: %d", err); }

    
}

static void hogp_init_error(struct bt_hogp *hogp, int error) { LOG_INF("Error initializing the HOGP struct: %d", error); } 

static void hogp_pm_update(struct bt_hogp *hogp) {  }

static uint8_t hogp_read_cb(struct bt_hogp *hogp, struct bt_hogp_rep_info *rep, uint8_t err, const uint8_t *data)
{
    if (err) {
        LOG_ERR("Error in report notification: %d", err);
        return 0;
    }

    size_t size = bt_hogp_rep_size(rep);
    LOG_HEXDUMP_INF(data, size, "Received input report (keycodes):");
    return 0;
}
3513.prj.conf

The hogp ready callback that is subscribing to the handle and invoking the read callback:
static void hogp_ready(struct bt_hogp *hogp) 
{ 
    LOG_INF("The HOG profile has been created successfully. Now attempting to subscribe...");
    
    struct bt_hogp_rep_info *rep = bt_hogp_rep_find(hogp, BT_HIDS_REPORT_TYPE_INPUT, 1);

    size_t hid = bt_hogp_rep_count(hogp);

    LOG_INF("There are %zu HID reports in the HOGP object.", hid);
    
    int err = bt_hogp_rep_subscribe(hogp, rep, hogp_read_cb);
    if (err) { LOG_INF("There was an error subscribing to the hogp read report: %d", err); }

    
}


the read callback itself:
static uint8_t hogp_read_cb(struct bt_hogp *hogp, struct bt_hogp_rep_info *rep, uint8_t err, const uint8_t *data)
{
    if (err) {
        LOG_ERR("Error in report notification: %d", err);
        return 0;
    }

    size_t size = bt_hogp_rep_size(rep);
    LOG_HEXDUMP_INF(data, size, "Received input report (keycodes):");
    return 0;
}


Currently, the callback does get called, meaning the subscribe function does succeed, but it gets called once and that's it.
As far as I understand it, this callback should be called every time the keyboard sends out a report on that handle.

Any help with identifying the correct handle to subscribe to and what the correct flow for subscribing is would be greatly appreciated!

Related