mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-12-13 05:54:34 +08:00
278 lines
6.2 KiB
C
278 lines
6.2 KiB
C
/*
|
|
* 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
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <poll.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/mgmt.h>
|
|
|
|
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;
|
|
}
|
|
|
|
static int mgmt_cmd_complete(int mgmt_sk, uint16_t index,
|
|
struct mgmt_ev_cmd_complete *ev, uint16_t len)
|
|
{
|
|
uint16_t opcode;
|
|
|
|
if (len < sizeof(*ev)) {
|
|
fprintf(stderr, "Too short (%u bytes) cmd complete event\n",
|
|
len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
opcode = bt_get_le16(&ev->opcode);
|
|
|
|
len -= sizeof(*ev);
|
|
|
|
printf("cmd complete, opcode 0x%04x len %u\n", opcode, len);
|
|
|
|
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);
|
|
|
|
printf("cmd status, opcode 0x%04x status 0x%02x\n", opcode, ev->status);
|
|
|
|
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;
|
|
}
|
|
|
|
printf("controller error 0x%02x\n", ev->error_code);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_index_added(int mgmt_sk, uint16_t index)
|
|
{
|
|
printf("index %u added\n", index);
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_index_removed(int mgmt_sk, uint16_t index)
|
|
{
|
|
printf("index %u removed\n", index);
|
|
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;
|
|
}
|
|
|
|
printf("index %u powered %s\n", index, ev->val ? "on" : "off");
|
|
|
|
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;
|
|
}
|
|
|
|
printf("index %u discoverable %s\n", index, ev->val ? "on" : "off");
|
|
|
|
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;
|
|
}
|
|
|
|
printf("index %u connectable %s\n", index, ev->val ? "on" : "off");
|
|
|
|
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;
|
|
}
|
|
|
|
printf("index %u pairable %s\n", index, ev->val ? "on" : "off");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mgmt_handle_event(int mgmt_sk, uint16_t ev, uint16_t index,
|
|
void *data, uint16_t len)
|
|
{
|
|
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:
|
|
printf("Unhandled event 0x%04x\n", ev);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
printf("event 0x%04x len 0x%04x index 0x%04x\n", ev, len, index);
|
|
|
|
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;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int mgmt_sk;
|
|
struct pollfd pollfd;
|
|
|
|
mgmt_sk = mgmt_open();
|
|
if (mgmt_sk < 0) {
|
|
fprintf(stderr, "Unable to open mgmt socket\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("mgmt socket successfully opened\n");
|
|
|
|
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);
|
|
|
|
return 0;
|
|
}
|