From b0cad0b33dfd3c7304657be39080cd030f48c8bd Mon Sep 17 00:00:00 2001 From: Norman Barker Date: Fri, 16 Jan 2015 17:11:04 -0700 Subject: [PATCH 1/2] Added sections on build and test and fixed a language typo --- README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 531fb8a..bd74c58 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Based on [Bluetooth serial port communication for Node.js](https://github.com/eelcocramer/node-bluetooth-serial-port) -## Pre-requests on Linux +## Prequisites on Linux * CMake * Needs Bluetooth development packages to build @@ -10,17 +10,30 @@ Based on [Bluetooth serial port communication for Node.js](https://github.com/ee `apt-get install libbluetooth-dev cmake gcc-c++` `zypper install bluez-devel cmake gcc-c++` -## Pre-request on OS X +## Prequisites on OS X * CMake from MacPorts * Needs XCode and XCode command line tools installed. -## Pre-request on Windows +## Prequisite on Windows * CMake # Documentation +## Build + +`mkdir build` +`cd build` +`cmake ..` +`make` + +On Windows use the Visual Studio Project files or NMake. + +## Test + +`make inquiretest` + ## Basic usage ```cpp From 53d9f70f4df121b8aea8553e7e3e86c52bf16f26 Mon Sep 17 00:00:00 2001 From: Norman Barker Date: Tue, 3 Feb 2015 17:57:49 -0700 Subject: [PATCH 2/2] Initial version of CAPI for bluetooth serial port library --- CMakeLists.txt | 12 +- README.md | 5 + src/capi/Error.cc | 27 +++++ src/capi/Error.h | 26 ++++ src/capi/bt_api.cc | 275 +++++++++++++++++++++++++++++++++++++++++++ src/capi/bt_api.h | 41 +++++++ src/capi/bt_config.h | 39 ++++++ src/capi/bt_export.h | 25 ++++ src/capi/bt_impl.h | 9 ++ test/main_c.cc | 27 +++++ 10 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 src/capi/Error.cc create mode 100644 src/capi/Error.h create mode 100644 src/capi/bt_api.cc create mode 100644 src/capi/bt_api.h create mode 100644 src/capi/bt_config.h create mode 100644 src/capi/bt_export.h create mode 100644 src/capi/bt_impl.h create mode 100644 test/main_c.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 9beb6e5..7e2f591 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,15 +6,21 @@ if (NOT MSVC) endif() if(WIN32) - set(srcs src/Enums.cc src/windows/BluetoothHelpers.cc src/windows/BTSerialPortBinding.cc src/windows/DeviceINQ.cc) + set(srcs src/Enums.cc src/windows/BluetoothHelpers.cc src/windows/BTSerialPortBinding.cc src/windows/DeviceINQ.cc) + set(c_api_srcs src/capi/bt_api.cc src/capi/Error.cc) include_directories(windows) + set(libs_c bluetoothserialport) set(libs ws2_32 bthprops) elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") # MacOSX set(srcs src/Enums.cc src/osx/BluetoothDeviceResources.mm src/osx/BluetoothWorker.mm src/osx/BTSerialPortBinding.mm src/osx/DeviceINQ.mm src/osx/pipe.c) + set(c_api_srcs src/capi/bt_api.cc src/capi/Error.cc) + set(libs_c bluetoothserialport) set(libs "-framework Foundation -framework IOBluetooth -fobjc-arc") include_directories(osx) else() set(srcs src/Enums.cc src/linux/BTSerialPortBinding.cc src/linux/DeviceINQ.cc) + set(c_api_srcs src/capi/bt_api.cc src/capi/Error.cc) + set(libs_c bluetoothserialport) set(libs bluetooth) include_directories(linux) endif() @@ -22,7 +28,11 @@ endif() include_directories(./src) add_library(bluetoothserialport STATIC ${srcs}) +add_library(bluetoothserialport_c SHARED ${c_api_srcs}) add_executable(inquiretest EXCLUDE_FROM_ALL test/main.cpp) +add_executable(inquiretest_c EXCLUDE_FROM_ALL test/main_c.cc) target_link_libraries(bluetoothserialport ${libs}) +target_link_libraries(bluetoothserialport_c ${libs_c}) target_link_libraries(inquiretest bluetoothserialport) +target_link_libraries(inquiretest_c bluetoothserialport_c) diff --git a/README.md b/README.md index bd74c58..d23f211 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ On Windows use the Visual Studio Project files or NMake. ## Test `make inquiretest` +`make inquiretest_c` ## Basic usage @@ -133,6 +134,10 @@ Returns text representation of deviceClass enum value Returns text representation of serviceClass enum value +## Acknowledgements + +C API is based on the C API implemented in https://github.com/libspatialindex/libspatialindex + ## LICENSE This module is available under a [FreeBSD license](http://opensource.org/licenses/BSD-2-Clause), see the [LICENSE file](./LICENSE.md) for details. diff --git a/src/capi/Error.cc b/src/capi/Error.cc new file mode 100644 index 0000000..92918cf --- /dev/null +++ b/src/capi/Error.cc @@ -0,0 +1,27 @@ +#include "capi/bt_impl.h" + +Error::Error(int code, std::string const& message, std::string const& method) : + m_code(code), + m_message(message), + m_method(method) +{ +} + +Error::Error(Error const& other) : + m_code(other.m_code), + m_message(other.m_message), + m_method(other.m_method) +{ +} + +Error& Error::operator=(Error const& rhs) +{ + if (&rhs != this) + { + m_code = rhs.m_code; + m_message = rhs.m_message; + m_method = rhs.m_method; + + } + return *this; +} \ No newline at end of file diff --git a/src/capi/Error.h b/src/capi/Error.h new file mode 100644 index 0000000..c5b7bcb --- /dev/null +++ b/src/capi/Error.h @@ -0,0 +1,26 @@ +#pragma once + +#include "bt_export.h" + +class BLUETOOTH_DLL Error +{ +public: + + Error(int code, std::string const& message, std::string const& method); + + /// Copy constructor. + Error(Error const& other); + + /// Assignment operator. + Error& operator=(Error const& rhs); + + int GetCode() const { return m_code; } + const char* GetMessage() const { return m_message.c_str(); } + const char* GetMethod() const { return m_method.c_str(); } + +private: + + int m_code; + std::string m_message; + std::string m_method; +}; \ No newline at end of file diff --git a/src/capi/bt_api.cc b/src/capi/bt_api.cc new file mode 100644 index 0000000..64f8c51 --- /dev/null +++ b/src/capi/bt_api.cc @@ -0,0 +1,275 @@ +#include "capi/bt_impl.h" +#include "BluetoothException.h" + +static std::stack errors; + +#define VALIDATE_POINTER0(ptr, func) \ + do { if( NULL == ptr ) { \ + RTError const ret = RT_Failure; \ + std::ostringstream msg; \ + msg << "Pointer \'" << #ptr << "\' is NULL in \'" << (func) <<"\'."; \ + std::string message(msg.str()); \ + Error_PushError( ret, message.c_str(), (func)); \ + return; \ + }} while(0) + +#define VALIDATE_POINTER1(ptr, func, rc) \ + do { if( NULL == ptr ) { \ + RTError const ret = RT_Failure; \ + std::ostringstream msg; \ + msg << "Pointer \'" << #ptr << "\' is NULL in \'" << (func) <<"\'."; \ + std::string message(msg.str()); \ + Error_PushError( ret, message.c_str(), (func)); \ + return (rc); \ + }} while(0) + +BLUETOOTH_SERIAL_PORT_C_START + +BLUETOOTH_C_DLL void Error_Reset(void) { + if (errors.empty()) return; + for (std::size_t i=0;i(errors.size()); +} + +BLUETOOTH_C_DLL BTDeviceINQH BTDeviceINQ_Create() +{ + return (BTDeviceINQH) DeviceINQ::Create(); +} + +BLUETOOTH_C_DLL RTError BTDeviceINQ_Inquire(BTDeviceINQH hDeviceINQ, BTDeviceH** hDevices, uint64_t* nDevices) +{ + VALIDATE_POINTER1(hDeviceINQ, "BTDeviceINQ_Inquire", RT_Failure); + DeviceINQ* pInq = static_cast(hDeviceINQ); + try + { + std::vector v = pInq->Inquire(); + *nDevices = v.size(); + if (*nDevices > 0) + { + *hDevices = (device**)malloc(*nDevices * sizeof(device*)); + for (int i = 0; i < *nDevices; i++) + { + *hDevices[i] = &v[i]; + } + } + else + { + *hDevices = NULL; + } + } + catch(BluetoothException &e) + { + Error_PushError(RT_Failure, + e.what(), + "BTDeviceINQ_Inquire"); + return RT_Failure; + } + return RT_None; +} + +BLUETOOTH_C_DLL RTError BTDeviceINQ_SdpSearch(BTDeviceINQH hDeviceINQ, const char* addr, int* channel_id) +{ + VALIDATE_POINTER1(hDeviceINQ, "BTDeviceINQ_SdpSearch", RT_Failure); + DeviceINQ* pInq = static_cast(hDeviceINQ); + try + { + *channel_id = pInq->SdpSearch(addr); + } + catch(BluetoothException &e) + { + Error_PushError(RT_Failure, + e.what(), + "BTDeviceINQ_SdpSearch"); + return RT_Failure; + } + return RT_None; +} + +BLUETOOTH_C_DLL void BTDeviceINQ_Destroy(BTDeviceINQH hDeviceINQ) +{ + VALIDATE_POINTER0(hDeviceINQ, "BTDeviceINQ_Destroy"); + DeviceINQ* pInq = static_cast(hDeviceINQ); + if (pInq) delete pInq; +} + +BLUETOOTH_C_DLL BTSerialPortBindingH BTSerialPortBinding_Create(const char* addr, int channel_id) +{ + return (BTSerialPortBindingH) BTSerialPortBinding::Create(addr, channel_id); +} + +BLUETOOTH_C_DLL RTError BTSerialPortBinding_Connect(BTSerialPortBindingH hBinding) +{ + VALIDATE_POINTER1(hBinding, "BTSerialPortBinding_Connect", RT_Failure); + BTSerialPortBinding* pBinding = static_cast(hBinding); + try + { + pBinding->Connect(); + } + catch(BluetoothException &e) + { + Error_PushError(RT_Failure, + e.what(), + "BTSerialPortBinding_Connect"); + return RT_Failure; + } + return RT_None; +} + +BLUETOOTH_C_DLL RTError BTSerialPortBinding_Close(BTSerialPortBindingH hBinding) +{ + VALIDATE_POINTER1(hBinding, "BTSerialPortBinding_Close", RT_Failure); + BTSerialPortBinding* pBinding = static_cast(hBinding); + try + { + pBinding->Close(); + } + catch(BluetoothException &e) + { + Error_PushError(RT_Failure, + e.what(), + "BTSerialPortBinding_Close"); + return RT_Failure; + } + return RT_None; +} + +BLUETOOTH_C_DLL RTError BTSerialPortBinding_Read(BTSerialPortBindingH hBinding, char* buffer, int length) +{ + VALIDATE_POINTER1(hBinding, "BTSerialPortBinding_Read", RT_Failure); + BTSerialPortBinding* pBinding = static_cast(hBinding); + try + { + pBinding->Read(buffer, length); + } + catch(BluetoothException &e) + { + Error_PushError(RT_Failure, + e.what(), + "BTSerialPortBinding_Read"); + return RT_Failure; + } + return RT_None; +} + +BLUETOOTH_C_DLL RTError BTSerialPortBinding_Write(BTSerialPortBindingH hBinding, const char *buffer, int length) +{ + VALIDATE_POINTER1(hBinding, "BTSerialPortBinding_Write", RT_Failure); + BTSerialPortBinding* pBinding = static_cast(hBinding); + try + { + pBinding->Write(buffer, length); + } + catch(BluetoothException &e) + { + Error_PushError(RT_Failure, + e.what(), + "BTSerialPortBinding_Write"); + return RT_Failure; + } + return RT_None; +} + +BLUETOOTH_C_DLL void BTSerialPortBinding_Destroy(BTSerialPortBindingH hBinding) +{ + VALIDATE_POINTER0(hBinding, "BTSerialPortBinding_Destroy"); + BTSerialPortBinding* pBinding = static_cast(hBinding); + if (pBinding) delete pBinding; +} + +BLUETOOTH_C_DLL void BT_Free(void* object) +{ + VALIDATE_POINTER0(object, "BT_Free"); + if (object != 0) + std::free(object); +} + +BLUETOOTH_C_DLL char* BTDevice_GetName(BTDeviceH hDevice) +{ + VALIDATE_POINTER1(hDevice, "BTDevice_GetName", 0); + device* pDev = static_cast(hDevice); + return STRDUP(pDev->name.c_str()); +} + +BLUETOOTH_C_DLL char* BTDevice_GetAddress(BTDeviceH hDevice) +{ + VALIDATE_POINTER1(hDevice, "BTDevice_GetAddress", 0); + device* pDev = static_cast(hDevice); + return STRDUP(pDev->address.c_str()); +} + +BLUETOOTH_C_DLL int BTDevice_IsConnected(BTDeviceH hDevice) +{ + VALIDATE_POINTER1(hDevice, "BTDevice_IsConnected", 0); + device* pDev = static_cast(hDevice); + return pDev->connected; +} + +BLUETOOTH_C_DLL int BTDevice_IsAuthenticated(BTDeviceH hDevice) +{ + VALIDATE_POINTER1(hDevice, "BTDevice_IsAuthenticated", 0); + device* pDev = static_cast(hDevice); + return pDev->authenticated; +} + +BLUETOOTH_C_DLL void BT_DestroyDevices(BTDeviceH* hDevices, uint32_t nDevices) +{ + VALIDATE_POINTER0(hDevices, "BT_DestroyDevices"); + // don't need to destroy actual devices they are returned as vector from + // DeviceINQ::Inquire() + delete hDevices; +} + +BLUETOOTH_C_DLL char* BT_Version() +{ + std::ostringstream ot; + +#ifdef BLUETOOTH_SERIAL_PORT_RELEASE_NAME + ot << BLUETOOTH_SERIAL_PORT_RELEASE_NAME; +#else + ot << "0.0.1"; +#endif + std::string out(ot.str()); + return STRDUP(out.c_str()); +} +BLUETOOTH_SERIAL_PORT_C_END + diff --git a/src/capi/bt_api.h b/src/capi/bt_api.h new file mode 100644 index 0000000..b4273e6 --- /dev/null +++ b/src/capi/bt_api.h @@ -0,0 +1,41 @@ +#ifndef BLUETOOTH_SERIAL_PORT_API_H_INCLUDED +#define BLUETOOTH_SERIAL_PORT_API_H_INCLUDED + +#define BLUETOOTH_SERIAL_PORT_C_API 1 + +#include "bt_config.h" + +BLUETOOTH_SERIAL_PORT_C_START + +BLUETOOTH_C_DLL BTDeviceINQH BTDeviceINQ_Create(); +BLUETOOTH_C_DLL RTError BTDeviceINQ_Inquire(BTDeviceINQH hDeviceINQ, BTDeviceH** hDevices, uint64_t* nDevices); +BLUETOOTH_C_DLL RTError BTDeviceINQ_SdpSearch(BTDeviceINQH hDeviceINQ, const char* addr, int* channel_id); +BLUETOOTH_C_DLL void BTDeviceINQ_Destroy(BTDeviceINQH hDeviceINQ); + +BLUETOOTH_C_DLL BTSerialPortBindingH BTSerialPortBinding_Create(const char* addr, int channel_id); +BLUETOOTH_C_DLL RTError BTSerialPortBinding_Connect(BTSerialPortBindingH hBinding); +BLUETOOTH_C_DLL RTError BTSerialPortBinding_Close(BTSerialPortBindingH hBinding); +BLUETOOTH_C_DLL RTError BTSerialPortBinding_Read(BTSerialPortBindingH hBinding, char* buffer, int length); +BLUETOOTH_C_DLL RTError BTSerialPortBinding_Write(BTSerialPortBindingH hBinding, const char *buffer, int length); +BLUETOOTH_C_DLL void BTSerialPortBinding_Destroy(BTSerialPortBindingH hBinding); + +BLUETOOTH_C_DLL void BT_Free(void* object); + +BLUETOOTH_C_DLL char* BTDevice_GetName(BTDeviceH hDevice); +BLUETOOTH_C_DLL char* BTDevice_GetAddress(BTDeviceH hDevice); +BLUETOOTH_C_DLL int BTDevice_IsConnected(BTDeviceH hDevice); +BLUETOOTH_C_DLL int BTDevice_IsAuthenticated(BTDeviceH hDevice); + +BLUETOOTH_C_DLL void Error_Reset(void); +BLUETOOTH_C_DLL char* Error_GetLastErrorMsg(void); +BLUETOOTH_C_DLL int Error_GetErrorCount(void); +BLUETOOTH_C_DLL int Error_GetLastErrorNum(void); +BLUETOOTH_C_DLL char* Error_GetLastErrorMethod(void); + +BLUETOOTH_C_DLL void BT_DestroyDevices(BTDeviceH* hDevices, uint32_t nDevices); + +BLUETOOTH_C_DLL char* BT_Version(); + +BLUETOOTH_SERIAL_PORT_C_END + +#endif \ No newline at end of file diff --git a/src/capi/bt_config.h b/src/capi/bt_config.h new file mode 100644 index 0000000..3ce3d78 --- /dev/null +++ b/src/capi/bt_config.h @@ -0,0 +1,39 @@ +#ifndef BLUETOOTH_SERIAL_PORT_CONFIG_H_INCLUDED +#define BLUETOOTH_SERIAL_PORT_CONFIG_H_INCLUDED + +#ifdef _MSC_VER + +#if _MSC_VER <= 1500 + typedef unsigned __int64 uint64_t; +#endif + + #include + #define STRDUP _strdup + +#else + + #include + #define STRDUP strdup +#endif + +#include + +#include "capi/bt_export.h" +#include "DeviceINQ.h" + +class BTSerialPortBinding; +class DeviceINQ; + +typedef enum +{ + RT_None = 0, + RT_Debug = 1, + RT_Warning = 2, + RT_Failure = 3, + RT_Fatal = 4 +} RTError; + +typedef BTSerialPortBinding *BTSerialPortBindingH; +typedef DeviceINQ *BTDeviceINQH; +typedef device *BTDeviceH; +#endif \ No newline at end of file diff --git a/src/capi/bt_export.h b/src/capi/bt_export.h new file mode 100644 index 0000000..d169f63 --- /dev/null +++ b/src/capi/bt_export.h @@ -0,0 +1,25 @@ +#pragma once + +#ifndef BLUETOOTH_C_DLL +#if defined(_MSC_VER) +# define BLUETOOTH_C_DLL __declspec(dllexport) +# define BLUETOOTH_DLL __declspec(dllexport) +#else +# if defined(USE_GCC_VISIBILITY_FLAG) +# define BLUETOOTH_C_DLL __attribute__ ((visibility("default"))) +# define BLUETOOTH_DLL __attribute__ ((visibility("default"))) +# else +# define BLUETOOTH_C_DLL +# define BLUETOOTH_DLL +# endif +#endif + +#ifdef __cplusplus +# define BLUETOOTH_SERIAL_PORT_C_START extern "C" { +# define BLUETOOTH_SERIAL_PORT_C_END } +#else +# define BLUETOOTH_SERIAL_PORT_C_START +# define BLUETOOTH_SERIAL_PORT_C_END +#endif + +#endif \ No newline at end of file diff --git a/src/capi/bt_impl.h b/src/capi/bt_impl.h new file mode 100644 index 0000000..9b8ad17 --- /dev/null +++ b/src/capi/bt_impl.h @@ -0,0 +1,9 @@ +#include +#include +#include + +#include "bt_config.h" +#include "BTSerialPortBinding.h" +#include "DeviceINQ.h" +#include "Enums.h" +#include "Error.h" diff --git a/test/main_c.cc b/test/main_c.cc new file mode 100644 index 0000000..4ffd730 --- /dev/null +++ b/test/main_c.cc @@ -0,0 +1,27 @@ +#include +#include +#include "capi/bt_api.h" + +int main( int argc, const char* argv[] ) +{ + printf("Scanning\n"); + BTDeviceINQH device = BTDeviceINQ_Create(); + BTDeviceH* pDevices; + uint64_t nDevices; + + if (BTDeviceINQ_Inquire(device, &pDevices, &nDevices) == RT_None) + { + printf("Discovered %llu devices\n", nDevices); + for (uint64_t i = 0; i < nDevices; i++) + { + char* pszName = BTDevice_GetName(pDevices[i]); + char* pszAddr = BTDevice_GetAddress(pDevices[i]); + printf("Name: %s\n", pszName); + printf("Address: %s\n", pszAddr); + free(pszName); + } + BT_DestroyDevices(pDevices, nDevices); + } + else + printf("Failed to find any devices"); +} \ No newline at end of file