1d40c9ac6SMatthias Ringwald /*
2d40c9ac6SMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH
3d40c9ac6SMatthias Ringwald *
4d40c9ac6SMatthias Ringwald * Redistribution and use in source and binary forms, with or without
5d40c9ac6SMatthias Ringwald * modification, are permitted provided that the following conditions
6d40c9ac6SMatthias Ringwald * are met:
7d40c9ac6SMatthias Ringwald *
8d40c9ac6SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright
9d40c9ac6SMatthias Ringwald * notice, this list of conditions and the following disclaimer.
10d40c9ac6SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright
11d40c9ac6SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the
12d40c9ac6SMatthias Ringwald * documentation and/or other materials provided with the distribution.
13d40c9ac6SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of
14d40c9ac6SMatthias Ringwald * contributors may be used to endorse or promote products derived
15d40c9ac6SMatthias Ringwald * from this software without specific prior written permission.
16d40c9ac6SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for
17d40c9ac6SMatthias Ringwald * personal benefit and not for any commercial purpose or for
18d40c9ac6SMatthias Ringwald * monetary gain.
19d40c9ac6SMatthias Ringwald *
20d40c9ac6SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21d40c9ac6SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22d40c9ac6SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25d40c9ac6SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26d40c9ac6SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27d40c9ac6SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28d40c9ac6SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29d40c9ac6SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30d40c9ac6SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31d40c9ac6SMatthias Ringwald * SUCH DAMAGE.
32d40c9ac6SMatthias Ringwald *
33d40c9ac6SMatthias Ringwald * Please inquire about commercial licensing options at
34d40c9ac6SMatthias Ringwald * [email protected]
35d40c9ac6SMatthias Ringwald *
36d40c9ac6SMatthias Ringwald */
37d40c9ac6SMatthias Ringwald
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "hid_device.c"
39d40c9ac6SMatthias Ringwald
40d40c9ac6SMatthias Ringwald #include <string.h>
41d40c9ac6SMatthias Ringwald
4284e3541eSMilanka Ringwald #include "bluetooth.h"
4384e3541eSMilanka Ringwald #include "bluetooth_psm.h"
4484e3541eSMilanka Ringwald #include "bluetooth_sdp.h"
4584e3541eSMilanka Ringwald #include "btstack_debug.h"
4684e3541eSMilanka Ringwald #include "btstack_event.h"
4784e3541eSMilanka Ringwald #include "btstack_hid_parser.h"
48d40c9ac6SMatthias Ringwald #include "classic/hid_device.h"
49d40c9ac6SMatthias Ringwald #include "classic/sdp_util.h"
508eb8d463SMatthias Ringwald #include "l2cap.h"
518eb8d463SMatthias Ringwald
52de81b46aSMatthias Ringwald // prototypes
53de81b46aSMatthias Ringwald static int dummy_write_report(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int * out_report_size, uint8_t * out_report);
54de81b46aSMatthias Ringwald static void dummy_set_report(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report);
55de81b46aSMatthias Ringwald static void dummy_report_data(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t * report);
56de81b46aSMatthias Ringwald
57c12110e4SMilanka Ringwald typedef enum {
58c12110e4SMilanka Ringwald HID_DEVICE_IDLE,
59c12110e4SMilanka Ringwald HID_DEVICE_CONNECTED,
60ba9a58feSMilanka Ringwald HID_DEVICE_W2_GET_REPORT,
61ba9a58feSMilanka Ringwald HID_DEVICE_W2_SET_REPORT,
62ba9a58feSMilanka Ringwald HID_DEVICE_W2_GET_PROTOCOL,
63ba9a58feSMilanka Ringwald HID_DEVICE_W2_SET_PROTOCOL,
64ba9a58feSMilanka Ringwald HID_DEVICE_W2_ANSWER_SET_PROTOCOL,
65ba9a58feSMilanka Ringwald HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST,
66c12110e4SMilanka Ringwald } hid_device_state_t;
67c12110e4SMilanka Ringwald
688eb8d463SMatthias Ringwald // hid device state
698eb8d463SMatthias Ringwald typedef struct hid_device {
708eb8d463SMatthias Ringwald uint16_t cid;
718eb8d463SMatthias Ringwald bd_addr_t bd_addr;
728eb8d463SMatthias Ringwald hci_con_handle_t con_handle;
738eb8d463SMatthias Ringwald uint16_t control_cid;
748eb8d463SMatthias Ringwald uint16_t interrupt_cid;
758eb8d463SMatthias Ringwald uint8_t incoming;
7657c643eeSMatthias Ringwald uint8_t connected;
77c12110e4SMilanka Ringwald hid_device_state_t state;
78c12110e4SMilanka Ringwald hid_report_type_t report_type;
79b56e8b56SMatthias Ringwald uint8_t report_id;
80a1118d11SMilanka Ringwald uint16_t expected_report_size;
811002ad19SMatthias Ringwald uint16_t response_size;
82e99e41f0SMatthias Ringwald uint8_t user_request_can_send_now;
83ba9a58feSMilanka Ringwald
84bc967e00SMilanka Ringwald hid_handshake_param_type_t report_status;
85ba9a58feSMilanka Ringwald hid_protocol_mode_t protocol_mode;
868eb8d463SMatthias Ringwald } hid_device_t;
878eb8d463SMatthias Ringwald
88de81b46aSMatthias Ringwald // higher layer callbacks
89de81b46aSMatthias Ringwald static btstack_packet_handler_t hid_device_callback;
90de81b46aSMatthias Ringwald static int (*hci_device_get_report) (uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int * out_report_size, uint8_t * out_report) = dummy_write_report;
91de81b46aSMatthias Ringwald static void (*hci_device_set_report) (uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report) = dummy_set_report;
92de81b46aSMatthias Ringwald static void (*hci_device_report_data) (uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t * report) = dummy_report_data;
93de81b46aSMatthias Ringwald
94de81b46aSMatthias Ringwald static hid_device_t hid_device_singleton;
95de81b46aSMatthias Ringwald
96de81b46aSMatthias Ringwald static bool hid_device_boot_protocol_mode_supported;
97de81b46aSMatthias Ringwald static const uint8_t * hid_device_descriptor;
98de81b46aSMatthias Ringwald static uint16_t hid_device_descriptor_len;
99de81b46aSMatthias Ringwald
100de81b46aSMatthias Ringwald
101de81b46aSMatthias Ringwald static uint16_t hid_device_cid = 0;
102de81b46aSMatthias Ringwald
103c12110e4SMilanka Ringwald
dummy_write_report(uint16_t hid_cid,hid_report_type_t report_type,uint16_t report_id,int * out_report_size,uint8_t * out_report)104a1118d11SMilanka Ringwald static int dummy_write_report(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int * out_report_size, uint8_t * out_report){
105c12110e4SMilanka Ringwald UNUSED(hid_cid);
106c12110e4SMilanka Ringwald UNUSED(report_type);
107c12110e4SMilanka Ringwald UNUSED(report_id);
108c12110e4SMilanka Ringwald UNUSED(out_report_size);
109c12110e4SMilanka Ringwald UNUSED(out_report);
110a1118d11SMilanka Ringwald return -1;
111c12110e4SMilanka Ringwald }
1124dfe6a8bSMilanka Ringwald
dummy_set_report(uint16_t hid_cid,hid_report_type_t report_type,int report_size,uint8_t * report)1134dfe6a8bSMilanka Ringwald static void dummy_set_report(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report){
114ba9a58feSMilanka Ringwald UNUSED(hid_cid);
115ba9a58feSMilanka Ringwald UNUSED(report_type);
116ba9a58feSMilanka Ringwald UNUSED(report_size);
117ba9a58feSMilanka Ringwald UNUSED(report);
118ba9a58feSMilanka Ringwald }
1194dfe6a8bSMilanka Ringwald
dummy_report_data(uint16_t hid_cid,hid_report_type_t report_type,uint16_t report_id,int report_size,uint8_t * report)1207d26fe66SMilanka Ringwald static void dummy_report_data(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t * report){
1217d26fe66SMilanka Ringwald UNUSED(hid_cid);
1227d26fe66SMilanka Ringwald UNUSED(report_type);
1237d26fe66SMilanka Ringwald UNUSED(report_id);
1247d26fe66SMilanka Ringwald UNUSED(report_size);
1257d26fe66SMilanka Ringwald UNUSED(report);
1267d26fe66SMilanka Ringwald }
1271002ad19SMatthias Ringwald
hid_device_get_next_cid(void)128c12110e4SMilanka Ringwald static uint16_t hid_device_get_next_cid(void){
129c12110e4SMilanka Ringwald hid_device_cid++;
130c12110e4SMilanka Ringwald if (!hid_device_cid){
131c12110e4SMilanka Ringwald hid_device_cid = 1;
132c12110e4SMilanka Ringwald }
133c12110e4SMilanka Ringwald return hid_device_cid;
134c12110e4SMilanka Ringwald }
135c12110e4SMilanka Ringwald
136c12110e4SMilanka Ringwald // TODO: store hid device connection into list
hid_device_get_instance_for_l2cap_cid(uint16_t cid)1374176cbc0SMatthias Ringwald static hid_device_t * hid_device_get_instance_for_l2cap_cid(uint16_t cid){
138de81b46aSMatthias Ringwald if ((hid_device_singleton.control_cid == cid) || (hid_device_singleton.interrupt_cid == cid)){
139de81b46aSMatthias Ringwald return &hid_device_singleton;
1404176cbc0SMatthias Ringwald }
1414176cbc0SMatthias Ringwald return NULL;
1424176cbc0SMatthias Ringwald }
1434176cbc0SMatthias Ringwald
hid_device_get_instance_for_hid_cid(uint16_t hid_cid)1444176cbc0SMatthias Ringwald static hid_device_t * hid_device_get_instance_for_hid_cid(uint16_t hid_cid){
145de81b46aSMatthias Ringwald if (hid_device_singleton.cid == hid_cid){
146de81b46aSMatthias Ringwald return &hid_device_singleton;
147c12110e4SMilanka Ringwald }
148c12110e4SMilanka Ringwald return NULL;
149c12110e4SMilanka Ringwald }
150c12110e4SMilanka Ringwald
hid_device_setup_instance(hid_device_t * hid_device,const uint8_t * bd_addr)1516c8af51cSMatthias Ringwald static void hid_device_setup_instance(hid_device_t *hid_device, const uint8_t *bd_addr) {
1526c8af51cSMatthias Ringwald (void)memcpy(hid_device->bd_addr, bd_addr, 6);
1536c8af51cSMatthias Ringwald hid_device->cid = hid_device_get_next_cid();
1546c8af51cSMatthias Ringwald // reset state
1556c8af51cSMatthias Ringwald hid_device->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1566c8af51cSMatthias Ringwald hid_device->con_handle = HCI_CON_HANDLE_INVALID;
1576c8af51cSMatthias Ringwald hid_device->incoming = 0;
1586c8af51cSMatthias Ringwald hid_device->connected = 0;
1596c8af51cSMatthias Ringwald hid_device->control_cid = 0;
1606c8af51cSMatthias Ringwald hid_device->interrupt_cid = 0;
1616c8af51cSMatthias Ringwald }
1626c8af51cSMatthias Ringwald
hid_device_provide_instance_for_bd_addr(bd_addr_t bd_addr)16372e3f392SMatthias Ringwald static hid_device_t * hid_device_provide_instance_for_bd_addr(bd_addr_t bd_addr){
164de81b46aSMatthias Ringwald if (!hid_device_singleton.cid){
1656c8af51cSMatthias Ringwald hid_device_setup_instance(&hid_device_singleton, bd_addr);
1666510739bSMilanka Ringwald }
167de81b46aSMatthias Ringwald return &hid_device_singleton;
168c12110e4SMilanka Ringwald }
169c12110e4SMilanka Ringwald
hid_device_create_instance(void)170c12110e4SMilanka Ringwald static hid_device_t * hid_device_create_instance(void){
171ba9a58feSMilanka Ringwald
172de81b46aSMatthias Ringwald return &hid_device_singleton;
173c12110e4SMilanka Ringwald }
174c12110e4SMilanka Ringwald
hid_create_sdp_record(uint8_t * service,uint32_t service_record_handle,const hid_sdp_record_t * params)17580d9d5d4SMilanka Ringwald void hid_create_sdp_record(uint8_t *service, uint32_t service_record_handle, const hid_sdp_record_t * params){
176d40c9ac6SMatthias Ringwald uint8_t * attribute;
177d40c9ac6SMatthias Ringwald de_create_sequence(service);
178d40c9ac6SMatthias Ringwald
179d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
180d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
181d40c9ac6SMatthias Ringwald
182d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
183d40c9ac6SMatthias Ringwald attribute = de_push_sequence(service);
184d40c9ac6SMatthias Ringwald {
185d40c9ac6SMatthias Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
186d40c9ac6SMatthias Ringwald }
187d40c9ac6SMatthias Ringwald de_pop_sequence(service, attribute);
188d40c9ac6SMatthias Ringwald
189d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
190d40c9ac6SMatthias Ringwald attribute = de_push_sequence(service);
191d40c9ac6SMatthias Ringwald {
192d40c9ac6SMatthias Ringwald uint8_t * l2cpProtocol = de_push_sequence(attribute);
193d40c9ac6SMatthias Ringwald {
194d40c9ac6SMatthias Ringwald de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
19584e3541eSMilanka Ringwald de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_HID_CONTROL);
196d40c9ac6SMatthias Ringwald }
197d40c9ac6SMatthias Ringwald de_pop_sequence(attribute, l2cpProtocol);
198d40c9ac6SMatthias Ringwald
199d40c9ac6SMatthias Ringwald uint8_t * hidProtocol = de_push_sequence(attribute);
200d40c9ac6SMatthias Ringwald {
201d40c9ac6SMatthias Ringwald de_add_number(hidProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
202d40c9ac6SMatthias Ringwald }
203d40c9ac6SMatthias Ringwald de_pop_sequence(attribute, hidProtocol);
204d40c9ac6SMatthias Ringwald }
205d40c9ac6SMatthias Ringwald de_pop_sequence(service, attribute);
206d40c9ac6SMatthias Ringwald
207d40c9ac6SMatthias Ringwald // TODO?
208d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
209d40c9ac6SMatthias Ringwald attribute = de_push_sequence(service);
210d40c9ac6SMatthias Ringwald {
211d40c9ac6SMatthias Ringwald de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e);
212d40c9ac6SMatthias Ringwald de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a);
213d40c9ac6SMatthias Ringwald de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100);
214d40c9ac6SMatthias Ringwald }
215d40c9ac6SMatthias Ringwald de_pop_sequence(service, attribute);
216d40c9ac6SMatthias Ringwald
217d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS);
218d40c9ac6SMatthias Ringwald attribute = de_push_sequence(service);
219d40c9ac6SMatthias Ringwald {
220d40c9ac6SMatthias Ringwald uint8_t * additionalDescriptorAttribute = de_push_sequence(attribute);
221d40c9ac6SMatthias Ringwald {
222d40c9ac6SMatthias Ringwald uint8_t * l2cpProtocol = de_push_sequence(additionalDescriptorAttribute);
223d40c9ac6SMatthias Ringwald {
224d40c9ac6SMatthias Ringwald de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
22584e3541eSMilanka Ringwald de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_HID_INTERRUPT);
226d40c9ac6SMatthias Ringwald }
227d40c9ac6SMatthias Ringwald de_pop_sequence(additionalDescriptorAttribute, l2cpProtocol);
228d40c9ac6SMatthias Ringwald
2299fa0921fSMatthias Ringwald uint8_t * hidProtocol = de_push_sequence(additionalDescriptorAttribute);
230d40c9ac6SMatthias Ringwald {
231d40c9ac6SMatthias Ringwald de_add_number(hidProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_HIDP);
232d40c9ac6SMatthias Ringwald }
2339fa0921fSMatthias Ringwald de_pop_sequence(additionalDescriptorAttribute, hidProtocol);
234d40c9ac6SMatthias Ringwald }
235d40c9ac6SMatthias Ringwald de_pop_sequence(attribute, additionalDescriptorAttribute);
236d40c9ac6SMatthias Ringwald }
237d40c9ac6SMatthias Ringwald de_pop_sequence(service, attribute);
238d40c9ac6SMatthias Ringwald
239d40c9ac6SMatthias Ringwald // 0x0100 "ServiceName"
240d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100);
241b56e8b56SMatthias Ringwald de_add_data(service, DE_STRING, (uint16_t) strlen(params->device_name), (uint8_t *) params->device_name);
242d40c9ac6SMatthias Ringwald
243d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
244d40c9ac6SMatthias Ringwald attribute = de_push_sequence(service);
245d40c9ac6SMatthias Ringwald {
246d40c9ac6SMatthias Ringwald uint8_t * hidProfile = de_push_sequence(attribute);
247d40c9ac6SMatthias Ringwald {
248d40c9ac6SMatthias Ringwald de_add_number(hidProfile, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
249d40c9ac6SMatthias Ringwald de_add_number(hidProfile, DE_UINT, DE_SIZE_16, 0x0101); // Version 1.1
250d40c9ac6SMatthias Ringwald }
251d40c9ac6SMatthias Ringwald de_pop_sequence(attribute, hidProfile);
252d40c9ac6SMatthias Ringwald }
253d40c9ac6SMatthias Ringwald de_pop_sequence(service, attribute);
254d40c9ac6SMatthias Ringwald
255d40c9ac6SMatthias Ringwald // Deprecated in v1.1.1
256d40c9ac6SMatthias Ringwald // de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_RELEASE_NUMBER);
257d40c9ac6SMatthias Ringwald // de_add_number(service, DE_UINT, DE_SIZE_16, 0x0101);
258d40c9ac6SMatthias Ringwald
259d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_PARSER_VERSION);
260d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0111); // v1.1.1
261d40c9ac6SMatthias Ringwald
262d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DEVICE_SUBCLASS);
26380d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_8, params->hid_device_subclass);
264d40c9ac6SMatthias Ringwald
265d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_COUNTRY_CODE);
26680d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_8, params->hid_country_code);
267d40c9ac6SMatthias Ringwald
268d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_VIRTUAL_CABLE);
26980d9d5d4SMilanka Ringwald de_add_number(service, DE_BOOL, DE_SIZE_8, params->hid_virtual_cable);
270d40c9ac6SMatthias Ringwald
271d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_RECONNECT_INITIATE);
27280d9d5d4SMilanka Ringwald de_add_number(service, DE_BOOL, DE_SIZE_8, params->hid_reconnect_initiate);
273ffe3f1a1SMilanka Ringwald
274d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST);
275d40c9ac6SMatthias Ringwald attribute = de_push_sequence(service);
276d40c9ac6SMatthias Ringwald {
277d40c9ac6SMatthias Ringwald uint8_t* hidDescriptor = de_push_sequence(attribute);
278d40c9ac6SMatthias Ringwald {
279d40c9ac6SMatthias Ringwald de_add_number(hidDescriptor, DE_UINT, DE_SIZE_8, 0x22); // Report Descriptor
28080d9d5d4SMilanka Ringwald de_add_data(hidDescriptor, DE_STRING, params->hid_descriptor_size, (uint8_t *) params->hid_descriptor);
281d40c9ac6SMatthias Ringwald }
282d40c9ac6SMatthias Ringwald de_pop_sequence(attribute, hidDescriptor);
283d40c9ac6SMatthias Ringwald }
284d40c9ac6SMatthias Ringwald de_pop_sequence(service, attribute);
285d40c9ac6SMatthias Ringwald
2869679ea81SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDLANGID_BASE_LIST);
2879679ea81SMatthias Ringwald attribute = de_push_sequence(service);
2889679ea81SMatthias Ringwald {
2899679ea81SMatthias Ringwald uint8_t* hig_lang_base = de_push_sequence(attribute);
2909679ea81SMatthias Ringwald {
2919679ea81SMatthias Ringwald // see: http://www.usb.org/developers/docs/USB_LANGIDs.pdf
2929679ea81SMatthias Ringwald de_add_number(hig_lang_base, DE_UINT, DE_SIZE_16, 0x0409); // HIDLANGID = English (US)
2939679ea81SMatthias Ringwald de_add_number(hig_lang_base, DE_UINT, DE_SIZE_16, 0x0100); // HIDLanguageBase = 0x0100 default
2949679ea81SMatthias Ringwald }
2959679ea81SMatthias Ringwald de_pop_sequence(attribute, hig_lang_base);
2969679ea81SMatthias Ringwald }
2979679ea81SMatthias Ringwald de_pop_sequence(service, attribute);
2989679ea81SMatthias Ringwald
29980d9d5d4SMilanka Ringwald // battery power
30080d9d5d4SMilanka Ringwald
3016510739bSMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_REMOTE_WAKE);
30280d9d5d4SMilanka Ringwald de_add_number(service, DE_BOOL, DE_SIZE_8, params->hid_remote_wake ? 1 : 0);
30380d9d5d4SMilanka Ringwald
30480d9d5d4SMilanka Ringwald // supervision timeout
30580d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_SUPERVISION_TIMEOUT);
30680d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, params->hid_supervision_timeout);
30780d9d5d4SMilanka Ringwald
30880d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_NORMALLY_CONNECTABLE);
30980d9d5d4SMilanka Ringwald de_add_number(service, DE_BOOL, DE_SIZE_8, params->hid_normally_connectable);
3106510739bSMilanka Ringwald
311d40c9ac6SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_BOOT_DEVICE);
31280d9d5d4SMilanka Ringwald de_add_number(service, DE_BOOL, DE_SIZE_8, params->hid_boot_device ? 1 : 0);
31380d9d5d4SMilanka Ringwald
31480d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MAX_LATENCY);
31580d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, params->hid_ssr_host_max_latency);
31680d9d5d4SMilanka Ringwald
31780d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MIN_TIMEOUT);
31880d9d5d4SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, params->hid_ssr_host_min_timeout);
319d40c9ac6SMatthias Ringwald }
3208eb8d463SMatthias Ringwald
hid_device_emit_connected_event(hid_device_t * context,uint8_t status)3218eb8d463SMatthias Ringwald static inline void hid_device_emit_connected_event(hid_device_t * context, uint8_t status){
3228eb8d463SMatthias Ringwald uint8_t event[15];
3238eb8d463SMatthias Ringwald int pos = 0;
3248eb8d463SMatthias Ringwald event[pos++] = HCI_EVENT_HID_META;
3258eb8d463SMatthias Ringwald pos++; // skip len
3268eb8d463SMatthias Ringwald event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
3278eb8d463SMatthias Ringwald little_endian_store_16(event,pos,context->cid);
3288eb8d463SMatthias Ringwald pos+=2;
3298eb8d463SMatthias Ringwald event[pos++] = status;
3306510739bSMilanka Ringwald reverse_bd_addr(context->bd_addr, &event[pos]);
3318eb8d463SMatthias Ringwald pos += 6;
3328eb8d463SMatthias Ringwald little_endian_store_16(event,pos,context->con_handle);
3338eb8d463SMatthias Ringwald pos += 2;
3348eb8d463SMatthias Ringwald event[pos++] = context->incoming;
3358eb8d463SMatthias Ringwald event[1] = pos - 2;
336de81b46aSMatthias Ringwald hid_device_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3378eb8d463SMatthias Ringwald }
3388eb8d463SMatthias Ringwald
hid_device_emit_event(hid_device_t * context,uint8_t subevent_type)3396510739bSMilanka Ringwald static inline void hid_device_emit_event(hid_device_t * context, uint8_t subevent_type){
340517089d1SMilanka Ringwald uint8_t event[5];
3416510739bSMilanka Ringwald int pos = 0;
3426510739bSMilanka Ringwald event[pos++] = HCI_EVENT_HID_META;
3436510739bSMilanka Ringwald pos++; // skip len
3446510739bSMilanka Ringwald event[pos++] = subevent_type;
3456510739bSMilanka Ringwald little_endian_store_16(event,pos,context->cid);
3466510739bSMilanka Ringwald pos+=2;
3476510739bSMilanka Ringwald event[1] = pos - 2;
348de81b46aSMatthias Ringwald hid_device_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
3496510739bSMilanka Ringwald }
3506510739bSMilanka Ringwald
hid_device_trigger_user_request_if_pending(const hid_device_t * hid_device)351d74c876fSMatthias Ringwald static void hid_device_trigger_user_request_if_pending(const hid_device_t *hid_device) {// request user can send now if pending
352d74c876fSMatthias Ringwald if (hid_device->user_request_can_send_now){
353d74c876fSMatthias Ringwald l2cap_request_can_send_now_event((hid_device->control_cid));
354d74c876fSMatthias Ringwald }
355d74c876fSMatthias Ringwald }
356d74c876fSMatthias Ringwald
hid_device_send_prepared_control_message(hid_device_t * hid_device,uint16_t message_len)3571002ad19SMatthias Ringwald static void hid_device_send_prepared_control_message(hid_device_t * hid_device, uint16_t message_len){
3581002ad19SMatthias Ringwald l2cap_send_prepared(hid_device->control_cid, message_len);
3591002ad19SMatthias Ringwald hid_device_trigger_user_request_if_pending(hid_device);
3601002ad19SMatthias Ringwald }
3611002ad19SMatthias Ringwald
hid_report_size_valid(uint16_t cid,int report_id,hid_report_type_t report_type,int report_size)362c8cb324eSMilanka Ringwald static int hid_report_size_valid(uint16_t cid, int report_id, hid_report_type_t report_type, int report_size){
363c8cb324eSMilanka Ringwald if (!report_size) return 0;
364c8cb324eSMilanka Ringwald if (hid_device_in_boot_protocol_mode(cid)){
365c8cb324eSMilanka Ringwald switch (report_id){
366c8cb324eSMilanka Ringwald case HID_BOOT_MODE_KEYBOARD_ID:
367c8cb324eSMilanka Ringwald if (report_size < 8) return 0;
368c8cb324eSMilanka Ringwald break;
369c8cb324eSMilanka Ringwald case HID_BOOT_MODE_MOUSE_ID:
370c8cb324eSMilanka Ringwald if (report_size < 1) return 0;
371c8cb324eSMilanka Ringwald break;
372c8cb324eSMilanka Ringwald default:
373c8cb324eSMilanka Ringwald return 0;
374c8cb324eSMilanka Ringwald }
375c8cb324eSMilanka Ringwald } else {
376*2cca3b08SMatthias Ringwald int size = btstack_hid_get_report_size_for_id(report_id, report_type, hid_device_descriptor, hid_device_descriptor_len);
377c1ab6cc1SMatthias Ringwald if ((size == 0) || (size != report_size)) return 0;
378c8cb324eSMilanka Ringwald }
379c8cb324eSMilanka Ringwald return 1;
380c8cb324eSMilanka Ringwald }
381c8cb324eSMilanka Ringwald
hid_get_report_size_for_id(uint16_t cid,int report_id,hid_report_type_t report_type,uint16_t descriptor_len,const uint8_t * descriptor)382a796c06fSMilanka Ringwald static int hid_get_report_size_for_id(uint16_t cid, int report_id, hid_report_type_t report_type, uint16_t descriptor_len, const uint8_t * descriptor){
383c8cb324eSMilanka Ringwald if (hid_device_in_boot_protocol_mode(cid)){
384c8cb324eSMilanka Ringwald switch (report_id){
385c8cb324eSMilanka Ringwald case HID_BOOT_MODE_KEYBOARD_ID:
386c8cb324eSMilanka Ringwald return 8;
387c8cb324eSMilanka Ringwald case HID_BOOT_MODE_MOUSE_ID:
388c8cb324eSMilanka Ringwald return 3;
389c8cb324eSMilanka Ringwald default:
390c8cb324eSMilanka Ringwald return 0;
391c8cb324eSMilanka Ringwald }
392c8cb324eSMilanka Ringwald } else {
393*2cca3b08SMatthias Ringwald return btstack_hid_get_report_size_for_id(report_id, report_type, descriptor, descriptor_len);
394c8cb324eSMilanka Ringwald }
395c8cb324eSMilanka Ringwald }
396c8cb324eSMilanka Ringwald
hid_report_id_status(uint16_t cid,uint16_t report_id)397c8cb324eSMilanka Ringwald static hid_report_id_status_t hid_report_id_status(uint16_t cid, uint16_t report_id){
398c8cb324eSMilanka Ringwald if (hid_device_in_boot_protocol_mode(cid)){
399c8cb324eSMilanka Ringwald switch (report_id){
400c8cb324eSMilanka Ringwald case HID_BOOT_MODE_KEYBOARD_ID:
401c8cb324eSMilanka Ringwald case HID_BOOT_MODE_MOUSE_ID:
402c8cb324eSMilanka Ringwald return HID_REPORT_ID_VALID;
403c8cb324eSMilanka Ringwald default:
404c8cb324eSMilanka Ringwald return HID_REPORT_ID_INVALID;
405c8cb324eSMilanka Ringwald }
406c8cb324eSMilanka Ringwald } else {
407*2cca3b08SMatthias Ringwald return btstack_hid_report_id_valid(report_id, hid_device_descriptor, hid_device_descriptor_len);
408c8cb324eSMilanka Ringwald }
409c8cb324eSMilanka Ringwald }
410c8cb324eSMilanka Ringwald
hid_device_set_report_cmd_is_valid(uint16_t cid,hid_report_type_t report_type,int report_size,uint8_t * report)4114dfe6a8bSMilanka Ringwald static hid_handshake_param_type_t hid_device_set_report_cmd_is_valid(uint16_t cid, hid_report_type_t report_type, int report_size, uint8_t * report){
4124dfe6a8bSMilanka Ringwald int pos = 0;
413c8cb324eSMilanka Ringwald int report_id = 0;
414c8cb324eSMilanka Ringwald
415*2cca3b08SMatthias Ringwald if (btstack_hid_report_id_declared(hid_device_descriptor, hid_device_descriptor_len)){
416c8cb324eSMilanka Ringwald report_id = report[pos++];
4174dfe6a8bSMilanka Ringwald hid_report_id_status_t report_id_status = hid_report_id_status(cid, report_id);
4184dfe6a8bSMilanka Ringwald switch (report_id_status){
4194dfe6a8bSMilanka Ringwald case HID_REPORT_ID_INVALID:
4204dfe6a8bSMilanka Ringwald return HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
4214dfe6a8bSMilanka Ringwald default:
4224dfe6a8bSMilanka Ringwald break;
4234dfe6a8bSMilanka Ringwald }
424c8cb324eSMilanka Ringwald }
4254dfe6a8bSMilanka Ringwald
4264dfe6a8bSMilanka Ringwald if (!hid_report_size_valid(cid, report_id, report_type, report_size-pos)){
427c8cb324eSMilanka Ringwald // TODO clarify DCT/BI-03c
4284dfe6a8bSMilanka Ringwald return HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
4294dfe6a8bSMilanka Ringwald }
4304dfe6a8bSMilanka Ringwald return HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
4314dfe6a8bSMilanka Ringwald }
4324dfe6a8bSMilanka Ringwald
packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t packet_size)4338eb8d463SMatthias Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
4348eb8d463SMatthias Ringwald UNUSED(channel);
4358eb8d463SMatthias Ringwald UNUSED(packet_size);
4368eb8d463SMatthias Ringwald int connected_before;
43757c643eeSMatthias Ringwald uint16_t psm;
43857c643eeSMatthias Ringwald uint8_t status;
439c12110e4SMilanka Ringwald hid_device_t * device = NULL;
4406510739bSMilanka Ringwald uint8_t param;
4416510739bSMilanka Ringwald bd_addr_t address;
4427d26fe66SMilanka Ringwald uint16_t local_cid;
4431002ad19SMatthias Ringwald uint16_t pos;
444ba9a58feSMilanka Ringwald int report_size;
4451002ad19SMatthias Ringwald uint8_t * outgoing_buffer;
446ca3f868cSMilanka Ringwald hid_message_type_t message_type;
4471002ad19SMatthias Ringwald bool need_report_id;
4481002ad19SMatthias Ringwald bool need_size;
4491002ad19SMatthias Ringwald uint16_t response_size;
450c12110e4SMilanka Ringwald
4518eb8d463SMatthias Ringwald switch (packet_type){
452c12110e4SMilanka Ringwald case L2CAP_DATA_PACKET:
4534176cbc0SMatthias Ringwald device = hid_device_get_instance_for_l2cap_cid(channel);
454c12110e4SMilanka Ringwald if (!device) {
455c12110e4SMilanka Ringwald log_error("no device with cid 0x%02x", channel);
4561002ad19SMatthias Ringwald break;
457c12110e4SMilanka Ringwald }
4581002ad19SMatthias Ringwald if (packet_size < 1) break;
4591002ad19SMatthias Ringwald
460ca3f868cSMilanka Ringwald message_type = (hid_message_type_t)(packet[0] >> 4);
461c12110e4SMilanka Ringwald switch (message_type){
462c12110e4SMilanka Ringwald case HID_MESSAGE_TYPE_GET_REPORT:
463a1118d11SMilanka Ringwald pos = 0;
464ca3f868cSMilanka Ringwald device->report_type = (hid_report_type_t)(packet[pos++] & 0x03);
465c12110e4SMilanka Ringwald device->report_id = 0;
4667d26fe66SMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
467a1118d11SMilanka Ringwald device->state = HID_DEVICE_W2_GET_REPORT;
4687d26fe66SMilanka Ringwald
4691002ad19SMatthias Ringwald // get and validate report id if needed
4701002ad19SMatthias Ringwald need_report_id = false;
4717d26fe66SMilanka Ringwald switch (device->protocol_mode){
4727d26fe66SMilanka Ringwald case HID_PROTOCOL_MODE_BOOT:
4731002ad19SMatthias Ringwald need_report_id = true;
4747d26fe66SMilanka Ringwald break;
4757d26fe66SMilanka Ringwald case HID_PROTOCOL_MODE_REPORT:
476*2cca3b08SMatthias Ringwald need_report_id = btstack_hid_report_id_declared(hid_device_descriptor, hid_device_descriptor_len) != 0;
477c8cb324eSMilanka Ringwald break;
4787bbeb3adSMilanka Ringwald default:
4797bbeb3adSMilanka Ringwald btstack_assert(false);
4807bbeb3adSMilanka Ringwald break;
481c8cb324eSMilanka Ringwald }
4821002ad19SMatthias Ringwald if (need_report_id){
4831002ad19SMatthias Ringwald if (packet_size >= (pos + 1)){
4841002ad19SMatthias Ringwald device->report_id = packet[pos++];
4851002ad19SMatthias Ringwald if (hid_report_id_status(device->cid, device->report_id) == HID_REPORT_ID_INVALID){
4861002ad19SMatthias Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
4871002ad19SMatthias Ringwald }
4881002ad19SMatthias Ringwald } else {
4891002ad19SMatthias Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_REPORT_ID;
4901002ad19SMatthias Ringwald }
4911002ad19SMatthias Ringwald }
492c8cb324eSMilanka Ringwald if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
493e99e41f0SMatthias Ringwald l2cap_request_can_send_now_event(device->control_cid);
494c8cb324eSMilanka Ringwald break;
495c8cb324eSMilanka Ringwald }
496c8cb324eSMilanka Ringwald
497938d471dSMatthias Ringwald // calculate response size
498938d471dSMatthias Ringwald device->expected_report_size = hid_get_report_size_for_id(device->cid, device->report_id, device->report_type, hid_device_descriptor_len, hid_device_descriptor);
499938d471dSMatthias Ringwald response_size = device->expected_report_size + pos; // DATA [+ ReportID]
500938d471dSMatthias Ringwald
5011002ad19SMatthias Ringwald // if size bit is set in header, next two bytes indicate host buffer size
5021002ad19SMatthias Ringwald need_size = (packet[0] & 0x08) != 0;
5031002ad19SMatthias Ringwald if (need_size){
5041002ad19SMatthias Ringwald if (packet_size >= (pos + 2)) {
505938d471dSMatthias Ringwald uint16_t host_buffer_size = little_endian_read_16(packet, pos);
506938d471dSMatthias Ringwald // host buffer size does not include the DATA header
507938d471dSMatthias Ringwald response_size = btstack_min(response_size, host_buffer_size + 1);
508a1118d11SMilanka Ringwald } else {
5091002ad19SMatthias Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
5101002ad19SMatthias Ringwald l2cap_request_can_send_now_event(device->control_cid);
5111002ad19SMatthias Ringwald break;
512a1118d11SMilanka Ringwald }
5131002ad19SMatthias Ringwald }
5141002ad19SMatthias Ringwald device->response_size = response_size;
515a1118d11SMilanka Ringwald
516e99e41f0SMatthias Ringwald l2cap_request_can_send_now_event(device->control_cid);
5177d26fe66SMilanka Ringwald break;
5187d26fe66SMilanka Ringwald
5197d26fe66SMilanka Ringwald case HID_MESSAGE_TYPE_SET_REPORT:
5207d26fe66SMilanka Ringwald device->state = HID_DEVICE_W2_SET_REPORT;
521ca3f868cSMilanka Ringwald device->report_type = (hid_report_type_t)(packet[0] & 0x03);
5227d26fe66SMilanka Ringwald if (packet_size < 1){
5237d26fe66SMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
5247d26fe66SMilanka Ringwald break;
5257d26fe66SMilanka Ringwald }
5267d26fe66SMilanka Ringwald
5277d26fe66SMilanka Ringwald switch (device->protocol_mode){
5287d26fe66SMilanka Ringwald case HID_PROTOCOL_MODE_BOOT:
5297d26fe66SMilanka Ringwald if (packet_size < 3){
5307d26fe66SMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
5317d26fe66SMilanka Ringwald break;
5327d26fe66SMilanka Ringwald }
5337d26fe66SMilanka Ringwald device->report_id = packet[1];
5344dfe6a8bSMilanka Ringwald device->report_status = hid_device_set_report_cmd_is_valid(device->cid, device->report_type, packet_size - 1, &packet[1]);
5354dfe6a8bSMilanka Ringwald if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) break;
5364dfe6a8bSMilanka Ringwald (*hci_device_set_report)(device->cid, device->report_type, packet_size-1, &packet[1]);
5377d26fe66SMilanka Ringwald break;
5387d26fe66SMilanka Ringwald case HID_PROTOCOL_MODE_REPORT:
5394dfe6a8bSMilanka Ringwald device->report_status = hid_device_set_report_cmd_is_valid(device->cid, device->report_type, packet_size - 1, &packet[1]);
5404dfe6a8bSMilanka Ringwald if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) break;
5414dfe6a8bSMilanka Ringwald
5427d26fe66SMilanka Ringwald if (packet_size >= 2){
5434dfe6a8bSMilanka Ringwald (*hci_device_set_report)(device->cid, device->report_type, packet_size-1, &packet[1]);
5447d26fe66SMilanka Ringwald } else {
5457d26fe66SMilanka Ringwald uint8_t payload[] = {0};
5464dfe6a8bSMilanka Ringwald (*hci_device_set_report)(device->cid, device->report_type, 1, payload);
5477d26fe66SMilanka Ringwald }
5487d26fe66SMilanka Ringwald break;
5497bbeb3adSMilanka Ringwald default:
5507bbeb3adSMilanka Ringwald btstack_assert(false);
5517bbeb3adSMilanka Ringwald break;
5527d26fe66SMilanka Ringwald }
553ca3f868cSMilanka Ringwald device->report_type = (hid_report_type_t)(packet[0] & 0x03);
554e99e41f0SMatthias Ringwald l2cap_request_can_send_now_event(device->control_cid);
555ba9a58feSMilanka Ringwald break;
556ba9a58feSMilanka Ringwald case HID_MESSAGE_TYPE_GET_PROTOCOL:
557ba9a58feSMilanka Ringwald device->state = HID_DEVICE_W2_GET_PROTOCOL;
558ba9a58feSMilanka Ringwald if (packet_size != 1) {
559ba9a58feSMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
560ba9a58feSMilanka Ringwald break;
561ba9a58feSMilanka Ringwald }
562ba9a58feSMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
563df3cc7a0SMilanka Ringwald // hid_device_request_can_send_now_event(channel);
564df3cc7a0SMilanka Ringwald l2cap_request_can_send_now_event(device->control_cid);
5657d26fe66SMilanka Ringwald break;
566ba9a58feSMilanka Ringwald
567ba9a58feSMilanka Ringwald case HID_MESSAGE_TYPE_SET_PROTOCOL:
568ba9a58feSMilanka Ringwald device->state = HID_DEVICE_W2_SET_PROTOCOL;
569ba9a58feSMilanka Ringwald if (packet_size != 1) {
570ba9a58feSMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
571ba9a58feSMilanka Ringwald break;
572ba9a58feSMilanka Ringwald }
573ba9a58feSMilanka Ringwald param = packet[0] & 0x01;
574de81b46aSMatthias Ringwald if (((hid_protocol_mode_t)param == HID_PROTOCOL_MODE_BOOT) && !hid_device_boot_protocol_mode_supported){
575ba9a58feSMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
576ba9a58feSMilanka Ringwald break;
577ba9a58feSMilanka Ringwald }
578ca3f868cSMilanka Ringwald device->protocol_mode = (hid_protocol_mode_t) param;
5797d26fe66SMilanka Ringwald switch (device->protocol_mode){
5807d26fe66SMilanka Ringwald case HID_PROTOCOL_MODE_BOOT:
581ba9a58feSMilanka Ringwald break;
5827d26fe66SMilanka Ringwald case HID_PROTOCOL_MODE_REPORT:
5837d26fe66SMilanka Ringwald break;
5847bbeb3adSMilanka Ringwald default:
5857bbeb3adSMilanka Ringwald btstack_assert(false);
5867bbeb3adSMilanka Ringwald break;
5877d26fe66SMilanka Ringwald }
5887d26fe66SMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
589e99e41f0SMatthias Ringwald l2cap_request_can_send_now_event(device->control_cid);
5907d26fe66SMilanka Ringwald break;
5917d26fe66SMilanka Ringwald
5926510739bSMilanka Ringwald case HID_MESSAGE_TYPE_HID_CONTROL:
5936510739bSMilanka Ringwald param = packet[0] & 0x0F;
594ffe3f1a1SMilanka Ringwald
595ffe3f1a1SMilanka Ringwald switch ((hid_control_param_t)param){
5966510739bSMilanka Ringwald case HID_CONTROL_PARAM_SUSPEND:
5976510739bSMilanka Ringwald hid_device_emit_event(device, HID_SUBEVENT_SUSPEND);
5986510739bSMilanka Ringwald break;
5996510739bSMilanka Ringwald case HID_CONTROL_PARAM_EXIT_SUSPEND:
6006510739bSMilanka Ringwald hid_device_emit_event(device, HID_SUBEVENT_EXIT_SUSPEND);
6016510739bSMilanka Ringwald break;
602acfd7ed2SMilanka Ringwald case HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG:
603acfd7ed2SMilanka Ringwald hid_device_emit_event(device, HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG);
604acfd7ed2SMilanka Ringwald break;
6056510739bSMilanka Ringwald default:
6066510739bSMilanka Ringwald device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
607e99e41f0SMatthias Ringwald l2cap_request_can_send_now_event(device->control_cid);
6086510739bSMilanka Ringwald break;
6096510739bSMilanka Ringwald }
6106510739bSMilanka Ringwald break;
6117d26fe66SMilanka Ringwald
6127d26fe66SMilanka Ringwald case HID_MESSAGE_TYPE_DATA:
6137d26fe66SMilanka Ringwald if (packet_size < 2) {
6147d26fe66SMilanka Ringwald break;
6157d26fe66SMilanka Ringwald }
61607678896SMilanka Ringwald pos = 0;
617ca3f868cSMilanka Ringwald device->report_type = (hid_report_type_t)(packet[pos++] & 0x03);
61807678896SMilanka Ringwald device->report_id = 0;
619*2cca3b08SMatthias Ringwald if (btstack_hid_report_id_declared(hid_device_descriptor, hid_device_descriptor_len)){
62007678896SMilanka Ringwald device->report_id = packet[pos++];
621738c9391SMilanka Ringwald }
62207678896SMilanka Ringwald
62307678896SMilanka Ringwald if (hid_report_id_status(device->cid, device->report_id) == HID_REPORT_ID_INVALID){
62407678896SMilanka Ringwald log_info("Ignore invalid report data packet");
62507678896SMilanka Ringwald break;
62607678896SMilanka Ringwald }
62707678896SMilanka Ringwald if (!hid_report_size_valid(device->cid, device->report_id, device->report_type, packet_size - pos)){
62807678896SMilanka Ringwald log_info("Ignore invalid report data packet, invalid size");
62907678896SMilanka Ringwald break;
63007678896SMilanka Ringwald }
63107678896SMilanka Ringwald (*hci_device_report_data)(device->cid, device->report_type, device->report_id, packet_size - pos, &packet[pos]);
6327d26fe66SMilanka Ringwald break;
633c12110e4SMilanka Ringwald default:
634c12110e4SMilanka Ringwald device->state = HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST;
635e99e41f0SMatthias Ringwald l2cap_request_can_send_now_event(device->control_cid);
636c12110e4SMilanka Ringwald break;
637c12110e4SMilanka Ringwald }
638c12110e4SMilanka Ringwald break;
6398eb8d463SMatthias Ringwald case HCI_EVENT_PACKET:
6408eb8d463SMatthias Ringwald switch (packet[0]){
6418eb8d463SMatthias Ringwald case L2CAP_EVENT_INCOMING_CONNECTION:
6428eb8d463SMatthias Ringwald switch (l2cap_event_incoming_connection_get_psm(packet)){
6438eb8d463SMatthias Ringwald case PSM_HID_CONTROL:
6448eb8d463SMatthias Ringwald case PSM_HID_INTERRUPT:
6456510739bSMilanka Ringwald l2cap_event_incoming_connection_get_address(packet, address);
64672e3f392SMatthias Ringwald device = hid_device_provide_instance_for_bd_addr(address);
647c12110e4SMilanka Ringwald if (!device) {
6483da09877SMatthias Ringwald log_error("L2CAP_EVENT_INCOMING_CONNECTION, cannot create instance for %s", bd_addr_to_str(address));
649c12110e4SMilanka Ringwald l2cap_decline_connection(channel);
650c12110e4SMilanka Ringwald break;
651c12110e4SMilanka Ringwald }
652c1ab6cc1SMatthias Ringwald if ((device->con_handle == HCI_CON_HANDLE_INVALID) || (l2cap_event_incoming_connection_get_handle(packet) == device->con_handle)){
653c12110e4SMilanka Ringwald device->con_handle = l2cap_event_incoming_connection_get_handle(packet);
654c12110e4SMilanka Ringwald device->incoming = 1;
6556510739bSMilanka Ringwald l2cap_event_incoming_connection_get_address(packet, device->bd_addr);
6563da09877SMatthias Ringwald psm = l2cap_event_incoming_connection_get_psm(packet);
6573da09877SMatthias Ringwald switch (psm){
6583da09877SMatthias Ringwald case PSM_HID_CONTROL:
6593da09877SMatthias Ringwald device->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
6603da09877SMatthias Ringwald break;
6613da09877SMatthias Ringwald case PSM_HID_INTERRUPT:
6623da09877SMatthias Ringwald device->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
6633da09877SMatthias Ringwald break;
6643da09877SMatthias Ringwald default:
6653da09877SMatthias Ringwald break;
6663da09877SMatthias Ringwald }
6673da09877SMatthias Ringwald
6688eb8d463SMatthias Ringwald l2cap_accept_connection(channel);
6698eb8d463SMatthias Ringwald } else {
6708eb8d463SMatthias Ringwald l2cap_decline_connection(channel);
6713da09877SMatthias Ringwald log_info("L2CAP_EVENT_INCOMING_CONNECTION, decline connection for %s", bd_addr_to_str(address));
6728eb8d463SMatthias Ringwald }
6738eb8d463SMatthias Ringwald break;
6748eb8d463SMatthias Ringwald default:
6758eb8d463SMatthias Ringwald l2cap_decline_connection(channel);
6768eb8d463SMatthias Ringwald break;
6778eb8d463SMatthias Ringwald }
6788eb8d463SMatthias Ringwald break;
6798eb8d463SMatthias Ringwald case L2CAP_EVENT_CHANNEL_OPENED:
6803da09877SMatthias Ringwald device = hid_device_get_instance_for_l2cap_cid(l2cap_event_channel_opened_get_local_cid(packet));
681c12110e4SMilanka Ringwald if (!device) {
682c12110e4SMilanka Ringwald log_error("L2CAP_EVENT_CHANNEL_OPENED, no hid device for local cid 0x%02x", l2cap_event_channel_opened_get_local_cid(packet));
683c12110e4SMilanka Ringwald return;
684c12110e4SMilanka Ringwald }
68557c643eeSMatthias Ringwald status = l2cap_event_channel_opened_get_status(packet);
68657c643eeSMatthias Ringwald if (status) {
687c12110e4SMilanka Ringwald if (device->incoming == 0){
68857c643eeSMatthias Ringwald // report error for outgoing connection
689c12110e4SMilanka Ringwald hid_device_emit_connected_event(device, status);
69057c643eeSMatthias Ringwald }
69157c643eeSMatthias Ringwald return;
69257c643eeSMatthias Ringwald }
693641577b2SMatthias Ringwald
694641577b2SMatthias Ringwald // store con_handle
695641577b2SMatthias Ringwald if (device->con_handle == HCI_CON_HANDLE_INVALID){
696641577b2SMatthias Ringwald device->con_handle = l2cap_event_channel_opened_get_handle(packet);
697641577b2SMatthias Ringwald }
698641577b2SMatthias Ringwald
699641577b2SMatthias Ringwald // store l2cap cid
70057c643eeSMatthias Ringwald psm = l2cap_event_channel_opened_get_psm(packet);
70157c643eeSMatthias Ringwald switch (psm){
7028eb8d463SMatthias Ringwald case PSM_HID_CONTROL:
703c12110e4SMilanka Ringwald device->control_cid = l2cap_event_channel_opened_get_local_cid(packet);
7048eb8d463SMatthias Ringwald break;
7058eb8d463SMatthias Ringwald case PSM_HID_INTERRUPT:
706c12110e4SMilanka Ringwald device->interrupt_cid = l2cap_event_channel_opened_get_local_cid(packet);
7078eb8d463SMatthias Ringwald break;
7088eb8d463SMatthias Ringwald default:
7098eb8d463SMatthias Ringwald break;
7108eb8d463SMatthias Ringwald }
7116510739bSMilanka Ringwald
71257c643eeSMatthias Ringwald // connect HID Interrupt for outgoing
713c1ab6cc1SMatthias Ringwald if ((device->incoming == 0) && (psm == PSM_HID_CONTROL)){
714e942df6fSshuffle2 status = l2cap_create_channel(packet_handler, device->bd_addr, PSM_HID_INTERRUPT, l2cap_max_mtu(), &device->interrupt_cid);
71557c643eeSMatthias Ringwald break;
71657c643eeSMatthias Ringwald }
717641577b2SMatthias Ringwald
718641577b2SMatthias Ringwald // emit connected if both channels are open
719641577b2SMatthias Ringwald connected_before = device->connected;
720c12110e4SMilanka Ringwald if (!connected_before && device->control_cid && device->interrupt_cid){
721c12110e4SMilanka Ringwald device->connected = 1;
722c12110e4SMilanka Ringwald hid_device_emit_connected_event(device, 0);
7238eb8d463SMatthias Ringwald }
7248eb8d463SMatthias Ringwald break;
7258eb8d463SMatthias Ringwald case L2CAP_EVENT_CHANNEL_CLOSED:
7264176cbc0SMatthias Ringwald device = hid_device_get_instance_for_l2cap_cid(l2cap_event_channel_closed_get_local_cid(packet));
727c12110e4SMilanka Ringwald if (!device) return;
728c12110e4SMilanka Ringwald
7296510739bSMilanka Ringwald // connected_before = device->connected;
730c12110e4SMilanka Ringwald device->incoming = 0;
731c12110e4SMilanka Ringwald if (l2cap_event_channel_closed_get_local_cid(packet) == device->interrupt_cid){
732c12110e4SMilanka Ringwald device->interrupt_cid = 0;
73357c18996SMilanka Ringwald }
73457c18996SMilanka Ringwald if (l2cap_event_channel_closed_get_local_cid(packet) == device->control_cid){
73557c18996SMilanka Ringwald device->control_cid = 0;
7368eb8d463SMatthias Ringwald }
7376510739bSMilanka Ringwald if (!device->interrupt_cid && !device->control_cid){
7386510739bSMilanka Ringwald device->connected = 0;
7393da09877SMatthias Ringwald device->con_handle = HCI_CON_HANDLE_INVALID;
7406510739bSMilanka Ringwald device->cid = 0;
741caf9a547SMilanka Ringwald hid_device_emit_event(device, HID_SUBEVENT_CONNECTION_CLOSED);
7428eb8d463SMatthias Ringwald }
7438eb8d463SMatthias Ringwald break;
744c12110e4SMilanka Ringwald
74557c18996SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW:
7467d26fe66SMilanka Ringwald local_cid = l2cap_event_can_send_now_get_local_cid(packet);
7474176cbc0SMatthias Ringwald device = hid_device_get_instance_for_l2cap_cid(local_cid);
748c12110e4SMilanka Ringwald if (!device) return;
7491002ad19SMatthias Ringwald
75069c72f1cSMatthias Ringwald l2cap_reserve_packet_buffer();
7511002ad19SMatthias Ringwald outgoing_buffer = l2cap_get_outgoing_buffer();
7521002ad19SMatthias Ringwald
753c12110e4SMilanka Ringwald switch (device->state){
754a1118d11SMilanka Ringwald case HID_DEVICE_W2_GET_REPORT:{
7557d26fe66SMilanka Ringwald if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL) {
7561002ad19SMatthias Ringwald outgoing_buffer[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
7571002ad19SMatthias Ringwald hid_device_send_prepared_control_message(device, 1);
7587d26fe66SMilanka Ringwald break;
7597d26fe66SMilanka Ringwald }
760a1118d11SMilanka Ringwald
761a1118d11SMilanka Ringwald pos = 0;
7621002ad19SMatthias Ringwald outgoing_buffer[pos++] = (HID_MESSAGE_TYPE_DATA << 4) | device->report_type;
7631002ad19SMatthias Ringwald if (device->report_id != 0){
7641002ad19SMatthias Ringwald outgoing_buffer[pos++] = device->report_id;
765a1118d11SMilanka Ringwald }
766a1118d11SMilanka Ringwald
7677d26fe66SMilanka Ringwald report_size = 0;
7681002ad19SMatthias Ringwald status = (*hci_device_get_report)(device->cid, device->report_type, device->report_id, &report_size, &outgoing_buffer[pos]);
769481c7cdcSMilanka Ringwald
770a1118d11SMilanka Ringwald switch (status){
771a1118d11SMilanka Ringwald case 0:
772a1118d11SMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_NOT_READY;
773a1118d11SMilanka Ringwald break;
774a1118d11SMilanka Ringwald case 1:
775a1118d11SMilanka Ringwald if (report_size == 0){
776a1118d11SMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
777a1118d11SMilanka Ringwald break;
778a1118d11SMilanka Ringwald }
779a1118d11SMilanka Ringwald if (device->expected_report_size != report_size){
780a1118d11SMilanka Ringwald log_error("Expected report size of %d bytes, received %d", device->expected_report_size, report_size);
781a1118d11SMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
782a1118d11SMilanka Ringwald break;
783a1118d11SMilanka Ringwald }
784a1118d11SMilanka Ringwald break;
785a1118d11SMilanka Ringwald default:
786a1118d11SMilanka Ringwald device->report_status = HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
787a1118d11SMilanka Ringwald break;
788a1118d11SMilanka Ringwald }
789ba9a58feSMilanka Ringwald if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
7901002ad19SMatthias Ringwald outgoing_buffer[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
7911002ad19SMatthias Ringwald hid_device_send_prepared_control_message(device, 1);
79269c72f1cSMatthias Ringwald } else {
7931002ad19SMatthias Ringwald hid_device_send_prepared_control_message(device, device->response_size);
79469c72f1cSMatthias Ringwald }
795c12110e4SMilanka Ringwald break;
796a1118d11SMilanka Ringwald }
797ba9a58feSMilanka Ringwald case HID_DEVICE_W2_SET_REPORT:
798ba9a58feSMilanka Ringwald case HID_DEVICE_W2_SET_PROTOCOL:
7991002ad19SMatthias Ringwald outgoing_buffer[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
8001002ad19SMatthias Ringwald hid_device_send_prepared_control_message(device, 1);
801ba9a58feSMilanka Ringwald break;
802ba9a58feSMilanka Ringwald case HID_DEVICE_W2_GET_PROTOCOL:
803ba9a58feSMilanka Ringwald if (device->report_status != HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL){
8041002ad19SMatthias Ringwald outgoing_buffer[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | device->report_status;
8051002ad19SMatthias Ringwald hid_device_send_prepared_control_message(device, 1);
80669c72f1cSMatthias Ringwald } else {
8071002ad19SMatthias Ringwald outgoing_buffer[0] = (HID_MESSAGE_TYPE_DATA << 4);
8081002ad19SMatthias Ringwald outgoing_buffer[1] = device->protocol_mode;
8091002ad19SMatthias Ringwald hid_device_send_prepared_control_message(device, 2);
81069c72f1cSMatthias Ringwald }
811ba9a58feSMilanka Ringwald break;
812ba9a58feSMilanka Ringwald case HID_DEVICE_W2_SEND_UNSUPPORTED_REQUEST:
8131002ad19SMatthias Ringwald outgoing_buffer[0] = (HID_MESSAGE_TYPE_HANDSHAKE << 4) | HID_HANDSHAKE_PARAM_TYPE_ERR_UNSUPPORTED_REQUEST;
8141002ad19SMatthias Ringwald hid_device_send_prepared_control_message(device, 1);
815c12110e4SMilanka Ringwald break;
816c12110e4SMilanka Ringwald default:
81769c72f1cSMatthias Ringwald l2cap_release_packet_buffer();
818e99e41f0SMatthias Ringwald if (device->user_request_can_send_now){
819e99e41f0SMatthias Ringwald device->user_request_can_send_now = 0;
820caf9a547SMilanka Ringwald hid_device_emit_event(device, HID_SUBEVENT_CAN_SEND_NOW);
821e99e41f0SMatthias Ringwald }
822c12110e4SMilanka Ringwald break;
823c12110e4SMilanka Ringwald }
824ba9a58feSMilanka Ringwald device->state = HID_DEVICE_IDLE;
8258eb8d463SMatthias Ringwald break;
8268eb8d463SMatthias Ringwald default:
8278eb8d463SMatthias Ringwald break;
8288eb8d463SMatthias Ringwald }
8298eb8d463SMatthias Ringwald break;
8308eb8d463SMatthias Ringwald default:
8318eb8d463SMatthias Ringwald break;
8328eb8d463SMatthias Ringwald }
8338eb8d463SMatthias Ringwald }
8348eb8d463SMatthias Ringwald
8358eb8d463SMatthias Ringwald /**
8368eb8d463SMatthias Ringwald * @brief Set up HID Device
8378eb8d463SMatthias Ringwald */
hid_device_init(bool boot_protocol_mode_supported,uint16_t descriptor_len,const uint8_t * descriptor)83851b27067SMilanka Ringwald void hid_device_init(bool boot_protocol_mode_supported, uint16_t descriptor_len, const uint8_t * descriptor){
839de81b46aSMatthias Ringwald hid_device_boot_protocol_mode_supported = boot_protocol_mode_supported;
840de81b46aSMatthias Ringwald hid_device_descriptor = descriptor;
841de81b46aSMatthias Ringwald hid_device_descriptor_len = descriptor_len;
842a1118d11SMilanka Ringwald hci_device_get_report = dummy_write_report;
843a1118d11SMilanka Ringwald hci_device_set_report = dummy_set_report;
844a1118d11SMilanka Ringwald hci_device_report_data = dummy_report_data;
845a1118d11SMilanka Ringwald
84678315a58SMatthias Ringwald l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, gap_get_security_level());
84778315a58SMatthias Ringwald l2cap_register_service(packet_handler, PSM_HID_CONTROL, 100, gap_get_security_level());
8488eb8d463SMatthias Ringwald }
8498eb8d463SMatthias Ringwald
hid_device_deinit(void)8502cc1b993SMatthias Ringwald void hid_device_deinit(void){
851de81b46aSMatthias Ringwald hid_device_callback = NULL;
852de81b46aSMatthias Ringwald hci_device_get_report = NULL;
853de81b46aSMatthias Ringwald hci_device_set_report = NULL;
854de81b46aSMatthias Ringwald hci_device_report_data = NULL;
855de81b46aSMatthias Ringwald
856de81b46aSMatthias Ringwald (void) memset(&hid_device_singleton, 0, sizeof(hid_device_t));
857de81b46aSMatthias Ringwald
858de81b46aSMatthias Ringwald hid_device_boot_protocol_mode_supported = false;
859de81b46aSMatthias Ringwald hid_device_descriptor = NULL;
860de81b46aSMatthias Ringwald hid_device_descriptor_len = 0;
861de81b46aSMatthias Ringwald hid_device_cid = 0;
8622cc1b993SMatthias Ringwald }
8632cc1b993SMatthias Ringwald
8648eb8d463SMatthias Ringwald /**
8658eb8d463SMatthias Ringwald * @brief Register callback for the HID Device client.
8668eb8d463SMatthias Ringwald * @param callback
8678eb8d463SMatthias Ringwald */
hid_device_register_packet_handler(btstack_packet_handler_t callback)8688eb8d463SMatthias Ringwald void hid_device_register_packet_handler(btstack_packet_handler_t callback){
869de81b46aSMatthias Ringwald hid_device_callback = callback;
8708eb8d463SMatthias Ringwald }
8718eb8d463SMatthias Ringwald
hid_device_register_report_request_callback(int (* callback)(uint16_t hid_cid,hid_report_type_t report_type,uint16_t report_id,int * out_report_size,uint8_t * out_report))872a1118d11SMilanka Ringwald void hid_device_register_report_request_callback(int (*callback)(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, int * out_report_size, uint8_t * out_report)){
873c12110e4SMilanka Ringwald if (callback == NULL){
874c12110e4SMilanka Ringwald callback = dummy_write_report;
875c12110e4SMilanka Ringwald }
876a1118d11SMilanka Ringwald hci_device_get_report = callback;
877c12110e4SMilanka Ringwald }
878c12110e4SMilanka Ringwald
hid_device_register_set_report_callback(void (* callback)(uint16_t hid_cid,hid_report_type_t report_type,int report_size,uint8_t * report))8794dfe6a8bSMilanka Ringwald void hid_device_register_set_report_callback(void (*callback)(uint16_t hid_cid, hid_report_type_t report_type, int report_size, uint8_t * report)){
880ba9a58feSMilanka Ringwald if (callback == NULL){
881ba9a58feSMilanka Ringwald callback = dummy_set_report;
882ba9a58feSMilanka Ringwald }
883ba9a58feSMilanka Ringwald hci_device_set_report = callback;
884ba9a58feSMilanka Ringwald }
885c12110e4SMilanka Ringwald
hid_device_register_report_data_callback(void (* callback)(uint16_t cid,hid_report_type_t report_type,uint16_t report_id,int report_size,uint8_t * report))8867d26fe66SMilanka Ringwald void hid_device_register_report_data_callback(void (*callback)(uint16_t cid, hid_report_type_t report_type, uint16_t report_id, int report_size, uint8_t * report)){
8877d26fe66SMilanka Ringwald if (callback == NULL){
8887d26fe66SMilanka Ringwald callback = dummy_report_data;
8897d26fe66SMilanka Ringwald }
8907d26fe66SMilanka Ringwald hci_device_report_data = callback;
8917d26fe66SMilanka Ringwald }
8927d26fe66SMilanka Ringwald
8937d26fe66SMilanka Ringwald
8948eb8d463SMatthias Ringwald /**
8958eb8d463SMatthias Ringwald * @brief Request can send now event to send HID Report
8968eb8d463SMatthias Ringwald * @param hid_cid
8978eb8d463SMatthias Ringwald */
hid_device_request_can_send_now_event(uint16_t hid_cid)8988eb8d463SMatthias Ringwald void hid_device_request_can_send_now_event(uint16_t hid_cid){
8994176cbc0SMatthias Ringwald hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
900e99e41f0SMatthias Ringwald if (!hid_device || !hid_device->interrupt_cid) return;
901e99e41f0SMatthias Ringwald hid_device->user_request_can_send_now = 1;
902e99e41f0SMatthias Ringwald l2cap_request_can_send_now_event(hid_device->interrupt_cid);
9038eb8d463SMatthias Ringwald }
9048eb8d463SMatthias Ringwald
9058eb8d463SMatthias Ringwald /**
9068eb8d463SMatthias Ringwald * @brief Send HID message on interrupt channel
9078eb8d463SMatthias Ringwald * @param hid_cid
9088eb8d463SMatthias Ringwald */
hid_device_send_interrupt_message(uint16_t hid_cid,const uint8_t * message,uint16_t message_len)9098eb8d463SMatthias Ringwald void hid_device_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
9104176cbc0SMatthias Ringwald hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
911c12110e4SMilanka Ringwald if (!hid_device || !hid_device->interrupt_cid) return;
9128eb8d463SMatthias Ringwald l2cap_send(hid_device->interrupt_cid, (uint8_t*) message, message_len);
913e9392950SMilanka Ringwald if (hid_device->user_request_can_send_now){
914e99e41f0SMatthias Ringwald l2cap_request_can_send_now_event((hid_device->interrupt_cid));
915e99e41f0SMatthias Ringwald }
9168eb8d463SMatthias Ringwald }
9178eb8d463SMatthias Ringwald
9188eb8d463SMatthias Ringwald /**
9198eb8d463SMatthias Ringwald * @brief Send HID message on control channel
9208eb8d463SMatthias Ringwald * @param hid_cid
9218eb8d463SMatthias Ringwald */
hid_device_send_control_message(uint16_t hid_cid,const uint8_t * message,uint16_t message_len)922c12110e4SMilanka Ringwald void hid_device_send_control_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
9234176cbc0SMatthias Ringwald hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
924c12110e4SMilanka Ringwald if (!hid_device || !hid_device->control_cid) return;
9258eb8d463SMatthias Ringwald l2cap_send(hid_device->control_cid, (uint8_t*) message, message_len);
926d74c876fSMatthias Ringwald hid_device_trigger_user_request_if_pending(hid_device);
9278eb8d463SMatthias Ringwald }
9288eb8d463SMatthias Ringwald
92957c643eeSMatthias Ringwald /*
93057c643eeSMatthias Ringwald * @brief Create HID connection to HID Host
93157c643eeSMatthias Ringwald * @param addr
93257c643eeSMatthias Ringwald * @param hid_cid to use for other commands
93357c643eeSMatthias Ringwald * @result status
93457c643eeSMatthias Ringwald */
hid_device_connect(bd_addr_t addr,uint16_t * hid_cid)93557c643eeSMatthias Ringwald uint8_t hid_device_connect(bd_addr_t addr, uint16_t * hid_cid){
936c12110e4SMilanka Ringwald hid_device_t * hid_device = hid_device_create_instance();
937c12110e4SMilanka Ringwald if (!hid_device){
938c12110e4SMilanka Ringwald log_error("hid_device_connect: could not create a hid device instace");
939c12110e4SMilanka Ringwald return BTSTACK_MEMORY_ALLOC_FAILED;
940c12110e4SMilanka Ringwald }
94172e3f392SMatthias Ringwald
9426c8af51cSMatthias Ringwald // setup instance
9436c8af51cSMatthias Ringwald hid_device_setup_instance(hid_device, addr);
9446c8af51cSMatthias Ringwald *hid_cid = hid_device->cid;
94572e3f392SMatthias Ringwald
94657c643eeSMatthias Ringwald // create l2cap control using fixed HID L2CAP PSM
94757c643eeSMatthias Ringwald log_info("Create outgoing HID Control");
948e942df6fSshuffle2 uint8_t status = l2cap_create_channel(packet_handler, hid_device->bd_addr, PSM_HID_CONTROL, l2cap_max_mtu(), &hid_device->control_cid);
94957c643eeSMatthias Ringwald return status;
95057c643eeSMatthias Ringwald }
9516510739bSMilanka Ringwald
9526510739bSMilanka Ringwald /*
9536510739bSMilanka Ringwald * @brief Disconnect from HID Host
9546510739bSMilanka Ringwald * @param hid_cid
9556510739bSMilanka Ringwald * @result status
9566510739bSMilanka Ringwald */
hid_device_disconnect_interrupt_channel(uint16_t hid_cid)95757c18996SMilanka Ringwald void hid_device_disconnect_interrupt_channel(uint16_t hid_cid){
9584176cbc0SMatthias Ringwald hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
95957c18996SMilanka Ringwald if (!hid_device){
96057c18996SMilanka Ringwald log_error("hid_device_disconnect_interrupt_channel: could not find hid device instace");
96157c18996SMilanka Ringwald return;
96257c18996SMilanka Ringwald }
96357c18996SMilanka Ringwald log_info("Disconnect from interrupt channel HID Host");
96457c18996SMilanka Ringwald if (hid_device->interrupt_cid){
965b93f8966SMatthias Ringwald l2cap_disconnect(hid_device->interrupt_cid);
96657c18996SMilanka Ringwald }
96757c18996SMilanka Ringwald }
96857c18996SMilanka Ringwald
hid_device_disconnect_control_channel(uint16_t hid_cid)96957c18996SMilanka Ringwald void hid_device_disconnect_control_channel(uint16_t hid_cid){
9704176cbc0SMatthias Ringwald hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
97157c18996SMilanka Ringwald if (!hid_device){
97257c18996SMilanka Ringwald log_error("hid_device_disconnect_control_channel: could not find hid device instace");
97357c18996SMilanka Ringwald return;
97457c18996SMilanka Ringwald }
97557c18996SMilanka Ringwald log_info("Disconnect from control channel HID Host");
97657c18996SMilanka Ringwald if (hid_device->control_cid){
977b93f8966SMatthias Ringwald l2cap_disconnect(hid_device->control_cid);
97857c18996SMilanka Ringwald }
97957c18996SMilanka Ringwald }
98057c18996SMilanka Ringwald
hid_device_disconnect(uint16_t hid_cid)9816510739bSMilanka Ringwald void hid_device_disconnect(uint16_t hid_cid){
9824176cbc0SMatthias Ringwald hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
9836510739bSMilanka Ringwald if (!hid_device){
9846510739bSMilanka Ringwald log_error("hid_device_disconnect: could not find hid device instace");
9856510739bSMilanka Ringwald return;
9866510739bSMilanka Ringwald }
9876510739bSMilanka Ringwald log_info("Disconnect from HID Host");
9886510739bSMilanka Ringwald if (hid_device->interrupt_cid){
989b93f8966SMatthias Ringwald l2cap_disconnect(hid_device->interrupt_cid);
9906510739bSMilanka Ringwald }
99157c18996SMilanka Ringwald if (hid_device->control_cid){
992b93f8966SMatthias Ringwald l2cap_disconnect(hid_device->control_cid);
99357c18996SMilanka Ringwald }
9946510739bSMilanka Ringwald }
9957d26fe66SMilanka Ringwald
hid_device_in_boot_protocol_mode(uint16_t hid_cid)9967d26fe66SMilanka Ringwald int hid_device_in_boot_protocol_mode(uint16_t hid_cid){
9974176cbc0SMatthias Ringwald hid_device_t * hid_device = hid_device_get_instance_for_hid_cid(hid_cid);
9987d26fe66SMilanka Ringwald if (!hid_device){
9997d26fe66SMilanka Ringwald log_error("hid_device_in_boot_protocol_mode: could not find hid device instace");
10007d26fe66SMilanka Ringwald return 0;
10017d26fe66SMilanka Ringwald }
10027d26fe66SMilanka Ringwald return hid_device->protocol_mode == HID_PROTOCOL_MODE_BOOT;
10037d26fe66SMilanka Ringwald }
1004