bluez/mgmt/main.c
2011-10-25 23:33:11 +02:00

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;
}