blob: d71fcfc351ecc732bacec961cefacd2575102086 [file] [log] [blame]
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "bdaddr"
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <log/log.h>
#define HCI_DEV_NONE 0xffff
#define HCI_CHANNEL_CONTROL 3
#define BTPROTO_HCI 1
struct sockaddr_hci {
sa_family_t hci_family;
uint16_t hci_dev;
uint16_t hci_channel;
};
#define BTMGMT_CMD_READ_CONFIG_INFO 0x0037
#define BTMGMT_CMD_SET_PUBLIC_ADDR 0x0039
#define BTMGMT_EV_CMD_COMPLETE 0x0001
#define BTMGMT_EV_CMD_STATUS 0x0002
#define BTMGMT_EV_UNCONF_INDEX_ADDED 0x001d
#define BTMGMT_OPT_PUBLIC_ADDRESS (1 << 1)
#define BTMGMT_ERR_INVALID_INDEX 0x11
struct btmgmt_hdr {
uint16_t cmd;
uint16_t id;
uint16_t len;
} __attribute__((packed));
struct btmgmt_cmd_set_public_addr {
struct btmgmt_hdr hdr;
uint8_t addr[6];
} __attribute__((packed));
struct btmgmt_ev_cmd_status {
struct btmgmt_hdr hdr;
uint16_t cmd;
uint8_t status;
} __attribute__((packed));
struct btmgmt_ev_cc_config_info {
struct btmgmt_ev_cmd_status ev;
uint16_t manufacturer;
uint32_t supported_options;
uint32_t missing_options;
} __attribute__((packed));
// TODO: Make this configurable
#define HCI_CONTROLLER 0
#define MAC_ADDRESS_SIZE 6
#define MAC_ADDRESS_LENGTH (MAC_ADDRESS_SIZE*2 + MAC_ADDRESS_SIZE-1)
#define MAC_ADDRESS_FORMAT "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
#define MAC_ADDRESS_ARGS(addr) \
(addr)[5], (addr)[4], (addr)[3], (addr)[2], (addr)[1], (addr)[0]
#define MESSAGE_BUFFER 512
static int btmgmt_connect() {
int s = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
if (s < 0)
return -1;
struct sockaddr_hci addr = {
.hci_family = AF_BLUETOOTH,
.hci_dev = HCI_DEV_NONE,
.hci_channel = HCI_CHANNEL_CONTROL,
};
if (bind(s, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
close(s);
return -1;
}
return s;
}
static void btmgmt_request_config_info(int s) {
struct btmgmt_hdr cmd = {
.cmd = BTMGMT_CMD_READ_CONFIG_INFO,
.id = HCI_CONTROLLER,
.len = 0,
};
if (write(s, &cmd, sizeof(cmd)) < 0) {
ALOGE("Failed to request controller configuration information: %s",
strerror(errno));
}
}
static void btmgmt_set_public_addr(int s, const uint8_t bdaddr[MAC_ADDRESS_SIZE]) {
struct btmgmt_cmd_set_public_addr cmd = {
.hdr = {
.cmd = BTMGMT_CMD_SET_PUBLIC_ADDR,
.id = HCI_CONTROLLER,
.len = sizeof(cmd) - sizeof(cmd.hdr),
},
};
memcpy(cmd.addr, bdaddr, sizeof(cmd.addr));
if (write(s, &cmd, sizeof(cmd)) != sizeof(cmd)) {
ALOGE("Failed to write set public address command: %s", strerror(errno));
}
}
static void btmgmt_complete_set_public_addr(struct btmgmt_ev_cmd_status* ev,
const uint8_t bdaddr[MAC_ADDRESS_SIZE]) {
if (ev->status == 0) {
ALOGI("Updated public address to " MAC_ADDRESS_FORMAT,
MAC_ADDRESS_ARGS(bdaddr));
} else {
ALOGE("Failed to update public address to " MAC_ADDRESS_FORMAT
": error 0x%x", MAC_ADDRESS_ARGS(bdaddr), ev->status);
}
}
static bool btmgmt_config_needs_public_addr(struct btmgmt_ev_cmd_status* ev) {
struct btmgmt_ev_cc_config_info* info = (struct btmgmt_ev_cc_config_info*) ev;
if (info->ev.status) {
if (info->ev.status != BTMGMT_ERR_INVALID_INDEX)
ALOGE("Failed to read controller configuration information: 0x%x",
info->ev.status);
return false;
}
if (info->ev.hdr.cmd != BTMGMT_EV_CMD_COMPLETE
|| info->ev.hdr.len < sizeof(*info) - sizeof(info->ev.hdr))
return false;
if (info->missing_options & BTMGMT_OPT_PUBLIC_ADDRESS) {
return true;
} else {
ALOGD("Controller is already configured with a public address");
return false;
}
}
int main(int argc, char* argv[]) {
if (argc < 2) {
ALOGE("Usage: bdaddr <bdaddr>");
return 1;
}
uint8_t bdaddr[MAC_ADDRESS_SIZE];
if (strlen(argv[1]) != MAC_ADDRESS_LENGTH
|| sscanf(argv[1], MAC_ADDRESS_FORMAT,
&bdaddr[5], &bdaddr[4], &bdaddr[3],
&bdaddr[2], &bdaddr[1], &bdaddr[0]) != MAC_ADDRESS_SIZE) {
ALOGE("Invalid MAC address: %s", argv[1]);
return 1;
}
int s = btmgmt_connect();
if (s < 0) {
ALOGE("Failed to create Bluetooth management socket: %s", strerror(errno));
return 1;
}
btmgmt_request_config_info(s);
char buf[MESSAGE_BUFFER];
struct btmgmt_hdr* hdr = (struct btmgmt_hdr*) buf;
struct btmgmt_ev_cmd_status* ev = (struct btmgmt_ev_cmd_status*) hdr;
while (true) {
ssize_t len = read(s, buf, sizeof(buf));
if (len < (ssize_t) sizeof(struct btmgmt_hdr))
continue;
if (len < (ssize_t) sizeof(struct btmgmt_hdr) + hdr->len)
continue;
if (hdr->id != HCI_CONTROLLER)
continue;
switch (hdr->cmd) {
case BTMGMT_EV_CMD_COMPLETE:
case BTMGMT_EV_CMD_STATUS:
if (hdr->len < sizeof(*ev) - sizeof(hdr))
continue;
switch (ev->cmd) {
case BTMGMT_CMD_READ_CONFIG_INFO:
if (btmgmt_config_needs_public_addr(ev))
btmgmt_set_public_addr(s, bdaddr);
break;
case BTMGMT_CMD_SET_PUBLIC_ADDR:
btmgmt_complete_set_public_addr(ev, bdaddr);
break;
}
break;
case BTMGMT_EV_UNCONF_INDEX_ADDED:
btmgmt_request_config_info(s);
break;
}
}
}