diff --git a/service/stacks/zephyr/sal_hid_device_interface.c b/service/stacks/zephyr/sal_hid_device_interface.c index ce997e8ba..f7892a404 100644 --- a/service/stacks/zephyr/sal_hid_device_interface.c +++ b/service/stacks/zephyr/sal_hid_device_interface.c @@ -31,6 +31,7 @@ #include "sal_zblue.h" #include "service_loop.h" +#define BT_HID_PROFILE_VERSION 0x0100 #define BT_HID_DEVICE_VERSION 0x0101 #define BT_HID_PARSER_VERSION 0x0111 #define BT_HID_DEVICE_SUBCLASS 0xc0 @@ -47,11 +48,22 @@ #define BT_HID_DEVICE_REPORT_DESC_SIZE 256 #define BT_HID_DEVICE_DESC_VALUE_INDEX 3 +/* struct bt_hid_report field offsets (with alignment padding) */ +#define BT_HID_REPORT_TYPE_OFFSET 0 +#define BT_HID_REPORT_LEN_OFFSET 4 +#define BT_HID_REPORT_DATA_OFFSET 8 + +/* Minimum data lengths for report parsing */ +#define BT_HID_REPORT_HDR_SIZE 8 +#define BT_HID_REPORT_MIN_RPT_ID 9 +#define BT_HID_REPORT_MIN_BUF_SIZE 11 + typedef struct sal_hid_connection { struct bt_hid_device* hid_device; bt_address_t addr; struct bt_conn* conn; bool le_hid; + uint8_t protocol; } sal_hid_connection_t; typedef struct sal_bt_hid_device_mgr { @@ -92,7 +104,7 @@ static struct bt_sdp_attribute hid_attrs_template[] = { { BT_SDP_TYPE_SIZE(BT_SDP_UUID16), BT_SDP_ARRAY_16(BT_SDP_HID_SVCLASS) }, { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), - BT_SDP_ARRAY_16(BT_HID_DEVICE_VERSION) }) })), + BT_SDP_ARRAY_16(BT_HID_PROFILE_VERSION) }) })), BT_SDP_LIST( BT_SDP_ATTR_ADD_PROTO_DESC_LIST, BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 15), @@ -110,6 +122,9 @@ static struct bt_sdp_attribute hid_attrs_template[] = { { BT_SDP_TYPE_SIZE(BT_SDP_UUID16), BT_SDP_ARRAY_16(BT_SDP_PROTO_HID) }) }) })), BT_SDP_SERVICE_NAME("HID CONTROL"), + { BT_SDP_ATTR_HID_DEVICE_RELEASE_NUMBER, + { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(BT_HID_DEVICE_VERSION) } }, { BT_SDP_ATTR_HID_PARSER_VERSION, { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), BT_SDP_ARRAY_16(BT_HID_PARSER_VERSION) } }, @@ -156,6 +171,12 @@ static struct bt_sdp_attribute hid_attrs_template[] = { BT_SDP_ARRAY_16(BT_HID_LANG_ID_OFFSET), }), })), + { BT_SDP_ATTR_HID_NORMALLY_CONNECTABLE, + { BT_SDP_TYPE_SIZE(BT_SDP_BOOL), + BT_SDP_ARRAY_8(0x01) } }, + { BT_SDP_ATTR_HID_PROFILE_VERSION, + { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(BT_HID_PROFILE_VERSION) } }, { BT_SDP_ATTR_HID_BOOT_DEVICE, { BT_SDP_TYPE_SIZE(BT_SDP_BOOL), BT_SDP_ARRAY_8(0x01) } }, @@ -238,6 +259,7 @@ static sal_hid_connection_t* hid_connection_new(bt_address_t* addr, struct bt_co memcpy(&hid_conn->addr, addr, sizeof(bt_address_t)); hid_conn->conn = conn; + hid_conn->protocol = BT_HID_PROTOCOL_REPORT_MODE; bt_conn_ref(conn); return hid_conn; @@ -466,9 +488,15 @@ static void hid_disconnected_callback(struct bt_hid_device* hid) void hid_set_report_callback(struct bt_hid_device* hid, const uint8_t* data, uint16_t len) { sal_hid_connection_t* hid_conn; + bt_address_t addr; BT_LOGD("hid:%p set report cb, len:%d", hid, len); + if (!data || len < 1) { + BT_LOGE("hid:%p set report data invalid, data:%p len:%d", hid, data, len); + return; + } + hid_conn_lock(); hid_conn = hid_find_connections_by_device(hid); if (!hid_conn) { @@ -477,16 +505,27 @@ void hid_set_report_callback(struct bt_hid_device* hid, const uint8_t* data, uin return; } + memcpy(&addr, &hid_conn->addr, sizeof(bt_address_t)); hid_conn_unlock(); - hid_device_on_set_report(&hid_conn->addr, data[0], len - 1, (uint8_t*)&data[1]); + hid_device_on_set_report(&addr, data[0], len - 1, (uint8_t*)&data[1]); } void hid_get_report_callback(struct bt_hid_device* hid, const uint8_t* data, uint16_t len) { sal_hid_connection_t* hid_conn; + bt_address_t addr; + uint8_t rpt_type; + uint8_t rpt_id = 0; + uint16_t buffer_size = 0; + int rpt_len; BT_LOGD("hid:%p get report cb, len:%d", hid, len); + if (len < BT_HID_REPORT_HDR_SIZE) { + BT_LOGE("hid:%p get report data too short: %d", hid, len); + return; + } + hid_conn_lock(); hid_conn = hid_find_connections_by_device(hid); if (!hid_conn) { @@ -495,13 +534,40 @@ void hid_get_report_callback(struct bt_hid_device* hid, const uint8_t* data, uin return; } + memcpy(&addr, &hid_conn->addr, sizeof(bt_address_t)); hid_conn_unlock(); - hid_device_on_get_report(&hid_conn->addr, data[0], data[1], data[2]); + + /* data points to struct bt_hid_report: + * uint8_t type; (offset BT_HID_REPORT_TYPE_OFFSET) + * int len; (offset BT_HID_REPORT_LEN_OFFSET, due to alignment) + * uint8_t data[]; (offset BT_HID_REPORT_DATA_OFFSET) + */ + rpt_type = data[BT_HID_REPORT_TYPE_OFFSET]; + memcpy(&rpt_len, &data[BT_HID_REPORT_LEN_OFFSET], sizeof(int)); + + if (rpt_len >= 1 && len >= BT_HID_REPORT_MIN_RPT_ID) { + rpt_id = data[BT_HID_REPORT_DATA_OFFSET]; + } + + if (rpt_len >= 3 && len >= BT_HID_REPORT_MIN_BUF_SIZE) { + buffer_size = data[BT_HID_REPORT_DATA_OFFSET + 1] | (data[BT_HID_REPORT_DATA_OFFSET + 2] << 8); + } + + hid_device_on_get_report(&addr, rpt_type, rpt_id, buffer_size); } void hid_set_protocol_callback(struct bt_hid_device* hid, uint8_t protocol) { + sal_hid_connection_t* hid_conn; + BT_LOGD("hid:%p set protocol:%d, ", hid, protocol); + + hid_conn_lock(); + hid_conn = hid_find_connections_by_device(hid); + if (hid_conn) { + hid_conn->protocol = protocol; + } + hid_conn_unlock(); } typedef struct { @@ -645,6 +711,7 @@ void hid_get_protocol_callback(struct bt_hid_device* hid) { hid_send_data_param_t* params; sal_hid_connection_t* hid_conn; + uint8_t protocol; BT_LOGD("hid:%p get protocol", hid); @@ -656,6 +723,8 @@ void hid_get_protocol_callback(struct bt_hid_device* hid) return; } + protocol = hid_conn->protocol; + params = zalloc(sizeof(hid_send_data_param_t)); if (!params) { hid_conn_unlock(); @@ -665,7 +734,7 @@ void hid_get_protocol_callback(struct bt_hid_device* hid) memcpy(¶ms->addr, &hid_conn->addr, sizeof(bt_address_t)); params->type = BT_HID_REPORT_TYPE_OTHER; - params->data[0] = BT_HID_PROTOCOL_REPORT_MODE; + params->data[0] = protocol; params->len = sizeof(uint8_t); hid_conn_unlock(); diff --git a/tools/hid_device.c b/tools/hid_device.c index 556cb7969..655414d11 100644 --- a/tools/hid_device.c +++ b/tools/hid_device.c @@ -202,27 +202,36 @@ static void hidd_connection_state_cb(void* cookie, bt_address_t* addr, bool le_h static void hidd_get_report_cb(void* cookie, bt_address_t* addr, uint8_t rpt_type, uint8_t rpt_id, uint16_t buffer_size) { + extern bt_instance_t* g_bttool_ins; uint8_t rpt_data[] = { 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; char addr_str[BT_ADDR_STR_LENGTH] = { 0 }; bt_addr_ba2str(addr, addr_str); - PRINT("%s, addr:%s, buffer size: %d", __func__, addr_str, buffer_size); + PRINT("%s, addr:%s, rpt_type:%d, rpt_id:%d, buffer size: %d", + __func__, addr_str, rpt_type, rpt_id, buffer_size); + + /* Only report ID 0x01 and 0x02 are valid per HID descriptor */ + if (rpt_id != 0x01 && rpt_id != 0x02) { + bt_hid_device_report_error(g_bttool_ins, addr, HID_STATUS_HANDSHAKE_INVALID_REPORT_ID); + return; + } if (rpt_id != 0) rpt_data[0] = rpt_id; - bt_hid_device_response_report(cookie, addr, rpt_type, rpt_data, sizeof(rpt_data)); + bt_hid_device_response_report(g_bttool_ins, addr, rpt_type, rpt_data, sizeof(rpt_data)); } static void hidd_set_report_cb(void* cookie, bt_address_t* addr, uint8_t rpt_type, uint16_t rpt_size, uint8_t* rpt_data) { + extern bt_instance_t* g_bttool_ins; char addr_str[BT_ADDR_STR_LENGTH] = { 0 }; bt_addr_ba2str(addr, addr_str); PRINT("%s, addr:%s, report type: %d", __func__, addr_str, rpt_type); lib_dumpbuffer("report data:", rpt_data, rpt_size); - bt_hid_device_report_error(cookie, addr, HID_STATUS_OK); + bt_hid_device_report_error(g_bttool_ins, addr, HID_STATUS_OK); } static void hidd_receive_report_cb(void* cookie, bt_address_t* addr, uint8_t rpt_type,