3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
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.
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.
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
33 #include "lib/bluetooth.h"
37 #include "src/shared/io.h"
38 #include "src/shared/queue.h"
39 #include "src/shared/util.h"
40 #include "src/shared/mgmt.h"
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;
58 mgmt_debug_func_t debug_callback;
59 mgmt_destroy_func_t debug_destroy;
69 mgmt_request_func_t callback;
70 mgmt_destroy_func_t destroy;
79 mgmt_notify_func_t callback;
80 mgmt_destroy_func_t destroy;
84 static void destroy_request(void *data)
86 struct mgmt_request *request = data;
89 request->destroy(request->user_data);
95 static bool match_request_id(const void *a, const void *b)
97 const struct mgmt_request *request = a;
98 unsigned int id = PTR_TO_UINT(b);
100 return request->id == id;
103 static bool match_request_index(const void *a, const void *b)
105 const struct mgmt_request *request = a;
106 uint16_t index = PTR_TO_UINT(b);
108 return request->index == index;
111 static void destroy_notify(void *data)
113 struct mgmt_notify *notify = data;
116 notify->destroy(notify->user_data);
121 static bool match_notify_id(const void *a, const void *b)
123 const struct mgmt_notify *notify = a;
124 unsigned int id = PTR_TO_UINT(b);
126 return notify->id == id;
129 static bool match_notify_index(const void *a, const void *b)
131 const struct mgmt_notify *notify = a;
132 uint16_t index = PTR_TO_UINT(b);
134 return notify->index == index;
137 static bool match_notify_removed(const void *a, const void *b)
139 const struct mgmt_notify *notify = a;
141 return notify->removed;
144 static void mark_notify_removed(void *data , void *user_data)
146 struct mgmt_notify *notify = data;
147 uint16_t index = PTR_TO_UINT(user_data);
149 if (notify->index == index || index == MGMT_INDEX_NONE)
150 notify->removed = true;
153 static void write_watch_destroy(void *user_data)
155 struct mgmt *mgmt = user_data;
157 mgmt->writer_active = false;
160 static bool send_request(struct mgmt *mgmt, struct mgmt_request *request)
165 iov.iov_base = request->buf;
166 iov.iov_len = request->len;
168 ret = io_send(mgmt->io, &iov, 1);
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,
175 destroy_request(request);
179 util_debug(mgmt->debug_callback, mgmt->debug_data,
180 "[0x%04x] command 0x%04x",
181 request->index, request->opcode);
183 util_hexdump('<', request->buf, ret, mgmt->debug_callback,
186 queue_push_tail(mgmt->pending_list, request);
191 static bool can_write_data(struct io *io, void *user_data)
193 struct mgmt *mgmt = user_data;
194 struct mgmt_request *request;
197 request = queue_pop_head(mgmt->reply_queue);
199 /* only reply commands can jump the queue */
200 if (!queue_isempty(mgmt->pending_list))
203 request = queue_pop_head(mgmt->request_queue);
209 /* allow multiple replies to jump the queue */
210 can_write = !queue_isempty(mgmt->reply_queue);
213 if (!send_request(mgmt, request))
219 static void wakeup_writer(struct mgmt *mgmt)
221 if (!queue_isempty(mgmt->pending_list)) {
222 /* only queued reply commands trigger wakeup */
223 if (queue_isempty(mgmt->reply_queue))
227 if (mgmt->writer_active)
230 mgmt->writer_active = true;
232 io_set_write_handler(mgmt->io, can_write_data, mgmt,
233 write_watch_destroy);
236 struct opcode_index {
241 static bool match_request_opcode_index(const void *a, const void *b)
243 const struct mgmt_request *request = a;
244 const struct opcode_index *match = b;
246 return request->opcode == match->opcode &&
247 request->index == match->index;
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)
254 struct opcode_index match = { .opcode = opcode, .index = index };
255 struct mgmt_request *request;
257 request = queue_remove_if(mgmt->pending_list,
258 match_request_opcode_index, &match);
260 if (request->callback)
261 request->callback(status, length, param,
264 destroy_request(request);
277 static void notify_handler(void *data, void *user_data)
279 struct mgmt_notify *notify = data;
280 struct event_index *match = user_data;
285 if (notify->event != match->event)
288 if (notify->index != match->index && notify->index != MGMT_INDEX_NONE)
291 if (notify->callback)
292 notify->callback(match->index, match->length, match->param,
296 static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index,
297 uint16_t length, const void *param)
299 struct event_index match = { .event = event, .index = index,
300 .length = length, .param = param };
302 mgmt->in_notify = true;
304 queue_foreach(mgmt->notify_list, notify_handler, &match);
306 mgmt->in_notify = false;
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;
315 static bool can_read_data(struct io *io, void *user_data)
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;
322 uint16_t opcode, event, index, length;
324 bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len);
328 util_hexdump('>', mgmt->buf, bytes_read,
329 mgmt->debug_callback, mgmt->debug_data);
331 if (bytes_read < MGMT_HDR_SIZE)
335 event = btohs(hdr->opcode);
336 index = btohs(hdr->index);
337 length = btohs(hdr->len);
339 if (bytes_read < length + MGMT_HDR_SIZE)
345 case MGMT_EV_CMD_COMPLETE:
346 cc = mgmt->buf + MGMT_HDR_SIZE;
347 opcode = btohs(cc->opcode);
349 util_debug(mgmt->debug_callback, mgmt->debug_data,
350 "[0x%04x] command 0x%04x complete: 0x%02x",
351 index, opcode, cc->status);
353 request_complete(mgmt, cc->status, opcode, index, length - 3,
354 mgmt->buf + MGMT_HDR_SIZE + 3);
356 case MGMT_EV_CMD_STATUS:
357 cs = mgmt->buf + MGMT_HDR_SIZE;
358 opcode = btohs(cs->opcode);
360 util_debug(mgmt->debug_callback, mgmt->debug_data,
361 "[0x%04x] command 0x%02x status: 0x%02x",
362 index, opcode, cs->status);
364 request_complete(mgmt, cs->status, opcode, index, 0, NULL);
367 util_debug(mgmt->debug_callback, mgmt->debug_data,
368 "[0x%04x] event 0x%04x", index, event);
370 process_notify(mgmt, event, index, length,
371 mgmt->buf + MGMT_HDR_SIZE);
380 struct mgmt *mgmt_new(int fd)
387 mgmt = new0(struct mgmt, 1);
389 mgmt->close_on_unref = false;
392 mgmt->buf = malloc(mgmt->len);
398 mgmt->io = io_new(fd);
405 mgmt->request_queue = queue_new();
406 mgmt->reply_queue = queue_new();
407 mgmt->pending_list = queue_new();
408 mgmt->notify_list = queue_new();
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);
421 mgmt->writer_active = false;
423 return mgmt_ref(mgmt);
426 struct mgmt *mgmt_new_default(void)
430 struct sockaddr common;
431 struct sockaddr_hci hci;
435 fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
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;
445 if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) {
456 mgmt->close_on_unref = true;
461 struct mgmt *mgmt_ref(struct mgmt *mgmt)
466 __sync_fetch_and_add(&mgmt->ref_count, 1);
471 void mgmt_unref(struct mgmt *mgmt)
476 if (__sync_sub_and_fetch(&mgmt->ref_count, 1))
479 mgmt_unregister_all(mgmt);
480 mgmt_cancel_all(mgmt);
482 queue_destroy(mgmt->reply_queue, NULL);
483 queue_destroy(mgmt->request_queue, NULL);
485 io_set_write_handler(mgmt->io, NULL, NULL, NULL);
486 io_set_read_handler(mgmt->io, NULL, NULL, NULL);
488 io_destroy(mgmt->io);
491 if (mgmt->close_on_unref)
494 if (mgmt->debug_destroy)
495 mgmt->debug_destroy(mgmt->debug_data);
500 if (!mgmt->in_notify) {
501 queue_destroy(mgmt->notify_list, NULL);
502 queue_destroy(mgmt->pending_list, NULL);
508 bool mgmt_set_debug(struct mgmt *mgmt, mgmt_debug_func_t callback,
509 void *user_data, mgmt_destroy_func_t destroy)
514 if (mgmt->debug_destroy)
515 mgmt->debug_destroy(mgmt->debug_data);
517 mgmt->debug_callback = callback;
518 mgmt->debug_destroy = destroy;
519 mgmt->debug_data = user_data;
524 bool mgmt_set_close_on_unref(struct mgmt *mgmt, bool do_close)
529 mgmt->close_on_unref = do_close;
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)
539 struct mgmt_request *request;
540 struct mgmt_hdr *hdr;
545 if (length > 0 && !param)
548 request = new0(struct mgmt_request, 1);
549 request->len = length + MGMT_HDR_SIZE;
550 request->buf = malloc(request->len);
557 memcpy(request->buf + MGMT_HDR_SIZE, param, length);
560 hdr->opcode = htobs(opcode);
561 hdr->index = htobs(index);
562 hdr->len = htobs(length);
564 request->opcode = opcode;
565 request->index = index;
567 request->callback = callback;
568 request->destroy = destroy;
569 request->user_data = user_data;
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)
579 struct mgmt_request *request;
584 request = create_request(opcode, index, length, param,
585 callback, user_data, destroy);
589 if (mgmt->next_request_id < 1)
590 mgmt->next_request_id = 1;
592 request->id = mgmt->next_request_id++;
594 if (!queue_push_tail(mgmt->request_queue, request)) {
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)
610 struct mgmt_request *request;
615 request = create_request(opcode, index, length, param,
616 callback, user_data, destroy);
620 if (mgmt->next_request_id < 1)
621 mgmt->next_request_id = 1;
623 request->id = mgmt->next_request_id++;
625 if (!send_request(mgmt, request))
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)
636 struct mgmt_request *request;
641 request = create_request(opcode, index, length, param,
642 callback, user_data, destroy);
646 if (mgmt->next_request_id < 1)
647 mgmt->next_request_id = 1;
649 request->id = mgmt->next_request_id++;
651 if (!queue_push_tail(mgmt->reply_queue, request)) {
662 bool mgmt_cancel(struct mgmt *mgmt, unsigned int id)
664 struct mgmt_request *request;
669 request = queue_remove_if(mgmt->request_queue, match_request_id,
674 request = queue_remove_if(mgmt->reply_queue, match_request_id,
679 request = queue_remove_if(mgmt->pending_list, match_request_id,
685 destroy_request(request);
692 bool mgmt_cancel_index(struct mgmt *mgmt, uint16_t index)
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);
707 bool mgmt_cancel_all(struct mgmt *mgmt)
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);
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)
723 struct mgmt_notify *notify;
728 notify = new0(struct mgmt_notify, 1);
729 notify->event = event;
730 notify->index = index;
732 notify->callback = callback;
733 notify->destroy = destroy;
734 notify->user_data = user_data;
736 if (mgmt->next_notify_id < 1)
737 mgmt->next_notify_id = 1;
739 notify->id = mgmt->next_notify_id++;
741 if (!queue_push_tail(mgmt->notify_list, notify)) {
749 bool mgmt_unregister(struct mgmt *mgmt, unsigned int id)
751 struct mgmt_notify *notify;
756 notify = queue_remove_if(mgmt->notify_list, match_notify_id,
761 if (!mgmt->in_notify) {
762 destroy_notify(notify);
766 notify->removed = true;
767 mgmt->need_notify_cleanup = true;
772 bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index)
777 if (mgmt->in_notify) {
778 queue_foreach(mgmt->notify_list, mark_notify_removed,
780 mgmt->need_notify_cleanup = true;
782 queue_remove_all(mgmt->notify_list, match_notify_index,
783 UINT_TO_PTR(index), destroy_notify);
788 bool mgmt_unregister_all(struct mgmt *mgmt)
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;
798 queue_remove_all(mgmt->notify_list, NULL, NULL, destroy_notify);