diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..c5fb3d4
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,27 @@
+project(based-connect)
+
+cmake_minimum_required(VERSION 3.2)
+if(COMMAND cmake_policy)
+ cmake_policy(SET CMP0003 NEW)
+endif(COMMAND cmake_policy)
+
+find_path(GETOPT_HEADER NAMES getopt.h REQUIRED)
+find_library(GETOPT_LIBRARIES NAMES getopt REQUIRED)
+message(WARNING "Found lib ${GETOPT_LIBRARIES}")
+
+include_directories(${GETOPT_HEADER})
+
+add_executable(based-connect
+ based.c
+ based.h
+ bluetooth.c
+ bluetooth.h
+ main.c
+ util.c
+ util.h
+)
+set_property(TARGET based-connect PROPERTY C_STANDARD 99)
+target_link_libraries(based-connect ${GETOPT_LIBRARIES})
+if (WIN32)
+ target_link_libraries(based-connect wsock32 ws2_32)
+endif (WIN32)
diff --git a/README.txt b/README.txt
index 73e89e7..dd4c2e3 100644
--- a/README.txt
+++ b/README.txt
@@ -55,9 +55,15 @@ Options:
Print the devices currently connected to the device.
!: indicates the current device
*: indicates other connected devices
+ S: indicates a device that music is shared to
- --connect-device=
- Attempt to connect to the device at address.
+ --connect-device=
+ Attempt to connect to the device at address.
+
+ --connect-music-share=,
+ Attempt to connect to the puppet device at address
+ (secondary headphones) and share music from source
+ address (computer).
--disconnect-device=
Disconnect the device at address.
@@ -71,8 +77,21 @@ Options:
Building
--------
-Simply run `make -j` to build the program. The executable produced will be
-called `based-connect`.
+Create a build directory (e.g., `mkdir build`) and enter it. Run
+```sh
+cmake ..
+```
+to create a Makefile, and then run
+```sh
+cmake --build .
+```
+to build the project.
+
+On Windows, you'll need `vcpkg` with the `getopt` package installed. Run
+```powershell
+cmake -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake" ..
+```
+to create the build files.
Installing
----------
@@ -84,6 +103,17 @@ Alternatively, if you run Arch Linux, you can download it from the AUR here:
https://aur.archlinux.org/packages/based-connect-git/
+Running
+-------
+
+On Windows, use
+```powershell
+Get-PnpDevice -class Bluetooth | Select-Object -Property FriendlyName,
+DeviceID
+```
+to enumerate the Bluetooth device ID. The device ID needs to be written as
+AA:BB:CC:DD:EE:FF.
+
Dependencies
------------
diff --git a/based.c b/based.c
index 40bd1bf..104ff89 100644
--- a/based.c
+++ b/based.c
@@ -2,14 +2,28 @@
#include
#include
#include
+#ifdef _WIN32
+#include
+#define read(sock, buf, size) recv(sock, (void *) buf, (int) size, 0)
+#define write(sock, buf, size) send(sock, (char *) buf, (int) size, 0)
+#define bswap_16(x) _byteswap_ushort(x)
+#else
#include
+#endif
#include "based.h"
#include "bluetooth.h"
+#include "malloc.h"
#define ANY 0x00
#define CN_BASE_PACK_LEN 4
+#define FUNCTION_BLOCK_DEVICE_MANAGEMENT 0x04
+
+#define DEVICE_MANAGEMENT_CONNECT 0x01
+
+#define OPERATOR_START 0x05
+
int has_noise_cancelling(unsigned int device_id) {
switch (device_id) {
case 0x4014:
@@ -22,10 +36,13 @@ int has_noise_cancelling(unsigned int device_id) {
}
static int masked_memcmp(const void *ptr1, const void *ptr2, size_t num, const void *mask) {
+ const uint8_t *p1 = (const uint8_t *) ptr1;
+ const uint8_t *p2 = (const uint8_t *) ptr2;
+ const uint8_t *m = (const uint8_t *) mask;
while (num-- > 0) {
- uint8_t mask_byte = *(uint8_t *) mask++;
- uint8_t byte1 = *(uint8_t *) ptr1++ & mask_byte;
- uint8_t byte2 = *(uint8_t *) ptr2++ & mask_byte;
+ uint8_t mask_byte = *m++;
+ uint8_t byte1 = *p1++ & mask_byte;
+ uint8_t byte2 = *p2++ & mask_byte;
if (byte1 != byte2) {
return byte1 - byte2;
@@ -34,43 +51,43 @@ static int masked_memcmp(const void *ptr1, const void *ptr2, size_t num, const v
return 0;
}
-static int read_check(int sock, void *recieve, size_t recieve_n, const void *ack,
+static int read_check(socktype_t sock, void *receive, size_t receive_n, const void *ack,
const void *mask) {
- int status = read(sock, recieve, recieve_n);
- if (status != recieve_n) {
+ int status = read(sock, receive, receive_n);
+ if (status != receive_n) {
return status ? status : 1;
}
return abs(mask
- ? masked_memcmp(ack, recieve, recieve_n, mask)
- : memcmp(ack, recieve, recieve_n));
+ ? masked_memcmp(ack, receive, receive_n, mask)
+ : memcmp(ack, receive, receive_n));
}
-static int write_check(int sock, const void *send, size_t send_n,
+static int write_check(socktype_t sock, const void *send_b, size_t send_n,
const void *ack, size_t ack_n) {
- uint8_t buffer[ack_n];
+ uint8_t *buffer = alloca(ack_n);
- int status = write(sock, send, send_n);
+ int status = write(sock, send_b, send_n);
if (status != send_n) {
return status ? status : 1;
}
- return read_check(sock, buffer, sizeof(buffer), ack, NULL);
+ return read_check(sock, buffer, ack_n, ack, NULL);
}
-int send_packet(int sock, const void *send, size_t send_n, uint8_t recieved[MAX_BT_PACK_LEN]) {
- int status = write(sock, send, send_n);
+int send_packet(socktype_t sock, const void *send_b, size_t send_n, uint8_t received[MAX_BT_PACK_LEN]) {
+ int status = write(sock, send_b, send_n);
if (status != send_n) {
return status ? status : 1;
}
- return read(sock, recieved, MAX_BT_PACK_LEN);
+ return read(sock, received, MAX_BT_PACK_LEN);
}
-int init_connection(int sock) {
- static const uint8_t send[] = { 0x00, 0x01, 0x01, 0x00 };
+int init_connection(socktype_t sock) {
+ static const uint8_t send_buf[] = { 0x00, 0x01, 0x01, 0x00 };
static const uint8_t ack[] = { 0x00, 0x01, 0x03, 0x05 };
- int status = write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ int status = write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
if (status) {
return status;
}
@@ -86,11 +103,11 @@ int init_connection(int sock) {
return 0;
}
-int get_device_id(int sock, unsigned int *device_id, unsigned int *index) {
- static const uint8_t send[] = { 0x00, 0x03, 0x01, 0x00 };
+int get_device_id(socktype_t sock, unsigned int *device_id, unsigned int *index) {
+ static const uint8_t send_buf[] = { 0x00, 0x03, 0x01, 0x00 };
static const uint8_t ack[] = { 0x00, 0x03, 0x03, 0x03 };
- int status = write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ int status = write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
if (status) {
return status;
}
@@ -113,7 +130,7 @@ int get_device_id(int sock, unsigned int *device_id, unsigned int *index) {
return 0;
}
-static int get_name(int sock, char name[MAX_NAME_LEN + 1]) {
+static int get_name(socktype_t sock, char name[MAX_NAME_LEN + 1]) {
static const uint8_t ack[] = { 0x01, 0x02, 0x03, ANY, 0x00 };
static const uint8_t mask[] = { 0xff, 0xff, 0xff, 0x00, 0xff };
uint8_t buffer[sizeof(ack)];
@@ -133,15 +150,15 @@ static int get_name(int sock, char name[MAX_NAME_LEN + 1]) {
return 0;
}
-int set_name(int sock, const char *name) {
- static uint8_t send[CN_BASE_PACK_LEN + MAX_NAME_LEN] = { 0x01, 0x02, 0x02, ANY };
+int set_name(socktype_t sock, const char *name) {
+ static uint8_t send_buf[CN_BASE_PACK_LEN + MAX_NAME_LEN] = { 0x01, 0x02, 0x02, ANY };
size_t length = strlen(name);
- send[3] = length;
- strncpy((char *) &send[CN_BASE_PACK_LEN], name, MAX_NAME_LEN);
+ send_buf[3] = (uint8_t) length;
+ strncpy_s((char *) &send_buf[CN_BASE_PACK_LEN], sizeof(send_buf) - CN_BASE_PACK_LEN, name, MAX_NAME_LEN);
size_t send_size = CN_BASE_PACK_LEN + length;
- int status = write(sock, send, send_size);
+ int status = write(sock, send_buf, send_size);
if (status != send_size) {
return status ? status : 1;
}
@@ -155,7 +172,7 @@ int set_name(int sock, const char *name) {
return abs(strcmp(name, got_name));
}
-static int get_prompt_language(int sock, enum PromptLanguage *language) {
+static int get_prompt_language(socktype_t sock, enum PromptLanguage *language) {
// TODO: ensure that this value is correct
// TODO: figure out what bytes 6 and 7 are for
static const uint8_t ack[] = { 0x01, 0x03, 0x03, 0x05, ANY, 0x00, ANY, ANY, 0xde };
@@ -171,12 +188,12 @@ static int get_prompt_language(int sock, enum PromptLanguage *language) {
return 0;
}
-int set_prompt_language(int sock, enum PromptLanguage language) {
- static uint8_t send[] = { 0x01, 0x03, 0x02, 0x01, ANY };
- send[4] = language;
+int set_prompt_language(socktype_t sock, enum PromptLanguage language) {
+ static uint8_t send_buf[] = { 0x01, 0x03, 0x02, 0x01, ANY };
+ send_buf[4] = language;
- int status = write(sock, send, sizeof(send));
- if (status != sizeof(send)) {
+ int status = write(sock, send_buf, sizeof(send_buf));
+ if (status != sizeof(send_buf)) {
return status ? status : 1;
}
@@ -189,7 +206,7 @@ int set_prompt_language(int sock, enum PromptLanguage language) {
return abs(language - got_language);
}
-int set_voice_prompts(int sock, int on) {
+int set_voice_prompts(socktype_t sock, int on) {
char name[MAX_NAME_LEN + 1];
enum PromptLanguage pl;
enum AutoOff ao;
@@ -209,7 +226,7 @@ int set_voice_prompts(int sock, int on) {
return set_prompt_language(sock, pl);
}
-static int get_auto_off(int sock, enum AutoOff *minutes) {
+static int get_auto_off(socktype_t sock, enum AutoOff *minutes) {
static const uint8_t ack[] = { 0x01, 0x04, 0x03, 0x01, ANY };
static const uint8_t mask[] = { 0xff, 0xff, 0xff, 0xff, 0x00 };
uint8_t buffer[sizeof(ack)];
@@ -223,12 +240,12 @@ static int get_auto_off(int sock, enum AutoOff *minutes) {
return 0;
}
-int set_auto_off(int sock, enum AutoOff minutes) {
- static uint8_t send[] = { 0x01, 0x04, 0x02, 0x01, ANY };
- send[4] = minutes;
+int set_auto_off(socktype_t sock, enum AutoOff minutes) {
+ static uint8_t send_buf[] = { 0x01, 0x04, 0x02, 0x01, ANY };
+ send_buf[4] = minutes;
- int status = write(sock, send, sizeof(send));
- if (status != sizeof(send)) {
+ int status = write(sock, send_buf, sizeof(send_buf));
+ if (status != sizeof(send_buf)) {
return status ? status : 1;
}
@@ -241,7 +258,7 @@ int set_auto_off(int sock, enum AutoOff minutes) {
return abs(minutes - got_minutes);
}
-static int get_noise_cancelling(int sock, enum NoiseCancelling *level) {
+static int get_noise_cancelling(socktype_t sock, enum NoiseCancelling *level) {
static const uint8_t ack[] = { 0x01, 0x06, 0x03, 0x02, ANY, 0x0b };
static const uint8_t mask[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff };
uint8_t buffer[sizeof(ack)];
@@ -255,12 +272,12 @@ static int get_noise_cancelling(int sock, enum NoiseCancelling *level) {
return 0;
}
-int set_noise_cancelling(int sock, enum NoiseCancelling level) {
- static uint8_t send[] = { 0x01, 0x06, 0x02, 0x01, ANY };
- send[4] = level;
+int set_noise_cancelling(socktype_t sock, enum NoiseCancelling level) {
+ static uint8_t send_buf[] = { 0x01, 0x06, 0x02, 0x01, ANY };
+ send_buf[4] = level;
- int status = write(sock, send, sizeof(send));
- if (status != sizeof(send)) {
+ int status = write(sock, send_buf, sizeof(send_buf));
+ if (status != sizeof(send_buf)) {
return status ? status : 1;
}
@@ -273,7 +290,7 @@ int set_noise_cancelling(int sock, enum NoiseCancelling level) {
return abs(level - got_level);
}
-int get_device_status(int sock, char name[MAX_NAME_LEN + 1], enum PromptLanguage *language,
+int get_device_status(socktype_t sock, char name[MAX_NAME_LEN + 1], enum PromptLanguage *language,
enum AutoOff *minutes, enum NoiseCancelling *level) {
unsigned int device_id;
unsigned int index;
@@ -282,9 +299,9 @@ int get_device_status(int sock, char name[MAX_NAME_LEN + 1], enum PromptLanguage
return status;
}
- static const uint8_t send[] = { 0x01, 0x01, 0x05, 0x00 };
- status = write(sock, send, sizeof(send));
- if (status != sizeof(send)) {
+ static const uint8_t send_buf[] = { 0x01, 0x01, 0x05, 0x00 };
+ status = write(sock, send_buf, sizeof(send_buf));
+ if (status != sizeof(send_buf)) {
return status ? status : 1;
}
@@ -320,24 +337,31 @@ int get_device_status(int sock, char name[MAX_NAME_LEN + 1], enum PromptLanguage
*level = NC_DNE;
}
- static const uint8_t ack2[] = { 0x01, 0x01, 0x06, 0x00 };
- uint8_t buffer2[sizeof(ack2)];
- return read_check(sock, buffer2, sizeof(buffer2), ack2, NULL);
+ return status;
}
-int set_pairing(int sock, enum Pairing pairing) {
- static uint8_t send[] = { 0x04, 0x08, 0x05, 0x01, ANY };
+int set_pairing(socktype_t sock, enum Pairing pairing) {
+ static uint8_t send_buf[] = { 0x04, 0x08, 0x05, 0x01, ANY };
static uint8_t ack[] = { 0x04, 0x08, 0x06, 0x01, ANY };
- send[4] = pairing;
+ send_buf[4] = pairing;
ack[4] = pairing;
- return write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ return write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
}
-int get_firmware_version(int sock, char version[VER_STR_LEN]) {
- static const uint8_t send[] = { 0x00, 0x05, 0x01, 0x00 };
+int set_self_voice(socktype_t sock, enum SelfVoice selfvoice) {
+ static uint8_t send_buf[] = { 0x01, 0x0b, 0x02, 0x02, 0x01, ANY, 0x38 };
+ static uint8_t ack[] = { 0x01, 0x0b, 0x03, 0x03, 0x01, ANY, 0x0f};
+
+ send_buf[5] = selfvoice;
+ ack[5] = selfvoice;
+ return write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
+}
+
+int get_firmware_version(socktype_t sock, char version[VER_STR_LEN]) {
+ static const uint8_t send_buf[] = { 0x00, 0x05, 0x01, 0x00 };
static const uint8_t ack[] = { 0x00, 0x05, 0x03, 0x05 };
- int status = write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ int status = write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
if (status) {
return status;
}
@@ -351,11 +375,11 @@ int get_firmware_version(int sock, char version[VER_STR_LEN]) {
return 0;
}
-int get_serial_number(int sock, char serial[0x100]) {
- static const uint8_t send[] = { 0x00, 0x07, 0x01, 0x00 };
+int get_serial_number(socktype_t sock, char serial[0x100]) {
+ static const uint8_t send_buf[] = { 0x00, 0x07, 0x01, 0x00 };
static const uint8_t ack[] = { 0x00, 0x07, 0x03 };
- int status = write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ int status = write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
if (status) {
return status;
}
@@ -375,11 +399,11 @@ int get_serial_number(int sock, char serial[0x100]) {
return 0;
}
-int get_battery_level(int sock, unsigned int *level) {
- static const uint8_t send[] = { 0x02, 0x02, 0x01, 0x00 };
+int get_battery_level(socktype_t sock, unsigned int *level) {
+ static const uint8_t send_buf[] = { 0x02, 0x02, 0x01, 0x00 };
static const uint8_t ack[] = { 0x02, 0x02, 0x03, 0x01 };
- int status = write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ int status = write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
if (status) {
return status;
}
@@ -390,13 +414,13 @@ int get_battery_level(int sock, unsigned int *level) {
return 0;
}
-int get_device_info(int sock, bdaddr_t address, struct Device *device) {
- static uint8_t send[10] = { 0x04, 0x05, 0x01, BT_ADDR_LEN };
+int get_device_info(socktype_t sock, bdaddr_t address, struct Device *device) {
+ static uint8_t send_buf[10] = { 0x04, 0x05, 0x01, BT_ADDR_LEN };
static const uint8_t ack[] = { 0x04, 0x05, 0x03 };
- memcpy(&send[4], &address.b, BT_ADDR_LEN);
+ memcpy(&send_buf[4], &BDADDR_BYTES(address), BT_ADDR_LEN);
- int status = write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ int status = write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
if (status) {
return status;
}
@@ -407,13 +431,13 @@ int get_device_info(int sock, bdaddr_t address, struct Device *device) {
return status ? status : 1;
}
- status = read(sock, &device->address.b, BT_ADDR_LEN);
+ status = read(sock, &BDADDR_BYTES(device->address), BT_ADDR_LEN);
if (status != BT_ADDR_LEN) {
return status ? status : 1;
}
length -= BT_ADDR_LEN;
- status = memcmp(&address.b, &device->address.b, BT_ADDR_LEN);
+ status = memcmp(&BDADDR_BYTES(address), &BDADDR_BYTES(device->address), BT_ADDR_LEN);
if (status) {
return abs(status);
}
@@ -444,12 +468,12 @@ int get_device_info(int sock, bdaddr_t address, struct Device *device) {
return 0;
}
-int get_paired_devices(int sock, bdaddr_t addresses[MAX_NUM_DEVICES], size_t *num_devices,
+int get_paired_devices(socktype_t sock, bdaddr_t addresses[MAX_NUM_DEVICES], size_t *num_devices,
enum DevicesConnected *connected) {
- static const uint8_t send[] = { 0x04, 0x04, 0x01, 0x00 };
+ static const uint8_t send_buf[] = { 0x04, 0x04, 0x01, 0x00 };
static const uint8_t ack[] = { 0x04, 0x04, 0x03 };
- int status = write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ int status = write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
if (status) {
return status;
}
@@ -475,7 +499,7 @@ int get_paired_devices(int sock, bdaddr_t addresses[MAX_NUM_DEVICES], size_t *nu
size_t i;
for (i = 0; i < num_devices_byte; ++i) {
- status = read(sock, &addresses[i].b, BT_ADDR_LEN);
+ status = read(sock, &BDADDR_BYTES(addresses[i]), BT_ADDR_LEN);
if (status != BT_ADDR_LEN) {
return status ? status : 1;
}
@@ -484,26 +508,35 @@ int get_paired_devices(int sock, bdaddr_t addresses[MAX_NUM_DEVICES], size_t *nu
return 0;
}
-int connect_device(int sock, bdaddr_t address) {
- static uint8_t send[11] = { 0x04, 0x01, 0x05, BT_ADDR_LEN + 1, 0x00 };
+int connect_device(socktype_t sock, bdaddr_t address) {
+ static uint8_t send_buf[11] = { 0x04, 0x01, 0x05, BT_ADDR_LEN + 1, 0x00 };
+ static uint8_t ack[10] = { 0x04, 0x01, 0x07, BT_ADDR_LEN };
+ memcpy(&send_buf[5], &BDADDR_BYTES(address), BT_ADDR_LEN);
+ memcpy(&ack[4], &BDADDR_BYTES(address), BT_ADDR_LEN);
+ return write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
+}
+
+int connect_music_share(socktype_t sock, bdaddr_t puppet_address, bdaddr_t master_address) {
+ static uint8_t send_buf[5 + 2 * BT_ADDR_LEN] = { FUNCTION_BLOCK_DEVICE_MANAGEMENT, DEVICE_MANAGEMENT_CONNECT, OPERATOR_START, BT_ADDR_LEN * 2 + 1, 0x10 };
static uint8_t ack[10] = { 0x04, 0x01, 0x07, BT_ADDR_LEN };
- memcpy(&send[5], &address.b, BT_ADDR_LEN);
- memcpy(&ack[4], &address.b, BT_ADDR_LEN);
- return write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ memcpy(&send_buf[5], &BDADDR_BYTES(puppet_address), BT_ADDR_LEN);
+ memcpy(&send_buf[5 + BT_ADDR_LEN], &BDADDR_BYTES(master_address), BT_ADDR_LEN);
+ memcpy(&ack[4], &BDADDR_BYTES(puppet_address), BT_ADDR_LEN);
+ return write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
}
-int disconnect_device(int sock, bdaddr_t address) {
- static uint8_t send[10] = { 0x04, 0x02, 0x05, BT_ADDR_LEN };
+int disconnect_device(socktype_t sock, bdaddr_t address) {
+ static uint8_t send_buf[10] = { 0x04, 0x02, 0x05, BT_ADDR_LEN };
static uint8_t ack[10] = { 0x04, 0x02, 0x07, BT_ADDR_LEN };
- memcpy(&send[4], &address.b, BT_ADDR_LEN);
- memcpy(&ack[4], &address.b, BT_ADDR_LEN);
- return write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ memcpy(&send_buf[4], &BDADDR_BYTES(address), BT_ADDR_LEN);
+ memcpy(&ack[4], &BDADDR_BYTES(address), BT_ADDR_LEN);
+ return write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
}
-int remove_device(int sock, bdaddr_t address) {
- static uint8_t send[10] = { 0x04, 0x03, 0x05, BT_ADDR_LEN };
+int remove_device(socktype_t sock, bdaddr_t address) {
+ static uint8_t send_buf[10] = { 0x04, 0x03, 0x05, BT_ADDR_LEN };
static uint8_t ack[10] = { 0x04, 0x03, 0x06, BT_ADDR_LEN };
- memcpy(&send[4], &address.b, BT_ADDR_LEN);
- memcpy(&ack[4], &address.b, BT_ADDR_LEN);
- return write_check(sock, send, sizeof(send), ack, sizeof(ack));
+ memcpy(&send_buf[4], &BDADDR_BYTES(address), BT_ADDR_LEN);
+ memcpy(&ack[4], &BDADDR_BYTES(address), BT_ADDR_LEN);
+ return write_check(sock, send_buf, sizeof(send_buf), ack, sizeof(ack));
}
diff --git a/based.h b/based.h
index acbde75..790908d 100644
--- a/based.h
+++ b/based.h
@@ -1,7 +1,8 @@
#ifndef BASED_H
#define BASED_H
-#include
+#include "socket.h"
+
#include
#include "bluetooth.h"
@@ -11,7 +12,7 @@
#define MAX_NUM_DEVICES 8
#define MAX_BT_PACK_LEN 0x1000
#define VER_STR_LEN 6
-#define VP_MASK 0x20
+#define VP_MASK 0x7F
enum NoiseCancelling {
NC_HIGH = 0x01,
@@ -38,9 +39,13 @@ enum PromptLanguage {
PL_PT = 0x27,
PL_ZH = 0x28,
PL_KO = 0x29,
+ PL_PL = 0x2B,
+ PL_RU = 0x2A,
PL_NL = 0x2e,
PL_JA = 0x2f,
PL_SV = 0x32
+
+
};
enum Pairing {
@@ -51,7 +56,8 @@ enum Pairing {
enum DeviceStatus {
DS_THIS = 0x03,
DS_CONNECTED = 0x01,
- DS_DISCONNECTED = 0x00
+ DS_DISCONNECTED = 0x00,
+ DS_MUSICSHARE = 0x45
};
enum DevicesConnected {
@@ -59,6 +65,13 @@ enum DevicesConnected {
DC_TWO = 0x03
};
+enum SelfVoice {
+ SV_OFF = 0x0,
+ SV_HIGH = 0x1,
+ SV_MEDIUM = 0x2,
+ SV_LOW = 0x3,
+};
+
struct Device {
bdaddr_t address;
enum DeviceStatus status;
@@ -66,25 +79,28 @@ struct Device {
};
int has_noise_cancelling(unsigned int device_id);
-int init_connection(int sock);
-int send_packet(int sock, const void *send, size_t send_n, uint8_t recieved[MAX_BT_PACK_LEN]);
-int get_device_id(int sock, unsigned int *device_id, unsigned int *index);
-int set_name(int sock, const char *name);
-int set_prompt_language(int sock, enum PromptLanguage language);
-int set_voice_prompts(int sock, int on);
-int set_auto_off(int sock, enum AutoOff minutes);
-int set_noise_cancelling(int sock, enum NoiseCancelling level);
-int get_device_status(int sock, char name[MAX_NAME_LEN + 1], enum PromptLanguage *language,
+int init_connection(socktype_t sock);
+int send_packet(socktype_t sock, const void *send, size_t send_n, uint8_t recieved[MAX_BT_PACK_LEN]);
+int get_device_id(socktype_t sock, unsigned int *device_id, unsigned int *index);
+int set_name(socktype_t sock, const char *name);
+int set_prompt_language(socktype_t sock, enum PromptLanguage language);
+int set_voice_prompts(socktype_t sock, int on);
+int set_auto_off(socktype_t sock, enum AutoOff minutes);
+int set_noise_cancelling(socktype_t sock, enum NoiseCancelling level);
+int get_device_status(socktype_t sock, char name[MAX_NAME_LEN + 1], enum PromptLanguage *language,
enum AutoOff *minutes, enum NoiseCancelling *level);
-int set_pairing(int sock, enum Pairing pairing);
-int get_firmware_version(int sock, char version[VER_STR_LEN]);
-int get_serial_number(int sock, char serial[0x100]);
-int get_battery_level(int sock, unsigned int *level);
-int get_device_info(int sock, bdaddr_t address, struct Device *device);
-int get_paired_devices(int sock, bdaddr_t addresses[MAX_NUM_DEVICES], size_t *num_devices,
+int set_pairing(socktype_t sock, enum Pairing pairing);
+int set_self_voice(socktype_t sock, enum SelfVoice selfvoice);
+int get_firmware_version(socktype_t sock, char version[VER_STR_LEN]);
+int get_firmware_version(socktype_t sock, char version[VER_STR_LEN]);
+int get_serial_number(socktype_t sock, char serial[0x100]);
+int get_battery_level(socktype_t sock, unsigned int *level);
+int get_device_info(socktype_t sock, bdaddr_t address, struct Device *device);
+int get_paired_devices(socktype_t sock, bdaddr_t addresses[MAX_NUM_DEVICES], size_t *num_devices,
enum DevicesConnected *connected);
-int connect_device(int sock, bdaddr_t address);
-int disconnect_device(int sock, bdaddr_t address);
-int remove_device(int sock, bdaddr_t address);
+int connect_device(socktype_t sock, bdaddr_t address);
+int connect_music_share(socktype_t sock, bdaddr_t address, bdaddr_t own_address);
+int disconnect_device(socktype_t sock, bdaddr_t address);
+int remove_device(socktype_t sock, bdaddr_t address);
#endif
diff --git a/bluetooth.c b/bluetooth.c
index 3571e92..937ad94 100644
--- a/bluetooth.c
+++ b/bluetooth.c
@@ -1,4 +1,11 @@
+#ifdef _WIN32
+#include
+#include
+#include
+#else
#include
+#endif
+
#include
#include "bluetooth.h"
@@ -7,9 +14,9 @@
* Based on code taken from the BlueZ library.
*/
-int reverse_ba2str(const bdaddr_t *ba, char *str) {
- return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
- ba->b[0], ba->b[1], ba->b[2], ba->b[3], ba->b[4], ba->b[5]);
+int reverse_ba2str(const bdaddr_t *ba, char *str, size_t str_len) {
+ return sprintf_s(str, str_len, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+ BDADDR_BYTES(*ba)[0], BDADDR_BYTES(*ba)[1], BDADDR_BYTES(*ba)[2], BDADDR_BYTES(*ba)[3], BDADDR_BYTES(*ba)[4], BDADDR_BYTES(*ba)[5]);
}
int reverse_str2ba(const char *str, bdaddr_t *ba) {
@@ -21,7 +28,60 @@ int reverse_str2ba(const char *str, bdaddr_t *ba) {
}
for (i = 0; i < 6; i++, str += 3)
- ba->b[i] = strtol(str, NULL, 16);
+ BDADDR_BYTES(*ba)[i] = (uint8_t) strtol(str, NULL, 16);
+
+ return 0;
+}
+
+#ifdef _WIN32
+int bachk(const char *str)
+{
+ char tmp[18], *ptr = tmp;
+ if (!str)
+ return -1;
+ if (strlen(str) != 17)
+ return -1;
+ memcpy(tmp, str, 18);
+ while (*ptr) {
+ *ptr = toupper(*ptr);
+ if (*ptr < '0'|| (*ptr > '9' && *ptr < 'A') || *ptr > 'F')
+ return -1;
+ ptr++;
+ *ptr = toupper(*ptr);
+ if (*ptr < '0'|| (*ptr > '9' && *ptr < 'A') || *ptr > 'F')
+ return -1;
+ ptr++;
+ *ptr = toupper(*ptr);
+ if (*ptr == 0)
+ break;
+ if (*ptr != ':')
+ return -1;
+ ptr++;
+ }
+ return 0;
+}
+int str2ba(const char *str, bdaddr_t *ba)
+{
+ uint8_t b[6];
+ const char *ptr = str;
+ int i;
+ for (i = 0; i < 6; i++) {
+ b[i] = (uint8_t) strtol(ptr, NULL, 16);
+ if (i != 5 && !(ptr = strchr(ptr, ':')))
+ ptr = ":00:00:00:00:00";
+ ptr++;
+ }
+ baswap(ba, (bdaddr_t *) b);
return 0;
}
+
+void baswap(bdaddr_t *dst, const bdaddr_t *src)
+{
+ register unsigned char *d = (unsigned char *) dst;
+ register const unsigned char *s = (const unsigned char *) src;
+ register int i;
+ for (i = 0; i < 6; i++)
+ d[i] = s[5-i];
+}
+#endif
diff --git a/bluetooth.h b/bluetooth.h
index 62cae9c..5f62c94 100644
--- a/bluetooth.h
+++ b/bluetooth.h
@@ -1,11 +1,25 @@
#ifndef BLUETOOTH_H
#define BLUETOOTH_H
+#ifdef _WIN32
+#define bdaddr_t BLUETOOTH_ADDRESS_STRUCT
+#define BDADDR_BYTES(x) ((x).rgBytes)
+#include
+#include
+#include
+#else
#include
+#define BDADDR_BYTES(x) ((x).b)
+#endif
#define BT_ADDR_LEN 6
-int reverse_ba2str(const bdaddr_t *ba, char *str);
+int reverse_ba2str(const bdaddr_t *ba, char *str, size_t str_len);
int reverse_str2ba(const char *str, bdaddr_t *ba);
+#ifdef _WIN32
+int bachk(const char *str);
+int str2ba(const char *str, bdaddr_t *ba);
+void baswap(bdaddr_t *dst, const bdaddr_t *src);
+#endif
#endif
diff --git a/main.c b/main.c
index f7615d7..a2891c9 100644
--- a/main.c
+++ b/main.c
@@ -1,14 +1,23 @@
+#ifndef _WIN32
#include
#include
+#endif
+
#include
#include
#include
+#ifdef _WIN32
+#include
+#else
#include
+#endif
+#include "socket.h"
#include "based.h"
#include "bluetooth.h"
#include "util.h"
+
static const char *program_name;
static void usage() {
@@ -45,18 +54,26 @@ static void usage() {
"\t\tPrint the devices currently connected to the device.\n"
"\t\t!: indicates the current device\n"
"\t\t*: indicates other connected devices\n"
+ "\t\tS: indicates a device that music is shared to\n"
"\t--connect-device=\n"
"\t\tAttempt to connect to the device at address.\n"
+ "\t--connect-music-share=,\n"
+ "\t\tAttempt to connect to the puppet device at address\n"
+ "\t\t(secondary headphones) and share music from source\n"
+ "\t\taddress (computer).\n"
"\t--disconnect-device=\n"
"\t\tDisconnect the device at address.\n"
"\t--remove-device=\n"
"\t\tRemove the device at address from the pairing list.\n"
"\t--device-id\n"
"\t\tPrint the device id followed by the index revision.\n"
+ "\t-e, --self-voice\n"
+ "\t\tChange the self voice level.\n"
+ "\t\tlevel: high, medium, low, off\n"
, program_name);
}
-static int do_set_name(int sock, const char *arg) {
+static int do_set_name(socktype_t sock, const char *arg) {
char name_buffer[MAX_NAME_LEN + 1] = { 0 };
int status;
@@ -64,13 +81,13 @@ static int do_set_name(int sock, const char *arg) {
fprintf(stderr, "Name exceeds %d character maximum. Truncating.\n", MAX_NAME_LEN);
status = 1;
} else {
- strncpy(name_buffer, arg, MAX_NAME_LEN);
+ strncpy_s(name_buffer, MAX_NAME_LEN, arg, MAX_NAME_LEN);
status = set_name(sock, name_buffer);
}
return status;
}
-static int do_set_prompt_language(int sock, const char *arg) {
+static int do_set_prompt_language(socktype_t sock, const char *arg) {
enum PromptLanguage pl;
if (strcmp(arg, "en") == 0) {
@@ -89,7 +106,11 @@ static int do_set_prompt_language(int sock, const char *arg) {
pl = PL_ZH;
} else if (strcmp(arg, "ko") == 0) {
pl = PL_KO;
- } else if (strcmp(arg, "nl") == 0) {
+ } else if (strcmp(arg, "pl") == 0) {
+ pl = PL_PL;
+ } else if (strcmp(arg, "ru") == 0) {
+ pl = PL_RU;
+ } else if (strcmp(arg, "nl") == 0) {
pl = PL_NL;
} else if (strcmp(arg, "ja") == 0) {
pl = PL_JA;
@@ -104,7 +125,7 @@ static int do_set_prompt_language(int sock, const char *arg) {
return set_prompt_language(sock, pl);
}
-static int do_set_voice_prompts(int sock, const char *arg) {
+static int do_set_voice_prompts(socktype_t sock, const char *arg) {
int on;
if (strcmp(arg, "on") == 0) {
@@ -120,7 +141,7 @@ static int do_set_voice_prompts(int sock, const char *arg) {
return set_voice_prompts(sock, on);
}
-static int do_set_auto_off(int sock, const char *arg) {
+static int do_set_auto_off(socktype_t sock, const char *arg) {
enum AutoOff ao;
int parsed = atoi(arg);
@@ -146,7 +167,7 @@ static int do_set_auto_off(int sock, const char *arg) {
return set_auto_off(sock, ao);
}
-static int do_set_noise_cancelling(int sock, const char *arg) {
+static int do_set_noise_cancelling(socktype_t sock, const char *arg) {
enum NoiseCancelling nc;
if (strcmp(arg, "high") == 0) {
@@ -177,7 +198,7 @@ static int do_set_noise_cancelling(int sock, const char *arg) {
return set_noise_cancelling(sock, nc);
}
-static int do_get_device_status(int sock) {
+static int do_get_device_status(socktype_t sock) {
char name[MAX_NAME_LEN + 1];
enum PromptLanguage pl;
enum AutoOff ao;
@@ -191,7 +212,7 @@ static int do_get_device_status(int sock) {
char *print;
printf("Name: %s\n", name);
- switch (pl | VP_MASK) {
+ switch (pl & VP_MASK) {
case PL_EN:
print = "en";
break;
@@ -225,8 +246,15 @@ static int do_get_device_status(int sock) {
case PL_SV:
print = "sv";
break;
+ case PL_RU:
+ print = "ru";
+ break;
+ case PL_PL:
+ print = "pl";
+ break;
default:
- return 1;
+ print = "unknown";
+ break;
}
printf("Language: %s\n", print);
printf("Voice Prompts: %s\n", pl & VP_MASK ? "on" : "off");
@@ -259,7 +287,7 @@ static int do_get_device_status(int sock) {
return 0;
}
-static int do_set_pairing(int sock, const char *arg) {
+static int do_set_pairing(socktype_t sock, const char *arg) {
enum Pairing p;
if (strcmp(arg, "on") == 0) {
@@ -275,7 +303,27 @@ static int do_set_pairing(int sock, const char *arg) {
return set_pairing(sock, p);
}
-static int do_get_firmware_version(int sock) {
+static int do_set_self_voice(socktype_t sock, const char *arg) {
+ enum SelfVoice p;
+
+ if (strcmp(arg, "high") == 0) {
+ p = SV_HIGH;
+ } else if (strcmp(arg, "medium") == 0) {
+ p = SV_MEDIUM;
+ } else if (strcmp(arg, "low") == 0) {
+ p = SV_LOW;
+ } else if (strcmp(arg, "off") == 0) {
+ p = SV_OFF;
+ } else {
+ fprintf(stderr, "Invalid self voice argument: %s\n", arg);
+ usage();
+ return 1;
+ }
+
+ return set_self_voice(sock, p);
+}
+
+static int do_get_firmware_version(socktype_t sock) {
char version[VER_STR_LEN];
int status = get_firmware_version(sock, version);
@@ -287,7 +335,7 @@ static int do_get_firmware_version(int sock) {
return 0;
}
-static int do_get_serial_number(int sock) {
+static int do_get_serial_number(socktype_t sock) {
char serial[0x100];
int status = get_serial_number(sock, serial);
@@ -299,7 +347,7 @@ static int do_get_serial_number(int sock) {
return 0;
}
-static int do_get_battery_level(int sock) {
+static int do_get_battery_level(socktype_t sock) {
unsigned int level;
int status = get_battery_level(sock, &level);
@@ -311,7 +359,7 @@ static int do_get_battery_level(int sock) {
return 0;
}
-static int do_get_paired_devices(int sock) {
+static int do_get_paired_devices(socktype_t sock) {
bdaddr_t devices[MAX_NUM_DEVICES];
size_t num_devices;
enum DevicesConnected connected;
@@ -343,7 +391,7 @@ static int do_get_paired_devices(int sock) {
}
char address[18];
- reverse_ba2str(&device.address, address);
+ reverse_ba2str(&device.address, address, sizeof(address));
char status_symb;
switch (device.status) {
@@ -356,6 +404,9 @@ static int do_get_paired_devices(int sock) {
case DS_DISCONNECTED:
status_symb = ' ';
break;
+ case DS_MUSICSHARE:
+ status_symb = 'S';
+ break;
default:
return 1;
}
@@ -366,25 +417,44 @@ static int do_get_paired_devices(int sock) {
return 0;
}
-static int do_connect_device(int sock, const char *arg) {
+static int do_connect_device(socktype_t sock, const char *arg) {
bdaddr_t address;
reverse_str2ba(arg, &address);
return connect_device(sock, address);
}
-static int do_disconnect_device(int sock, const char *arg) {
+static int do_connect_music_share(socktype_t sock, const char *arg) {
+ bdaddr_t master_address = {0};
+ bdaddr_t puppet_address = {0};
+ const char *master_address_str = strchr(arg, ',');
+ char puppet_address_str[BT_ADDR_LEN * 3];
+ if (master_address_str != NULL && *master_address_str != '\0' && (master_address_str - arg) <= BT_ADDR_LEN * 3) {
+ strncpy_s(puppet_address_str, sizeof(puppet_address_str), arg, sizeof(puppet_address_str) - 1);
+ puppet_address_str[sizeof(puppet_address_str) - 1] = 0;
+ reverse_str2ba(puppet_address_str, &puppet_address);
+ reverse_str2ba(master_address_str + 1, &master_address);
+ return connect_music_share(sock, puppet_address, master_address);
+ }
+ else {
+ fprintf(stderr, "Invalid music share argument: Argument must be given as \",\"\n");
+ return 1;
+ }
+}
+
+
+static int do_disconnect_device(socktype_t sock, const char *arg) {
bdaddr_t address;
reverse_str2ba(arg, &address);
return disconnect_device(sock, address);
}
-static int do_remove_device(int sock, const char *arg) {
+static int do_remove_device(socktype_t sock, const char *arg) {
bdaddr_t address;
reverse_str2ba(arg, &address);
return remove_device(sock, address);
}
-static int do_get_device_id(int sock) {
+static int do_get_device_id(socktype_t sock) {
unsigned int device_id;
unsigned int index;
int status = get_device_id(sock, &device_id, &index);
@@ -397,7 +467,7 @@ static int do_get_device_id(int sock) {
return 0;
}
-static int do_send_packet(int sock, const char *arg) {
+static int do_send_packet(socktype_t sock, const char *arg) {
uint8_t send[sizeof(arg) / 2];
size_t i;
for (i = 0; arg[i * 2]; ++i) {
@@ -420,7 +490,7 @@ static int do_send_packet(int sock, const char *arg) {
}
int main(int argc, char *argv[]) {
- static const char *short_opt = "hn:l:v:o:c:dp:fsba";
+ static const char *short_opt = "hn:l:v:o:c:e:dp:fsba";
static const struct option long_opt[] = {
{ "help", no_argument, NULL, 'h' },
{ "name", required_argument, NULL, 'n' },
@@ -435,26 +505,48 @@ int main(int argc, char *argv[]) {
{ "battery-level", no_argument, NULL, 'b' },
{ "paired-devices", no_argument, NULL, 'a' },
{ "connect-device", required_argument, NULL, 2 },
+ { "connect-music-share", required_argument, NULL, 6 },
{ "disconnect-device", required_argument, NULL, 3 },
{ "remove-device", required_argument, NULL, 4 },
{ "device-id", no_argument, NULL, 5 },
+ { "self-voice", required_argument, NULL, 'e' },
{ "send-packet", required_argument, NULL, 1 },
{ 0, 0, 0, 0 }
};
- static const struct timeval send_timeout = { 5, 0 };
- static const struct timeval recieve_timeout = { 1, 0 };
- int sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+#ifdef _WIN32
+ WORD version_requested = MAKEWORD(2, 2);
+ WSADATA wsa_data;
+ WSAStartup(version_requested, &wsa_data);
+#endif
+
+ socktype_t sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+
+#ifdef _WIN32
+ static const DWORD send_timeout = 5000;
+ if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char *) &send_timeout, sizeof(send_timeout)) < 0) {
+ psockerror("Could not set socket send timeout");
+ return 1;
+ }
+ static const DWORD receive_timeout = 1000;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &receive_timeout, sizeof(receive_timeout)) < 0) {
+ psockerror("Could not set socket receive timeout");
+ return 1;
+ }
+#else
+ static const struct timeval send_timeout = { 5, 0 };
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &send_timeout, sizeof(send_timeout)) < 0) {
- perror("Could not set socket send timeout");
+ psockerror("Could not set socket send timeout");
return 1;
}
- if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &recieve_timeout, sizeof(recieve_timeout)) < 0) {
- perror("Could not set socket recieve timeout");
+ static const struct timeval receive_timeout = { 1, 0 };
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &recieve_timeout, sizeof(receive_timeout)) < 0) {
+ psockerror("Could not set socket recieve timeout");
return 1;
}
+#endif
program_name = argv[0];
@@ -481,17 +573,26 @@ int main(int argc, char *argv[]) {
usage();
return 1;
}
-
- struct sockaddr_rc address;
+#ifdef _WIN32
+ SOCKADDR_BTH address = {0};
+ address.addressFamily = AF_BTH;
+ address.port = BOSE_CHANNEL;
+ if (str2ba(argv[optind], (BLUETOOTH_ADDRESS_STRUCT *) &address.btAddr) != 0) {
+ fprintf(stderr, "Invalid bluetooth address: %s\n", argv[optind]);
+ return 1;
+ }
+#else
+ struct sockaddr_rc address = {0};
address.rc_family = AF_BLUETOOTH;
address.rc_channel = BOSE_CHANNEL;
if (str2ba(argv[optind], &address.rc_bdaddr) != 0) {
fprintf(stderr, "Invalid bluetooth address: %s\n", argv[optind]);
return 1;
}
+#endif
if (connect(sock, (struct sockaddr *) &address, sizeof(address)) != 0) {
- perror("Could not connect to Bluetooth device");
+ psockerror("Could not connect to Bluetooth device");
return 1;
}
@@ -538,9 +639,15 @@ int main(int argc, char *argv[]) {
case 'a':
status = do_get_paired_devices(sock);
break;
+ case 'e':
+ status = do_set_self_voice(sock, optarg);
+ break;
case 2:
status = do_connect_device(sock, optarg);
break;
+ case 6:
+ status = do_connect_music_share(sock, optarg);
+ break;
case 3:
status = do_disconnect_device(sock, optarg);
break;
@@ -559,7 +666,7 @@ int main(int argc, char *argv[]) {
}
if (status < 0) {
- perror("Error trying to change setting");
+ psockerror("Error trying to change setting");
}
close(sock);
diff --git a/scripts/README.md b/scripts/README.md
new file mode 100644
index 0000000..f758702
--- /dev/null
+++ b/scripts/README.md
@@ -0,0 +1,4 @@
+# Scripts
+
+## analyze\_trace.py
+This is a dissector for recorded bluetooth PCAP files. Record your trace on an Android phone by activating the "HCI snoop log" in developer mode ([described here](https://source.android.com/docs/core/bluetooth/verifying_debugging#debugging-with-logs)). Then pull the bugreport with `adb bugreport my-bugreport.zip` and get the bluetooth log files from `FS/data/misc/bluetooth/logs` in the zip. I imported the first `.cfa` file into Wireshark and then added the others with Wireshark's `Merge ...` menu option. Save the PCAP, and call the python dissector script on it. Known messages will be decoded as far as the protocol is known, so that the traffic is easier to read.
diff --git a/scripts/analyze_trace.py b/scripts/analyze_trace.py
new file mode 100644
index 0000000..279b71e
--- /dev/null
+++ b/scripts/analyze_trace.py
@@ -0,0 +1,184 @@
+import sys
+import binascii
+import os
+import subprocess
+import argparse
+
+PRODUCT_INFO = {0: "FUNCTION_BLOCK_INFO",
+ 1: "BMAP_VERSION",
+ 2: "ALL_FUNCTION_BLOCKS",
+ 3: "PRODUCT_ID_VARIANT",
+ 4: "GET_ALL_FUNCTIONS",
+ 5: "FIRMWARE_VERSION",
+ 6: "MAC_ADDRESS",
+ 7: "SERIAL_NUMBER",
+ 10: "HARDWARE_REVISION",
+ 11: "COMPONENT_DEVICES",
+ }
+
+DEVICE_MANAGEMENT = {
+ 0: "FUNCTION_BLOCK_INFO",
+ 1: "CONNECT",
+ 2: "DISCONNECT",
+ 3: "REMOVE_DEVICE",
+ 4: "LIST_DEVICES",
+ 5: "INFO",
+ 6: "EXTENDED_INFO",
+ 7: "CLEAR_DEVICE_LIST",
+ 8: "PAIRING_MODE",
+ 9: "LOCAL_MAC_ADDRESS",
+ 10: "PREPARE_P2P",
+ 11: "P2P_MODE",
+ 12: "ROUTING",
+}
+
+AUDIO_MANAGEMENT = {0: "FUNCTION_BLOCK_INFO",
+1: "SOURCE",
+2: "GET_ALL",
+3: "CONTROL",
+4: "STATUS",
+5: "VOLUME",
+6: "NOW_PLAYING",
+}
+
+SETTINGS = {
+0: "FUNCTION_BLOCK_INFO",
+1: "GET_ALL",
+2: "PRODUCT_NAME",
+3: "VOICE_PROMPTS",
+4: "STANDBY_TIMER",
+5: "CNC",
+6: "ANR",
+7: "BASS_CONTROL",
+8: "ALERTS",
+9: "BUTTONS",
+10: "MULTIPOINT",
+11: "SIDETONE",
+21: "IMU_VOLUME_CONTROL",
+}
+
+STATUS = {
+0: "FUNCTION_BLOCK_INFO",
+1: "GET_ALL_FUNCTIONS",
+2: "BATTERY_LEVEL",
+3: "AUX_CABLE_DETECTION",
+4: "MIC_LEVEL",
+5: "CHARGER_DETECT",
+}
+
+CONTROL = {
+0: "FUNCTION_BLOCK_INFO",
+1: "GET_ALL",
+2: "CHIRP",
+}
+
+VPA = {
+0: "FUNCTION_BLOCK_INFO",
+1: "GET_ALL",
+2: "SUPPORTED_VPAS",
+}
+
+FUNCTION_BLOCKS = {
+ 0: ("PRODUCT_INFO", PRODUCT_INFO),
+ 1: ("SETTINGS", SETTINGS),
+ 2: ("STATUS", STATUS),
+ 3: ("FIRMWARE_UPDATE", {}),
+ 4: ("DEVICE_MANAGEMENT", DEVICE_MANAGEMENT),
+ 5: ("AUDIO_MANAGEMENT", AUDIO_MANAGEMENT),
+ 6: ("CALL_MANAGEMENT", {}),
+ 7: ("CONTROL", CONTROL),
+ 8: ("DEBUG", {}),
+ 9: ("NOTIFICATION", {}),
+ 10: ("RESERVED_BOSEBUILD_1", {}),
+ 11: ("RESERVED_BOSEBUILD_2", {}),
+ 12: ("HEARING_ASSISTANCE", {}),
+ 13: ("DATA_COLLECTION", {}),
+ 14: ("HEART_RATE", {}),
+ 16: ("VPA", VPA),
+ 21: ("AUGMENTED_REALITY", {}),
+}
+
+OPERATORS = {
+0: "SET",
+1: "GET",
+2: "SET_GET",
+3: "STATUS",
+4: "ERROR",
+5: "START",
+6: "RESULT",
+7: "PROCESSING",
+}
+
+ERRORS = {
+ 1: ("LENGTH", "Invalid length"),
+ 2: ("CHKSUM", "Invalid Checksum"),
+ 3: ("FBLOCK_NOT_SUPP", "FBlock not supported"),
+ 4: ("FUNC_NOT_SUPP", "Function not supported"),
+ 5: ("OP_NOT_SUPP", "Operator is not supported for that function."),
+ 6: ("INVALID_DATA", "Data values sent to headset are incorrect."),
+ 7: ("DATA_UNAVAILABLE", "Requested data is not available"),
+ 8: ("RUNTIME", "Failure to read/write the information requested that is temporary."),
+ 9: ("TIMEOUT", "Timeout related errors."),
+ 10: ("INVALID_STATE", "Action requested is not applicable to the current state."),
+ 11: ("DEVICE_NOT_FOUND", "Device not found in Paired Device List"),
+ 12: ("BUSY", "Device is busy to service the BMAP message"),
+ 13: ("NOCONN_TIMEOUT", "Soundlink device fails to connect to a device in the Paired Device List"),
+ 14: ("NOCONN_KEY", "Soundlink device fails to connect to a device because the pairing information has been deleted from the source device."),
+ 15: ("OTA_UPDATE", "OTA firmware update cannot be initialized because an update is already in progress"),
+ 16: ("OTA_LOW_BATT", "OTA firmware update cannot be initialized because product battery voltage is too low"),
+ 17: ("OTA_NO_CHARGER", "OTA firmware update cannot be applied because charger is not connected"),
+ -1: ("FBLOCK_SPECIFIC", "Error code is Function Block specific and an extra byte will be included in the payload to differentiate between different FBlock specific error codes. Refer to the respective FBlock section for a list of error codes for that particular FBlock."),
+}
+
+TSHARK_WINDOWS = r'C:\Program Files\Wireshark\tshark.exe'
+TSHARK = TSHARK_WINDOWS if os.path.exists(TSHARK_WINDOWS) else "tshark"
+
+def parse_packet(p):
+ try:
+ return ((p[0], p[1], p[2], p[4: 4 + p[3]]), p[4 + p[3]:])
+ except IndexError:
+ return (None, b'')
+
+def parse_packets(p):
+ rest = p
+ while rest:
+ (p, rest) = parse_packet(rest)
+ yield p
+
+
+def parse_packets_readable(p):
+ for p in parse_packets(p):
+ if p is None:
+ yield None
+ else:
+ yield (f"{FUNCTION_BLOCKS[p[0]][0]} ({p[0]})", \
+ f"{FUNCTION_BLOCKS[p[0]][1].get(p[1], 'UNKNOWN')} ({p[1]})", \
+ f"{OPERATORS.get(p[2], 'UNKNOWN')} ({p[2]})", \
+ f"{ERRORS[p[3][0]]} ({p[3][0]})" if p[2] == 4 and len(p[3]) == 1 else p[3])
+
+
+
+def parse_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("tracefile", help = "Wireshark pcap file")
+ return parser.parse_args()
+
+
+def main(args):
+ proc = subprocess.run((TSHARK, "-r", args.tracefile, "-e", "hci_h4.direction", "-e", "btspp.data", "-Tfields"), capture_output = True)
+
+ text = proc.stdout.decode(encoding = "ascii").replace("\r\n", "\n")
+
+ a = [x.split("\t") for x in text.split("\n") if len(x.split("\t")) > 1 and x.split("\t")[1]]
+ b = [(x[0], binascii.unhexlify(x[1])) for x in a]
+ c = [x[1] for x in b if x[0] == '0x00']
+ #d = [(FUNCTION_BLOCKS[y[0]][0], FUNCTION_BLOCKS[y[0]][0].get(y[1], ""), y[2], y[3]) for y in parse_packets(x[1][0]) if x[0] == '0x00' else x[1] for x in b]
+ d = [("=>" if x[0] == '0x00' else "<=", list(parse_packets_readable(x[1]))) for x in b]
+
+ for x in d:
+ print(x)
+
+
+if __name__ == "__main__":
+ main(parse_args())
+
diff --git a/socket.h b/socket.h
new file mode 100644
index 0000000..72b32ec
--- /dev/null
+++ b/socket.h
@@ -0,0 +1,29 @@
+// Minimal abstraction layer over WinSock2 / BSD sockets
+
+#ifndef _SOCKET_H
+#define _SOCKET_H
+
+#ifdef _WIN32
+
+#include
+#include
+#define psockerror(x) do { \
+ wchar_t *s = NULL; \
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&s, 0, NULL); \
+ fprintf(stderr, "%s: %S\n", x, s); \
+ LocalFree(s); \
+ } while (0)
+#define close(x) closesocket(x)
+typedef SOCKET socktype_t;
+#define AF_BLUETOOTH AF_BTH
+#define BTPROTO_RFCOMM BTHPROTO_RFCOMM
+
+#else
+
+#include
+#define psockerror(x) perror(x)
+typedef int socktype_t;
+
+#endif
+
+#endif /* _SOCKET_H */