| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2019-2025, Jacques Gagnon | ||
| 3 | * SPDX-License-Identifier: Apache-2.0 | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include "host.h" | ||
| 8 | #include "hci.h" | ||
| 9 | #include "att.h" | ||
| 10 | #include "att_cfg.h" | ||
| 11 | #include "att_hid.h" | ||
| 12 | #include "mon.h" | ||
| 13 | #include "zephyr/uuid.h" | ||
| 14 | #include "zephyr/att.h" | ||
| 15 | #include "zephyr/gatt.h" | ||
| 16 | #include "adapter/hid_parser.h" | ||
| 17 | #include "adapter/mapping_quirks.h" | ||
| 18 | #include "hidp/sw2.h" | ||
| 19 | |||
| 20 | enum { | ||
| 21 | BT_ATT_HID_DEVICE_NAME = 0, | ||
| 22 | BT_ATT_HID_APPEARANCE, | ||
| 23 | BT_ATT_HID_PNP, | ||
| 24 | BT_ATT_HID_FIND_HID_HDLS, | ||
| 25 | BT_ATT_HID_IDENT_HID_HLDS, | ||
| 26 | BT_ATT_HID_CHAR_PROP, | ||
| 27 | BT_ATT_HID_REPORT_MAP, | ||
| 28 | BT_ATT_HID_REPORT_REF, | ||
| 29 | BT_ATT_HID_REPORT_CFG, | ||
| 30 | BT_ATT_HID_PPCP_CFG, | ||
| 31 | BT_ATT_HID_BATT_LVL, | ||
| 32 | BT_ATT_HID_INIT, | ||
| 33 | BT_ATT_HID_STATE_MAX, | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct bt_att_read_type_data { | ||
| 37 | uint16_t handle; | ||
| 38 | uint8_t char_prop; | ||
| 39 | uint16_t char_value_handle; | ||
| 40 | uint16_t uuid; | ||
| 41 | } __packed; | ||
| 42 | |||
| 43 | struct bt_att_report_ref { | ||
| 44 | uint8_t report_id; | ||
| 45 | uint8_t report_type; | ||
| 46 | } __packed; | ||
| 47 | |||
| 48 | struct bt_att_group_data_uuid16 { | ||
| 49 | uint16_t start_handle; | ||
| 50 | uint16_t end_handle; | ||
| 51 | uint16_t uuid; | ||
| 52 | } __packed; | ||
| 53 | |||
| 54 | struct bt_att_hid_report { | ||
| 55 | uint8_t id; | ||
| 56 | uint8_t type; | ||
| 57 | uint8_t char_prop; | ||
| 58 | uint16_t report_hdl; | ||
| 59 | uint16_t cfg_hdl; | ||
| 60 | uint16_t ref_hdl; | ||
| 61 | }; | ||
| 62 | |||
| 63 | struct bt_att_hid { | ||
| 64 | char device_name[32]; | ||
| 65 | uint16_t appearance; | ||
| 66 | uint16_t dev_name_hdl; | ||
| 67 | uint16_t start_hdl; | ||
| 68 | uint16_t end_hdl; | ||
| 69 | uint16_t map_hdl; | ||
| 70 | uint8_t report_cnt; | ||
| 71 | uint8_t report_found; | ||
| 72 | uint8_t report_idx; | ||
| 73 | struct bt_att_hid_report reports[HID_MAX_REPORT]; | ||
| 74 | }; | ||
| 75 | |||
| 76 | typedef void (*bit_att_hid_start_func_t)(struct bt_dev *device, | ||
| 77 | struct bt_att_hid *hid_data); | ||
| 78 | typedef void (*bit_att_hid_process_func_t)(struct bt_dev *device, | ||
| 79 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len); | ||
| 80 | |||
| 81 | static struct bt_att_hid att_hid[BT_MAX_DEV] = {0}; | ||
| 82 | |||
| 83 | static void bt_att_hid_start_next_state(struct bt_dev *device, | ||
| 84 | struct bt_att_hid *hid_data); | ||
| 85 | |||
| 86 | ✗ | static void bt_att_hid_start_device_name(struct bt_dev *device, | |
| 87 | struct bt_att_hid *hid_data) { | ||
| 88 | ✗ | bt_att_cmd_read_type_req_uuid16(device->acl_handle, 0x0001, 0xFFFF, BT_UUID_GAP_DEVICE_NAME); | |
| 89 | ✗ | } | |
| 90 | |||
| 91 | ✗ | static void bt_att_hid_process_device_name(struct bt_dev *device, | |
| 92 | struct bt_att_hid *hid_data, uint32_t att_len, | ||
| 93 | uint8_t *data, uint32_t data_len) { | ||
| 94 | ✗ | char tmp[32] = {0}; | |
| 95 | ✗ | uint32_t name_len = data_len + strlen(hid_data->device_name); | |
| 96 | |||
| 97 | ✗ | if (data) { | |
| 98 | ✗ | if (name_len < sizeof(hid_data->device_name)) { | |
| 99 | ✗ | memcpy(tmp, data, data_len); | |
| 100 | ✗ | strcat(hid_data->device_name, tmp); | |
| 101 | ✗ | if (att_len == device->mtu) { | |
| 102 | ✗ | bt_att_cmd_read_blob_req(device->acl_handle, | |
| 103 | ✗ | hid_data->dev_name_hdl, strlen(hid_data->device_name)); | |
| 104 | ✗ | return; | |
| 105 | } | ||
| 106 | } | ||
| 107 | else { | ||
| 108 | ✗ | memcpy(tmp, data, | |
| 109 | sizeof(hid_data->device_name) - strlen(hid_data->device_name) - 1); | ||
| 110 | ✗ | strcat(hid_data->device_name, tmp); | |
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | ✗ | bt_hid_set_type_flags_from_name(device, hid_data->device_name); | |
| 115 | ✗ | printf("dev: %ld type: %ld %s\n", device->ids.id, device->ids.type, hid_data->device_name); | |
| 116 | ✗ | bt_mon_log(true, "dev: %ld type: %ld %s\n", device->ids.id, device->ids.type, hid_data->device_name); | |
| 117 | |||
| 118 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 119 | } | ||
| 120 | |||
| 121 | ✗ | static void bt_att_hid_start_appearance(struct bt_dev *device, | |
| 122 | struct bt_att_hid *hid_data) { | ||
| 123 | ✗ | bt_att_cmd_read_type_req_uuid16(device->acl_handle, 0x0001, 0xFFFF, BT_UUID_GAP_APPEARANCE); | |
| 124 | ✗ | } | |
| 125 | |||
| 126 | ✗ | static void bt_att_hid_process_appearance(struct bt_dev *device, | |
| 127 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 128 | ✗ | if (data) { | |
| 129 | ✗ | hid_data->appearance = *(uint16_t *)data; | |
| 130 | } | ||
| 131 | ✗ | printf("dev: %ld appearance: %03X:%02X\n", | |
| 132 | ✗ | device->ids.id, hid_data->appearance >> 6, hid_data->appearance & 0x3F); | |
| 133 | ✗ | bt_mon_log(true, "dev: %ld appearance: %03X:%02X\n", | |
| 134 | ✗ | device->ids.id, hid_data->appearance >> 6, hid_data->appearance & 0x3F); | |
| 135 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 136 | ✗ | } | |
| 137 | |||
| 138 | ✗ | static void bt_att_hid_start_pnp(struct bt_dev *device, | |
| 139 | struct bt_att_hid *hid_data) { | ||
| 140 | ✗ | bt_att_cmd_read_type_req_uuid16(device->acl_handle, 0x0001, 0xFFFF, BT_UUID_DIS_PNP_ID); | |
| 141 | ✗ | } | |
| 142 | |||
| 143 | ✗ | static void bt_att_hid_process_pnp(struct bt_dev *device, | |
| 144 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 145 | ✗ | struct bt_data *bt_data = &bt_adapter.data[device->ids.id]; | |
| 146 | ✗ | if (data) { | |
| 147 | ✗ | bt_data->base.vid = *(uint16_t *)&data[1]; | |
| 148 | ✗ | bt_data->base.pid = *(uint16_t *)&data[3]; | |
| 149 | } | ||
| 150 | ✗ | printf("%s: VID: 0x%04X PID: 0x%04X\n", __FUNCTION__, bt_data->base.vid, bt_data->base.pid); | |
| 151 | ✗ | bt_mon_log(true, "%s: VID: 0x%04X PID: 0x%04X\n", __FUNCTION__, bt_data->base.vid, bt_data->base.pid); | |
| 152 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 153 | ✗ | } | |
| 154 | |||
| 155 | ✗ | static void bt_att_hid_start_find_hid_hdls(struct bt_dev *device, | |
| 156 | struct bt_att_hid *hid_data) { | ||
| 157 | ✗ | bt_att_cmd_read_group_req_uuid16(device->acl_handle, 0x0001, BT_UUID_GATT_PRIMARY); | |
| 158 | ✗ | } | |
| 159 | |||
| 160 | ✗ | static void bt_att_hid_process_find_hid_hdls(struct bt_dev *device, | |
| 161 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 162 | ✗ | if (data) { | |
| 163 | ✗ | const uint32_t elem_cnt = (att_len - 2) / data_len; | |
| 164 | |||
| 165 | ✗ | if (data_len == 6) { | |
| 166 | struct bt_att_group_data_uuid16 *elem = (struct bt_att_group_data_uuid16 *)data; | ||
| 167 | ✗ | for (uint32_t i = 0; i < elem_cnt; i++) { | |
| 168 | ✗ | if (elem[i].uuid == BT_UUID_HIDS) { | |
| 169 | ✗ | hid_data->start_hdl = elem[i].start_handle; | |
| 170 | ✗ | hid_data->end_hdl = elem[i].end_handle; | |
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | ✗ | struct bt_att_group_data *last_elem = (struct bt_att_group_data *)((uint8_t *)data + data_len * (elem_cnt - 1)); | |
| 175 | ✗ | bt_att_cmd_read_group_req_uuid16(device->acl_handle, last_elem->end_handle, BT_UUID_GATT_PRIMARY); | |
| 176 | } | ||
| 177 | else { | ||
| 178 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 179 | } | ||
| 180 | ✗ | } | |
| 181 | |||
| 182 | ✗ | static void bt_att_hid_start_ident_hid_hdls(struct bt_dev *device, | |
| 183 | struct bt_att_hid *hid_data) { | ||
| 184 | ✗ | bt_att_cmd_find_info_req(device->acl_handle, hid_data->start_hdl, hid_data->end_hdl); | |
| 185 | ✗ | } | |
| 186 | |||
| 187 | ✗ | static void bt_att_hid_process_ident_hid_hdls(struct bt_dev *device, | |
| 188 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *info, uint32_t format) { | ||
| 189 | ✗ | if (info) { | |
| 190 | ✗ | uint16_t last_handle; | |
| 191 | ✗ | struct bt_att_info_16 *info16; | |
| 192 | ✗ | struct bt_att_info_128 *info128; | |
| 193 | ✗ | uint32_t elem_cnt; | |
| 194 | |||
| 195 | ✗ | if (format == BT_ATT_INFO_16) { | |
| 196 | ✗ | info16 = (struct bt_att_info_16 *)info; | |
| 197 | ✗ | elem_cnt = (att_len - 2)/sizeof(*info16); | |
| 198 | |||
| 199 | ✗ | last_handle = info16[elem_cnt - 1].handle; | |
| 200 | } | ||
| 201 | else { | ||
| 202 | ✗ | info128 = (struct bt_att_info_128 *)info; | |
| 203 | ✗ | elem_cnt = (att_len - 2)/sizeof(*info128); | |
| 204 | |||
| 205 | ✗ | last_handle = info128[elem_cnt - 1].handle; | |
| 206 | } | ||
| 207 | |||
| 208 | ✗ | for (uint32_t i = 0; i < elem_cnt; i++) { | |
| 209 | ✗ | uint16_t handle; | |
| 210 | ✗ | uint16_t uuid; | |
| 211 | |||
| 212 | ✗ | if (format == BT_ATT_INFO_16) { | |
| 213 | ✗ | handle = info16[i].handle; | |
| 214 | ✗ | uuid = info16[i].uuid; | |
| 215 | } | ||
| 216 | else { | ||
| 217 | ✗ | handle = info128[i].handle; | |
| 218 | ✗ | uuid = *(uint16_t *)&info128[i].uuid[12]; | |
| 219 | } | ||
| 220 | |||
| 221 | ✗ | printf("INFO HDL: %04X UUID: %04X REPORT_CNT: %d\n", handle, uuid, hid_data->report_cnt); | |
| 222 | ✗ | bt_mon_log(true, "INFO HDL: %04X UUID: %04X REPORT_CNT: %d\n", handle, uuid, hid_data->report_cnt); | |
| 223 | |||
| 224 | ✗ | switch (uuid) { | |
| 225 | ✗ | case BT_UUID_GATT_PRIMARY: | |
| 226 | ✗ | hid_data->report_cnt = 0; | |
| 227 | ✗ | break; | |
| 228 | ✗ | case BT_UUID_GATT_CHRC: | |
| 229 | ✗ | if (hid_data->report_found) { | |
| 230 | ✗ | hid_data->report_cnt++; | |
| 231 | ✗ | if (hid_data->report_cnt == HID_MAX_REPORT) { | |
| 232 | ✗ | goto find_info_rsp_end; | |
| 233 | } | ||
| 234 | } | ||
| 235 | ✗ | hid_data->report_found = 0; | |
| 236 | ✗ | break; | |
| 237 | ✗ | case BT_UUID_HIDS_REPORT: | |
| 238 | ✗ | hid_data->reports[hid_data->report_cnt].report_hdl = handle; | |
| 239 | ✗ | hid_data->report_found = 1; | |
| 240 | ✗ | break; | |
| 241 | ✗ | case BT_UUID_GATT_CCC: | |
| 242 | ✗ | hid_data->reports[hid_data->report_cnt].cfg_hdl = handle; | |
| 243 | ✗ | break; | |
| 244 | ✗ | case BT_UUID_HIDS_REPORT_REF: | |
| 245 | ✗ | hid_data->reports[hid_data->report_cnt].ref_hdl = handle; | |
| 246 | ✗ | break; | |
| 247 | ✗ | case BT_UUID_HIDS_REPORT_MAP: | |
| 248 | ✗ | hid_data->map_hdl = handle; | |
| 249 | ✗ | break; | |
| 250 | case BT_UUID_HIDS_INFO: | ||
| 251 | case BT_UUID_HIDS_CTRL_POINT: | ||
| 252 | break; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | ✗ | find_info_rsp_end: | |
| 257 | ✗ | if (last_handle < hid_data->end_hdl) { | |
| 258 | ✗ | bt_att_cmd_find_info_req(device->acl_handle, last_handle + 1, hid_data->end_hdl); | |
| 259 | } | ||
| 260 | else { | ||
| 261 | ✗ | device->hid_state = BT_ATT_HID_CHAR_PROP; | |
| 262 | ✗ | bt_att_cmd_read_type_req_uuid16(device->acl_handle, | |
| 263 | ✗ | hid_data->start_hdl, hid_data->end_hdl, BT_UUID_GATT_CHRC); | |
| 264 | } | ||
| 265 | } | ||
| 266 | else { | ||
| 267 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 268 | } | ||
| 269 | ✗ | } | |
| 270 | |||
| 271 | ✗ | static void bt_att_hid_start_char_prop(struct bt_dev *device, | |
| 272 | struct bt_att_hid *hid_data) { | ||
| 273 | ✗ | bt_att_cmd_read_type_req_uuid16(device->acl_handle, | |
| 274 | ✗ | hid_data->start_hdl, hid_data->end_hdl, BT_UUID_GATT_CHRC); | |
| 275 | ✗ | } | |
| 276 | |||
| 277 | ✗ | static void bt_att_hid_process_char_prop(struct bt_dev *device, | |
| 278 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 279 | ✗ | if (data) { | |
| 280 | ✗ | struct bt_att_read_type_data *read_type_data = (struct bt_att_read_type_data *)data; | |
| 281 | ✗ | const uint32_t elem_cnt = (att_len - 2) / data_len; | |
| 282 | ✗ | uint16_t last_handle = read_type_data[elem_cnt - 1].handle; | |
| 283 | |||
| 284 | ✗ | for (uint32_t i = 0; i < elem_cnt; i++) { | |
| 285 | ✗ | for (uint32_t j = 0; j < HID_MAX_REPORT; j++) { | |
| 286 | ✗ | if (hid_data->reports[j].report_hdl == read_type_data[i].char_value_handle) { | |
| 287 | ✗ | printf("CHAR_PROP Handl: %04X Prop: %02X\n", read_type_data[i].char_value_handle, read_type_data[i].char_prop); | |
| 288 | ✗ | bt_mon_log(true, "CHAR_PROP Handl: %04X Prop: %02X\n", read_type_data[i].char_value_handle, read_type_data[i].char_prop); | |
| 289 | ✗ | hid_data->reports[j].char_prop = read_type_data[i].char_prop; | |
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | ✗ | if (last_handle < hid_data->end_hdl) { | |
| 295 | ✗ | bt_att_cmd_read_type_req_uuid16(device->acl_handle, | |
| 296 | ✗ | last_handle + 1, hid_data->end_hdl, BT_UUID_GATT_CHRC); | |
| 297 | ✗ | return; | |
| 298 | } | ||
| 299 | } | ||
| 300 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 301 | } | ||
| 302 | |||
| 303 | ✗ | static void bt_att_hid_start_report_map(struct bt_dev *device, | |
| 304 | struct bt_att_hid *hid_data) { | ||
| 305 | ✗ | bt_att_cmd_read_req(device->acl_handle, hid_data->map_hdl); | |
| 306 | ✗ | } | |
| 307 | |||
| 308 | ✗ | static void bt_att_hid_process_report_map(struct bt_dev *device, | |
| 309 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 310 | ✗ | struct bt_data *bt_data = &bt_adapter.data[device->ids.id]; | |
| 311 | |||
| 312 | ✗ | if (bt_data->base.sdp_data == NULL) { | |
| 313 | ✗ | bt_data->base.sdp_data = malloc(BT_SDP_DATA_SIZE); | |
| 314 | ✗ | if (bt_data->base.sdp_data == NULL) { | |
| 315 | ✗ | printf("# dev: %ld Failed to alloc report memory\n", device->ids.id); | |
| 316 | ✗ | return; | |
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | ✗ | if (data) { | |
| 321 | ✗ | memcpy(bt_data->base.sdp_data + bt_data->base.sdp_len, data, data_len); | |
| 322 | ✗ | bt_data->base.sdp_len += data_len; | |
| 323 | |||
| 324 | ✗ | if (att_len == device->mtu) { | |
| 325 | ✗ | bt_att_cmd_read_blob_req(device->acl_handle, hid_data->map_hdl, bt_data->base.sdp_len); | |
| 326 | ✗ | return; | |
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | ✗ | if (bt_data->base.sdp_data) { | |
| 331 | ✗ | hid_parser(bt_data, bt_data->base.sdp_data, bt_data->base.sdp_len); | |
| 332 | ✗ | free(bt_data->base.sdp_data); | |
| 333 | ✗ | bt_data->base.sdp_data = NULL; | |
| 334 | } | ||
| 335 | ✗ | if (hid_data->reports[hid_data->report_idx].ref_hdl) { | |
| 336 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 337 | } | ||
| 338 | else { | ||
| 339 | ✗ | printf("# dev: %ld No report found!!\n", device->ids.id); | |
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | ✗ | static void bt_att_hid_start_report_ref(struct bt_dev *device, | |
| 344 | struct bt_att_hid *hid_data) { | ||
| 345 | ✗ | hid_data->report_idx = 0; | |
| 346 | ✗ | bt_att_cmd_read_req(device->acl_handle, hid_data->reports[hid_data->report_idx].ref_hdl); | |
| 347 | ✗ | } | |
| 348 | |||
| 349 | ✗ | static void bt_att_hid_process_report_ref(struct bt_dev *device, | |
| 350 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 351 | ✗ | if (data) { | |
| 352 | ✗ | struct bt_att_report_ref *report_ref = (struct bt_att_report_ref *)data; | |
| 353 | |||
| 354 | ✗ | hid_data->reports[hid_data->report_idx].id = report_ref->report_id; | |
| 355 | ✗ | hid_data->reports[hid_data->report_idx++].type = report_ref->report_type; | |
| 356 | |||
| 357 | ✗ | if (hid_data->report_idx < HID_MAX_REPORT && hid_data->reports[hid_data->report_idx].ref_hdl) { | |
| 358 | ✗ | bt_att_cmd_read_req(device->acl_handle, hid_data->reports[hid_data->report_idx].ref_hdl); | |
| 359 | ✗ | return; | |
| 360 | } | ||
| 361 | } | ||
| 362 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 363 | } | ||
| 364 | |||
| 365 | ✗ | static void bt_att_hid_continue_report_cfg(struct bt_dev *device, | |
| 366 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 367 | ✗ | uint32_t i = hid_data->report_idx; | |
| 368 | ✗ | for (; i < HID_MAX_REPORT; i++) { | |
| 369 | ✗ | if (hid_data->reports[i].char_prop & BT_GATT_CHRC_NOTIFY) { | |
| 370 | ✗ | uint16_t data = BT_GATT_CCC_NOTIFY; | |
| 371 | ✗ | bt_att_cmd_write_req(device->acl_handle, hid_data->reports[i].cfg_hdl, (uint8_t *)&data, sizeof(data)); | |
| 372 | ✗ | hid_data->report_idx = i + 1; | |
| 373 | ✗ | break; | |
| 374 | } | ||
| 375 | } | ||
| 376 | ✗ | if (i >= HID_MAX_REPORT) { | |
| 377 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 378 | } | ||
| 379 | ✗ | } | |
| 380 | |||
| 381 | ✗ | static void bt_att_hid_start_report_cfg(struct bt_dev *device, | |
| 382 | struct bt_att_hid *hid_data) { | ||
| 383 | ✗ | hid_data->report_idx = 0; | |
| 384 | ✗ | bt_att_hid_continue_report_cfg(device, hid_data, 0, NULL, 0); | |
| 385 | ✗ | } | |
| 386 | |||
| 387 | ✗ | static void bt_att_hid_start_ppcp_cfg(struct bt_dev *device, | |
| 388 | struct bt_att_hid *hid_data) { | ||
| 389 | ✗ | bt_att_cmd_read_type_req_uuid16(device->acl_handle, 0x0001, 0xFFFF, BT_UUID_GAP_PPCP); | |
| 390 | ✗ | } | |
| 391 | |||
| 392 | ✗ | static void bt_att_hid_process_ppcp_cfg(struct bt_dev *device, | |
| 393 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 394 | ✗ | if (data) { | |
| 395 | ✗ | struct bt_l2cap_conn_param_req *conn_param_req = (struct bt_l2cap_conn_param_req *)data; | |
| 396 | ✗ | struct hci_cp_le_conn_update le_conn_update = {0}; | |
| 397 | |||
| 398 | ✗ | le_conn_update.handle = device->acl_handle; | |
| 399 | ✗ | le_conn_update.conn_interval_min = conn_param_req->min_interval; | |
| 400 | ✗ | le_conn_update.conn_interval_max = conn_param_req->max_interval; | |
| 401 | ✗ | le_conn_update.conn_latency = conn_param_req->latency; | |
| 402 | ✗ | le_conn_update.supervision_timeout = conn_param_req->timeout; | |
| 403 | ✗ | le_conn_update.min_ce_len = 0; | |
| 404 | ✗ | le_conn_update.max_ce_len = 0; | |
| 405 | |||
| 406 | ✗ | bt_hci_le_conn_update(&le_conn_update); | |
| 407 | } | ||
| 408 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 409 | ✗ | } | |
| 410 | |||
| 411 | ✗ | static void bt_att_hid_start_batt_lvl(struct bt_dev *device, | |
| 412 | struct bt_att_hid *hid_data) { | ||
| 413 | ✗ | bt_att_cmd_read_type_req_uuid16(device->acl_handle, 0x0001, 0xFFFF, BT_UUID_BAS_BATTERY_LEVEL); | |
| 414 | ✗ | } | |
| 415 | |||
| 416 | ✗ | static void bt_att_hid_process_batt_lvl(struct bt_dev *device, | |
| 417 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 418 | ✗ | bt_att_hid_start_next_state(device, hid_data); | |
| 419 | ✗ | } | |
| 420 | |||
| 421 | ✗ | static void bt_att_hid_start_init(struct bt_dev *device, | |
| 422 | struct bt_att_hid *hid_data) { | ||
| 423 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 424 | ✗ | atomic_set_bit(&device->flags, BT_DEV_HID_INTR_READY); | |
| 425 | ✗ | bt_hid_init(device); | |
| 426 | } | ||
| 427 | ✗ | } | |
| 428 | |||
| 429 | static bit_att_hid_start_func_t start_state_func[BT_ATT_HID_STATE_MAX] = { | ||
| 430 | bt_att_hid_start_device_name, | ||
| 431 | bt_att_hid_start_appearance, | ||
| 432 | bt_att_hid_start_pnp, | ||
| 433 | bt_att_hid_start_find_hid_hdls, | ||
| 434 | bt_att_hid_start_ident_hid_hdls, | ||
| 435 | bt_att_hid_start_char_prop, | ||
| 436 | bt_att_hid_start_report_map, | ||
| 437 | bt_att_hid_start_report_ref, | ||
| 438 | bt_att_hid_start_report_cfg, | ||
| 439 | bt_att_hid_start_ppcp_cfg, | ||
| 440 | bt_att_hid_start_batt_lvl, | ||
| 441 | bt_att_hid_start_init, /* Need to be last one */ | ||
| 442 | }; | ||
| 443 | static bit_att_hid_process_func_t process_state_func[BT_ATT_HID_STATE_MAX] = { | ||
| 444 | bt_att_hid_process_device_name, | ||
| 445 | bt_att_hid_process_appearance, | ||
| 446 | bt_att_hid_process_pnp, | ||
| 447 | bt_att_hid_process_find_hid_hdls, | ||
| 448 | bt_att_hid_process_ident_hid_hdls, | ||
| 449 | bt_att_hid_process_char_prop, | ||
| 450 | bt_att_hid_process_report_map, | ||
| 451 | bt_att_hid_process_report_ref, | ||
| 452 | bt_att_hid_continue_report_cfg, | ||
| 453 | bt_att_hid_process_ppcp_cfg, | ||
| 454 | bt_att_hid_process_batt_lvl, | ||
| 455 | NULL, /* Need to be last one */ | ||
| 456 | }; | ||
| 457 | |||
| 458 | ✗ | static void bt_att_hid_start_first_state(struct bt_dev *device, | |
| 459 | struct bt_att_hid *hid_data) { | ||
| 460 | ✗ | device->hid_state = 0; | |
| 461 | ✗ | if (device->hid_state < BT_ATT_HID_STATE_MAX && start_state_func[0]) { | |
| 462 | ✗ | start_state_func[0](device, hid_data); | |
| 463 | } | ||
| 464 | ✗ | } | |
| 465 | |||
| 466 | ✗ | static void bt_att_hid_process_current_state(struct bt_dev *device, | |
| 467 | struct bt_att_hid *hid_data, uint32_t att_len, uint8_t *data, uint32_t data_len) { | ||
| 468 | ✗ | if (device->hid_state < BT_ATT_HID_STATE_MAX && process_state_func[device->hid_state]) { | |
| 469 | ✗ | process_state_func[device->hid_state](device, hid_data, att_len, data, data_len); | |
| 470 | } | ||
| 471 | ✗ | } | |
| 472 | |||
| 473 | ✗ | static void bt_att_hid_start_next_state(struct bt_dev *device, | |
| 474 | struct bt_att_hid *hid_data) { | ||
| 475 | ✗ | if (device->ids.type == BT_SW2 && !atomic_test_bit(&device->flags, BT_DEV_HID_INIT_DONE)) { | |
| 476 | ✗ | bt_hid_sw2_init(device); | |
| 477 | } | ||
| 478 | else { | ||
| 479 | ✗ | device->hid_state++; | |
| 480 | ✗ | if (device->hid_state < BT_ATT_HID_STATE_MAX && start_state_func[device->hid_state]) { | |
| 481 | ✗ | start_state_func[device->hid_state](device, hid_data); | |
| 482 | } | ||
| 483 | } | ||
| 484 | ✗ | } | |
| 485 | |||
| 486 | ✗ | static int32_t bt_att_get_report_index(struct bt_att_hid *hid_data, uint8_t report_id, uint8_t report_type) { | |
| 487 | ✗ | for (uint32_t i = 0; i < HID_MAX_REPORT; i++) { | |
| 488 | ✗ | if (hid_data->reports[i].id == report_id && hid_data->reports[i].type == report_type) { | |
| 489 | ✗ | return i; | |
| 490 | } | ||
| 491 | } | ||
| 492 | return -1; | ||
| 493 | } | ||
| 494 | |||
| 495 | ✗ | void bt_att_hid_init(struct bt_dev *device) { | |
| 496 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_PENDING)) { | |
| 497 | ✗ | struct bt_att_hid *hid_data = &att_hid[device->ids.id]; | |
| 498 | |||
| 499 | ✗ | atomic_set_bit(&device->flags, BT_DEV_HID_INTR_PENDING); | |
| 500 | |||
| 501 | ✗ | memset((uint8_t *)hid_data, 0, sizeof(*hid_data)); | |
| 502 | ✗ | bt_att_cmd_mtu_req(device->acl_handle, bt_att_get_le_max_mtu()); | |
| 503 | } | ||
| 504 | ✗ | } | |
| 505 | |||
| 506 | ✗ | void bt_att_write_hid_report(struct bt_dev *device, uint8_t report_id, uint8_t *data, uint32_t len) { | |
| 507 | ✗ | struct bt_att_hid *hid_data = &att_hid[device->ids.id]; | |
| 508 | |||
| 509 | ✗ | int32_t index = bt_att_get_report_index(hid_data, report_id, 0x02); | |
| 510 | ✗ | if (index > -1) { | |
| 511 | ✗ | uint16_t att_handle = hid_data->reports[index].report_hdl; | |
| 512 | ✗ | uint8_t char_prop = hid_data->reports[index].char_prop; | |
| 513 | |||
| 514 | ✗ | if (att_handle) { | |
| 515 | ✗ | if (char_prop & BT_GATT_CHRC_WRITE) { | |
| 516 | ✗ | bt_att_cmd_write_req(device->acl_handle, att_handle, data, len); | |
| 517 | } | ||
| 518 | else { | ||
| 519 | ✗ | bt_att_cmd_write_cmd(device->acl_handle, att_handle, data, len); | |
| 520 | } | ||
| 521 | } | ||
| 522 | } | ||
| 523 | ✗ | } | |
| 524 | |||
| 525 | ✗ | void bt_att_hid_hdlr(struct bt_dev *device, struct bt_hci_pkt *bt_hci_acl_pkt, uint32_t len) { | |
| 526 | ✗ | struct bt_att_hid *hid_data = &att_hid[device->ids.id]; | |
| 527 | ✗ | uint32_t att_len = len - (BT_HCI_H4_HDR_SIZE + BT_HCI_ACL_HDR_SIZE + sizeof(struct bt_l2cap_hdr)); | |
| 528 | |||
| 529 | ✗ | switch (bt_hci_acl_pkt->att_hdr.code) { | |
| 530 | ✗ | case BT_ATT_OP_ERROR_RSP: | |
| 531 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 532 | ✗ | bt_att_hid_process_current_state(device, hid_data, att_len, NULL, 0); | |
| 533 | } | ||
| 534 | break; | ||
| 535 | ✗ | case BT_ATT_OP_MTU_RSP: | |
| 536 | { | ||
| 537 | ✗ | struct bt_att_exchange_mtu_rsp *mtu_rsp = (struct bt_att_exchange_mtu_rsp *)bt_hci_acl_pkt->att_data; | |
| 538 | |||
| 539 | ✗ | device->mtu = mtu_rsp->mtu; | |
| 540 | |||
| 541 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 542 | ✗ | bt_hci_stop_inquiry(); | |
| 543 | |||
| 544 | ✗ | bt_att_hid_start_first_state(device, hid_data); | |
| 545 | } | ||
| 546 | break; | ||
| 547 | } | ||
| 548 | ✗ | case BT_ATT_OP_FIND_INFO_RSP: | |
| 549 | { | ||
| 550 | ✗ | struct bt_att_find_info_rsp *find_info_rsp = (struct bt_att_find_info_rsp *)bt_hci_acl_pkt->att_data; | |
| 551 | |||
| 552 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 553 | ✗ | switch (device->hid_state) { | |
| 554 | ✗ | case BT_ATT_HID_IDENT_HID_HLDS: | |
| 555 | ✗ | bt_att_hid_process_ident_hid_hdls(device, hid_data, att_len, find_info_rsp->info, find_info_rsp->format); | |
| 556 | ✗ | break; | |
| 557 | ✗ | default: | |
| 558 | ✗ | printf("# %s: Invalid state: %ld rsp: 0x%02X\n", __FUNCTION__, device->hid_state, bt_hci_acl_pkt->att_hdr.code); | |
| 559 | ✗ | break; | |
| 560 | } | ||
| 561 | } | ||
| 562 | break; | ||
| 563 | } | ||
| 564 | ✗ | case BT_ATT_OP_READ_TYPE_RSP: | |
| 565 | { | ||
| 566 | ✗ | struct bt_att_read_type_rsp *read_type_rsp = (struct bt_att_read_type_rsp *)bt_hci_acl_pkt->att_data; | |
| 567 | ✗ | uint8_t rsp_len = read_type_rsp->len - sizeof(read_type_rsp->data[0].handle); | |
| 568 | |||
| 569 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 570 | ✗ | switch (device->hid_state) { | |
| 571 | ✗ | case BT_ATT_HID_DEVICE_NAME: | |
| 572 | ✗ | hid_data->dev_name_hdl = read_type_rsp->data[0].handle; | |
| 573 | ✗ | if (rsp_len) { | |
| 574 | ✗ | bt_att_hid_process_device_name(device, hid_data, att_len, read_type_rsp->data[0].value, rsp_len); | |
| 575 | } | ||
| 576 | else { | ||
| 577 | ✗ | bt_att_cmd_read_req(device->acl_handle, read_type_rsp->data[0].handle); | |
| 578 | } | ||
| 579 | break; | ||
| 580 | ✗ | case BT_ATT_HID_APPEARANCE: | |
| 581 | ✗ | bt_att_hid_process_appearance(device, hid_data, att_len, read_type_rsp->data[0].value, rsp_len); | |
| 582 | ✗ | break; | |
| 583 | ✗ | case BT_ATT_HID_PNP: | |
| 584 | ✗ | bt_att_hid_process_pnp(device, hid_data, att_len, read_type_rsp->data[0].value, rsp_len); | |
| 585 | ✗ | break; | |
| 586 | ✗ | case BT_ATT_HID_CHAR_PROP: | |
| 587 | ✗ | bt_att_hid_process_char_prop(device, hid_data, att_len, (uint8_t *)read_type_rsp->data, read_type_rsp->len); | |
| 588 | ✗ | break; | |
| 589 | ✗ | case BT_ATT_HID_PPCP_CFG: | |
| 590 | ✗ | bt_att_hid_process_ppcp_cfg(device, hid_data, att_len, read_type_rsp->data[0].value, rsp_len); | |
| 591 | ✗ | break; | |
| 592 | ✗ | case BT_ATT_HID_BATT_LVL: | |
| 593 | ✗ | bt_att_hid_process_batt_lvl(device, hid_data, att_len, read_type_rsp->data[0].value, rsp_len); | |
| 594 | ✗ | break; | |
| 595 | ✗ | default: | |
| 596 | ✗ | printf("# %s: Invalid state: %ld rsp: 0x%02X\n", __FUNCTION__, device->hid_state, bt_hci_acl_pkt->att_hdr.code); | |
| 597 | ✗ | break; | |
| 598 | } | ||
| 599 | } | ||
| 600 | break; | ||
| 601 | } | ||
| 602 | ✗ | case BT_ATT_OP_READ_RSP: | |
| 603 | { | ||
| 604 | ✗ | struct bt_att_read_rsp *read_rsp = (struct bt_att_read_rsp *)bt_hci_acl_pkt->att_data; | |
| 605 | |||
| 606 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 607 | ✗ | switch (device->hid_state) { | |
| 608 | ✗ | case BT_ATT_HID_DEVICE_NAME: | |
| 609 | ✗ | bt_att_hid_process_device_name(device, hid_data, att_len, read_rsp->value, att_len - 1); | |
| 610 | ✗ | break; | |
| 611 | ✗ | case BT_ATT_HID_REPORT_MAP: | |
| 612 | ✗ | bt_att_hid_process_report_map(device, hid_data, att_len, read_rsp->value, att_len - 1); | |
| 613 | ✗ | break; | |
| 614 | ✗ | case BT_ATT_HID_REPORT_REF: | |
| 615 | ✗ | bt_att_hid_process_report_ref(device, hid_data, att_len, read_rsp->value, att_len - 1); | |
| 616 | ✗ | break; | |
| 617 | ✗ | default: | |
| 618 | ✗ | printf("# %s: Invalid state: %ld rsp: 0x%02X\n", __FUNCTION__, device->hid_state, bt_hci_acl_pkt->att_hdr.code); | |
| 619 | ✗ | break; | |
| 620 | } | ||
| 621 | } | ||
| 622 | break; | ||
| 623 | } | ||
| 624 | ✗ | case BT_ATT_OP_READ_BLOB_RSP: | |
| 625 | { | ||
| 626 | ✗ | struct bt_att_read_blob_rsp *read_blob_rsp = (struct bt_att_read_blob_rsp *)bt_hci_acl_pkt->att_data; | |
| 627 | |||
| 628 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 629 | ✗ | switch (device->hid_state) { | |
| 630 | ✗ | case BT_ATT_HID_DEVICE_NAME: | |
| 631 | ✗ | bt_att_hid_process_device_name(device, hid_data, att_len, read_blob_rsp->value, att_len - 1); | |
| 632 | ✗ | break; | |
| 633 | ✗ | case BT_ATT_HID_REPORT_MAP: | |
| 634 | ✗ | bt_att_hid_process_report_map(device, hid_data, att_len, read_blob_rsp->value, att_len - 1); | |
| 635 | ✗ | break; | |
| 636 | ✗ | default: | |
| 637 | ✗ | printf("# %s: Invalid state: %ld rsp: 0x%02X\n", __FUNCTION__, device->hid_state, bt_hci_acl_pkt->att_hdr.code); | |
| 638 | ✗ | break; | |
| 639 | } | ||
| 640 | } | ||
| 641 | break; | ||
| 642 | } | ||
| 643 | ✗ | case BT_ATT_OP_READ_GROUP_RSP: | |
| 644 | { | ||
| 645 | ✗ | struct bt_att_read_group_rsp *read_group_rsp = (struct bt_att_read_group_rsp *)bt_hci_acl_pkt->att_data; | |
| 646 | |||
| 647 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 648 | ✗ | switch (device->hid_state) { | |
| 649 | ✗ | case BT_ATT_HID_FIND_HID_HDLS: | |
| 650 | ✗ | bt_att_hid_process_find_hid_hdls(device, hid_data, att_len, (uint8_t *)read_group_rsp->data, read_group_rsp->len); | |
| 651 | ✗ | break; | |
| 652 | ✗ | default: | |
| 653 | ✗ | printf("# %s: Invalid state: %ld rsp: 0x%02X\n", __FUNCTION__, device->hid_state, bt_hci_acl_pkt->att_hdr.code); | |
| 654 | ✗ | break; | |
| 655 | } | ||
| 656 | } | ||
| 657 | break; | ||
| 658 | } | ||
| 659 | ✗ | case BT_ATT_OP_WRITE_RSP: | |
| 660 | { | ||
| 661 | ✗ | if (!atomic_test_bit(&device->flags, BT_DEV_HID_INTR_READY)) { | |
| 662 | ✗ | switch (device->hid_state) { | |
| 663 | ✗ | case BT_ATT_HID_REPORT_CFG: | |
| 664 | ✗ | bt_att_hid_continue_report_cfg(device, hid_data, att_len, NULL, 0); | |
| 665 | ✗ | break; | |
| 666 | ✗ | default: | |
| 667 | ✗ | printf("# %s: Invalid state: %ld rsp: 0x%02X\n", __FUNCTION__, device->hid_state, bt_hci_acl_pkt->att_hdr.code); | |
| 668 | ✗ | break; | |
| 669 | } | ||
| 670 | } | ||
| 671 | break; | ||
| 672 | } | ||
| 673 | ✗ | case BT_ATT_OP_NOTIFY: | |
| 674 | { | ||
| 675 | ✗ | struct bt_att_notify *notify = (struct bt_att_notify *)bt_hci_acl_pkt->att_data; | |
| 676 | |||
| 677 | ✗ | switch (device->ids.type) { | |
| 678 | ✗ | case BT_SW2: | |
| 679 | ✗ | bt_hid_sw2_hdlr(device, notify->handle, notify->value, att_len - sizeof(notify->handle)); | |
| 680 | ✗ | break; | |
| 681 | default: | ||
| 682 | ✗ | for (uint32_t i = 0; i < HID_MAX_REPORT; i++) { | |
| 683 | ✗ | if (notify->handle == hid_data->reports[i].report_hdl) { | |
| 684 | ✗ | bt_host_bridge(device, hid_data->reports[i].id, notify->value, att_len - sizeof(notify->handle)); | |
| 685 | ✗ | break; | |
| 686 | } | ||
| 687 | } | ||
| 688 | break; | ||
| 689 | } | ||
| 690 | break; | ||
| 691 | } | ||
| 692 | ✗ | default: | |
| 693 | ✗ | bt_att_cfg_hdlr(device, bt_hci_acl_pkt, len); | |
| 694 | ✗ | break; | |
| 695 | } | ||
| 696 | ✗ | } | |
| 697 |