Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bluetooth IP Address Advertisement #325

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,8 @@ if ( NOT APPLE )
target_include_directories( airplay PRIVATE ${DNSSD_INCLUDE_DIR} )
endif()
endif()

pkg_check_modules (BLUETOOTH REQUIRED bluez)
include_directories(${BLUETOOTH_INCLUDE_DIRS})
# link_directories(${BLUEZ_LIBRARY_DIRS})
target_link_libraries( airplay PUBLIC ${BLUETOOTH_LIBRARIES} )
128 changes: 128 additions & 0 deletions lib/ble.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <unistd.h> // For close()
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h> // For struct ifreq
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

#include "ble.h"

#define DEVICE_NAME "hci0"

static int open_device(char *device_name) {
int dev_id = hci_devid(device_name);
if (dev_id < 0)
dev_id = hci_get_route(NULL); // Find the device ID of the first available Bluetooth interface

int dd = hci_open_dev(dev_id);
if (dd < 0) {
return -1; // Failed to obtain device descriptor
}

return dd;
}

static int send_command(uint8_t ogf, uint16_t ocf, int len, unsigned char *data) {
struct hci_filter flt;

int dd = open_device(DEVICE_NAME);

hci_filter_clear(&flt);
hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
hci_filter_all_events(&flt);
if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
hci_close_dev(dd);
printf("HCI filter set up failed");
return -1;
}

if (hci_send_cmd(dd, ogf, ocf, len, data) < 0) {
hci_close_dev(dd);
printf("Failed to send BLE command to controller (usually requires elevated privileges)\n");
return -1;
}

hci_close_dev(dd);
return 0;
}

static char* get_ip_addr(const char *interface) {
struct ifreq ifr;
static char ip_address[INET_ADDRSTRLEN];

int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) {
return NULL;
}

strncpy(ifr.ifr_name, interface, IFNAMSIZ - 1);
ifr.ifr_name[IFNAMSIZ - 1] = '\0';

if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) {
perror("ioctl");
close(fd);
return NULL;
}

struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr;
inet_ntop(AF_INET, &addr->sin_addr, ip_address, sizeof(ip_address));
close(fd);
return ip_address;
}

static int set_advertisement_data(const char *interface) {
char* ip_address = get_ip_addr(interface);
int byte1, byte2, byte3, byte4;

sscanf(ip_address, "%d.%d.%d.%d", &byte1, &byte2, &byte3, &byte4);

uint8_t advertisement[] = {
0x0f, 0x02, 0x01, 0x1a, 0x0b, 0xff, 0x4c, 0x00,
0x09, 0x06, 0x03,
0x30,
(uint8_t)byte1, (uint8_t)byte2, (uint8_t)byte3, (uint8_t)byte4
};

return send_command(0x08, 0x0008, 16, advertisement);
}

int configure_ble(const char *interface, uint8_t *ble_address) {
uint8_t configure_cmd[] = {
0xa0, 0x00,
0xa0, 0x00,
0x03,
0x01,
0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07,
0x00
};

if (send_command(0x08, 0x0006, 15, configure_cmd) != 0) return -1;

if (send_command(0x08, 0x0005, 6, ble_address) != 0) return -1;

return set_advertisement_data(interface);
}

int ble_advertise(bool on) {
uint8_t cmd[] = {
on ? 1 : 0
};
return send_command(0x08, 0x000a, 1, cmd);
}

18 changes: 18 additions & 0 deletions lib/ble.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef BLE_H
#define BLE_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

int configure_ble(const char *interface, uint8_t *ble_address);

int ble_advertise(bool on);

#ifdef __cplusplus
}
#endif

#endif
7 changes: 7 additions & 0 deletions lib/dnssd.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,13 @@ dnssd_get_airplay_txt(dnssd_t *dnssd, int *length)
return dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record);
}

const char *
dnssd_get_raop_txt(dnssd_t *dnssd, int *length)
{
*length = dnssd->TXTRecordGetLength(&dnssd->raop_record);
return dnssd->TXTRecordGetBytesPtr(&dnssd->raop_record);
}

const char *
dnssd_get_name(dnssd_t *dnssd, int *length)
{
Expand Down
1 change: 1 addition & 0 deletions lib/dnssd.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ DNSSD_API void dnssd_unregister_raop(dnssd_t *dnssd);
DNSSD_API void dnssd_unregister_airplay(dnssd_t *dnssd);

DNSSD_API const char *dnssd_get_airplay_txt(dnssd_t *dnssd, int *length);
DNSSD_API const char *dnssd_get_raop_txt(dnssd_t *dnssd, int *length);
DNSSD_API const char *dnssd_get_name(dnssd_t *dnssd, int *length);
DNSSD_API const char *dnssd_get_hw_addr(dnssd_t *dnssd, int *length);
DNSSD_API void dnssd_set_airplay_features(dnssd_t *dnssd, int bit, int val);
Expand Down
8 changes: 5 additions & 3 deletions lib/raop.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
}

/* this rejects unsupported messages from _airplay._tcp for video streaming protocol*/
if (!cseq) {
if (!cseq && strstr(url, "/info") == NULL) {
return;
}

Expand Down Expand Up @@ -255,7 +255,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {

logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url);
raop_handler_t handler = NULL;
if (!strcmp(method, "GET") && !strcmp(url, "/info")) {
if (!strcmp(method, "GET") && strstr(url, "/info") != NULL) {
handler = &raop_handler_info;
} else if (!strcmp(method, "POST") && !strcmp(url, "/pair-pin-start")) {
handler = &raop_handler_pairpinstart;
Expand Down Expand Up @@ -292,7 +292,9 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
}
finish:;
http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION);
http_response_add_header(*response, "CSeq", cseq);
if (cseq) {
http_response_add_header(*response, "CSeq", cseq);
}
http_response_finish(*response, response_data, response_datalen);

int len;
Expand Down
5 changes: 5 additions & 0 deletions lib/raop_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ raop_handler_info(raop_conn_t *conn,
plist_t txt_airplay_node = plist_new_data(airplay_txt, airplay_txt_len);
plist_dict_set_item(res_node, "txtAirPlay", txt_airplay_node);

int raop_txt_len = 0;
const char *raop_txt = dnssd_get_raop_txt(conn->raop->dnssd, &raop_txt_len);
plist_t txt_raop_node = plist_new_data(raop_txt, raop_txt_len);
plist_dict_set_item(res_node, "txtRAOP", txt_raop_node);

uint64_t features = dnssd_get_airplay_features(conn->raop->dnssd);
plist_t features_node = plist_new_uint(features);
plist_dict_set_item(res_node, "features", features_node);
Expand Down
18 changes: 18 additions & 0 deletions uxplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "lib/stream.h"
#include "lib/logger.h"
#include "lib/dnssd.h"
#include "lib/ble.h"
#include "renderers/video_renderer.h"
#include "renderers/audio_renderer.h"

Expand Down Expand Up @@ -140,6 +141,8 @@ static std::vector <std::string> registered_keys;
static double db_low = -30.0;
static double db_high = 0.0;
static bool taper_volume = false;
static bool ble_advertisement = false;
static uint8_t ble_address[6] = {0};

/* logging */

Expand Down Expand Up @@ -633,6 +636,7 @@ static void print_info (char *name) {
printf(" =1,2,..; fn=\"audiodump\"; change with \"-admp [n] filename\".\n");
printf(" x increases when audio format changes. If n is given, <= n\n");
printf(" audio packets are dumped. \"aud\"= unknown format.\n");
printf("-btip Advertise IP Address of this receiver over Bluetooth for discovery\n");
printf("-d Enable debug logging\n");
printf("-v Displays version information\n");
printf("-h Displays this help\n");
Expand Down Expand Up @@ -1125,6 +1129,11 @@ static void parse_arguments (int argc, char *argv[]) {
db_low = db1;
db_high = db2;
printf("db range %f:%f\n", db_low, db_high);
} else if (arg == "-btip") {
ble_advertisement = true;
ble_address[0] = rand() % 255; ble_address[1] = rand() % 255;
ble_address[2] = rand() % 255; ble_address[3] = rand() % 255;
ble_address[4] = rand() % 255; ble_address[5] = rand() % 255;
} else {
fprintf(stderr, "unknown option %s, stopping (for help use option \"-h\")\n",argv[i]);
exit(1);
Expand Down Expand Up @@ -2154,6 +2163,12 @@ int main (int argc, char *argv[]) {
}

restart:
if (ble_advertisement) {
if (configure_ble("eth0", ble_address) != 0 || ble_advertise(true) != 0) {
ble_advertisement = false;
LOGI("Failed to initialise BLE interface: Disabling until application restarts");
}
}
if (start_dnssd(server_hw_addr, server_name)) {
goto cleanup;
}
Expand Down Expand Up @@ -2201,6 +2216,9 @@ int main (int argc, char *argv[]) {
stop_dnssd();
}
cleanup:
if (ble_advertisement) {
ble_advertise(false);
}
if (use_audio) {
audio_renderer_destroy();
}
Expand Down