| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2019 SILVAIR sp. z o.o. All rights reserved. |
| * |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include "lib/bluetooth.h" |
| #include "lib/mgmt.h" |
| #include "src/shared/mgmt.h" |
| |
| #include "ell/queue.h" |
| #include "ell/log.h" |
| #include "ell/util.h" |
| |
| #include "mesh/mesh-mgmt.h" |
| |
| struct read_info_reg { |
| mesh_mgmt_read_info_func_t cb; |
| void *user_data; |
| }; |
| |
| struct read_info_req { |
| int index; |
| struct mesh_io *io; |
| }; |
| |
| static struct mgmt *mgmt_mesh; |
| static struct l_queue *read_info_regs; |
| |
| static void process_read_info_req(void *data, void *user_data) |
| { |
| struct read_info_reg *reg = data; |
| int index = L_PTR_TO_UINT(user_data); |
| |
| reg->cb(index, reg->user_data); |
| } |
| |
| static void read_info_cb(uint8_t status, uint16_t length, |
| const void *param, void *user_data) |
| { |
| int index = L_PTR_TO_UINT(user_data); |
| const struct mgmt_rp_read_info *rp = param; |
| uint32_t current_settings, supported_settings; |
| |
| l_debug("hci %u status 0x%02x", index, status); |
| |
| if (status != MGMT_STATUS_SUCCESS) { |
| l_error("Failed to read info for hci index %u: %s (0x%02x)", |
| index, mgmt_errstr(status), status); |
| return; |
| } |
| |
| if (length < sizeof(*rp)) { |
| l_error("Read info response too short"); |
| return; |
| } |
| |
| current_settings = btohl(rp->current_settings); |
| supported_settings = btohl(rp->supported_settings); |
| |
| l_debug("settings: supp %8.8x curr %8.8x", |
| supported_settings, current_settings); |
| |
| if (current_settings & MGMT_SETTING_POWERED) { |
| l_info("Controller hci %u is in use", index); |
| return; |
| } |
| |
| if (!(supported_settings & MGMT_SETTING_LE)) { |
| l_info("Controller hci %u does not support LE", index); |
| return; |
| } |
| |
| l_queue_foreach(read_info_regs, process_read_info_req, |
| L_UINT_TO_PTR(index)); |
| } |
| |
| static void index_added(uint16_t index, uint16_t length, const void *param, |
| void *user_data) |
| { |
| mgmt_send(mgmt_mesh, MGMT_OP_READ_INFO, index, 0, NULL, |
| read_info_cb, L_UINT_TO_PTR(index), NULL); |
| } |
| |
| static void index_removed(uint16_t index, uint16_t length, const void *param, |
| void *user_data) |
| { |
| l_warn("Hci dev %4.4x removed", index); |
| } |
| |
| static void read_index_list_cb(uint8_t status, uint16_t length, |
| const void *param, void *user_data) |
| { |
| const struct mgmt_rp_read_index_list *rp = param; |
| uint16_t num; |
| int i; |
| |
| if (status != MGMT_STATUS_SUCCESS) { |
| l_error("Failed to read index list: %s (0x%02x)", |
| mgmt_errstr(status), status); |
| return; |
| } |
| |
| if (length < sizeof(*rp)) { |
| l_error("Read index list response sixe too short"); |
| return; |
| } |
| |
| num = btohs(rp->num_controllers); |
| |
| l_debug("Number of controllers: %u", num); |
| |
| if (num * sizeof(uint16_t) + sizeof(*rp) != length) { |
| l_error("Incorrect packet size for index list response"); |
| return; |
| } |
| |
| for (i = 0; i < num; i++) { |
| uint16_t index; |
| |
| index = btohs(rp->index[i]); |
| index_added(index, 0, NULL, user_data); |
| } |
| } |
| |
| static bool mesh_mgmt_init(void) |
| { |
| if (!read_info_regs) |
| read_info_regs = l_queue_new(); |
| |
| if (!mgmt_mesh) { |
| mgmt_mesh = mgmt_new_default(); |
| |
| if (!mgmt_mesh) { |
| l_error("Failed to initialize mesh management"); |
| return false; |
| } |
| |
| mgmt_register(mgmt_mesh, MGMT_EV_INDEX_ADDED, |
| MGMT_INDEX_NONE, index_added, NULL, NULL); |
| mgmt_register(mgmt_mesh, MGMT_EV_INDEX_REMOVED, |
| MGMT_INDEX_NONE, index_removed, NULL, NULL); |
| } |
| |
| return true; |
| } |
| |
| bool mesh_mgmt_list(mesh_mgmt_read_info_func_t cb, void *user_data) |
| { |
| struct read_info_reg *reg; |
| |
| if (!mesh_mgmt_init()) |
| return false; |
| |
| reg = l_new(struct read_info_reg, 1); |
| reg->cb = cb; |
| reg->user_data = user_data; |
| |
| l_queue_push_tail(read_info_regs, reg); |
| |
| /* Use MGMT to find a candidate controller */ |
| l_debug("send read index_list"); |
| if (mgmt_send(mgmt_mesh, MGMT_OP_READ_INDEX_LIST, |
| MGMT_INDEX_NONE, 0, NULL, |
| read_index_list_cb, NULL, NULL) <= 0) |
| return false; |
| |
| return true; |
| } |