| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- // SPDX-License-Identifier: LGPL-2.1-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2017 Intel Corporation. All rights reserved.
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <stdio.h>
- #include <errno.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdbool.h>
- #include <sys/uio.h>
- #include <wordexp.h>
- #include <glib.h>
- #include "src/shared/io.h"
- #include "src/shared/shell.h"
- #include "gdbus/gdbus.h"
- #include "lib/bluetooth.h"
- #include "lib/uuid.h"
- #include "tools/mesh-gatt/node.h"
- #include "tools/mesh-gatt/util.h"
- #include "tools/mesh-gatt/gatt.h"
- #include "tools/mesh-gatt/prov.h"
- #include "tools/mesh-gatt/net.h"
- #define MESH_PROV_DATA_OUT_UUID_STR "00002adc-0000-1000-8000-00805f9b34fb"
- #define MESH_PROXY_DATA_OUT_UUID_STR "00002ade-0000-1000-8000-00805f9b34fb"
- static struct io *write_io;
- static uint16_t write_mtu;
- static struct io *notify_io;
- static uint16_t notify_mtu;
- struct write_data {
- GDBusProxy *proxy;
- void *user_data;
- struct iovec iov;
- GDBusReturnFunction cb;
- uint8_t *gatt_data;
- uint8_t gatt_len;
- };
- struct notify_data {
- GDBusProxy *proxy;
- bool enable;
- GDBusReturnFunction cb;
- void *user_data;
- };
- bool mesh_gatt_is_child(GDBusProxy *proxy, GDBusProxy *parent,
- const char *name)
- {
- DBusMessageIter iter;
- const char *parent_path;
- if (!parent)
- return FALSE;
- if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
- return FALSE;
- dbus_message_iter_get_basic(&iter, &parent_path);
- if (!strcmp(parent_path, g_dbus_proxy_get_path(parent)))
- return TRUE;
- else
- return FALSE;
- }
- /* Refactor this once actual MTU is available */
- #define GATT_MTU 23
- static void write_data_free(void *user_data)
- {
- struct write_data *data = user_data;
- g_free(data->gatt_data);
- free(data);
- }
- uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size)
- {
- const uint8_t *data = *pkt;
- uint8_t gatt_hdr = *data++;
- uint8_t type = gatt_hdr & GATT_TYPE_MASK;
- static uint8_t gatt_size;
- static uint8_t gatt_pkt[67];
- print_byte_array("GATT-RX:\t", *pkt, size);
- if (size < 1) {
- gatt_pkt[0] = GATT_TYPE_INVALID;
- /* TODO: Disconnect GATT per last paragraph sec 6.6 */
- return 0;
- }
- size--;
- switch (gatt_hdr & GATT_SAR_MASK) {
- case GATT_SAR_FIRST:
- gatt_size = 1;
- gatt_pkt[0] = type;
- /* TODO: Start Proxy Timeout */
- /* fall through */
- case GATT_SAR_CONTINUE:
- if (gatt_pkt[0] != type ||
- gatt_size + size > MAX_GATT_SIZE) {
- /* Invalidate packet and return failure */
- gatt_pkt[0] = GATT_TYPE_INVALID;
- /* TODO: Disconnect GATT per last paragraph sec 6.6 */
- return 0;
- }
- memcpy(gatt_pkt + gatt_size, data, size);
- gatt_size += size;
- /* We are good to this point, but incomplete */
- return 0;
- default:
- case GATT_SAR_COMPLETE:
- gatt_size = 1;
- gatt_pkt[0] = type;
- /* fall through */
- case GATT_SAR_LAST:
- if (gatt_pkt[0] != type ||
- gatt_size + size > MAX_GATT_SIZE) {
- /* Invalidate packet and return failure */
- gatt_pkt[0] = GATT_TYPE_INVALID;
- /* Disconnect GATT per last paragraph sec 6.6 */
- return 0;
- }
- memcpy(gatt_pkt + gatt_size, data, size);
- gatt_size += size;
- *pkt = gatt_pkt;
- return gatt_size;
- }
- }
- static int sock_send(struct io *io, struct iovec *iov, size_t iovlen)
- {
- struct msghdr msg;
- int ret;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = iov;
- msg.msg_iovlen = iovlen;
- ret = sendmsg(io_get_fd(io), &msg, MSG_NOSIGNAL);
- if (ret < 0) {
- ret = -errno;
- bt_shell_printf("sendmsg: %s", strerror(-ret));
- }
- return ret;
- }
- static bool sock_write(struct io *io, void *user_data)
- {
- struct write_data *data = user_data;
- struct iovec iov[2];
- uint8_t sar;
- uint8_t max_len;
- if (data == NULL)
- return true;
- max_len = write_mtu ? write_mtu - 3 - 1 : GATT_MTU - 3 - 1;
- print_byte_array("GATT-TX:\t", data->gatt_data, data->gatt_len);
- sar = data->gatt_data[0];
- data->iov.iov_base = data->gatt_data + 1;
- data->iov.iov_len--;
- sar = data->gatt_data[0] & GATT_TYPE_MASK;
- data->gatt_len--;
- if (data->gatt_len > max_len) {
- sar |= GATT_SAR_FIRST;
- data->iov.iov_len = max_len;
- }
- iov[0].iov_base = &sar;
- iov[0].iov_len = sizeof(sar);
- while (1) {
- int err;
- iov[1] = data->iov;
- err = sock_send(io, iov, 2);
- if (err < 0) {
- write_data_free(data);
- return false;
- }
- switch (sar & GATT_SAR_MASK) {
- case GATT_SAR_FIRST:
- case GATT_SAR_CONTINUE:
- data->gatt_len -= max_len;
- data->iov.iov_base = data->iov.iov_base + max_len;
- sar &= GATT_TYPE_MASK;
- if (max_len < data->gatt_len) {
- data->iov.iov_len = max_len;
- sar |= GATT_SAR_CONTINUE;
- } else {
- data->iov.iov_len = data->gatt_len;
- sar |= GATT_SAR_LAST;
- }
- break;
- default:
- if(data->cb)
- data->cb(NULL, data->user_data);
- write_data_free(data);
- return true;
- }
- }
- }
- static void write_io_destroy(void)
- {
- io_destroy(write_io);
- write_io = NULL;
- write_mtu = 0;
- }
- static void notify_io_destroy(void)
- {
- io_destroy(notify_io);
- notify_io = NULL;
- notify_mtu = 0;
- }
- static bool sock_hup(struct io *io, void *user_data)
- {
- bt_shell_printf("%s closed\n", io == notify_io ? "Notify" : "Write");
- if (io == notify_io)
- notify_io_destroy();
- else
- write_io_destroy();
- return false;
- }
- static struct io *sock_io_new(int fd)
- {
- struct io *io;
- io = io_new(fd);
- io_set_close_on_destroy(io, true);
- io_set_disconnect_handler(io, sock_hup, NULL, NULL);
- return io;
- }
- static void acquire_write_reply(DBusMessage *message, void *user_data)
- {
- struct write_data *data = user_data;
- DBusError error;
- int fd;
- dbus_error_init(&error);
- if (dbus_set_error_from_message(&error, message) == TRUE) {
- dbus_error_free(&error);
- bt_shell_printf("Failed to write\n");
- write_data_free(data);
- return;
- }
- if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
- DBUS_TYPE_UINT16, &write_mtu,
- DBUS_TYPE_INVALID) == false)) {
- bt_shell_printf("Invalid AcquireWrite response\n");
- return;
- }
- bt_shell_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_mtu);
- write_io = sock_io_new(fd);
- sock_write(write_io, data);
- }
- static void acquire_setup(DBusMessageIter *iter, void *user_data)
- {
- DBusMessageIter dict;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &dict);
- dbus_message_iter_close_container(iter, &dict);
- }
- bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
- GDBusReturnFunction cb, void *user_data)
- {
- struct write_data *data;
- if (!buf || !len)
- return false;
- if (len > 69)
- return false;
- data = g_new0(struct write_data, 1);
- if (!data)
- return false;
- /* TODO: should keep in queue in case we need to cancel write? */
- data->gatt_len = len;
- data->gatt_data = g_memdup(buf, len);
- data->gatt_data[0] &= GATT_TYPE_MASK;
- data->iov.iov_base = data->gatt_data;
- data->iov.iov_len = len;
- data->proxy = proxy;
- data->user_data = user_data;
- data->cb = cb;
- if (write_io)
- return sock_write(write_io, data);
- if (g_dbus_proxy_method_call(proxy, "AcquireWrite",
- acquire_setup, acquire_write_reply,
- data, NULL) == FALSE) {
- bt_shell_printf("Failed to AcquireWrite\n");
- write_data_free(data);
- return false;
- }
- return true;
- }
- static void notify_reply(DBusMessage *message, void *user_data)
- {
- struct notify_data *data = user_data;
- DBusError error;
- dbus_error_init(&error);
- if (dbus_set_error_from_message(&error, message) == TRUE) {
- bt_shell_printf("Failed to %s notify: %s\n",
- data->enable ? "start" : "stop", error.name);
- dbus_error_free(&error);
- goto done;
- }
- bt_shell_printf("Notify %s\n", data->enable ? "started" : "stopped");
- done:
- if (data->cb)
- data->cb(message, data->user_data);
- g_free(data);
- }
- static bool sock_read(struct io *io, bool prov, void *user_data)
- {
- struct mesh_node *node = user_data;
- struct msghdr msg;
- struct iovec iov;
- uint8_t buf[512];
- uint8_t *res;
- int fd = io_get_fd(io);
- ssize_t len, len_sar;
- if (io != notify_io)
- return true;
- iov.iov_base = buf;
- iov.iov_len = sizeof(buf);
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- while ((len = recvmsg(fd, &msg, MSG_DONTWAIT))) {
- if (len < 0) {
- if (errno == EAGAIN)
- break;
- return false;
- }
- res = buf;
- len_sar = mesh_gatt_sar(&res, len);
- if (len_sar) {
- if (prov)
- prov_data_ready(node, res, len_sar);
- else
- net_data_ready(res, len_sar);
- }
- }
- /* When socket is orderly closed, then recvmsg returns 0 */
- if (len == 0)
- return false;
- return true;
- }
- static bool sock_read_prov(struct io *io, void *user_data)
- {
- return sock_read(io, true, user_data);
- }
- static bool sock_read_proxy(struct io *io, void *user_data)
- {
- return sock_read(io, false, user_data);
- }
- static void acquire_notify_reply(DBusMessage *message, void *user_data)
- {
- struct notify_data *data = user_data;
- DBusMessageIter iter;
- DBusError error;
- int fd;
- const char *uuid;
- dbus_error_init(&error);
- if (dbus_set_error_from_message(&error, message) == TRUE) {
- dbus_error_free(&error);
- if (g_dbus_proxy_method_call(data->proxy, "StartNotify", NULL,
- notify_reply, data, NULL) == FALSE) {
- bt_shell_printf("Failed to StartNotify\n");
- g_free(data);
- }
- return;
- }
- if (notify_io) {
- io_destroy(notify_io);
- notify_io = NULL;
- }
- notify_mtu = 0;
- if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
- DBUS_TYPE_UINT16, ¬ify_mtu,
- DBUS_TYPE_INVALID) == false)) {
- if (g_dbus_proxy_method_call(data->proxy, "StartNotify", NULL,
- notify_reply, data, NULL) == FALSE) {
- bt_shell_printf("Failed to StartNotify\n");
- g_free(data);
- }
- return;
- }
- bt_shell_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_mtu);
- if (g_dbus_proxy_get_property(data->proxy, "UUID", &iter) == FALSE)
- goto done;
- notify_io = sock_io_new(fd);
- dbus_message_iter_get_basic(&iter, &uuid);
- if (!bt_uuid_strcmp(uuid, MESH_PROV_DATA_OUT_UUID_STR))
- io_set_read_handler(notify_io, sock_read_prov, data->user_data,
- NULL);
- else if (!bt_uuid_strcmp(uuid, MESH_PROXY_DATA_OUT_UUID_STR))
- io_set_read_handler(notify_io, sock_read_proxy, data->user_data,
- NULL);
- done:
- if (data->cb)
- data->cb(message, data->user_data);
- g_free(data);
- }
- bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,
- void *user_data)
- {
- struct notify_data *data;
- DBusMessageIter iter;
- const char *method;
- GDBusSetupFunction setup = NULL;
- data = g_new0(struct notify_data, 1);
- data->proxy = proxy;
- data->enable = enable;
- data->cb = cb;
- data->user_data = user_data;
- if (enable == TRUE) {
- if (g_dbus_proxy_get_property(proxy, "NotifyAcquired", &iter)) {
- method = "AcquireNotify";
- cb = acquire_notify_reply;
- setup = acquire_setup;
- } else {
- method = "StartNotify";
- cb = notify_reply;
- }
- } else {
- if (notify_io) {
- notify_io_destroy();
- if (cb)
- cb(NULL, user_data);
- g_free(data);
- return true;
- } else {
- method = "StopNotify";
- cb = notify_reply;
- }
- }
- if (g_dbus_proxy_method_call(proxy, method, setup, cb,
- data, NULL) == FALSE) {
- bt_shell_printf("Failed to %s\n", method);
- return false;
- }
- return true;
- }
|