diff --git a/dbus/CMakeLists.txt b/dbus/CMakeLists.txt index 2fe1081..76a7b4a 100644 --- a/dbus/CMakeLists.txt +++ b/dbus/CMakeLists.txt @@ -83,6 +83,7 @@ include_directories(. ${CMAKE_SOURCE_DIR}/common ${CMAKE_CURRENT_BINARY_DIR} ${G set(gattlib_SRCS gattlib.c gattlib_adapter.c gattlib_char.c + gattlib_stream.c bluez5/lib/uuid.c ${CMAKE_SOURCE_DIR}/common/gattlib_common.c ${CMAKE_CURRENT_BINARY_DIR}/org-bluez-adaptater1.c diff --git a/dbus/gattlib_stream.c b/dbus/gattlib_stream.c new file mode 100644 index 0000000..b421019 --- /dev/null +++ b/dbus/gattlib_stream.c @@ -0,0 +1,96 @@ +/* + * + * GattLib - GATT Library + * + * Copyright (C) 2016-2019 Olivier Martin + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include "gattlib_internal.h" + +#if BLUEZ_VERSION < BLUEZ_VERSIONS(5, 48) + +int gattlib_write_char_by_uuid_stream_open(gatt_connection_t* connection, uuid_t* uuid, gatt_stream_t **stream, uint16_t *mtu) +{ + return GATTLIB_NOT_SUPPORTED; +} + +int gattlib_write_char_stream_write(gatt_stream_t *stream, const void *buffer, size_t buffer_len) +{ + return GATTLIB_NOT_SUPPORTED; +} + +int gattlib_write_char_stream_close(gatt_stream_t *stream) +{ + return GATTLIB_NOT_SUPPORTED; +} + +#else + +int gattlib_write_char_by_uuid_stream_open(gatt_connection_t* connection, uuid_t* uuid, gatt_stream_t **stream, uint16_t *mtu) +{ + struct dbus_characteristic dbus_characteristic = get_characteristic_from_uuid(connection, uuid); + GError *error = NULL; + GUnixFDList *fd_list; + GVariant *out_fd; + int fd; + + GVariantBuilder *variant_options = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + + org_bluez_gatt_characteristic1_call_acquire_write_sync( + dbus_characteristic.gatt, + g_variant_builder_end(variant_options), + NULL /* fd_list */, + &out_fd, mtu, + &fd_list, + NULL /* cancellable */, &error); + if (error != NULL) { + fprintf(stderr, "Failed to acquired write DBus GATT characteristic: %s\n", error->message); + g_error_free(error); + return GATTLIB_ERROR_DBUS; + } + + error = NULL; + fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(out_fd), &error); + if (error != NULL) { + fprintf(stderr, "Failed to retrieve Unix File Descriptor: %s\n", error->message); + g_error_free(error); + return GATTLIB_ERROR_DBUS; + } + + // We abuse the pointer 'stream' to pass the 'File Descriptor' + *stream = (gatt_stream_t*)(unsigned long)fd; + + return GATTLIB_SUCCESS; +} + +int gattlib_write_char_stream_write(gatt_stream_t *stream, const void *buffer, size_t buffer_len) +{ + write((unsigned long)stream, buffer, buffer_len); + return GATTLIB_SUCCESS; +} + +int gattlib_write_char_stream_close(gatt_stream_t *stream) +{ + close((unsigned long)stream); + return GATTLIB_SUCCESS; +} + +#endif /* #if BLUEZ_VERSION < BLUEZ_VERSIONS(5, 48) */ diff --git a/gattlib-py/gattlib/__init__.py b/gattlib-py/gattlib/__init__.py index 026247d..afa13e6 100644 --- a/gattlib-py/gattlib/__init__.py +++ b/gattlib-py/gattlib/__init__.py @@ -78,6 +78,10 @@ gattlib_read_char_by_uuid.argtypes = [c_void_p, POINTER(GattlibUuid), POINTER(c_ gattlib_write_char_by_uuid = gattlib.gattlib_write_char_by_uuid gattlib_write_char_by_uuid.argtypes = [c_void_p, POINTER(GattlibUuid), c_void_p, c_size_t] +# int gattlib_write_char_by_uuid_stream_open(gatt_connection_t* connection, uuid_t* uuid, gatt_stream_t **stream, uint16_t *mtu) +gattlib_write_char_by_uuid_stream_open = gattlib.gattlib_write_char_by_uuid_stream_open +gattlib_write_char_by_uuid_stream_open.argtypes = [c_void_p, POINTER(GattlibUuid), POINTER(c_void_p), POINTER(c_uint16)] + # int gattlib_notification_start(gatt_connection_t* connection, const uuid_t* uuid); gattlib_notification_start = gattlib.gattlib_notification_start gattlib_notification_start.argtypes = [c_void_p, POINTER(GattlibUuid)] diff --git a/gattlib-py/gattlib/gatt.py b/gattlib-py/gattlib/gatt.py index fa98272..6e92dd1 100644 --- a/gattlib-py/gattlib/gatt.py +++ b/gattlib-py/gattlib/gatt.py @@ -3,6 +3,35 @@ from .uuid import gattlib_uuid_to_uuid, gattlib_uuid_to_int from .exception import handle_return +class GattStream(): + + def __init__(self, fd, mtu): + self._fd = fd + self._mtu = mtu + + @property + def mtu(self): + # Remove ATT Header (3 bytes) + return self._mtu - 3 + + def write(self, data, mtu=None): + if mtu is None: + mtu = self.mtu + + while len(data) > 0: + frame = data[0:mtu] + data = data[mtu:] + + buffer_type = c_char * len(frame) + buffer = frame + buffer_len = len(frame) + + gattlib.gattlib_write_char_stream_write(self._fd, buffer_type.from_buffer_copy(buffer), buffer_len) + + def close(self): + gattlib.gattlib_write_char_stream_close(self._fd) + + class GattService(): def __init__(self, device, gattlib_primary_service): @@ -62,6 +91,15 @@ class GattCharacteristic(): ret = gattlib_write_char_by_uuid(self.connection, self._gattlib_characteristic.uuid, buffer_type.from_buffer_copy(buffer), buffer_len) handle_return(ret) + def stream_open(self): + _stream = c_void_p(None) + _mtu = c_uint16(0) + + ret = gattlib_write_char_by_uuid_stream_open(self.connection, self._gattlib_characteristic.uuid, byref(_stream), byref(_mtu)) + handle_return(ret) + + return GattStream(_stream, _mtu.value) + def register_notification(self, callback, user_data=None): self._device._notification_add_gatt_characteristic_callback(self, callback, user_data) diff --git a/include/gattlib.h b/include/gattlib.h index bc00f1f..bf3c30e 100644 --- a/include/gattlib.h +++ b/include/gattlib.h @@ -94,6 +94,7 @@ extern "C" { #define GATTLIB_DISCOVER_FILTER_USE_RSSI (1 << 1) typedef struct _gatt_connection_t gatt_connection_t; +typedef struct _gatt_stream_t gatt_stream_t; typedef void (*gattlib_event_handler_t)(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data); @@ -336,6 +337,10 @@ int gattlib_write_char_by_handle(gatt_connection_t* connection, uint16_t handle, */ int gattlib_write_without_response_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, const void* buffer, size_t buffer_len); +int gattlib_write_char_by_uuid_stream_open(gatt_connection_t* connection, uuid_t* uuid, gatt_stream_t **stream, uint16_t *mtu); +int gattlib_write_char_stream_write(gatt_stream_t *stream, const void *buffer, size_t buffer_len); +int gattlib_write_char_stream_close(gatt_stream_t *stream); + /** * @brief Function to write without response to the GATT characteristic handle *