Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / android / hal-hidhost.c
1 /*
2  * Copyright (C) 2013 Intel Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <stdbool.h>
19 #include <stddef.h>
20 #include <string.h>
21 #include <stdlib.h>
22
23 #include "hal-log.h"
24 #include "hal.h"
25 #include "hal-msg.h"
26 #include "ipc-common.h"
27 #include "hal-ipc.h"
28
29 static const bthh_callbacks_t *cbacks;
30
31 static bool interface_ready(void)
32 {
33         return cbacks != NULL;
34 }
35
36 static void handle_conn_state(void *buf, uint16_t len, int fd)
37 {
38         struct hal_ev_hidhost_conn_state *ev = buf;
39
40         if (cbacks->connection_state_cb)
41                 cbacks->connection_state_cb((bt_bdaddr_t *) ev->bdaddr,
42                                                                 ev->state);
43 }
44
45 static void handle_info(void *buf, uint16_t len, int fd)
46 {
47         struct hal_ev_hidhost_info *ev = buf;
48         bthh_hid_info_t info;
49
50         info.attr_mask = ev->attr;
51         info.sub_class = ev->subclass;
52         info.app_id = ev->app_id;
53         info.vendor_id = ev->vendor;
54         info.product_id = ev->product;
55         info.version = ev->version;
56         info.ctry_code = ev->country;
57         info.dl_len = ev->descr_len;
58         memcpy(info.dsc_list, ev->descr, info.dl_len);
59
60         if (cbacks->hid_info_cb)
61                 cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info);
62 }
63
64 static void handle_proto_mode(void *buf, uint16_t len, int fd)
65 {
66         struct hal_ev_hidhost_proto_mode *ev = buf;
67
68         if (cbacks->protocol_mode_cb)
69                 cbacks->protocol_mode_cb((bt_bdaddr_t *) ev->bdaddr,
70                                                         ev->status, ev->mode);
71 }
72
73 static void handle_idle_time(void *buf, uint16_t len, int fd)
74 {
75         struct hal_ev_hidhost_idle_time *ev = buf;
76
77         if (cbacks->idle_time_cb)
78                 cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
79                                                                 ev->idle_rate);
80 }
81
82 static void handle_get_report(void *buf, uint16_t len, int fd)
83 {
84         struct hal_ev_hidhost_get_report *ev = buf;
85
86         if (len != sizeof(*ev) + ev->len) {
87                 error("invalid get report event, aborting");
88                 exit(EXIT_FAILURE);
89         }
90
91         if (cbacks->get_report_cb)
92                 cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
93                                                         ev->data, ev->len);
94 }
95
96 static void handle_virtual_unplug(void *buf, uint16_t len, int fd)
97 {
98         struct hal_ev_hidhost_virtual_unplug *ev = buf;
99
100         if (cbacks->virtual_unplug_cb)
101                 cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr,
102                                                                 ev->status);
103 }
104
105 static void handle_handshake(void *buf, uint16_t len, int fd)
106 {
107 #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
108         struct hal_ev_hidhost_handshake *ev = buf;
109
110         if (cbacks->handshake_cb)
111                 cbacks->handshake_cb((bt_bdaddr_t *) ev->bdaddr, ev->status);
112 #endif
113 }
114
115 /*
116  * handlers will be called from notification thread context,
117  * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
118  */
119 static const struct hal_ipc_handler ev_handlers[] = {
120         /* HAL_EV_HIDHOST_CONN_STATE */
121         { handle_conn_state, false, sizeof(struct hal_ev_hidhost_conn_state) },
122         /* HAL_EV_HIDHOST_INFO */
123         { handle_info, false, sizeof(struct hal_ev_hidhost_info) },
124         /* HAL_EV_HIDHOST_PROTO_MODE */
125         { handle_proto_mode, false, sizeof(struct hal_ev_hidhost_proto_mode) },
126         /* HAL_EV_HIDHOST_IDLE_TIME */
127         { handle_idle_time, false, sizeof(struct hal_ev_hidhost_idle_time) },
128         /* HAL_EV_HIDHOST_GET_REPORT */
129         { handle_get_report, true, sizeof(struct hal_ev_hidhost_get_report) },
130         /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
131         { handle_virtual_unplug, false,
132                                 sizeof(struct hal_ev_hidhost_virtual_unplug) },
133         { handle_handshake, false, sizeof(struct hal_ev_hidhost_handshake) },
134 };
135
136 static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr)
137 {
138         struct hal_cmd_hidhost_connect cmd;
139
140         DBG("");
141
142         if (!interface_ready())
143                 return BT_STATUS_NOT_READY;
144
145         if (!bd_addr)
146                 return BT_STATUS_PARM_INVALID;
147
148         memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
149
150         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
151                                         sizeof(cmd), &cmd, NULL, NULL, NULL);
152 }
153
154 static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
155 {
156         struct hal_cmd_hidhost_disconnect cmd;
157
158         DBG("");
159
160         if (!interface_ready())
161                 return BT_STATUS_NOT_READY;
162
163         if (!bd_addr)
164                 return BT_STATUS_PARM_INVALID;
165
166         memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
167
168         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
169                                         sizeof(cmd), &cmd, NULL, NULL, NULL);
170 }
171
172 static bt_status_t virtual_unplug(bt_bdaddr_t *bd_addr)
173 {
174         struct hal_cmd_hidhost_virtual_unplug cmd;
175
176         DBG("");
177
178         if (!interface_ready())
179                 return BT_STATUS_NOT_READY;
180
181         if (!bd_addr)
182                 return BT_STATUS_PARM_INVALID;
183
184         memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
185
186         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
187                                         HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
188                                         sizeof(cmd), &cmd, NULL, NULL, NULL);
189 }
190
191 static bt_status_t set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
192 {
193         struct hal_cmd_hidhost_set_info cmd;
194
195         DBG("");
196
197         if (!interface_ready())
198                 return BT_STATUS_NOT_READY;
199
200         if (!bd_addr)
201                 return BT_STATUS_PARM_INVALID;
202
203         memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
204         cmd.attr = hid_info.attr_mask;
205         cmd.subclass = hid_info.sub_class;
206         cmd.app_id = hid_info.app_id;
207         cmd.vendor = hid_info.vendor_id;
208         cmd.product = hid_info.product_id;
209         cmd.country = hid_info.ctry_code;
210         cmd.descr_len = hid_info.dl_len;
211         memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len);
212
213         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
214                                         sizeof(cmd), &cmd, NULL, NULL, NULL);
215 }
216
217 static bt_status_t get_protocol(bt_bdaddr_t *bd_addr,
218                                         bthh_protocol_mode_t protocol_mode)
219 {
220         struct hal_cmd_hidhost_get_protocol cmd;
221
222         DBG("");
223
224         if (!interface_ready())
225                 return BT_STATUS_NOT_READY;
226
227         if (!bd_addr)
228                 return BT_STATUS_PARM_INVALID;
229
230         memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
231
232         /* type match IPC type */
233         cmd.mode = protocol_mode;
234
235         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
236                                 HAL_OP_HIDHOST_GET_PROTOCOL,
237                                 sizeof(cmd), &cmd, NULL, NULL, NULL);
238 }
239
240 static bt_status_t set_protocol(bt_bdaddr_t *bd_addr,
241                                         bthh_protocol_mode_t protocol_mode)
242 {
243         struct hal_cmd_hidhost_set_protocol cmd;
244
245         DBG("");
246
247         if (!interface_ready())
248                 return BT_STATUS_NOT_READY;
249
250         if (!bd_addr)
251                 return BT_STATUS_PARM_INVALID;
252
253         memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
254
255         /* type match IPC type */
256         cmd.mode = protocol_mode;
257
258         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
259                                 HAL_OP_HIDHOST_SET_PROTOCOL,
260                                 sizeof(cmd), &cmd, NULL, NULL, NULL);
261 }
262
263 static bt_status_t get_report(bt_bdaddr_t *bd_addr,
264                                                 bthh_report_type_t report_type,
265                                                 uint8_t report_id,
266                                                 int buffer_size)
267 {
268         struct hal_cmd_hidhost_get_report cmd;
269
270         DBG("");
271
272         if (!interface_ready())
273                 return BT_STATUS_NOT_READY;
274
275         if (!bd_addr)
276                 return BT_STATUS_PARM_INVALID;
277
278         memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
279         cmd.id = report_id;
280         cmd.buf_size = buffer_size;
281
282         /* type match IPC type */
283         cmd.type = report_type;
284
285         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
286                                         sizeof(cmd), &cmd, NULL, NULL, NULL);
287 }
288
289 static bt_status_t set_report(bt_bdaddr_t *bd_addr,
290                                                 bthh_report_type_t report_type,
291                                                 char *report)
292 {
293         uint8_t buf[IPC_MTU];
294         struct hal_cmd_hidhost_set_report *cmd = (void *) buf;
295
296         DBG("");
297
298         if (!interface_ready())
299                 return BT_STATUS_NOT_READY;
300
301         if (!bd_addr || !report)
302                 return BT_STATUS_PARM_INVALID;
303
304         memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
305         cmd->len = strlen(report);
306         memcpy(cmd->data, report, cmd->len);
307
308         /* type match IPC type */
309         cmd->type = report_type;
310
311         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
312                                 sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL);
313 }
314
315 static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data)
316 {
317         uint8_t buf[IPC_MTU];
318         struct hal_cmd_hidhost_send_data *cmd = (void *) buf;
319
320         DBG("");
321
322         if (!interface_ready())
323                 return BT_STATUS_NOT_READY;
324
325         if (!bd_addr || !data)
326                 return BT_STATUS_PARM_INVALID;
327
328         memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr));
329         cmd->len = strlen(data);
330         memcpy(cmd->data, data, cmd->len);
331
332         return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
333                         sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL);
334 }
335
336 static bt_status_t init(bthh_callbacks_t *callbacks)
337 {
338         struct hal_cmd_register_module cmd;
339         int ret;
340
341         DBG("");
342
343         if (interface_ready())
344                 return BT_STATUS_DONE;
345
346         /* store reference to user callbacks */
347         cbacks = callbacks;
348
349         hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers,
350                                 sizeof(ev_handlers)/sizeof(ev_handlers[0]));
351
352         cmd.service_id = HAL_SERVICE_ID_HIDHOST;
353         cmd.mode = HAL_MODE_DEFAULT;
354         cmd.max_clients = 1;
355
356         ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
357                                         sizeof(cmd), &cmd, NULL, NULL, NULL);
358
359         if (ret != BT_STATUS_SUCCESS) {
360                 cbacks = NULL;
361                 hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
362         }
363
364         return ret;
365 }
366
367 static void cleanup(void)
368 {
369         struct hal_cmd_unregister_module cmd;
370
371         DBG("");
372
373         if (!interface_ready())
374                 return;
375
376         cmd.service_id = HAL_SERVICE_ID_HIDHOST;
377
378         hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
379                                         sizeof(cmd), &cmd, NULL, NULL, NULL);
380
381         hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
382
383         cbacks = NULL;
384 }
385
386 static bthh_interface_t hidhost_if = {
387         .size = sizeof(hidhost_if),
388         .init = init,
389         .connect = hidhost_connect,
390         .disconnect = disconnect,
391         .virtual_unplug = virtual_unplug,
392         .set_info = set_info,
393         .get_protocol = get_protocol,
394         .set_protocol = set_protocol,
395         .get_report = get_report,
396         .set_report = set_report,
397         .send_data = send_data,
398         .cleanup = cleanup
399 };
400
401 bthh_interface_t *bt_get_hidhost_interface(void)
402 {
403         return &hidhost_if;
404 }