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 */