2011-10-25 21:36:00 +08:00
|
|
|
/*
|
|
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011 Intel Corporation. All rights reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
|
2011-10-26 18:01:47 +08:00
|
|
|
#include <stdlib.h>
|
2011-10-25 23:07:08 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <poll.h>
|
2011-10-26 18:01:47 +08:00
|
|
|
#include <getopt.h>
|
2011-10-27 04:50:47 +08:00
|
|
|
#include <stdbool.h>
|
2011-10-25 23:07:08 +08:00
|
|
|
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
|
|
#include <bluetooth/hci.h>
|
|
|
|
#include <bluetooth/mgmt.h>
|
|
|
|
|
2011-10-27 05:15:39 +08:00
|
|
|
#ifndef NELEM
|
|
|
|
#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static const char *mgmt_op[] = {
|
|
|
|
"<0x0000>",
|
|
|
|
"Read Version",
|
|
|
|
"Read Features",
|
|
|
|
"Read Index List",
|
|
|
|
"Read Controller Info",
|
|
|
|
"Set Powered",
|
|
|
|
"Set Discoverable",
|
|
|
|
"Set Connectable",
|
2011-11-01 16:34:11 +08:00
|
|
|
"Set Pairable", /* 0x0008 */
|
2011-10-27 05:15:39 +08:00
|
|
|
"Add UUID",
|
|
|
|
"Remove UUID",
|
|
|
|
"Set Dev Class",
|
|
|
|
"Set Service Cache",
|
|
|
|
"Load Link Keys",
|
|
|
|
"Remove Keys",
|
|
|
|
"Disconnect",
|
2011-11-01 16:34:11 +08:00
|
|
|
"Get Connections", /* 0x0010 */
|
2011-10-27 05:15:39 +08:00
|
|
|
"PIN Code Reply",
|
|
|
|
"PIN Code Neg Reply",
|
|
|
|
"Set IO Capability",
|
|
|
|
"Pair Device",
|
|
|
|
"User Confirm Reply",
|
|
|
|
"User Confirm Neg Reply",
|
|
|
|
"Set Local Name",
|
2011-11-01 16:34:11 +08:00
|
|
|
"Read Local OOB Data", /* 0x0018 */
|
2011-10-27 05:15:39 +08:00
|
|
|
"Add Remote OOB Data",
|
|
|
|
"Remove Remove OOB Data",
|
|
|
|
"Start Discoery",
|
|
|
|
"Block Device",
|
|
|
|
"Unblock Device",
|
|
|
|
"Set Fast Connectable",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *mgmt_ev[] = {
|
|
|
|
"<0x0000>",
|
|
|
|
"Command Complete",
|
|
|
|
"Command Status",
|
|
|
|
"Controller Error",
|
|
|
|
"Index Added",
|
|
|
|
"Index Removed",
|
|
|
|
"Powered",
|
|
|
|
"Discoverable",
|
2011-11-01 16:34:11 +08:00
|
|
|
"Connectable", /* 0x0008 */
|
2011-10-27 05:15:39 +08:00
|
|
|
"Pairable",
|
|
|
|
"New Link Key",
|
|
|
|
"Device Connected",
|
|
|
|
"Device Disconnected",
|
|
|
|
"Connect Failed",
|
|
|
|
"PIN Code Request",
|
|
|
|
"User Confirm Request",
|
2011-11-01 16:34:11 +08:00
|
|
|
"Authentication Failed", /* 0x0010 */
|
2011-10-27 05:15:39 +08:00
|
|
|
"Local Name Changed",
|
|
|
|
"Device Found",
|
|
|
|
"Remote Name",
|
|
|
|
"Discovering",
|
|
|
|
"Device Blocked",
|
|
|
|
"Device Unblocked",
|
|
|
|
};
|
|
|
|
|
2011-10-27 04:50:47 +08:00
|
|
|
static bool monitor = false;
|
|
|
|
|
2011-10-26 23:41:41 +08:00
|
|
|
typedef void (*cmd_cb)(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
|
|
|
|
void *rsp, uint16_t len, void *user_data);
|
|
|
|
|
2011-10-27 22:21:30 +08:00
|
|
|
static struct pending_cmd {
|
2011-10-26 23:41:41 +08:00
|
|
|
uint16_t op;
|
|
|
|
uint16_t id;
|
|
|
|
cmd_cb cb;
|
|
|
|
void *user_data;
|
2011-10-27 22:21:30 +08:00
|
|
|
struct pending_cmd *next;
|
2011-10-27 23:05:56 +08:00
|
|
|
} *pending = NULL;
|
2011-10-26 23:41:41 +08:00
|
|
|
|
2011-10-27 05:15:39 +08:00
|
|
|
static const char *mgmt_opstr(uint16_t op)
|
|
|
|
{
|
|
|
|
if (op >= NELEM(mgmt_op))
|
|
|
|
return "<unknown opcode>";
|
|
|
|
return mgmt_op[op];
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *mgmt_evstr(uint16_t ev)
|
|
|
|
{
|
|
|
|
if (ev >= NELEM(mgmt_ev))
|
|
|
|
return "<unknown event>";
|
|
|
|
return mgmt_ev[ev];
|
|
|
|
}
|
|
|
|
|
2011-10-26 23:41:41 +08:00
|
|
|
static int mgmt_send_cmd(int mgmt_sk, uint16_t op, uint16_t id, void *data,
|
|
|
|
size_t len, cmd_cb func, void *user_data)
|
|
|
|
{
|
|
|
|
char buf[1024];
|
2011-10-27 23:05:56 +08:00
|
|
|
struct pending_cmd *cmd;
|
2011-10-26 23:41:41 +08:00
|
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
|
|
|
|
|
|
if (len + MGMT_HDR_SIZE > sizeof(buf))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-10-27 23:05:56 +08:00
|
|
|
cmd = calloc(1, sizeof(struct pending_cmd));
|
|
|
|
if (cmd == NULL)
|
2011-10-26 23:41:41 +08:00
|
|
|
return -errno;
|
|
|
|
|
2011-10-27 23:05:56 +08:00
|
|
|
cmd->op = op;
|
|
|
|
cmd->id = id;
|
|
|
|
cmd->cb = func;
|
|
|
|
cmd->user_data = user_data;
|
2011-10-26 23:41:41 +08:00
|
|
|
|
|
|
|
memset(buf, 0, sizeof(buf));
|
2011-10-27 16:41:47 +08:00
|
|
|
hdr->opcode = htobs(op);
|
2011-10-26 23:41:41 +08:00
|
|
|
hdr->index = htobs(id);
|
|
|
|
hdr->len = htobs(len);
|
|
|
|
memcpy(buf + MGMT_HDR_SIZE, data, len);
|
|
|
|
|
|
|
|
if (write(mgmt_sk, buf, MGMT_HDR_SIZE + len) < 0) {
|
2011-10-27 22:30:16 +08:00
|
|
|
fprintf(stderr, "Unable to write to socket: %s\n",
|
2011-10-26 23:41:41 +08:00
|
|
|
strerror(errno));
|
2011-10-27 23:05:56 +08:00
|
|
|
free(cmd);
|
2011-10-26 23:41:41 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-10-27 23:05:56 +08:00
|
|
|
cmd->next = pending;
|
|
|
|
pending = cmd;
|
|
|
|
|
2011-10-26 23:41:41 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-25 23:07:08 +08:00
|
|
|
static int mgmt_open(void)
|
|
|
|
{
|
|
|
|
struct sockaddr_hci addr;
|
|
|
|
int sk;
|
|
|
|
|
|
|
|
sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
|
|
|
|
if (sk < 0) {
|
|
|
|
fprintf(stderr, "socket: %s\n", strerror(errno));
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
|
|
addr.hci_family = AF_BLUETOOTH;
|
|
|
|
addr.hci_dev = HCI_DEV_NONE;
|
|
|
|
addr.hci_channel = HCI_CHANNEL_CONTROL;
|
|
|
|
|
|
|
|
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
|
|
fprintf(stderr, "bind: %s\n", strerror(errno));
|
|
|
|
close(sk);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
2011-10-27 22:21:30 +08:00
|
|
|
static void mgmt_check_pending(int mgmt_sk, uint16_t op, uint16_t index,
|
|
|
|
uint16_t status, void *data, uint16_t len)
|
|
|
|
{
|
|
|
|
struct pending_cmd *c, *prev;
|
|
|
|
|
2011-10-27 23:05:56 +08:00
|
|
|
for (c = pending, prev = NULL; c != NULL; prev = c, c = c->next) {
|
2011-10-27 22:21:30 +08:00
|
|
|
if (c->op != op)
|
|
|
|
continue;
|
|
|
|
if (c->id != index)
|
|
|
|
continue;
|
|
|
|
|
2011-10-27 23:05:56 +08:00
|
|
|
if (c == pending)
|
|
|
|
pending = c->next;
|
2011-10-27 22:21:30 +08:00
|
|
|
else
|
|
|
|
prev->next = c->next;
|
|
|
|
|
|
|
|
c->cb(mgmt_sk, op, index, status, data, len, c->user_data);
|
|
|
|
|
|
|
|
free(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-26 05:33:11 +08:00
|
|
|
static int mgmt_cmd_complete(int mgmt_sk, uint16_t index,
|
|
|
|
struct mgmt_ev_cmd_complete *ev, uint16_t len)
|
|
|
|
{
|
2011-10-26 23:41:41 +08:00
|
|
|
uint16_t op;
|
2011-10-26 05:33:11 +08:00
|
|
|
|
|
|
|
if (len < sizeof(*ev)) {
|
|
|
|
fprintf(stderr, "Too short (%u bytes) cmd complete event\n",
|
|
|
|
len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-10-26 23:41:41 +08:00
|
|
|
op = bt_get_le16(&ev->opcode);
|
2011-10-26 05:33:11 +08:00
|
|
|
|
|
|
|
len -= sizeof(*ev);
|
|
|
|
|
2011-10-27 04:50:47 +08:00
|
|
|
if (monitor)
|
2011-10-27 05:15:39 +08:00
|
|
|
printf("%s complete, opcode 0x%04x len %u\n", mgmt_opstr(op),
|
|
|
|
op, len);
|
2011-10-26 23:41:41 +08:00
|
|
|
|
2011-10-27 22:21:30 +08:00
|
|
|
mgmt_check_pending(mgmt_sk, op, index, 0, ev->data, len);
|
2011-10-26 05:33:11 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_cmd_status(int mgmt_sk, uint16_t index,
|
|
|
|
struct mgmt_ev_cmd_status *ev, uint16_t len)
|
|
|
|
{
|
|
|
|
uint16_t opcode;
|
|
|
|
|
|
|
|
if (len < sizeof(*ev)) {
|
|
|
|
fprintf(stderr, "Too short (%u bytes) cmd status event\n",
|
|
|
|
len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
opcode = bt_get_le16(&ev->opcode);
|
|
|
|
|
2011-10-27 16:42:37 +08:00
|
|
|
if (monitor)
|
|
|
|
printf("cmd status, opcode 0x%04x status 0x%02x\n",
|
|
|
|
opcode, ev->status);
|
2011-10-26 05:33:11 +08:00
|
|
|
|
2011-10-27 22:21:30 +08:00
|
|
|
if (ev->status != 0)
|
|
|
|
mgmt_check_pending(mgmt_sk, opcode, index, ev->status,
|
|
|
|
NULL, 0);
|
2011-10-26 23:41:41 +08:00
|
|
|
|
2011-10-26 05:33:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_controller_error(uint16_t index,
|
|
|
|
struct mgmt_ev_controller_error *ev,
|
|
|
|
uint16_t len)
|
|
|
|
{
|
|
|
|
if (len < sizeof(*ev)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Too short (%u bytes) controller error event\n", len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-10-27 16:47:31 +08:00
|
|
|
printf("hci%u error 0x%02x\n", index, ev->error_code);
|
2011-10-26 05:33:11 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_index_added(int mgmt_sk, uint16_t index)
|
|
|
|
{
|
2011-10-27 16:47:31 +08:00
|
|
|
printf("hci%u added\n", index);
|
2011-10-26 05:33:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_index_removed(int mgmt_sk, uint16_t index)
|
|
|
|
{
|
2011-10-27 16:47:31 +08:00
|
|
|
printf("hci%u removed\n", index);
|
2011-10-26 05:33:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_powered(int mgmt_sk, uint16_t index, struct mgmt_mode *ev,
|
|
|
|
uint16_t len)
|
|
|
|
{
|
|
|
|
if (len < sizeof(*ev)) {
|
|
|
|
fprintf(stderr, "Too short (%u bytes) mgmt powered event\n",
|
|
|
|
len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-10-27 16:47:31 +08:00
|
|
|
printf("hci%u powered %s\n", index, ev->val ? "on" : "off");
|
2011-10-26 05:33:11 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_discoverable(int mgmt_sk, uint16_t index, struct mgmt_mode *ev,
|
|
|
|
uint16_t len)
|
|
|
|
{
|
|
|
|
if (len < sizeof(*ev)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Too short (%u bytes) mgmt discoverable event\n", len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-10-27 16:47:31 +08:00
|
|
|
printf("hci%u discoverable %s\n", index, ev->val ? "on" : "off");
|
2011-10-26 05:33:11 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_connectable(int mgmt_sk, uint16_t index, struct mgmt_mode *ev,
|
|
|
|
uint16_t len)
|
|
|
|
{
|
|
|
|
if (len < sizeof(*ev)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Too short (%u bytes) mgmt connectable event\n", len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-10-27 16:47:31 +08:00
|
|
|
printf("hci%u connectable %s\n", index, ev->val ? "on" : "off");
|
2011-10-26 05:33:11 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_pairable(int mgmt_sk, uint16_t index, struct mgmt_mode *ev,
|
|
|
|
uint16_t len)
|
|
|
|
{
|
|
|
|
if (len < sizeof(*ev)) {
|
|
|
|
fprintf(stderr, "Too short (%u bytes) mgmt pairable event\n",
|
|
|
|
len);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-10-27 16:47:31 +08:00
|
|
|
printf("hci%u pairable %s\n", index, ev->val ? "on" : "off");
|
2011-10-26 05:33:11 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-25 23:07:08 +08:00
|
|
|
static int mgmt_handle_event(int mgmt_sk, uint16_t ev, uint16_t index,
|
|
|
|
void *data, uint16_t len)
|
|
|
|
{
|
2011-10-27 05:15:39 +08:00
|
|
|
if (monitor)
|
|
|
|
printf("event: %s\n", mgmt_evstr(ev));
|
|
|
|
|
2011-10-26 05:33:11 +08:00
|
|
|
switch (ev) {
|
|
|
|
case MGMT_EV_CMD_COMPLETE:
|
|
|
|
return mgmt_cmd_complete(mgmt_sk, index, data, len);
|
|
|
|
case MGMT_EV_CMD_STATUS:
|
|
|
|
return mgmt_cmd_status(mgmt_sk, index, data, len);
|
|
|
|
case MGMT_EV_CONTROLLER_ERROR:
|
|
|
|
return mgmt_controller_error(index, data, len);
|
|
|
|
case MGMT_EV_INDEX_ADDED:
|
|
|
|
return mgmt_index_added(mgmt_sk, index);
|
|
|
|
case MGMT_EV_INDEX_REMOVED:
|
|
|
|
return mgmt_index_removed(mgmt_sk, index);
|
|
|
|
case MGMT_EV_POWERED:
|
|
|
|
return mgmt_powered(mgmt_sk, index, data, len);
|
|
|
|
case MGMT_EV_DISCOVERABLE:
|
|
|
|
return mgmt_discoverable(mgmt_sk, index, data, len);
|
|
|
|
case MGMT_EV_CONNECTABLE:
|
|
|
|
return mgmt_connectable(mgmt_sk, index, data, len);
|
|
|
|
case MGMT_EV_PAIRABLE:
|
|
|
|
return mgmt_pairable(mgmt_sk, index, data, len);
|
|
|
|
default:
|
2011-10-27 04:50:47 +08:00
|
|
|
if (monitor)
|
2011-10-27 05:15:39 +08:00
|
|
|
printf("Unhandled event 0x%04x (%s)\n", ev, mgmt_evstr(ev));
|
2011-10-26 05:33:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2011-10-25 23:07:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mgmt_process_data(int mgmt_sk)
|
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
struct mgmt_hdr *hdr = (void *) buf;
|
|
|
|
uint16_t len, ev, index;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
ret = read(mgmt_sk, buf, sizeof(buf));
|
|
|
|
if (ret < 0) {
|
|
|
|
fprintf(stderr, "read: %s\n", strerror(errno));
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < MGMT_HDR_SIZE) {
|
|
|
|
fprintf(stderr, "Too small mgmt packet (%zd bytes)\n", ret);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev = bt_get_le16(&hdr->opcode);
|
|
|
|
index = bt_get_le16(&hdr->index);
|
|
|
|
len = bt_get_le16(&hdr->len);
|
|
|
|
|
2011-10-27 04:50:47 +08:00
|
|
|
if (monitor)
|
|
|
|
printf("event 0x%04x len 0x%04x index 0x%04x\n", ev, len, index);
|
2011-10-25 23:07:08 +08:00
|
|
|
|
|
|
|
if (ret != MGMT_HDR_SIZE + len) {
|
|
|
|
fprintf(stderr, "Packet length mismatch. ret %zd len %u",
|
|
|
|
ret, len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mgmt_handle_event(mgmt_sk, ev, index, buf + MGMT_HDR_SIZE, len);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-28 02:56:42 +08:00
|
|
|
static void cmd_monitor(int mgmt_sk, uint16_t index, int argc, char **argv)
|
2011-10-26 18:01:47 +08:00
|
|
|
{
|
|
|
|
printf("Monitoring mgmt events...\n");
|
2011-10-27 04:50:47 +08:00
|
|
|
monitor = true;
|
2011-10-26 18:01:47 +08:00
|
|
|
}
|
|
|
|
|
2011-10-26 23:41:41 +08:00
|
|
|
static void info_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
|
|
|
|
void *rsp, uint16_t len, void *user_data)
|
|
|
|
{
|
|
|
|
struct mgmt_rp_read_info *rp = rsp;
|
|
|
|
char addr[18];
|
|
|
|
|
|
|
|
if (status != 0) {
|
2011-10-27 22:30:16 +08:00
|
|
|
fprintf(stderr, "Reading hci%u info failed with status %u\n",
|
2011-10-26 23:41:41 +08:00
|
|
|
id, status);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2011-10-27 22:27:34 +08:00
|
|
|
if (len < sizeof(*rp)) {
|
|
|
|
fprintf(stderr, "Too small info reply (%u bytes)\n", len);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2011-10-26 23:41:41 +08:00
|
|
|
ba2str(&rp->bdaddr, addr);
|
|
|
|
printf("hci%u:\ttype %u addr %s\n", id, rp->type, addr);
|
|
|
|
printf("\tclass 0x%02x%02x%02x\n",
|
|
|
|
rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
|
|
|
|
printf("\tmanufacturer %d HCI ver %d:%d\n",
|
|
|
|
bt_get_le16(&rp->manufacturer),
|
|
|
|
rp->hci_ver, bt_get_le16(&rp->hci_rev));
|
|
|
|
printf("\tpowered %u discoverable %u pairable %u sec_mode %u\n",
|
|
|
|
rp->powered, rp->discoverable,
|
|
|
|
rp->pairable, rp->sec_mode);
|
2011-10-27 23:05:56 +08:00
|
|
|
printf("\tname %s\n\n", (char *) rp->name);
|
|
|
|
|
|
|
|
if (pending == NULL)
|
|
|
|
exit(EXIT_SUCCESS);
|
2011-10-26 23:41:41 +08:00
|
|
|
}
|
|
|
|
|
2011-10-27 22:21:30 +08:00
|
|
|
static void index_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
|
|
|
|
void *rsp, uint16_t len, void *user_data)
|
|
|
|
{
|
|
|
|
struct mgmt_rp_read_index_list *rp = rsp;
|
|
|
|
uint16_t count;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (status != 0) {
|
|
|
|
fprintf(stderr, "Reading index list failed with status %u\n",
|
|
|
|
status);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len < sizeof(*rp)) {
|
|
|
|
fprintf(stderr, "Too small index list reply (%u bytes)\n",
|
|
|
|
len);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
count = bt_get_le16(&rp->num_controllers);
|
|
|
|
|
|
|
|
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Index count (%u) doesn't match reply length (%u)\n",
|
|
|
|
count, len);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2011-10-27 23:06:26 +08:00
|
|
|
if (monitor)
|
|
|
|
printf("Index list with %u item%s\n",
|
|
|
|
count, count > 1 ? "s" : "");
|
|
|
|
|
2011-10-28 02:55:57 +08:00
|
|
|
if (count == 0)
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
|
2011-10-27 23:06:26 +08:00
|
|
|
if (monitor && count > 0)
|
|
|
|
printf("\t");
|
|
|
|
|
2011-10-27 22:21:30 +08:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
uint16_t index;
|
|
|
|
|
|
|
|
index = bt_get_le16(&rp->index[i]);
|
|
|
|
|
2011-10-27 23:06:26 +08:00
|
|
|
if (monitor)
|
|
|
|
printf("hci%u ", index);
|
|
|
|
|
2011-10-27 22:21:30 +08:00
|
|
|
if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INFO, index, NULL,
|
|
|
|
0, info_rsp, NULL) < 0) {
|
2011-10-27 22:30:16 +08:00
|
|
|
fprintf(stderr, "Unable to send read_info cmd\n");
|
2011-10-27 22:21:30 +08:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
2011-10-27 23:06:26 +08:00
|
|
|
|
|
|
|
if (monitor && count > 0)
|
|
|
|
printf("\n");
|
2011-10-27 22:21:30 +08:00
|
|
|
}
|
|
|
|
|
2011-10-28 02:56:42 +08:00
|
|
|
static void cmd_info(int mgmt_sk, uint16_t index, int argc, char **argv)
|
2011-10-26 23:41:41 +08:00
|
|
|
{
|
2011-10-28 02:56:42 +08:00
|
|
|
if (index == MGMT_INDEX_NONE) {
|
2011-10-27 22:21:30 +08:00
|
|
|
if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INDEX_LIST,
|
|
|
|
MGMT_INDEX_NONE, NULL, 0,
|
|
|
|
index_rsp, NULL) < 0) {
|
2011-10-27 22:30:16 +08:00
|
|
|
fprintf(stderr, "Unable to send index_list cmd\n");
|
2011-10-27 22:21:30 +08:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2011-10-26 23:41:41 +08:00
|
|
|
}
|
|
|
|
|
2011-10-28 02:56:42 +08:00
|
|
|
if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INFO, index, NULL,
|
2011-10-26 23:41:41 +08:00
|
|
|
0, info_rsp, NULL) < 0) {
|
2011-10-27 22:30:16 +08:00
|
|
|
fprintf(stderr, "Unable to send read_info cmd\n");
|
2011-10-26 23:41:41 +08:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-27 16:43:06 +08:00
|
|
|
static void power_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
|
|
|
|
void *rsp, uint16_t len, void *user_data)
|
|
|
|
{
|
|
|
|
struct mgmt_mode *rp = rsp;
|
|
|
|
|
|
|
|
if (status != 0) {
|
2011-10-27 22:30:16 +08:00
|
|
|
fprintf(stderr, "Changing powered state for hci%u "
|
2011-10-27 16:43:06 +08:00
|
|
|
"failed with status %u\n", id, status);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2011-10-27 22:27:34 +08:00
|
|
|
if (len < sizeof(*rp)) {
|
|
|
|
fprintf(stderr, "Too small set_powered response (%u bytes)\n",
|
|
|
|
len);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2011-10-27 16:43:06 +08:00
|
|
|
printf("hci%u powered %s\n", id, rp->val ? "on" : "off");
|
|
|
|
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-28 02:56:42 +08:00
|
|
|
static void cmd_power(int mgmt_sk, uint16_t index, int argc, char **argv)
|
2011-10-27 16:43:06 +08:00
|
|
|
{
|
|
|
|
uint8_t power;
|
|
|
|
|
|
|
|
if (argc < 2) {
|
|
|
|
printf("Specify \"on\" or \"off\"\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(argv[1], "on") == 0)
|
|
|
|
power = 1;
|
|
|
|
else if (strcasecmp(argv[1], "off") == 0)
|
|
|
|
power = 0;
|
|
|
|
else
|
|
|
|
power = atoi(argv[1]);
|
|
|
|
|
2011-10-28 02:56:42 +08:00
|
|
|
if (index == MGMT_INDEX_NONE)
|
|
|
|
index = 0;
|
|
|
|
|
|
|
|
if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_POWERED, index, &power,
|
2011-10-27 16:43:06 +08:00
|
|
|
sizeof(power), power_rsp, NULL) < 0) {
|
2011-10-27 22:30:16 +08:00
|
|
|
fprintf(stderr, "Unable to send set_powered cmd\n");
|
2011-10-27 16:43:06 +08:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-28 03:06:04 +08:00
|
|
|
static void discov_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
|
|
|
|
void *rsp, uint16_t len, void *user_data)
|
|
|
|
{
|
|
|
|
struct mgmt_mode *rp = rsp;
|
|
|
|
|
|
|
|
if (status != 0) {
|
|
|
|
fprintf(stderr, "Changing discov state for hci%u "
|
|
|
|
"failed with status %u\n", id, status);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len < sizeof(*rp)) {
|
|
|
|
fprintf(stderr, "Too small set_discov response (%u bytes)\n",
|
|
|
|
len);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("hci%u discov %s\n", id, rp->val ? "on" : "off");
|
|
|
|
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_discov(int mgmt_sk, uint16_t index, int argc, char **argv)
|
|
|
|
{
|
|
|
|
uint8_t discov;
|
|
|
|
|
|
|
|
if (argc < 2) {
|
|
|
|
printf("Specify \"on\" or \"off\"\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(argv[1], "on") == 0)
|
|
|
|
discov = 1;
|
|
|
|
else if (strcasecmp(argv[1], "off") == 0)
|
|
|
|
discov = 0;
|
|
|
|
else
|
|
|
|
discov = atoi(argv[1]);
|
|
|
|
|
|
|
|
if (index == MGMT_INDEX_NONE)
|
|
|
|
index = 0;
|
|
|
|
|
|
|
|
if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_DISCOVERABLE, index, &discov,
|
|
|
|
sizeof(discov), discov_rsp, NULL) < 0) {
|
|
|
|
fprintf(stderr, "Unable to send set_discov cmd\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
2011-10-26 18:01:47 +08:00
|
|
|
static struct {
|
|
|
|
char *cmd;
|
2011-10-28 02:56:42 +08:00
|
|
|
void (*func)(int mgmt_sk, uint16_t index, int argc, char **argv);
|
2011-10-26 18:01:47 +08:00
|
|
|
char *doc;
|
|
|
|
} command[] = {
|
|
|
|
{ "monitor", cmd_monitor, "Monitor events" },
|
2011-10-26 23:41:41 +08:00
|
|
|
{ "info", cmd_info, "Show controller info" },
|
2011-10-27 16:43:06 +08:00
|
|
|
{ "power", cmd_power, "Toggle powered state" },
|
2011-10-28 03:06:04 +08:00
|
|
|
{ "discov", cmd_discov, "Toggle discoverable state" },
|
2011-10-26 18:01:47 +08:00
|
|
|
{ NULL, NULL, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static void usage(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printf("btmgmt ver %s\n", VERSION);
|
|
|
|
printf("Usage:\n"
|
|
|
|
"\tbtmgmt [options] <command> [command parameters]\n");
|
|
|
|
|
|
|
|
printf("Options:\n"
|
2011-10-28 02:56:42 +08:00
|
|
|
"\t--index <id>\tSpecify adapter index\n"
|
2011-10-27 22:30:29 +08:00
|
|
|
"\t--verbose\tEnable extra logging\n"
|
2011-10-26 18:01:47 +08:00
|
|
|
"\t--help\tDisplay help\n");
|
|
|
|
|
|
|
|
printf("Commands:\n");
|
|
|
|
for (i = 0; command[i].cmd; i++)
|
|
|
|
printf("\t%-4s\t%s\n", command[i].cmd, command[i].doc);
|
|
|
|
|
|
|
|
printf("\n"
|
|
|
|
"For more information on the usage of each command use:\n"
|
|
|
|
"\tbtmgmt <command> --help\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct option main_options[] = {
|
2011-10-28 02:56:42 +08:00
|
|
|
{ "index", 1, 0, 'i' },
|
2011-10-27 22:30:29 +08:00
|
|
|
{ "verbose", 0, 0, 'v' },
|
2011-10-26 18:01:47 +08:00
|
|
|
{ "help", 0, 0, 'h' },
|
|
|
|
{ 0, 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
2011-10-25 21:36:00 +08:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2011-10-26 18:01:47 +08:00
|
|
|
int opt, i, mgmt_sk;
|
2011-10-28 02:56:42 +08:00
|
|
|
uint16_t index = MGMT_INDEX_NONE;
|
2011-10-25 23:07:08 +08:00
|
|
|
struct pollfd pollfd;
|
|
|
|
|
2011-10-28 02:56:42 +08:00
|
|
|
while ((opt = getopt_long(argc, argv, "+hvi:",
|
|
|
|
main_options, NULL)) != -1) {
|
2011-10-26 18:01:47 +08:00
|
|
|
switch (opt) {
|
2011-10-28 02:56:42 +08:00
|
|
|
case 'i':
|
|
|
|
if (strlen(optarg) > 3 &&
|
|
|
|
strncasecmp(optarg, "hci", 3) == 0)
|
|
|
|
index = atoi(&optarg[4]);
|
|
|
|
else
|
|
|
|
index = atoi(optarg);
|
|
|
|
break;
|
2011-10-27 22:30:29 +08:00
|
|
|
case 'v':
|
|
|
|
monitor = true;
|
|
|
|
break;
|
2011-10-26 18:01:47 +08:00
|
|
|
case 'h':
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
optind = 0;
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
usage();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-25 23:07:08 +08:00
|
|
|
mgmt_sk = mgmt_open();
|
|
|
|
if (mgmt_sk < 0) {
|
|
|
|
fprintf(stderr, "Unable to open mgmt socket\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-10-26 18:01:47 +08:00
|
|
|
for (i = 0; command[i].cmd; i++) {
|
|
|
|
if (strcmp(command[i].cmd, argv[0]) != 0)
|
|
|
|
continue;
|
|
|
|
|
2011-10-28 02:56:42 +08:00
|
|
|
command[i].func(mgmt_sk, index, argc, argv);
|
2011-10-26 18:01:47 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-10-25 23:07:08 +08:00
|
|
|
pollfd.fd = mgmt_sk;
|
|
|
|
pollfd.events = POLLIN;
|
|
|
|
pollfd.revents = 0;
|
|
|
|
|
|
|
|
while (poll(&pollfd, 1, -1) >= 0) {
|
|
|
|
if (pollfd.revents & (POLLHUP | POLLERR | POLLNVAL))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (pollfd.revents & POLLIN)
|
|
|
|
mgmt_process_data(mgmt_sk);
|
|
|
|
|
|
|
|
pollfd.revents = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(mgmt_sk);
|
|
|
|
|
2011-10-25 21:36:00 +08:00
|
|
|
return 0;
|
|
|
|
}
|