mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-29 07:04:19 +08:00
bb957be6a4
LE uses shorter EIR (31 bytes) than HCI (240 bytes)
2150 lines
48 KiB
C
2150 lines
48 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2010 Nokia Corporation
|
|
* Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/sdp.h>
|
|
#include <bluetooth/sdp_lib.h>
|
|
#include <bluetooth/mgmt.h>
|
|
|
|
#include "plugin.h"
|
|
#include "log.h"
|
|
#include "adapter.h"
|
|
#include "manager.h"
|
|
#include "device.h"
|
|
#include "event.h"
|
|
#include "oob.h"
|
|
|
|
#define MGMT_BUF_SIZE 1024
|
|
|
|
static int max_index = -1;
|
|
static struct controller_info {
|
|
gboolean valid;
|
|
gboolean notified;
|
|
uint8_t type;
|
|
bdaddr_t bdaddr;
|
|
uint8_t features[8];
|
|
uint8_t dev_class[3];
|
|
uint16_t manufacturer;
|
|
uint8_t hci_ver;
|
|
uint16_t hci_rev;
|
|
gboolean enabled;
|
|
gboolean connectable;
|
|
gboolean discoverable;
|
|
gboolean pairable;
|
|
uint8_t sec_mode;
|
|
GSList *connections;
|
|
} *controllers = NULL;
|
|
|
|
static int mgmt_sock = -1;
|
|
static guint mgmt_watch = 0;
|
|
|
|
static uint8_t mgmt_version = 0;
|
|
static uint16_t mgmt_revision = 0;
|
|
|
|
static void read_version_complete(int sk, void *buf, size_t len)
|
|
{
|
|
struct mgmt_hdr hdr;
|
|
struct mgmt_rp_read_version *rp = buf;
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small read version complete event");
|
|
return;
|
|
}
|
|
|
|
mgmt_revision = btohs(bt_get_unaligned(&rp->revision));
|
|
mgmt_version = rp->version;
|
|
|
|
DBG("version %u revision %u", mgmt_version, mgmt_revision);
|
|
|
|
memset(&hdr, 0, sizeof(hdr));
|
|
hdr.opcode = htobs(MGMT_OP_READ_INDEX_LIST);
|
|
hdr.index = htobs(MGMT_INDEX_NONE);
|
|
if (write(sk, &hdr, sizeof(hdr)) < 0)
|
|
error("Unable to read controller index list: %s (%d)",
|
|
strerror(errno), errno);
|
|
}
|
|
|
|
static void add_controller(uint16_t index)
|
|
{
|
|
if (index > max_index) {
|
|
size_t size = sizeof(struct controller_info) * (index + 1);
|
|
max_index = index;
|
|
controllers = g_realloc(controllers, size);
|
|
}
|
|
|
|
memset(&controllers[index], 0, sizeof(struct controller_info));
|
|
|
|
controllers[index].valid = TRUE;
|
|
|
|
DBG("Added controller %u", index);
|
|
}
|
|
|
|
static void read_info(int sk, uint16_t index)
|
|
{
|
|
struct mgmt_hdr hdr;
|
|
|
|
memset(&hdr, 0, sizeof(hdr));
|
|
hdr.opcode = htobs(MGMT_OP_READ_INFO);
|
|
hdr.index = htobs(index);
|
|
|
|
if (write(sk, &hdr, sizeof(hdr)) < 0)
|
|
error("Unable to send read_info command: %s (%d)",
|
|
strerror(errno), errno);
|
|
}
|
|
|
|
static void get_connections(int sk, uint16_t index)
|
|
{
|
|
struct mgmt_hdr hdr;
|
|
|
|
memset(&hdr, 0, sizeof(hdr));
|
|
hdr.opcode = htobs(MGMT_OP_GET_CONNECTIONS);
|
|
hdr.index = htobs(index);
|
|
|
|
if (write(sk, &hdr, sizeof(hdr)) < 0)
|
|
error("Unable to send get_connections command: %s (%d)",
|
|
strerror(errno), errno);
|
|
}
|
|
|
|
static void mgmt_index_added(int sk, uint16_t index)
|
|
{
|
|
add_controller(index);
|
|
read_info(sk, index);
|
|
}
|
|
|
|
static void remove_controller(uint16_t index)
|
|
{
|
|
if (index > max_index)
|
|
return;
|
|
|
|
if (!controllers[index].valid)
|
|
return;
|
|
|
|
btd_manager_unregister_adapter(index);
|
|
|
|
memset(&controllers[index], 0, sizeof(struct controller_info));
|
|
|
|
DBG("Removed controller %u", index);
|
|
}
|
|
|
|
static void mgmt_index_removed(int sk, uint16_t index)
|
|
{
|
|
remove_controller(index);
|
|
}
|
|
|
|
static int mgmt_set_mode(int index, uint16_t opcode, uint8_t val)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_mode)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_mode *cp = (void *) &buf[sizeof(*hdr)];
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(opcode);
|
|
hdr->index = htobs(index);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
|
|
cp->val = val;
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_set_connectable(int index, gboolean connectable)
|
|
{
|
|
DBG("index %d connectable %d", index, connectable);
|
|
return mgmt_set_mode(index, MGMT_OP_SET_CONNECTABLE, connectable);
|
|
}
|
|
|
|
static int mgmt_set_discoverable(int index, gboolean discoverable)
|
|
{
|
|
DBG("index %d discoverable %d", index, discoverable);
|
|
return mgmt_set_mode(index, MGMT_OP_SET_DISCOVERABLE, discoverable);
|
|
}
|
|
|
|
static int mgmt_set_pairable(int index, gboolean pairable)
|
|
{
|
|
DBG("index %d pairable %d", index, pairable);
|
|
return mgmt_set_mode(index, MGMT_OP_SET_PAIRABLE, pairable);
|
|
}
|
|
|
|
static int mgmt_update_powered(int index, uint8_t powered)
|
|
{
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
gboolean pairable;
|
|
uint8_t on_mode;
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u", index);
|
|
return -ENODEV;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
info->enabled = powered;
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (adapter == NULL) {
|
|
DBG("Adapter not found");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (!powered) {
|
|
info->connectable = FALSE;
|
|
info->pairable = FALSE;
|
|
info->discoverable = FALSE;
|
|
|
|
btd_adapter_stop(adapter);
|
|
return 0;
|
|
}
|
|
|
|
btd_adapter_start(adapter);
|
|
|
|
btd_adapter_get_mode(adapter, NULL, &on_mode, &pairable);
|
|
|
|
if (on_mode == MODE_DISCOVERABLE && !info->discoverable)
|
|
mgmt_set_discoverable(index, TRUE);
|
|
else if (on_mode == MODE_CONNECTABLE && !info->connectable)
|
|
mgmt_set_connectable(index, TRUE);
|
|
else {
|
|
uint8_t mode = 0;
|
|
|
|
if (info->connectable)
|
|
mode |= SCAN_PAGE;
|
|
if (info->discoverable)
|
|
mode |= SCAN_INQUIRY;
|
|
|
|
adapter_mode_changed(adapter, mode);
|
|
}
|
|
|
|
if (info->pairable != pairable)
|
|
mgmt_set_pairable(index, pairable);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mgmt_powered(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_mode *ev = buf;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small powered event");
|
|
return;
|
|
}
|
|
|
|
DBG("Controller %u powered %u", index, ev->val);
|
|
|
|
mgmt_update_powered(index, ev->val);
|
|
}
|
|
|
|
static void mgmt_discoverable(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_mode *ev = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
uint8_t mode;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small discoverable event");
|
|
return;
|
|
}
|
|
|
|
DBG("Controller %u discoverable %u", index, ev->val);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in discoverable event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
info->discoverable = ev->val ? TRUE : FALSE;
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (!adapter)
|
|
return;
|
|
|
|
if (info->connectable)
|
|
mode = SCAN_PAGE;
|
|
else
|
|
mode = 0;
|
|
|
|
if (info->discoverable)
|
|
mode |= SCAN_INQUIRY;
|
|
|
|
adapter_mode_changed(adapter, mode);
|
|
}
|
|
|
|
static void mgmt_connectable(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_mode *ev = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
uint8_t mode;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small connectable event");
|
|
return;
|
|
}
|
|
|
|
DBG("Controller %u connectable %u", index, ev->val);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in connectable event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
info->connectable = ev->val ? TRUE : FALSE;
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (!adapter)
|
|
return;
|
|
|
|
if (info->discoverable)
|
|
mode = SCAN_INQUIRY;
|
|
else
|
|
mode = 0;
|
|
|
|
if (info->connectable)
|
|
mode |= SCAN_PAGE;
|
|
|
|
adapter_mode_changed(adapter, mode);
|
|
}
|
|
|
|
static void mgmt_pairable(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_mode *ev = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small pairable event");
|
|
return;
|
|
}
|
|
|
|
DBG("Controller %u pairable %u", index, ev->val);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in pairable event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
info->pairable = ev->val ? TRUE : FALSE;
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (!adapter)
|
|
return;
|
|
|
|
btd_adapter_pairable_changed(adapter, info->pairable);
|
|
}
|
|
|
|
static void mgmt_new_key(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_new_key *ev = buf;
|
|
struct controller_info *info;
|
|
|
|
if (len != sizeof(*ev)) {
|
|
error("new_key event size mismatch (%zu != %zu)",
|
|
len, sizeof(*ev));
|
|
return;
|
|
}
|
|
|
|
DBG("Controller %u new key of type %u pin_len %u", index,
|
|
ev->key.type, ev->key.pin_len);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in new_key event", index);
|
|
return;
|
|
}
|
|
|
|
if (ev->key.pin_len > 16) {
|
|
error("Invalid PIN length (%u) in new_key event",
|
|
ev->key.pin_len);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
if (ev->store_hint)
|
|
btd_event_link_key_notify(&info->bdaddr, &ev->key.bdaddr,
|
|
ev->key.val, ev->key.type,
|
|
ev->key.pin_len);
|
|
|
|
btd_event_bonding_complete(&info->bdaddr, &ev->key.bdaddr, 0);
|
|
}
|
|
|
|
static void mgmt_device_connected(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_device_connected *ev = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small device_connected event");
|
|
return;
|
|
}
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
|
|
DBG("hci%u device %s connected", index, addr);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in device_connected event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
btd_event_conn_complete(&info->bdaddr, &ev->bdaddr);
|
|
}
|
|
|
|
static void mgmt_device_disconnected(int sk, uint16_t index, void *buf,
|
|
size_t len)
|
|
{
|
|
struct mgmt_ev_device_disconnected *ev = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small device_disconnected event");
|
|
return;
|
|
}
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
|
|
DBG("hci%u device %s disconnected", index, addr);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in device_disconnected event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
btd_event_disconn_complete(&info->bdaddr, &ev->bdaddr);
|
|
}
|
|
|
|
static void mgmt_connect_failed(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_connect_failed *ev = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small connect_failed event");
|
|
return;
|
|
}
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
|
|
DBG("hci%u %s status %u", index, addr, ev->status);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in connect_failed event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
btd_event_conn_failed(&info->bdaddr, &ev->bdaddr, ev->status);
|
|
|
|
/* In the case of security mode 3 devices */
|
|
btd_event_bonding_complete(&info->bdaddr, &ev->bdaddr, ev->status);
|
|
}
|
|
|
|
static int mgmt_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin,
|
|
size_t pin_len)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_pin_code_reply)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
size_t buf_len;
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s pinlen %zu", index, addr, pin_len);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
if (pin == NULL) {
|
|
struct mgmt_cp_pin_code_neg_reply *cp;
|
|
|
|
hdr->opcode = htobs(MGMT_OP_PIN_CODE_NEG_REPLY);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
cp = (void *) &buf[sizeof(*hdr)];
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
|
|
buf_len = sizeof(*hdr) + sizeof(*cp);
|
|
} else {
|
|
struct mgmt_cp_pin_code_reply *cp;
|
|
|
|
if (pin_len > 16)
|
|
return -EINVAL;
|
|
|
|
hdr->opcode = htobs(MGMT_OP_PIN_CODE_REPLY);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
cp = (void *) &buf[sizeof(*hdr)];
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
cp->pin_len = pin_len;
|
|
memcpy(cp->pin_code, pin, pin_len);
|
|
|
|
buf_len = sizeof(*hdr) + sizeof(*cp);
|
|
}
|
|
|
|
if (write(mgmt_sock, buf, buf_len) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mgmt_pin_code_request(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_pin_code_request *ev = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
int err;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small pin_code_request event");
|
|
return;
|
|
}
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
|
|
DBG("hci%u %s", index, addr);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in pin_code_request event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
err = btd_event_request_pin(&info->bdaddr, &ev->bdaddr, ev->secure);
|
|
if (err < 0) {
|
|
error("btd_event_request_pin: %s", strerror(-err));
|
|
mgmt_pincode_reply(index, &ev->bdaddr, NULL, 0);
|
|
}
|
|
}
|
|
|
|
static int mgmt_confirm_reply(int index, bdaddr_t *bdaddr, gboolean success)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_user_confirm_reply)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_user_confirm_reply *cp;
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s success %d", index, addr, success);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
if (success)
|
|
hdr->opcode = htobs(MGMT_OP_USER_CONFIRM_REPLY);
|
|
else
|
|
hdr->opcode = htobs(MGMT_OP_USER_CONFIRM_NEG_REPLY);
|
|
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
cp = (void *) &buf[sizeof(*hdr)];
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct confirm_data {
|
|
int index;
|
|
bdaddr_t bdaddr;
|
|
};
|
|
|
|
static gboolean confirm_accept(gpointer user_data)
|
|
{
|
|
struct confirm_data *data = user_data;
|
|
struct controller_info *info = &controllers[data->index];
|
|
|
|
DBG("auto-accepting incoming pairing request");
|
|
|
|
if (data->index > max_index || !info->valid)
|
|
return FALSE;
|
|
|
|
mgmt_confirm_reply(data->index, &data->bdaddr, TRUE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void mgmt_user_confirm_request(int sk, uint16_t index, void *buf,
|
|
size_t len)
|
|
{
|
|
struct mgmt_ev_user_confirm_request *ev = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
int err;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small user_confirm_request event");
|
|
return;
|
|
}
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
|
|
DBG("hci%u %s confirm_hint %u", index, addr, ev->confirm_hint);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in user_confirm_request event",
|
|
index);
|
|
return;
|
|
}
|
|
|
|
if (ev->confirm_hint) {
|
|
struct confirm_data *data;
|
|
|
|
data = g_new0(struct confirm_data, 1);
|
|
data->index = index;
|
|
bacpy(&data->bdaddr, &ev->bdaddr);
|
|
|
|
g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1,
|
|
confirm_accept, data, g_free);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
err = btd_event_user_confirm(&info->bdaddr, &ev->bdaddr,
|
|
btohl(ev->value));
|
|
if (err < 0) {
|
|
error("btd_event_user_confirm: %s", strerror(-err));
|
|
mgmt_confirm_reply(index, &ev->bdaddr, FALSE);
|
|
}
|
|
}
|
|
|
|
static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid)
|
|
{
|
|
if (uuid->type == SDP_UUID16)
|
|
sdp_uuid16_to_uuid128(uuid128, uuid);
|
|
else if (uuid->type == SDP_UUID32)
|
|
sdp_uuid32_to_uuid128(uuid128, uuid);
|
|
else
|
|
memcpy(uuid128, uuid, sizeof(*uuid));
|
|
}
|
|
|
|
static int mgmt_add_uuid(int index, uuid_t *uuid, uint8_t svc_hint)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_uuid)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_add_uuid *cp = (void *) &buf[sizeof(*hdr)];
|
|
uuid_t uuid128;
|
|
uint128_t uint128;
|
|
|
|
DBG("index %d", index);
|
|
|
|
uuid_to_uuid128(&uuid128, uuid);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_ADD_UUID);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
|
|
htob128(&uint128, (uint128_t *) cp->uuid);
|
|
|
|
cp->svc_hint = svc_hint;
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_remove_uuid(int index, uuid_t *uuid)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_uuid)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_remove_uuid *cp = (void *) &buf[sizeof(*hdr)];
|
|
uuid_t uuid128;
|
|
uint128_t uint128;
|
|
|
|
DBG("index %d", index);
|
|
|
|
uuid_to_uuid128(&uuid128, uuid);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_REMOVE_UUID);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
|
|
htob128(&uint128, (uint128_t *) cp->uuid);
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int clear_uuids(int index)
|
|
{
|
|
uuid_t uuid_any;
|
|
|
|
memset(&uuid_any, 0, sizeof(uuid_any));
|
|
uuid_any.type = SDP_UUID128;
|
|
|
|
return mgmt_remove_uuid(index, &uuid_any);
|
|
}
|
|
|
|
static void read_index_list_complete(int sk, void *buf, size_t len)
|
|
{
|
|
struct mgmt_rp_read_index_list *rp = buf;
|
|
uint16_t num;
|
|
int i;
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small read index list complete event");
|
|
return;
|
|
}
|
|
|
|
num = btohs(bt_get_unaligned(&rp->num_controllers));
|
|
|
|
if (num * sizeof(uint16_t) + sizeof(*rp) != len) {
|
|
error("Incorrect packet size for index list event");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < num; i++) {
|
|
uint16_t index;
|
|
|
|
index = btohs(bt_get_unaligned(&rp->index[i]));
|
|
|
|
add_controller(index);
|
|
get_connections(sk, index);
|
|
clear_uuids(index);
|
|
}
|
|
}
|
|
|
|
static int mgmt_set_powered(int index, gboolean powered)
|
|
{
|
|
DBG("index %d powered %d", index, powered);
|
|
return mgmt_set_mode(index, MGMT_OP_SET_POWERED, powered);
|
|
}
|
|
|
|
static void read_info_complete(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_rp_read_info *rp = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
uint8_t mode;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small read info complete event");
|
|
return;
|
|
}
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in read info complete", index);
|
|
return;
|
|
}
|
|
|
|
mgmt_set_mode(index, MGMT_OP_SET_SERVICE_CACHE, 1);
|
|
|
|
info = &controllers[index];
|
|
info->type = rp->type;
|
|
info->enabled = rp->powered;
|
|
info->connectable = rp->connectable;
|
|
info->discoverable = rp->discoverable;
|
|
info->pairable = rp->pairable;
|
|
info->sec_mode = rp->sec_mode;
|
|
bacpy(&info->bdaddr, &rp->bdaddr);
|
|
memcpy(info->dev_class, rp->dev_class, 3);
|
|
memcpy(info->features, rp->features, 8);
|
|
info->manufacturer = btohs(bt_get_unaligned(&rp->manufacturer));
|
|
info->hci_ver = rp->hci_ver;
|
|
info->hci_rev = btohs(bt_get_unaligned(&rp->hci_rev));
|
|
|
|
ba2str(&info->bdaddr, addr);
|
|
DBG("hci%u type %u addr %s", index, info->type, addr);
|
|
DBG("hci%u class 0x%02x%02x%02x", index,
|
|
info->dev_class[2], info->dev_class[1], info->dev_class[0]);
|
|
DBG("hci%u manufacturer %d HCI ver %d:%d", index, info->manufacturer,
|
|
info->hci_ver, info->hci_rev);
|
|
DBG("hci%u enabled %u discoverable %u pairable %u sec_mode %u", index,
|
|
info->enabled, info->discoverable,
|
|
info->pairable, info->sec_mode);
|
|
DBG("hci%u name %s", index, (char *) rp->name);
|
|
|
|
adapter = btd_manager_register_adapter(index);
|
|
if (adapter == NULL) {
|
|
error("mgmtops: unable to register adapter");
|
|
return;
|
|
}
|
|
|
|
btd_adapter_get_mode(adapter, &mode, NULL, NULL);
|
|
if (mode == MODE_OFF) {
|
|
mgmt_set_powered(index, FALSE);
|
|
return;
|
|
}
|
|
|
|
if (info->enabled)
|
|
mgmt_update_powered(index, TRUE);
|
|
else
|
|
mgmt_set_powered(index, TRUE);
|
|
|
|
adapter_name_changed(adapter, (char *) rp->name);
|
|
|
|
btd_adapter_unref(adapter);
|
|
}
|
|
|
|
static void set_powered_complete(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_mode *rp = buf;
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small set powered complete event");
|
|
return;
|
|
}
|
|
|
|
DBG("hci%d powered %u", index, rp->val);
|
|
|
|
mgmt_update_powered(index, rp->val);
|
|
}
|
|
|
|
static void set_discoverable_complete(int sk, uint16_t index, void *buf,
|
|
size_t len)
|
|
{
|
|
struct mgmt_mode *rp = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
uint8_t mode;
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small set discoverable complete event");
|
|
return;
|
|
}
|
|
|
|
DBG("hci%d discoverable %u", index, rp->val);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in discoverable complete", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
info->discoverable = rp->val ? TRUE : FALSE;
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (!adapter)
|
|
return;
|
|
|
|
/* set_discoverable will always also change page scanning */
|
|
mode = SCAN_PAGE;
|
|
|
|
if (info->discoverable)
|
|
mode |= SCAN_INQUIRY;
|
|
|
|
adapter_mode_changed(adapter, mode);
|
|
}
|
|
|
|
static void set_connectable_complete(int sk, uint16_t index, void *buf,
|
|
size_t len)
|
|
{
|
|
struct mgmt_mode *rp = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small set connectable complete event");
|
|
return;
|
|
}
|
|
|
|
DBG("hci%d connectable %u", index, rp->val);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in connectable complete", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
info->connectable = rp->val ? TRUE : FALSE;
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (adapter)
|
|
adapter_mode_changed(adapter, rp->val ? SCAN_PAGE : 0);
|
|
}
|
|
|
|
static void set_pairable_complete(int sk, uint16_t index, void *buf,
|
|
size_t len)
|
|
{
|
|
struct mgmt_mode *rp = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small set pairable complete event");
|
|
return;
|
|
}
|
|
|
|
DBG("hci%d pairable %u", index, rp->val);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in pairable complete", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
info->pairable = rp->val ? TRUE : FALSE;
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (!adapter)
|
|
return;
|
|
|
|
btd_adapter_pairable_changed(adapter, info->pairable);
|
|
}
|
|
|
|
static void disconnect_complete(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_rp_disconnect *rp = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small disconnect complete event");
|
|
return;
|
|
}
|
|
|
|
ba2str(&rp->bdaddr, addr);
|
|
|
|
DBG("hci%d %s disconnected", index, addr);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in disconnect complete", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
btd_event_disconn_complete(&info->bdaddr, &rp->bdaddr);
|
|
|
|
btd_event_bonding_complete(&info->bdaddr, &rp->bdaddr,
|
|
HCI_CONNECTION_TERMINATED);
|
|
}
|
|
|
|
static void pair_device_complete(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_rp_pair_device *rp = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small pair_device complete event");
|
|
return;
|
|
}
|
|
|
|
ba2str(&rp->bdaddr, addr);
|
|
|
|
DBG("hci%d %s pairing complete status %u", index, addr, rp->status);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in pair_device complete", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
btd_event_bonding_complete(&info->bdaddr, &rp->bdaddr, rp->status);
|
|
}
|
|
|
|
static void get_connections_complete(int sk, uint16_t index, void *buf,
|
|
size_t len)
|
|
{
|
|
struct mgmt_rp_get_connections *rp = buf;
|
|
struct controller_info *info;
|
|
int i;
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small get_connections complete event");
|
|
return;
|
|
}
|
|
|
|
if (len < (sizeof(*rp) + (rp->conn_count * sizeof(bdaddr_t)))) {
|
|
error("Too small get_connections complete event");
|
|
return;
|
|
}
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in get_connections complete",
|
|
index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
for (i = 0; i < rp->conn_count; i++) {
|
|
bdaddr_t *bdaddr = g_memdup(&rp->conn[i], sizeof(bdaddr_t));
|
|
info->connections = g_slist_append(info->connections, bdaddr);
|
|
}
|
|
|
|
read_info(sk, index);
|
|
}
|
|
|
|
static void set_local_name_complete(int sk, uint16_t index, void *buf,
|
|
size_t len)
|
|
{
|
|
struct mgmt_cp_set_local_name *rp = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
|
|
if (len < sizeof(*rp)) {
|
|
error("Too small set_local_name complete event");
|
|
return;
|
|
}
|
|
|
|
DBG("hci%d name %s", index, (char *) rp->name);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in set_local_name complete", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (adapter == NULL) {
|
|
DBG("Adapter not found");
|
|
return;
|
|
}
|
|
|
|
adapter_name_changed(adapter, (char *) rp->name);
|
|
}
|
|
|
|
static void read_local_oob_data_complete(int sk, uint16_t index, void *buf,
|
|
size_t len)
|
|
{
|
|
struct mgmt_rp_read_local_oob_data *rp = buf;
|
|
struct btd_adapter *adapter;
|
|
|
|
if (len != sizeof(*rp)) {
|
|
error("Wrong read_local_oob_data_complete event size");
|
|
return;
|
|
}
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in read_local_oob_data_complete",
|
|
index);
|
|
return;
|
|
}
|
|
|
|
DBG("hci%u", index);
|
|
|
|
adapter = manager_find_adapter_by_id(index);
|
|
|
|
if (adapter)
|
|
oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
|
|
}
|
|
|
|
static void read_local_oob_data_failed(int sk, uint16_t index)
|
|
{
|
|
struct btd_adapter *adapter;
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in read_local_oob_data_failed",
|
|
index);
|
|
return;
|
|
}
|
|
|
|
DBG("hci%u", index);
|
|
|
|
adapter = manager_find_adapter_by_id(index);
|
|
|
|
if (adapter)
|
|
oob_read_local_data_complete(adapter, NULL, NULL);
|
|
}
|
|
|
|
static void mgmt_cmd_complete(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_cmd_complete *ev = buf;
|
|
uint16_t opcode;
|
|
|
|
DBG("");
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small management command complete event packet");
|
|
return;
|
|
}
|
|
|
|
opcode = btohs(bt_get_unaligned(&ev->opcode));
|
|
|
|
len -= sizeof(*ev);
|
|
|
|
switch (opcode) {
|
|
case MGMT_OP_READ_VERSION:
|
|
read_version_complete(sk, ev->data, len);
|
|
break;
|
|
case MGMT_OP_READ_INDEX_LIST:
|
|
read_index_list_complete(sk, ev->data, len);
|
|
break;
|
|
case MGMT_OP_READ_INFO:
|
|
read_info_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_SET_POWERED:
|
|
set_powered_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_SET_DISCOVERABLE:
|
|
set_discoverable_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_SET_CONNECTABLE:
|
|
set_connectable_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_SET_PAIRABLE:
|
|
set_pairable_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_ADD_UUID:
|
|
DBG("add_uuid complete");
|
|
break;
|
|
case MGMT_OP_REMOVE_UUID:
|
|
DBG("remove_uuid complete");
|
|
break;
|
|
case MGMT_OP_SET_DEV_CLASS:
|
|
DBG("set_dev_class complete");
|
|
break;
|
|
case MGMT_OP_SET_SERVICE_CACHE:
|
|
DBG("set_service_cache complete");
|
|
break;
|
|
case MGMT_OP_LOAD_KEYS:
|
|
DBG("load_keys complete");
|
|
break;
|
|
case MGMT_OP_REMOVE_KEY:
|
|
DBG("remove_key complete");
|
|
break;
|
|
case MGMT_OP_DISCONNECT:
|
|
DBG("disconnect complete");
|
|
disconnect_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_GET_CONNECTIONS:
|
|
get_connections_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_PIN_CODE_REPLY:
|
|
DBG("pin_code_reply complete");
|
|
break;
|
|
case MGMT_OP_PIN_CODE_NEG_REPLY:
|
|
DBG("pin_code_neg_reply complete");
|
|
break;
|
|
case MGMT_OP_SET_IO_CAPABILITY:
|
|
DBG("set_io_capability complete");
|
|
break;
|
|
case MGMT_OP_PAIR_DEVICE:
|
|
pair_device_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_USER_CONFIRM_REPLY:
|
|
DBG("user_confirm_reply complete");
|
|
break;
|
|
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
|
|
DBG("user_confirm_net_reply complete");
|
|
break;
|
|
case MGMT_OP_SET_LOCAL_NAME:
|
|
set_local_name_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_READ_LOCAL_OOB_DATA:
|
|
read_local_oob_data_complete(sk, index, ev->data, len);
|
|
break;
|
|
case MGMT_OP_ADD_REMOTE_OOB_DATA:
|
|
DBG("add_remote_oob_data complete");
|
|
break;
|
|
case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
|
|
DBG("remove_remote_oob_data complete");
|
|
break;
|
|
case MGMT_OP_BLOCK_DEVICE:
|
|
DBG("block_device complete");
|
|
break;
|
|
case MGMT_OP_UNBLOCK_DEVICE:
|
|
DBG("unblock_device complete");
|
|
break;
|
|
case MGMT_OP_SET_FAST_CONNECTABLE:
|
|
DBG("set_fast_connectable complete");
|
|
break;
|
|
default:
|
|
error("Unknown command complete for opcode %u", opcode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void mgmt_cmd_status(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_cmd_status *ev = buf;
|
|
uint16_t opcode;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small management command status event packet");
|
|
return;
|
|
}
|
|
|
|
opcode = btohs(bt_get_unaligned(&ev->opcode));
|
|
|
|
DBG("status %u opcode %u (index %u)", ev->status, opcode, index);
|
|
|
|
switch (opcode) {
|
|
case MGMT_OP_READ_LOCAL_OOB_DATA:
|
|
read_local_oob_data_failed(sk, index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void mgmt_controller_error(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_controller_error *ev = buf;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small management controller error event packet");
|
|
return;
|
|
}
|
|
|
|
DBG("index %u error_code %u", index, ev->error_code);
|
|
}
|
|
|
|
static void mgmt_auth_failed(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct controller_info *info;
|
|
struct mgmt_ev_auth_failed *ev = buf;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small mgmt_auth_failed event packet");
|
|
return;
|
|
}
|
|
|
|
DBG("hci%u auth failed status %u", index, ev->status);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in auth_failed event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
btd_event_bonding_complete(&info->bdaddr, &ev->bdaddr, ev->status);
|
|
}
|
|
|
|
static void mgmt_local_name_changed(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_cp_set_local_name *ev = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small mgmt_local_name_changed event packet");
|
|
return;
|
|
}
|
|
|
|
DBG("hci%u local name changed: %s", index, (char *) ev->name);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in name_changed event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (adapter)
|
|
adapter_name_changed(adapter, (char *) ev->name);
|
|
}
|
|
|
|
static void mgmt_device_found(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_device_found *ev = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
uint8_t *eir;
|
|
uint32_t cls;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small mgmt_device_found event packet");
|
|
return;
|
|
}
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in device_found event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
cls = ev->dev_class[0] | (ev->dev_class[1] << 8) |
|
|
(ev->dev_class[2] << 16);
|
|
|
|
if (ev->eir[0] == 0)
|
|
eir = NULL;
|
|
else
|
|
eir = ev->eir;
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
DBG("hci%u addr %s, class %u rssi %d %s", index, addr, cls,
|
|
ev->rssi, eir ? "eir" : "");
|
|
|
|
btd_event_device_found(&info->bdaddr, &ev->bdaddr, cls, ev->rssi, eir,
|
|
HCI_MAX_EIR_LENGTH);
|
|
}
|
|
|
|
static void mgmt_remote_name(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_ev_remote_name *ev = buf;
|
|
struct controller_info *info;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small mgmt_remote_name packet");
|
|
return;
|
|
}
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in remote_name event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
DBG("hci%u addr %s, name %s", index, addr, ev->name);
|
|
|
|
btd_event_remote_name(&info->bdaddr, &ev->bdaddr, 0, (char *) ev->name);
|
|
}
|
|
|
|
static void mgmt_discovering(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct mgmt_mode *ev = buf;
|
|
struct controller_info *info;
|
|
struct btd_adapter *adapter;
|
|
int state;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small discovering event");
|
|
return;
|
|
}
|
|
|
|
DBG("Controller %u discovering %u", index, ev->val);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in discovering event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
adapter = manager_find_adapter(&info->bdaddr);
|
|
if (!adapter)
|
|
return;
|
|
|
|
if (ev->val)
|
|
state = STATE_DISCOV;
|
|
else
|
|
state = STATE_IDLE;
|
|
|
|
adapter_set_state(adapter, state);
|
|
}
|
|
|
|
static void mgmt_device_blocked(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct controller_info *info;
|
|
struct mgmt_ev_device_blocked *ev = buf;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small mgmt_device_blocked event packet");
|
|
return;
|
|
}
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
DBG("Device blocked, index %u, addr %s", index, addr);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in device_blocked event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
btd_event_device_blocked(&info->bdaddr, &ev->bdaddr);
|
|
}
|
|
|
|
static void mgmt_device_unblocked(int sk, uint16_t index, void *buf, size_t len)
|
|
{
|
|
struct controller_info *info;
|
|
struct mgmt_ev_device_unblocked *ev = buf;
|
|
char addr[18];
|
|
|
|
if (len < sizeof(*ev)) {
|
|
error("Too small mgmt_device_unblocked event packet");
|
|
return;
|
|
}
|
|
|
|
ba2str(&ev->bdaddr, addr);
|
|
DBG("Device unblocked, index %u, addr %s", index, addr);
|
|
|
|
if (index > max_index) {
|
|
error("Unexpected index %u in device_unblocked event", index);
|
|
return;
|
|
}
|
|
|
|
info = &controllers[index];
|
|
|
|
btd_event_device_unblocked(&info->bdaddr, &ev->bdaddr);
|
|
}
|
|
|
|
static gboolean mgmt_event(GIOChannel *io, GIOCondition cond, gpointer user_data)
|
|
{
|
|
char buf[MGMT_BUF_SIZE];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
int sk;
|
|
ssize_t ret;
|
|
uint16_t len, opcode, index;
|
|
|
|
DBG("cond %d", cond);
|
|
|
|
if (cond & G_IO_NVAL)
|
|
return FALSE;
|
|
|
|
sk = g_io_channel_unix_get_fd(io);
|
|
|
|
if (cond & (G_IO_ERR | G_IO_HUP)) {
|
|
error("Error on management socket");
|
|
return FALSE;
|
|
}
|
|
|
|
ret = read(sk, buf, sizeof(buf));
|
|
if (ret < 0) {
|
|
error("Unable to read from management socket: %s (%d)",
|
|
strerror(errno), errno);
|
|
return TRUE;
|
|
}
|
|
|
|
DBG("Received %zd bytes from management socket", ret);
|
|
|
|
if (ret < MGMT_HDR_SIZE) {
|
|
error("Too small Management packet");
|
|
return TRUE;
|
|
}
|
|
|
|
opcode = btohs(bt_get_unaligned(&hdr->opcode));
|
|
len = btohs(bt_get_unaligned(&hdr->len));
|
|
index = btohs(bt_get_unaligned(&hdr->index));
|
|
|
|
if (ret != MGMT_HDR_SIZE + len) {
|
|
error("Packet length mismatch. ret %zd len %u", ret, len);
|
|
return TRUE;
|
|
}
|
|
|
|
switch (opcode) {
|
|
case MGMT_EV_CMD_COMPLETE:
|
|
mgmt_cmd_complete(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_CMD_STATUS:
|
|
mgmt_cmd_status(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_CONTROLLER_ERROR:
|
|
mgmt_controller_error(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_INDEX_ADDED:
|
|
mgmt_index_added(sk, index);
|
|
break;
|
|
case MGMT_EV_INDEX_REMOVED:
|
|
mgmt_index_removed(sk, index);
|
|
break;
|
|
case MGMT_EV_POWERED:
|
|
mgmt_powered(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_DISCOVERABLE:
|
|
mgmt_discoverable(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_CONNECTABLE:
|
|
mgmt_connectable(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_PAIRABLE:
|
|
mgmt_pairable(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_NEW_KEY:
|
|
mgmt_new_key(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_DEVICE_CONNECTED:
|
|
mgmt_device_connected(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_DEVICE_DISCONNECTED:
|
|
mgmt_device_disconnected(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_CONNECT_FAILED:
|
|
mgmt_connect_failed(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_PIN_CODE_REQUEST:
|
|
mgmt_pin_code_request(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_USER_CONFIRM_REQUEST:
|
|
mgmt_user_confirm_request(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_AUTH_FAILED:
|
|
mgmt_auth_failed(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_LOCAL_NAME_CHANGED:
|
|
mgmt_local_name_changed(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_DEVICE_FOUND:
|
|
mgmt_device_found(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_REMOTE_NAME:
|
|
mgmt_remote_name(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_DISCOVERING:
|
|
mgmt_discovering(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_DEVICE_BLOCKED:
|
|
mgmt_device_blocked(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
case MGMT_EV_DEVICE_UNBLOCKED:
|
|
mgmt_device_unblocked(sk, index, buf + MGMT_HDR_SIZE, len);
|
|
break;
|
|
default:
|
|
error("Unknown Management opcode %u (index %u)", opcode, index);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int mgmt_setup(void)
|
|
{
|
|
struct mgmt_hdr hdr;
|
|
struct sockaddr_hci addr;
|
|
GIOChannel *io;
|
|
GIOCondition condition;
|
|
int dd, err;
|
|
|
|
dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
|
|
if (dd < 0)
|
|
return -errno;
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.hci_family = AF_BLUETOOTH;
|
|
addr.hci_dev = HCI_DEV_NONE;
|
|
addr.hci_channel = HCI_CHANNEL_CONTROL;
|
|
|
|
if (bind(dd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
err = -errno;
|
|
goto fail;
|
|
}
|
|
|
|
memset(&hdr, 0, sizeof(hdr));
|
|
hdr.opcode = htobs(MGMT_OP_READ_VERSION);
|
|
hdr.index = htobs(MGMT_INDEX_NONE);
|
|
if (write(dd, &hdr, sizeof(hdr)) < 0) {
|
|
err = -errno;
|
|
goto fail;
|
|
}
|
|
|
|
io = g_io_channel_unix_new(dd);
|
|
condition = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
|
|
mgmt_watch = g_io_add_watch(io, condition, mgmt_event, NULL);
|
|
g_io_channel_unref(io);
|
|
|
|
mgmt_sock = dd;
|
|
|
|
info("Bluetooth Management interface initialized");
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
close(dd);
|
|
return err;
|
|
}
|
|
|
|
static void mgmt_cleanup(void)
|
|
{
|
|
g_free(controllers);
|
|
controllers = NULL;
|
|
max_index = -1;
|
|
|
|
if (mgmt_sock >= 0) {
|
|
close(mgmt_sock);
|
|
mgmt_sock = -1;
|
|
}
|
|
|
|
if (mgmt_watch > 0) {
|
|
g_source_remove(mgmt_watch);
|
|
mgmt_watch = 0;
|
|
}
|
|
}
|
|
|
|
static int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_dev_class)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_set_dev_class *cp = (void *) &buf[sizeof(*hdr)];
|
|
|
|
DBG("index %d major %u minor %u", index, major, minor);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_SET_DEV_CLASS);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
cp->major = major;
|
|
cp->minor = minor;
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_set_limited_discoverable(int index, gboolean limited)
|
|
{
|
|
DBG("index %d limited %d", index, limited);
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_start_discovery(int index)
|
|
{
|
|
struct mgmt_hdr hdr;
|
|
|
|
DBG("index %d", index);
|
|
|
|
memset(&hdr, 0, sizeof(hdr));
|
|
hdr.opcode = htobs(MGMT_OP_START_DISCOVERY);
|
|
hdr.index = htobs(index);
|
|
|
|
if (write(mgmt_sock, &hdr, sizeof(hdr)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_stop_discovery(int index)
|
|
{
|
|
struct mgmt_hdr hdr;
|
|
|
|
DBG("index %d", index);
|
|
|
|
memset(&hdr, 0, sizeof(hdr));
|
|
hdr.opcode = htobs(MGMT_OP_STOP_DISCOVERY);
|
|
hdr.index = htobs(index);
|
|
|
|
if (write(mgmt_sock, &hdr, sizeof(hdr)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_resolve_name(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s", index, addr);
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_set_name(int index, const char *name)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_local_name)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_set_local_name *cp = (void *) &buf[sizeof(*hdr)];
|
|
|
|
DBG("index %d, name %s", index, name);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_SET_LOCAL_NAME);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
strncpy((char *) cp->name, name, sizeof(cp->name) - 1);
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_cancel_resolve_name(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s", index, addr);
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_set_fast_connectable(int index, gboolean enable)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_fast_connectable)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_set_fast_connectable *cp = (void *) &buf[sizeof(*hdr)];
|
|
|
|
DBG("index %d enable %d", index, enable);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_SET_FAST_CONNECTABLE);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
cp->enable = enable;
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_read_clock(int index, bdaddr_t *bdaddr, int which, int timeout,
|
|
uint32_t *clock, uint16_t *accuracy)
|
|
{
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s which %d timeout %d", index, addr, which,
|
|
timeout);
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_read_bdaddr(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char addr[18];
|
|
struct controller_info *info = &controllers[index];
|
|
|
|
ba2str(&info->bdaddr, addr);
|
|
DBG("index %d addr %s", index, addr);
|
|
|
|
if (!info->valid)
|
|
return -ENODEV;
|
|
|
|
bacpy(bdaddr, &info->bdaddr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_block_device(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_block_device)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_block_device *cp;
|
|
size_t buf_len;
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s", index, addr);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
hdr->opcode = htobs(MGMT_OP_BLOCK_DEVICE);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
cp = (void *) &buf[sizeof(*hdr)];
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
|
|
buf_len = sizeof(*hdr) + sizeof(*cp);
|
|
|
|
if (write(mgmt_sock, buf, buf_len) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_unblock_device(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_unblock_device)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_unblock_device *cp;
|
|
size_t buf_len;
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s", index, addr);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
hdr->opcode = htobs(MGMT_OP_UNBLOCK_DEVICE);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
cp = (void *) &buf[sizeof(*hdr)];
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
|
|
buf_len = sizeof(*hdr) + sizeof(*cp);
|
|
|
|
if (write(mgmt_sock, buf, buf_len) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_get_conn_list(int index, GSList **conns)
|
|
{
|
|
struct controller_info *info = &controllers[index];
|
|
|
|
DBG("index %d", index);
|
|
|
|
*conns = info->connections;
|
|
info->connections = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_disconnect(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_disconnect)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_disconnect *cp = (void *) &buf[sizeof(*hdr)];
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d %s", index, addr);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_DISCONNECT);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
error("write: %s (%d)", strerror(errno), errno);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_remove_bonding(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_key)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_remove_key *cp = (void *) &buf[sizeof(*hdr)];
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s", index, addr);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_REMOVE_KEY);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
cp->disconnect = 1;
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_passkey_reply(int index, bdaddr_t *bdaddr, uint32_t passkey)
|
|
{
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("index %d addr %s passkey %06u", index, addr, passkey);
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_encrypt_link(int index, bdaddr_t *dst, bt_hci_result_t cb,
|
|
gpointer user_data)
|
|
{
|
|
char addr[18];
|
|
|
|
ba2str(dst, addr);
|
|
DBG("index %d addr %s", index, addr);
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_set_did(int index, uint16_t vendor, uint16_t product,
|
|
uint16_t version)
|
|
{
|
|
DBG("index %d vendor %u product %u version %u",
|
|
index, vendor, product, version);
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_disable_cod_cache(int index)
|
|
{
|
|
DBG("index %d", index);
|
|
return mgmt_set_mode(index, MGMT_OP_SET_SERVICE_CACHE, 0);
|
|
}
|
|
|
|
static int mgmt_restore_powered(int index)
|
|
{
|
|
DBG("index %d", index);
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_load_keys(int index, GSList *keys, gboolean debug_keys)
|
|
{
|
|
char *buf;
|
|
struct mgmt_hdr *hdr;
|
|
struct mgmt_cp_load_keys *cp;
|
|
struct mgmt_key_info *key;
|
|
size_t key_count, cp_size;
|
|
GSList *l;
|
|
int err;
|
|
|
|
key_count = g_slist_length(keys);
|
|
|
|
DBG("index %d keys %zu debug_keys %d", index, key_count, debug_keys);
|
|
|
|
cp_size = sizeof(*cp) + (key_count * sizeof(*key));
|
|
|
|
buf = g_try_malloc0(sizeof(*hdr) + cp_size);
|
|
if (buf == NULL)
|
|
return -ENOMEM;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
hdr = (void *) buf;
|
|
hdr->opcode = htobs(MGMT_OP_LOAD_KEYS);
|
|
hdr->len = htobs(cp_size);
|
|
hdr->index = htobs(index);
|
|
|
|
cp = (void *) (buf + sizeof(*hdr));
|
|
cp->debug_keys = debug_keys;
|
|
cp->key_count = htobs(key_count);
|
|
|
|
for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) {
|
|
struct link_key_info *info = l->data;
|
|
|
|
bacpy(&key->bdaddr, &info->bdaddr);
|
|
key->type = info->type;
|
|
memcpy(key->val, info->key, 16);
|
|
key->pin_len = info->pin_len;
|
|
}
|
|
|
|
if (write(mgmt_sock, buf, sizeof(*hdr) + cp_size) < 0)
|
|
err = -errno;
|
|
else
|
|
err = 0;
|
|
|
|
g_free(buf);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int mgmt_set_io_capability(int index, uint8_t io_capability)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_io_capability)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_set_io_capability *cp = (void *) &buf[sizeof(*hdr)];
|
|
|
|
DBG("hci%d io_capability 0x%02x", index, io_capability);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_SET_IO_CAPABILITY);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
cp->io_capability = io_capability;
|
|
|
|
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_create_bonding(int index, bdaddr_t *bdaddr, uint8_t io_cap)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_pair_device)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_pair_device *cp = (void *) &buf[sizeof(*hdr)];
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("hci%d bdaddr %s io_cap 0x%02x", index, addr, io_cap);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
hdr->opcode = htobs(MGMT_OP_PAIR_DEVICE);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
hdr->index = htobs(index);
|
|
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
cp->io_cap = io_cap;
|
|
|
|
if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_cancel_bonding(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("hci%d bdaddr %s", index, addr);
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int mgmt_read_local_oob_data(int index)
|
|
{
|
|
struct mgmt_hdr hdr;
|
|
|
|
DBG("hci%d", index);
|
|
|
|
hdr.opcode = htobs(MGMT_OP_READ_LOCAL_OOB_DATA);
|
|
hdr.len = 0;
|
|
hdr.index = htobs(index);
|
|
|
|
if (write(mgmt_sock, &hdr, sizeof(hdr)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_add_remote_oob_data(int index, bdaddr_t *bdaddr,
|
|
uint8_t *hash, uint8_t *randomizer)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_remote_oob_data)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_add_remote_oob_data *cp = (void *) &buf[sizeof(*hdr)];
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("hci%d bdaddr %s", index, addr);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
hdr->opcode = htobs(MGMT_OP_ADD_REMOTE_OOB_DATA);
|
|
hdr->index = htobs(index);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
memcpy(cp->hash, hash, 16);
|
|
memcpy(cp->randomizer, randomizer, 16);
|
|
|
|
if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_remove_remote_oob_data(int index, bdaddr_t *bdaddr)
|
|
{
|
|
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_remote_oob_data)];
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
struct mgmt_cp_remove_remote_oob_data *cp = (void *) &buf[sizeof(*hdr)];
|
|
char addr[18];
|
|
|
|
ba2str(bdaddr, addr);
|
|
DBG("hci%d bdaddr %s", index, addr);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
hdr->opcode = htobs(MGMT_OP_REMOVE_REMOTE_OOB_DATA);
|
|
hdr->index = htobs(index);
|
|
hdr->len = htobs(sizeof(*cp));
|
|
|
|
bacpy(&cp->bdaddr, bdaddr);
|
|
|
|
if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct btd_adapter_ops mgmt_ops = {
|
|
.setup = mgmt_setup,
|
|
.cleanup = mgmt_cleanup,
|
|
.set_powered = mgmt_set_powered,
|
|
.set_discoverable = mgmt_set_discoverable,
|
|
.set_pairable = mgmt_set_pairable,
|
|
.set_limited_discoverable = mgmt_set_limited_discoverable,
|
|
.start_discovery = mgmt_start_discovery,
|
|
.stop_discovery = mgmt_stop_discovery,
|
|
.resolve_name = mgmt_resolve_name,
|
|
.cancel_resolve_name = mgmt_cancel_resolve_name,
|
|
.set_name = mgmt_set_name,
|
|
.set_dev_class = mgmt_set_dev_class,
|
|
.set_fast_connectable = mgmt_set_fast_connectable,
|
|
.read_clock = mgmt_read_clock,
|
|
.read_bdaddr = mgmt_read_bdaddr,
|
|
.block_device = mgmt_block_device,
|
|
.unblock_device = mgmt_unblock_device,
|
|
.get_conn_list = mgmt_get_conn_list,
|
|
.disconnect = mgmt_disconnect,
|
|
.remove_bonding = mgmt_remove_bonding,
|
|
.pincode_reply = mgmt_pincode_reply,
|
|
.confirm_reply = mgmt_confirm_reply,
|
|
.passkey_reply = mgmt_passkey_reply,
|
|
.encrypt_link = mgmt_encrypt_link,
|
|
.set_did = mgmt_set_did,
|
|
.add_uuid = mgmt_add_uuid,
|
|
.remove_uuid = mgmt_remove_uuid,
|
|
.disable_cod_cache = mgmt_disable_cod_cache,
|
|
.restore_powered = mgmt_restore_powered,
|
|
.load_keys = mgmt_load_keys,
|
|
.set_io_capability = mgmt_set_io_capability,
|
|
.create_bonding = mgmt_create_bonding,
|
|
.cancel_bonding = mgmt_cancel_bonding,
|
|
.read_local_oob_data = mgmt_read_local_oob_data,
|
|
.add_remote_oob_data = mgmt_add_remote_oob_data,
|
|
.remove_remote_oob_data = mgmt_remove_remote_oob_data,
|
|
};
|
|
|
|
static int mgmt_init(void)
|
|
{
|
|
return btd_register_adapter_ops(&mgmt_ops, TRUE);
|
|
}
|
|
|
|
static void mgmt_exit(void)
|
|
{
|
|
btd_adapter_cleanup_ops(&mgmt_ops);
|
|
}
|
|
|
|
BLUETOOTH_PLUGIN_DEFINE(mgmtops, VERSION,
|
|
BLUETOOTH_PLUGIN_PRIORITY_LOW, mgmt_init, mgmt_exit)
|