Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / src / shared / mgmt.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
6  *
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #include "lib/bluetooth.h"
34 #include "lib/mgmt.h"
35 #include "lib/hci.h"
36
37 #include "src/shared/io.h"
38 #include "src/shared/queue.h"
39 #include "src/shared/util.h"
40 #include "src/shared/mgmt.h"
41
42 struct mgmt {
43         int ref_count;
44         int fd;
45         bool close_on_unref;
46         struct io *io;
47         bool writer_active;
48         struct queue *request_queue;
49         struct queue *reply_queue;
50         struct queue *pending_list;
51         struct queue *notify_list;
52         unsigned int next_request_id;
53         unsigned int next_notify_id;
54         bool need_notify_cleanup;
55         bool in_notify;
56         void *buf;
57         uint16_t len;
58         mgmt_debug_func_t debug_callback;
59         mgmt_destroy_func_t debug_destroy;
60         void *debug_data;
61 };
62
63 struct mgmt_request {
64         unsigned int id;
65         uint16_t opcode;
66         uint16_t index;
67         void *buf;
68         uint16_t len;
69         mgmt_request_func_t callback;
70         mgmt_destroy_func_t destroy;
71         void *user_data;
72 };
73
74 struct mgmt_notify {
75         unsigned int id;
76         uint16_t event;
77         uint16_t index;
78         bool removed;
79         mgmt_notify_func_t callback;
80         mgmt_destroy_func_t destroy;
81         void *user_data;
82 };
83
84 static void destroy_request(void *data)
85 {
86         struct mgmt_request *request = data;
87
88         if (request->destroy)
89                 request->destroy(request->user_data);
90
91         free(request->buf);
92         free(request);
93 }
94
95 static bool match_request_id(const void *a, const void *b)
96 {
97         const struct mgmt_request *request = a;
98         unsigned int id = PTR_TO_UINT(b);
99
100         return request->id == id;
101 }
102
103 static bool match_request_index(const void *a, const void *b)
104 {
105         const struct mgmt_request *request = a;
106         uint16_t index = PTR_TO_UINT(b);
107
108         return request->index == index;
109 }
110
111 static void destroy_notify(void *data)
112 {
113         struct mgmt_notify *notify = data;
114
115         if (notify->destroy)
116                 notify->destroy(notify->user_data);
117
118         free(notify);
119 }
120
121 static bool match_notify_id(const void *a, const void *b)
122 {
123         const struct mgmt_notify *notify = a;
124         unsigned int id = PTR_TO_UINT(b);
125
126         return notify->id == id;
127 }
128
129 static bool match_notify_index(const void *a, const void *b)
130 {
131         const struct mgmt_notify *notify = a;
132         uint16_t index = PTR_TO_UINT(b);
133
134         return notify->index == index;
135 }
136
137 static bool match_notify_removed(const void *a, const void *b)
138 {
139         const struct mgmt_notify *notify = a;
140
141         return notify->removed;
142 }
143
144 static void mark_notify_removed(void *data , void *user_data)
145 {
146         struct mgmt_notify *notify = data;
147         uint16_t index = PTR_TO_UINT(user_data);
148
149         if (notify->index == index || index == MGMT_INDEX_NONE)
150                 notify->removed = true;
151 }
152
153 static void write_watch_destroy(void *user_data)
154 {
155         struct mgmt *mgmt = user_data;
156
157         mgmt->writer_active = false;
158 }
159
160 static bool send_request(struct mgmt *mgmt, struct mgmt_request *request)
161 {
162         struct iovec iov;
163         ssize_t ret;
164
165         iov.iov_base = request->buf;
166         iov.iov_len = request->len;
167
168         ret = io_send(mgmt->io, &iov, 1);
169         if (ret < 0) {
170                 util_debug(mgmt->debug_callback, mgmt->debug_data,
171                                 "write failed: %s", strerror(-ret));
172                 if (request->callback)
173                         request->callback(MGMT_STATUS_FAILED, 0, NULL,
174                                                         request->user_data);
175                 destroy_request(request);
176                 return false;
177         }
178
179         util_debug(mgmt->debug_callback, mgmt->debug_data,
180                                 "[0x%04x] command 0x%04x",
181                                 request->index, request->opcode);
182
183         util_hexdump('<', request->buf, ret, mgmt->debug_callback,
184                                                         mgmt->debug_data);
185
186         queue_push_tail(mgmt->pending_list, request);
187
188         return true;
189 }
190
191 static bool can_write_data(struct io *io, void *user_data)
192 {
193         struct mgmt *mgmt = user_data;
194         struct mgmt_request *request;
195         bool can_write;
196
197         request = queue_pop_head(mgmt->reply_queue);
198         if (!request) {
199                 /* only reply commands can jump the queue */
200                 if (!queue_isempty(mgmt->pending_list))
201                         return false;
202
203                 request = queue_pop_head(mgmt->request_queue);
204                 if (!request)
205                         return false;
206
207                 can_write = false;
208         } else {
209                 /* allow multiple replies to jump the queue */
210                 can_write = !queue_isempty(mgmt->reply_queue);
211         }
212
213         if (!send_request(mgmt, request))
214                 return true;
215
216         return can_write;
217 }
218
219 static void wakeup_writer(struct mgmt *mgmt)
220 {
221         if (!queue_isempty(mgmt->pending_list)) {
222                 /* only queued reply commands trigger wakeup */
223                 if (queue_isempty(mgmt->reply_queue))
224                         return;
225         }
226
227         if (mgmt->writer_active)
228                 return;
229
230         mgmt->writer_active = true;
231
232         io_set_write_handler(mgmt->io, can_write_data, mgmt,
233                                                 write_watch_destroy);
234 }
235
236 struct opcode_index {
237         uint16_t opcode;
238         uint16_t index;
239 };
240
241 static bool match_request_opcode_index(const void *a, const void *b)
242 {
243         const struct mgmt_request *request = a;
244         const struct opcode_index *match = b;
245
246         return request->opcode == match->opcode &&
247                                         request->index == match->index;
248 }
249
250 static void request_complete(struct mgmt *mgmt, uint8_t status,
251                                         uint16_t opcode, uint16_t index,
252                                         uint16_t length, const void *param)
253 {
254         struct opcode_index match = { .opcode = opcode, .index = index };
255         struct mgmt_request *request;
256
257         request = queue_remove_if(mgmt->pending_list,
258                                         match_request_opcode_index, &match);
259         if (request) {
260                 if (request->callback)
261                         request->callback(status, length, param,
262                                                         request->user_data);
263
264                 destroy_request(request);
265         }
266
267         wakeup_writer(mgmt);
268 }
269
270 struct event_index {
271         uint16_t event;
272         uint16_t index;
273         uint16_t length;
274         const void *param;
275 };
276
277 static void notify_handler(void *data, void *user_data)
278 {
279         struct mgmt_notify *notify = data;
280         struct event_index *match = user_data;
281
282         if (notify->removed)
283                 return;
284
285         if (notify->event != match->event)
286                 return;
287
288         if (notify->index != match->index && notify->index != MGMT_INDEX_NONE)
289                 return;
290
291         if (notify->callback)
292                 notify->callback(match->index, match->length, match->param,
293                                                         notify->user_data);
294 }
295
296 static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index,
297                                         uint16_t length, const void *param)
298 {
299         struct event_index match = { .event = event, .index = index,
300                                         .length = length, .param = param };
301
302         mgmt->in_notify = true;
303
304         queue_foreach(mgmt->notify_list, notify_handler, &match);
305
306         mgmt->in_notify = false;
307
308         if (mgmt->need_notify_cleanup) {
309                 queue_remove_all(mgmt->notify_list, match_notify_removed,
310                                                         NULL, destroy_notify);
311                 mgmt->need_notify_cleanup = false;
312         }
313 }
314
315 static bool can_read_data(struct io *io, void *user_data)
316 {
317         struct mgmt *mgmt = user_data;
318         struct mgmt_hdr *hdr;
319         struct mgmt_ev_cmd_complete *cc;
320         struct mgmt_ev_cmd_status *cs;
321         ssize_t bytes_read;
322         uint16_t opcode, event, index, length;
323
324         bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len);
325         if (bytes_read < 0)
326                 return false;
327
328         util_hexdump('>', mgmt->buf, bytes_read,
329                                 mgmt->debug_callback, mgmt->debug_data);
330
331         if (bytes_read < MGMT_HDR_SIZE)
332                 return true;
333
334         hdr = mgmt->buf;
335         event = btohs(hdr->opcode);
336         index = btohs(hdr->index);
337         length = btohs(hdr->len);
338
339         if (bytes_read < length + MGMT_HDR_SIZE)
340                 return true;
341
342         mgmt_ref(mgmt);
343
344         switch (event) {
345         case MGMT_EV_CMD_COMPLETE:
346                 cc = mgmt->buf + MGMT_HDR_SIZE;
347                 opcode = btohs(cc->opcode);
348
349                 util_debug(mgmt->debug_callback, mgmt->debug_data,
350                                 "[0x%04x] command 0x%04x complete: 0x%02x",
351                                                 index, opcode, cc->status);
352
353                 request_complete(mgmt, cc->status, opcode, index, length - 3,
354                                                 mgmt->buf + MGMT_HDR_SIZE + 3);
355                 break;
356         case MGMT_EV_CMD_STATUS:
357                 cs = mgmt->buf + MGMT_HDR_SIZE;
358                 opcode = btohs(cs->opcode);
359
360                 util_debug(mgmt->debug_callback, mgmt->debug_data,
361                                 "[0x%04x] command 0x%02x status: 0x%02x",
362                                                 index, opcode, cs->status);
363
364                 request_complete(mgmt, cs->status, opcode, index, 0, NULL);
365                 break;
366         default:
367                 util_debug(mgmt->debug_callback, mgmt->debug_data,
368                                 "[0x%04x] event 0x%04x", index, event);
369
370                 process_notify(mgmt, event, index, length,
371                                                 mgmt->buf + MGMT_HDR_SIZE);
372                 break;
373         }
374
375         mgmt_unref(mgmt);
376
377         return true;
378 }
379
380 struct mgmt *mgmt_new(int fd)
381 {
382         struct mgmt *mgmt;
383
384         if (fd < 0)
385                 return NULL;
386
387         mgmt = new0(struct mgmt, 1);
388         mgmt->fd = fd;
389         mgmt->close_on_unref = false;
390
391         mgmt->len = 512;
392         mgmt->buf = malloc(mgmt->len);
393         if (!mgmt->buf) {
394                 free(mgmt);
395                 return NULL;
396         }
397
398         mgmt->io = io_new(fd);
399         if (!mgmt->io) {
400                 free(mgmt->buf);
401                 free(mgmt);
402                 return NULL;
403         }
404
405         mgmt->request_queue = queue_new();
406         mgmt->reply_queue = queue_new();
407         mgmt->pending_list = queue_new();
408         mgmt->notify_list = queue_new();
409
410         if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, NULL)) {
411                 queue_destroy(mgmt->notify_list, NULL);
412                 queue_destroy(mgmt->pending_list, NULL);
413                 queue_destroy(mgmt->reply_queue, NULL);
414                 queue_destroy(mgmt->request_queue, NULL);
415                 io_destroy(mgmt->io);
416                 free(mgmt->buf);
417                 free(mgmt);
418                 return NULL;
419         }
420
421         mgmt->writer_active = false;
422
423         return mgmt_ref(mgmt);
424 }
425
426 struct mgmt *mgmt_new_default(void)
427 {
428         struct mgmt *mgmt;
429         union {
430                 struct sockaddr common;
431                 struct sockaddr_hci hci;
432         } addr;
433         int fd;
434
435         fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
436                                                                 BTPROTO_HCI);
437         if (fd < 0)
438                 return NULL;
439
440         memset(&addr, 0, sizeof(addr));
441         addr.hci.hci_family = AF_BLUETOOTH;
442         addr.hci.hci_dev = HCI_DEV_NONE;
443         addr.hci.hci_channel = HCI_CHANNEL_CONTROL;
444
445         if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) {
446                 close(fd);
447                 return NULL;
448         }
449
450         mgmt = mgmt_new(fd);
451         if (!mgmt) {
452                 close(fd);
453                 return NULL;
454         }
455
456         mgmt->close_on_unref = true;
457
458         return mgmt;
459 }
460
461 struct mgmt *mgmt_ref(struct mgmt *mgmt)
462 {
463         if (!mgmt)
464                 return NULL;
465
466         __sync_fetch_and_add(&mgmt->ref_count, 1);
467
468         return mgmt;
469 }
470
471 void mgmt_unref(struct mgmt *mgmt)
472 {
473         if (!mgmt)
474                 return;
475
476         if (__sync_sub_and_fetch(&mgmt->ref_count, 1))
477                 return;
478
479         mgmt_unregister_all(mgmt);
480         mgmt_cancel_all(mgmt);
481
482         queue_destroy(mgmt->reply_queue, NULL);
483         queue_destroy(mgmt->request_queue, NULL);
484
485         io_set_write_handler(mgmt->io, NULL, NULL, NULL);
486         io_set_read_handler(mgmt->io, NULL, NULL, NULL);
487
488         io_destroy(mgmt->io);
489         mgmt->io = NULL;
490
491         if (mgmt->close_on_unref)
492                 close(mgmt->fd);
493
494         if (mgmt->debug_destroy)
495                 mgmt->debug_destroy(mgmt->debug_data);
496
497         free(mgmt->buf);
498         mgmt->buf = NULL;
499
500         if (!mgmt->in_notify) {
501                 queue_destroy(mgmt->notify_list, NULL);
502                 queue_destroy(mgmt->pending_list, NULL);
503                 free(mgmt);
504                 return;
505         }
506 }
507
508 bool mgmt_set_debug(struct mgmt *mgmt, mgmt_debug_func_t callback,
509                                 void *user_data, mgmt_destroy_func_t destroy)
510 {
511         if (!mgmt)
512                 return false;
513
514         if (mgmt->debug_destroy)
515                 mgmt->debug_destroy(mgmt->debug_data);
516
517         mgmt->debug_callback = callback;
518         mgmt->debug_destroy = destroy;
519         mgmt->debug_data = user_data;
520
521         return true;
522 }
523
524 bool mgmt_set_close_on_unref(struct mgmt *mgmt, bool do_close)
525 {
526         if (!mgmt)
527                 return false;
528
529         mgmt->close_on_unref = do_close;
530
531         return true;
532 }
533
534 static struct mgmt_request *create_request(uint16_t opcode, uint16_t index,
535                                 uint16_t length, const void *param,
536                                 mgmt_request_func_t callback,
537                                 void *user_data, mgmt_destroy_func_t destroy)
538 {
539         struct mgmt_request *request;
540         struct mgmt_hdr *hdr;
541
542         if (!opcode)
543                 return NULL;
544
545         if (length > 0 && !param)
546                 return NULL;
547
548         request = new0(struct mgmt_request, 1);
549         request->len = length + MGMT_HDR_SIZE;
550         request->buf = malloc(request->len);
551         if (!request->buf) {
552                 free(request);
553                 return NULL;
554         }
555
556         if (length > 0)
557                 memcpy(request->buf + MGMT_HDR_SIZE, param, length);
558
559         hdr = request->buf;
560         hdr->opcode = htobs(opcode);
561         hdr->index = htobs(index);
562         hdr->len = htobs(length);
563
564         request->opcode = opcode;
565         request->index = index;
566
567         request->callback = callback;
568         request->destroy = destroy;
569         request->user_data = user_data;
570
571         return request;
572 }
573
574 unsigned int mgmt_send(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
575                                 uint16_t length, const void *param,
576                                 mgmt_request_func_t callback,
577                                 void *user_data, mgmt_destroy_func_t destroy)
578 {
579         struct mgmt_request *request;
580
581         if (!mgmt)
582                 return 0;
583
584         request = create_request(opcode, index, length, param,
585                                         callback, user_data, destroy);
586         if (!request)
587                 return 0;
588
589         if (mgmt->next_request_id < 1)
590                 mgmt->next_request_id = 1;
591
592         request->id = mgmt->next_request_id++;
593
594         if (!queue_push_tail(mgmt->request_queue, request)) {
595                 free(request->buf);
596                 free(request);
597                 return 0;
598         }
599
600         wakeup_writer(mgmt);
601
602         return request->id;
603 }
604
605 unsigned int mgmt_send_nowait(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
606                                 uint16_t length, const void *param,
607                                 mgmt_request_func_t callback,
608                                 void *user_data, mgmt_destroy_func_t destroy)
609 {
610         struct mgmt_request *request;
611
612         if (!mgmt)
613                 return 0;
614
615         request = create_request(opcode, index, length, param,
616                                         callback, user_data, destroy);
617         if (!request)
618                 return 0;
619
620         if (mgmt->next_request_id < 1)
621                 mgmt->next_request_id = 1;
622
623         request->id = mgmt->next_request_id++;
624
625         if (!send_request(mgmt, request))
626                 return 0;
627
628         return request->id;
629 }
630
631 unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
632                                 uint16_t length, const void *param,
633                                 mgmt_request_func_t callback,
634                                 void *user_data, mgmt_destroy_func_t destroy)
635 {
636         struct mgmt_request *request;
637
638         if (!mgmt)
639                 return 0;
640
641         request = create_request(opcode, index, length, param,
642                                         callback, user_data, destroy);
643         if (!request)
644                 return 0;
645
646         if (mgmt->next_request_id < 1)
647                 mgmt->next_request_id = 1;
648
649         request->id = mgmt->next_request_id++;
650
651         if (!queue_push_tail(mgmt->reply_queue, request)) {
652                 free(request->buf);
653                 free(request);
654                 return 0;
655         }
656
657         wakeup_writer(mgmt);
658
659         return request->id;
660 }
661
662 bool mgmt_cancel(struct mgmt *mgmt, unsigned int id)
663 {
664         struct mgmt_request *request;
665
666         if (!mgmt || !id)
667                 return false;
668
669         request = queue_remove_if(mgmt->request_queue, match_request_id,
670                                                         UINT_TO_PTR(id));
671         if (request)
672                 goto done;
673
674         request = queue_remove_if(mgmt->reply_queue, match_request_id,
675                                                         UINT_TO_PTR(id));
676         if (request)
677                 goto done;
678
679         request = queue_remove_if(mgmt->pending_list, match_request_id,
680                                                         UINT_TO_PTR(id));
681         if (!request)
682                 return false;
683
684 done:
685         destroy_request(request);
686
687         wakeup_writer(mgmt);
688
689         return true;
690 }
691
692 bool mgmt_cancel_index(struct mgmt *mgmt, uint16_t index)
693 {
694         if (!mgmt)
695                 return false;
696
697         queue_remove_all(mgmt->request_queue, match_request_index,
698                                         UINT_TO_PTR(index), destroy_request);
699         queue_remove_all(mgmt->reply_queue, match_request_index,
700                                         UINT_TO_PTR(index), destroy_request);
701         queue_remove_all(mgmt->pending_list, match_request_index,
702                                         UINT_TO_PTR(index), destroy_request);
703
704         return true;
705 }
706
707 bool mgmt_cancel_all(struct mgmt *mgmt)
708 {
709         if (!mgmt)
710                 return false;
711
712         queue_remove_all(mgmt->pending_list, NULL, NULL, destroy_request);
713         queue_remove_all(mgmt->reply_queue, NULL, NULL, destroy_request);
714         queue_remove_all(mgmt->request_queue, NULL, NULL, destroy_request);
715
716         return true;
717 }
718
719 unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index,
720                                 mgmt_notify_func_t callback,
721                                 void *user_data, mgmt_destroy_func_t destroy)
722 {
723         struct mgmt_notify *notify;
724
725         if (!mgmt || !event)
726                 return 0;
727
728         notify = new0(struct mgmt_notify, 1);
729         notify->event = event;
730         notify->index = index;
731
732         notify->callback = callback;
733         notify->destroy = destroy;
734         notify->user_data = user_data;
735
736         if (mgmt->next_notify_id < 1)
737                 mgmt->next_notify_id = 1;
738
739         notify->id = mgmt->next_notify_id++;
740
741         if (!queue_push_tail(mgmt->notify_list, notify)) {
742                 free(notify);
743                 return 0;
744         }
745
746         return notify->id;
747 }
748
749 bool mgmt_unregister(struct mgmt *mgmt, unsigned int id)
750 {
751         struct mgmt_notify *notify;
752
753         if (!mgmt || !id)
754                 return false;
755
756         notify = queue_remove_if(mgmt->notify_list, match_notify_id,
757                                                         UINT_TO_PTR(id));
758         if (!notify)
759                 return false;
760
761         if (!mgmt->in_notify) {
762                 destroy_notify(notify);
763                 return true;
764         }
765
766         notify->removed = true;
767         mgmt->need_notify_cleanup = true;
768
769         return true;
770 }
771
772 bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index)
773 {
774         if (!mgmt)
775                 return false;
776
777         if (mgmt->in_notify) {
778                 queue_foreach(mgmt->notify_list, mark_notify_removed,
779                                                         UINT_TO_PTR(index));
780                 mgmt->need_notify_cleanup = true;
781         } else
782                 queue_remove_all(mgmt->notify_list, match_notify_index,
783                                         UINT_TO_PTR(index), destroy_notify);
784
785         return true;
786 }
787
788 bool mgmt_unregister_all(struct mgmt *mgmt)
789 {
790         if (!mgmt)
791                 return false;
792
793         if (mgmt->in_notify) {
794                 queue_foreach(mgmt->notify_list, mark_notify_removed,
795                                                 UINT_TO_PTR(MGMT_INDEX_NONE));
796                 mgmt->need_notify_cleanup = true;
797         } else
798                 queue_remove_all(mgmt->notify_list, NULL, NULL, destroy_notify);
799
800         return true;
801 }