From 3c7bd525bb738686bc41c91dd43417aecc7ba818 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 22 Sep 2013 07:21:46 +0200 Subject: [PATCH] Add support for native bluetooth communication For the time being, the bluetooth communication code is of very limited use. It's not used anywhere in the library, and as an internal api it's also not available to applications. It serves mainly as a reference implementation for future use. The implementation supports Windows and Linux. --- configure.ac | 20 ++ msvc/libdivecomputer.vcproj | 12 +- src/Makefile.am | 5 +- src/bluetooth.c | 604 ++++++++++++++++++++++++++++++++++++ src/bluetooth.h | 174 +++++++++++ 5 files changed, 811 insertions(+), 4 deletions(-) create mode 100644 src/bluetooth.c create mode 100644 src/bluetooth.h diff --git a/configure.ac b/configure.ac index 8aee86d..695a86b 100644 --- a/configure.ac +++ b/configure.ac @@ -111,8 +111,28 @@ AS_IF([test "x$with_hidapi" != "xno"], [ ]) ]) +# Checks for BlueZ (bluetooth) support. +AC_ARG_WITH([bluez], + [AS_HELP_STRING([--without-bluez], + [Build without the BlueZ library])], + [], [with_bluez=auto]) +AS_IF([test "x$with_bluez" != "xno"], [ + PKG_CHECK_MODULES([BLUEZ], [bluez], [have_bluez=yes], [have_bluez=no]) + AS_IF([test "x$have_bluez" = "xyes"], [ + AC_DEFINE([HAVE_BLUEZ], [1], [BlueZ library]) + DEPENDENCIES="$DEPENDENCIES bluez" + ]) +]) + AC_SUBST([DEPENDENCIES]) +# Checks for Windows bluetooth support. +AC_CHECK_HEADERS([winsock2.h ws2bth.h], , , [ +#if HAVE_WINSOCK2_H +# include +# endif +]) + # Checks for IrDA support. AC_CHECK_HEADERS([winsock2.h af_irda.h], , , [ #if HAVE_WINSOCK2_H diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 738dd12..f7b7883 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -42,7 +42,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\include" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -119,7 +119,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="..\include" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H" RuntimeLibrary="2" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -194,6 +194,10 @@ RelativePath="..\src\atomics_cobalt_parser.c" > + + @@ -524,6 +528,10 @@ RelativePath="..\include\libdivecomputer\atomics_cobalt.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index 966bf94..60b5654 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,9 @@ AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include -AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS) +AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS) $(BLUEZ_CFLAGS) lib_LTLIBRARIES = libdivecomputer.la -libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) -lm +libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm libdivecomputer_la_LDFLAGS = \ -version-info $(DC_VERSION_LIBTOOL) \ -no-undefined \ @@ -77,6 +77,7 @@ endif libdivecomputer_la_SOURCES += irda.h irda.c libdivecomputer_la_SOURCES += usbhid.h usbhid.c +libdivecomputer_la_SOURCES += bluetooth.h bluetooth.c if OS_WIN32 libdivecomputer_la_SOURCES += libdivecomputer.rc diff --git a/src/bluetooth.c b/src/bluetooth.c new file mode 100644 index 0000000..bc9ffe0 --- /dev/null +++ b/src/bluetooth.c @@ -0,0 +1,604 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include // malloc, free + +#ifdef _WIN32 +#define NOGDI +#include +#include +#ifdef HAVE_WS2BTH_H +#define BLUETOOTH +#include +#endif +#else +#include // errno +#include // close +#include // socket, getsockopt +#include // socket, getsockopt +#include // select +#include // ioctl +#include +#ifdef HAVE_BLUEZ +#define BLUETOOTH +#include +#include +#include +#include +#endif +#endif + +#include "bluetooth.h" +#include "common-private.h" +#include "context-private.h" + +#ifdef _WIN32 +typedef int s_ssize_t; +typedef DWORD s_errcode_t; +#define S_ERRNO WSAGetLastError () +#define S_EINTR WSAEINTR +#define S_EAGAIN WSAEWOULDBLOCK +#define S_ENOMEM WSA_NOT_ENOUGH_MEMORY +#define S_EINVAL WSAEINVAL +#define S_EACCES WSAEACCES +#define S_EAFNOSUPPORT WSAEAFNOSUPPORT +#define S_INVALID INVALID_SOCKET +#define S_IOCTL ioctlsocket +#define S_CLOSE closesocket +#else +typedef ssize_t s_ssize_t; +typedef int s_errcode_t; +#define S_ERRNO errno +#define S_EINTR EINTR +#define S_EAGAIN EAGAIN +#define S_ENOMEM ENOMEM +#define S_EINVAL EINVAL +#define S_EACCES EACCES +#define S_EAFNOSUPPORT EAFNOSUPPORT +#define S_INVALID -1 +#define S_IOCTL ioctl +#define S_CLOSE close +#endif + +#ifdef _WIN32 +#define DC_ADDRESS_FORMAT "%012I64X" +#else +#define DC_ADDRESS_FORMAT "%012llX" +#endif + +#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) + +#define MAX_DEVICES 255 +#define MAX_PERIODS 8 + +struct dc_bluetooth_t { + dc_context_t *context; +#ifdef _WIN32 + SOCKET fd; +#else + int fd; +#endif + int timeout; +}; + +#ifdef BLUETOOTH +static dc_status_t +syserror(s_errcode_t errcode) +{ + switch (errcode) { + case S_EINVAL: + return DC_STATUS_INVALIDARGS; + case S_ENOMEM: + return DC_STATUS_NOMEMORY; + case S_EACCES: + return DC_STATUS_NOACCESS; + case S_EAFNOSUPPORT: + return DC_STATUS_UNSUPPORTED; + default: + return DC_STATUS_IO; + } +} +#endif + +#ifdef HAVE_BLUEZ +static dc_bluetooth_address_t +dc_address_get (const bdaddr_t *ba) +{ + dc_bluetooth_address_t address = 0; + + size_t shift = 0; + for (size_t i = 0; i < C_ARRAY_SIZE(ba->b); ++i) { + address |= (dc_bluetooth_address_t) ba->b[i] << shift; + shift += 8; + } + + return address; +} + +static void +dc_address_set (bdaddr_t *ba, dc_bluetooth_address_t address) +{ + size_t shift = 0; + for (size_t i = 0; i < C_ARRAY_SIZE(ba->b); ++i) { + ba->b[i] = (address >> shift) & 0xFF; + shift += 8; + } +} +#endif + +dc_status_t +dc_bluetooth_open (dc_bluetooth_t **out, dc_context_t *context) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + dc_bluetooth_t *device = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + device = (dc_bluetooth_t *) malloc (sizeof (dc_bluetooth_t)); + if (device == NULL) { + SYSERROR (context, S_ENOMEM); + return DC_STATUS_NOMEMORY; + } + + // Library context. + device->context = context; + + // Default to blocking reads. + device->timeout = -1; + +#ifdef _WIN32 + // Initialize the winsock dll. + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD (2, 2); + int rc = WSAStartup (wVersionRequested, &wsaData); + if (rc != 0) { + SYSERROR (context, rc); + status = DC_STATUS_UNSUPPORTED; + goto error_free; + } + + // Confirm that the winsock dll supports version 2.2. + // Note that if the dll supports versions greater than 2.2 in addition to + // 2.2, it will still return 2.2 since that is the version we requested. + if (LOBYTE (wsaData.wVersion) != 2 || + HIBYTE (wsaData.wVersion) != 2) { + ERROR (context, "Incorrect winsock version."); + status = DC_STATUS_UNSUPPORTED; + goto error_wsacleanup; + } +#endif + + // Open the socket. +#ifdef _WIN32 + device->fd = socket (AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); +#else + device->fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); +#endif + if (device->fd == S_INVALID) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (context, errcode); + status = syserror(errcode); + goto error_wsacleanup; + } + + *out = device; + + return DC_STATUS_SUCCESS; + +error_wsacleanup: +#ifdef _WIN32 + WSACleanup (); +error_free: +#endif + free (device); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_close (dc_bluetooth_t *device) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + + if (device == NULL) + return DC_STATUS_SUCCESS; + + // Terminate all send and receive operations. + shutdown (device->fd, 0); + + // Close the socket. + if (S_CLOSE (device->fd) != 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + dc_status_set_error(&status, syserror(errcode)); + } + +#ifdef _WIN32 + // Terminate the winsock dll. + if (WSACleanup () != 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + dc_status_set_error(&status, syserror(errcode)); + } +#endif + + // Free memory. + free (device); + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_set_timeout (dc_bluetooth_t *device, int timeout) +{ +#ifdef BLUETOOTH + if (device == NULL) + return DC_STATUS_INVALIDARGS; + + INFO (device->context, "Timeout: value=%i", timeout); + + device->timeout = timeout; + + return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_discover (dc_bluetooth_t *device, dc_bluetooth_callback_t callback, void *userdata) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + + if (device == NULL) + return DC_STATUS_INVALIDARGS; + +#ifdef _WIN32 + WSAQUERYSET wsaq; + memset(&wsaq, 0, sizeof (wsaq)); + wsaq.dwSize = sizeof (wsaq); + wsaq.dwNameSpace = NS_BTH; + wsaq.lpcsaBuffer = NULL; + + HANDLE hLookup; + if (WSALookupServiceBegin(&wsaq, LUP_CONTAINERS | LUP_FLUSHCACHE, &hLookup) != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == WSASERVICE_NOT_FOUND) { + // No remote bluetooth devices found. + status = DC_STATUS_SUCCESS; + } else { + SYSERROR (device->context, errcode); + status = syserror(errcode); + } + goto error_exit; + } + + unsigned char buf[4096]; + LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf; + memset(pwsaResults, 0, sizeof(WSAQUERYSET)); + pwsaResults->dwSize = sizeof(WSAQUERYSET); + pwsaResults->dwNameSpace = NS_BTH; + pwsaResults->lpBlob = NULL; + + while (1) { + DWORD dwSize = sizeof(buf); + if (WSALookupServiceNext (hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults) != 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == WSA_E_NO_MORE || errcode == WSAENOMORE) { + break; // No more results. + } + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_close; + } + + if (pwsaResults->dwNumberOfCsAddrs == 0 || + pwsaResults->lpcsaBuffer == NULL || + pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr == NULL) { + ERROR (device->context, "Invalid results returned"); + status = DC_STATUS_IO; + goto error_close; + } + + SOCKADDR_BTH *sa = (SOCKADDR_BTH *) pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr; + dc_bluetooth_address_t address = sa->btAddr; + const char *name = (char *) pwsaResults->lpszServiceInstanceName; + + INFO (device->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name); + + if (callback) callback (address, name, userdata); + + } + +error_close: + WSALookupServiceEnd (hLookup); +#else + // Get the resource number for the first available bluetooth adapter. + int dev = hci_get_route (NULL); + if (dev < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_exit; + } + + // Open a socket to the bluetooth adapter. + int fd = hci_open_dev (dev); + if (fd < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_exit; + } + + // Allocate a buffer to store the results of the discovery. + inquiry_info *devices = (inquiry_info *) malloc (MAX_DEVICES * sizeof(inquiry_info)); + if (devices == NULL) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_close; + } + + // Perform the bluetooth device discovery. The inquiry lasts for at + // most MAX_PERIODS * 1.28 seconds, and at most MAX_DEVICES devices + // will be returned. + int ndevices = hci_inquiry (dev, MAX_PERIODS, MAX_DEVICES, NULL, &devices, IREQ_CACHE_FLUSH); + if (ndevices < 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto error_free; + } + + for (unsigned int i = 0; i < ndevices; ++i) { + dc_bluetooth_address_t address = dc_address_get (&devices[i].bdaddr); + + // Get the user friendly name. + char buf[HCI_MAX_NAME_LENGTH], *name = buf; + int rc = hci_read_remote_name (fd, &devices[i].bdaddr, sizeof(buf), buf, 0); + if (rc < 0) { + name = NULL; + } + + INFO (device->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name); + + if (callback) callback (address, name, userdata); + } + +error_free: + free(devices); +error_close: + hci_close_dev(fd); +#endif + +error_exit: + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_connect (dc_bluetooth_t *device, dc_bluetooth_address_t address, unsigned int port) +{ +#ifdef BLUETOOTH + if (device == NULL) + return DC_STATUS_INVALIDARGS; + + INFO (device->context, "Connect: address=" DC_ADDRESS_FORMAT ", port=%d", address, port); + +#ifdef _WIN32 + SOCKADDR_BTH sa; + sa.addressFamily = AF_BTH; + sa.btAddr = address; + sa.port = port; + memset(&sa.serviceClassId, 0, sizeof(sa.serviceClassId)); +#else + struct sockaddr_rc sa; + sa.rc_family = AF_BLUETOOTH; + sa.rc_channel = port; + dc_address_set (&sa.rc_bdaddr, address); +#endif + + if (connect (device->fd, (struct sockaddr *) &sa, sizeof (sa)) != 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + return syserror(errcode); + } + + return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_get_available (dc_bluetooth_t *device, size_t *value) +{ +#ifdef BLUETOOTH + if (device == NULL) + return DC_STATUS_INVALIDARGS; + +#ifdef _WIN32 + unsigned long bytes = 0; +#else + int bytes = 0; +#endif + + if (S_IOCTL (device->fd, FIONREAD, &bytes) != 0) { + s_errcode_t errcode = S_ERRNO; + SYSERROR (device->context, errcode); + return syserror(errcode); + } + + if (value) + *value = bytes; + + return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_read (dc_bluetooth_t *device, void *data, size_t size, size_t *actual) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + size_t nbytes = 0; + + if (device == NULL) { + status = DC_STATUS_INVALIDARGS; + goto out_invalidargs; + } + + while (nbytes < size) { + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); + + struct timeval tvt; + if (device->timeout > 0) { + tvt.tv_sec = (device->timeout / 1000); + tvt.tv_usec = (device->timeout % 1000) * 1000; + } else if (device->timeout == 0) { + timerclear (&tvt); + } + + int rc = select (device->fd + 1, &fds, NULL, NULL, device->timeout >= 0 ? &tvt : NULL); + if (rc < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (rc == 0) { + break; // Timeout. + } + + s_ssize_t n = recv (device->fd, (char*) data + nbytes, size - nbytes, 0); + if (n < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR || errcode == S_EAGAIN) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (n == 0) { + break; // EOF reached. + } + + nbytes += n; + } + + if (nbytes != size) { + status = DC_STATUS_TIMEOUT; + } + +out: + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes); + +out_invalidargs: + if (actual) + *actual = nbytes; + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_bluetooth_write (dc_bluetooth_t *device, const void *data, size_t size, size_t *actual) +{ +#ifdef BLUETOOTH + dc_status_t status = DC_STATUS_SUCCESS; + size_t nbytes = 0; + + if (device == NULL) { + status = DC_STATUS_INVALIDARGS; + goto out_invalidargs; + } + + while (nbytes < size) { + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); + + int rc = select (device->fd + 1, NULL, &fds, NULL, NULL); + if (rc < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (rc == 0) { + break; // Timeout. + } + + s_ssize_t n = send (device->fd, (char*) data + nbytes, size - nbytes, 0); + if (n < 0) { + s_errcode_t errcode = S_ERRNO; + if (errcode == S_EINTR || errcode == S_EAGAIN) + continue; // Retry. + SYSERROR (device->context, errcode); + status = syserror(errcode); + goto out; + } else if (n == 0) { + break; // EOF. + } + + nbytes += n; + } + + if (nbytes != size) { + status = DC_STATUS_TIMEOUT; + } + +out: + HEXDUMP (device->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes); + +out_invalidargs: + if (actual) + *actual = nbytes; + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} diff --git a/src/bluetooth.h b/src/bluetooth.h new file mode 100644 index 0000000..d2e3868 --- /dev/null +++ b/src/bluetooth.h @@ -0,0 +1,174 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef DC_BLUETOOTH_H +#define DC_BLUETOOTH_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Opaque object representing a bluetooth connection. + */ +typedef struct dc_bluetooth_t dc_bluetooth_t; + +/** + * Bluetooth address (48 bits). + */ +#if defined (_WIN32) && !defined (__GNUC__) +typedef unsigned __int64 dc_bluetooth_address_t; +#else +typedef unsigned long long dc_bluetooth_address_t; +#endif + +/** + * Bluetooth enumeration callback. + * + * @param[in] address The bluetooth device address. + * @param[in] name The bluetooth device name. + * @param[in] userdata The user data pointer. + */ +typedef void (*dc_bluetooth_callback_t) (dc_bluetooth_address_t address, const char *name, void *userdata); + +/** + * Open an bluetooth connection. + * + * @param[out] bluetooth A location to store the bluetooth connection. + * @param[in] context A valid context object. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_open (dc_bluetooth_t **bluetooth, dc_context_t *context); + +/** + * Close the bluetooth connection and free all resources. + * + * @param[in] bluetooth A valid bluetooth connection. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_close (dc_bluetooth_t *bluetooth); + +/** + * Set the read timeout. + * + * There are three distinct modes available: + * + * 1. Blocking (timeout < 0): + * + * The read operation is blocked until all the requested bytes have + * been received. If the requested number of bytes does not arrive, + * the operation will block forever. + * + * 2. Non-blocking (timeout == 0): + * + * The read operation returns immediately with the bytes that have + * already been received, even if no bytes have been received. + * + * 3. Timeout (timeout > 0): + * + * The read operation is blocked until all the requested bytes have + * been received. If the requested number of bytes does not arrive + * within the specified amount of time, the operation will return + * with the bytes that have already been received. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[in] timeout The timeout in milliseconds. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_set_timeout (dc_bluetooth_t *bluetooth, int timeout); + +/** + * Enumerate the bluetooth devices. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[in] callback The callback function to call. + * @param[in] userdata User data to pass to the callback function. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_discover (dc_bluetooth_t *bluetooth, dc_bluetooth_callback_t callback, void *userdata); + +/** + * Connect to an bluetooth device. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[in] address The bluetooth device address. + * @param[in] port The bluetooth port number. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_connect (dc_bluetooth_t *bluetooth, dc_bluetooth_address_t address, unsigned int port); + +/** + * Query the number of available bytes in the input buffer. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[out] value A location to store the number of bytes in + * the input buffer. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_get_available (dc_bluetooth_t *bluetooth, size_t *value); + +/** + * Read data from the bluetooth connection. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[out] data The memory buffer to read the data into. + * @param[in] size The number of bytes to read. + * @param[out] actual An (optional) location to store the actual + * number of bytes transferred. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_read (dc_bluetooth_t *bluetooth, void *data, size_t size, size_t *actual); + +/** + * Write data to the bluetooth connection. + * + * @param[in] bluetooth A valid bluetooth connection. + * @param[in] data The memory buffer to write the data from. + * @param[in] size The number of bytes to write. + * @param[out] actual An (optional) location to store the actual + * number of bytes transferred. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_bluetooth_write (dc_bluetooth_t *bluetooth, const void *data, size_t size, size_t *actual); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_BLUETOOTH_H */