Merge BlueZ 5.40 to master
Merge BlueZ 5.40 from gfiber-internal/upstream to master.
Change-Id: Ib8ba5c60af9d7d37e025d696bff6f091f39b427e
diff --git a/ChangeLog b/ChangeLog
index ce664b4..dab8141 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+ver 5.40:
+ Fix issue with not storing GATT attributes.
+ Fix issue with optional GATT notifications.
+ Fix issue with reading GATT extended properties.
+ Fix issue with GATT device name properties.
+ Fix issue with previously paired devices.
+ Fix issue with handling device removal.
+ Fix issue with profile connection handling.
+ Add support for TTY monitor protocol.
+
ver 5.39:
Fix issue with missing uHID kernel support.
Fix issue with GATT reliable write handling.
diff --git a/HACKING b/HACKING
index a8fb403..d93730d 100644
--- a/HACKING
+++ b/HACKING
@@ -108,6 +108,19 @@
welcome! In order to ease the inclusion of your patch, it's important to follow
some rules, otherwise it will likely be rejected by maintainers.
+Make sure the author name and email are set properly:
+
+ # git config --global user.name <name>
+ # git config --global user.email <email>
+
+The preferred way to send patches is by email, using git send-email:
+
+ # git config --global sendemail.smtpencryption <tls>
+ # git config --global sendemail.smtpserver <smtp.gmail.com>
+ # git config --global sendemail.smtpuser <yourname@gmail.com>
+ # git config --global sendemail.smtpserverport <587>
+ # git config sendemail.to linux-bluetooth@vger.kernel.org
+
BlueZ rules for submitting patches follow most of the rules used by Linux kernel
(https://www.kernel.org/doc/Documentation/SubmittingPatches) with some remarks:
@@ -128,3 +141,17 @@
should be limited to 50 characters and the description should be wrapped at 72
characters except if it contains quoted information from debug tools like
backtraces, compiler errors, etc.
+
+6) Prefix the email subject with [PATCH BlueZ]:
+
+ # git config format.subjectprefix "PATCH BlueZ"
+
+7) Add a cover letter when introducing a new feature explaning what problem
+you're trying to solve:
+
+ # git format-patch --cover-letter -M origin/master -o outgoing/
+ # edit outgoing/0000-*
+
+8) Submit:
+
+ # git send-email outgoing/*
diff --git a/Makefile.am b/Makefile.am
index bac371b..5510102 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,7 +80,7 @@
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:11:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:12:18
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
endif
@@ -118,7 +118,8 @@
src/shared/gatt-client.h src/shared/gatt-client.c \
src/shared/gatt-server.h src/shared/gatt-server.c \
src/shared/gatt-db.h src/shared/gatt-db.c \
- src/shared/gap.h src/shared/gap.c
+ src/shared/gap.h src/shared/gap.c \
+ src/shared/tty.h
src_libshared_glib_la_SOURCES = $(shared_sources) \
src/shared/io-glib.c \
@@ -261,6 +262,8 @@
EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \
doc/pts-opp.txt
+EXTRA_DIST += doc/btsnoop.txt
+
EXTRA_DIST += tools/magic.btsnoop
AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
diff --git a/Makefile.tools b/Makefile.tools
index 36d2a1b..af709b8 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -36,7 +36,8 @@
monitor/keys.h monitor/keys.c \
monitor/analyze.h monitor/analyze.c \
monitor/intel.h monitor/intel.c \
- monitor/broadcom.h monitor/broadcom.c
+ monitor/broadcom.h monitor/broadcom.c \
+ monitor/tty.h
monitor_btmon_LDADD = lib/libbluetooth-internal.la \
src/libshared-mainloop.la @UDEV_LIBS@
endif
diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
index 7526782..4b09663 100644
--- a/android/bluetoothd-snoop.c
+++ b/android/bluetoothd-snoop.c
@@ -139,7 +139,7 @@
flags = get_flags_from_opcode(opcode);
if (flags != 0xff)
- btsnoop_write(snoop, tv, flags, monitor_buf, pktlen);
+ btsnoop_write(snoop, tv, flags, 0, monitor_buf, pktlen);
}
}
diff --git a/client/gatt.c b/client/gatt.c
index 7dd3c94..fee1cf9 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -379,9 +379,23 @@
rl_hexdump(value, len);
}
+static void read_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);
+ /* TODO: Add offset support */
+ dbus_message_iter_close_container(iter, &dict);
+}
+
static void read_attribute(GDBusProxy *proxy)
{
- if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply,
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply,
NULL, NULL) == FALSE) {
rl_printf("Failed to read\n");
return;
@@ -421,12 +435,21 @@
static void write_setup(DBusMessageIter *iter, void *user_data)
{
struct iovec *iov = user_data;
- DBusMessageIter array;
+ DBusMessageIter array, dict;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
&iov->iov_base, iov->iov_len);
dbus_message_iter_close_container(iter, &array);
+
+ 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);
+ /* TODO: Add offset support */
+ dbus_message_iter_close_container(iter, &dict);
}
static void write_attribute(GDBusProxy *proxy, char *arg)
diff --git a/client/main.c b/client/main.c
index 209cf0c..c75b558 100644
--- a/client/main.c
+++ b/client/main.c
@@ -350,7 +350,6 @@
rl_set_prompt(desc ? desc : PROMPT_ON);
printf("\r");
rl_on_new_line();
- rl_redisplay();
g_free(desc);
}
diff --git a/configure.ac b/configure.ac
index 1af8e00..62c82ea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
-AC_INIT(bluez, 5.39)
+AC_INIT(bluez, 5.40)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
diff --git a/doc/btsnoop.txt b/doc/btsnoop.txt
new file mode 100644
index 0000000..975a53f
--- /dev/null
+++ b/doc/btsnoop.txt
@@ -0,0 +1,178 @@
+BTSnoop/Monitor protocol formats
+********************************
+
+Opcode definitions
+==================
+
+New Index
+---------
+
+ Code: 0x0000
+ Parameters: Type (1 Octet
+ Bus (1 Octet)
+ BD_Addr (6 Octets)
+ Name (8 Octets)
+
+ This opcode indicates that a new controller instance with a
+ given index was added. With some protocols, like the TTY-based
+ one there is only a single supported controller, meaning the
+ index is implicitly 0.
+
+Deleted Index
+-------------
+
+ Code: 0x0001
+
+ This opcode indicates that the controller with a specific index
+ was removed.
+
+Command Packet
+--------------
+
+ Code: 0x0002
+
+ HCI command packet.
+
+Event Packet
+------------
+
+ Code: 0x0003
+
+ HCI event packet.
+
+ACL TX Packet
+-------------
+
+ Code: 0x0004
+
+ Outgoing ACL packet.
+
+ACL RX Packet
+-------------
+
+ Code: 0x0005
+
+ Incoming ACL packet.
+
+SCO TX Packet
+--------------
+
+ Code: 0x0006
+
+ Outgoing SCO packet.
+
+SCO RX Packet
+-------------
+
+ Code: 0x0007
+
+ Incomnig SCO packet.
+
+Open Index
+----------
+
+ Code: 0x0008
+
+ The HCI transport for the specified controller has been opened.
+
+Close Index
+-----------
+
+ Code: 0x0009
+
+ The HCI transport for the specified controller has been closed.
+
+Index Information
+-----------------
+
+ Code: 0x000a
+ Parameters: BD_Addr (6 Octets)
+ Manufacturer (2 Octets)
+
+ Information about a specific controller.
+
+Vendor Diagnostics
+------------------
+
+ Code: 0x000b
+
+ Vendor diagnostic information.
+
+System Note
+-----------
+
+ Code: 0x000c
+
+ System note.
+
+User Logging
+------------
+
+ Code: 0x000d
+ Parameters: Priority (1 Octet)
+ Ident_Length (1 Octet)
+ Ident (Ident_Length Octets)
+
+ User logging information.
+
+
+TTY-based protocol
+==================
+
+This section covers the protocol that can be parsed by btmon when
+passing it the --tty parameter. The protocol is little endian, packet
+based, and has the following header for each packet:
+
+struct tty_hdr {
+ uint16_t data_len;
+ uint16_t opcode;
+ uint8_t flags;
+ uint8_t hdr_len;
+ uint8_t ext_hdr[0];
+} __attribute__ ((packed));
+
+The actual payload starts at ext_hdr + hdr_len and has the length of
+data_len - 4 - hdr_len. Each field of the header is defined as follows:
+
+data_len:
+ This is the total length of the entire packet, excuding the
+ data_len field itself.
+
+opcode:
+ The BTSnoop opcode
+
+flags:
+ Special flags for the packet. Currently no flags are defined.
+
+hdr_len:
+ Length of the extended header.
+
+ext_hdr:
+ This is a sequence of header extension fields formatted as:
+
+ struct {
+ uint8_t type;
+ uint8_t value[length];
+ }
+
+ The length of the value is dependent on the type. Currently the
+ following types are defined:
+
+ Type Length Meaning
+ ----------------------------------------------------------------
+ 1 Command drops 1 byte Dropped HCI command packets
+ 2 Event drops 1 byte Dropped HCI event packets
+ 3 ACL TX drops 1 byte Dropped ACL TX packets
+ 4 ACL RX drops 1 byte Dropped ACL RX packets
+ 5 SCO TX drops 1 byte Dropped SCO TX packets
+ 6 SCO RX drops 1 byte Dropped SCO RX packets
+ 7 Other drops 1 byte Dropped other packets
+ 8 32-bit timestamp 4 bytes Timestamp in 1/10th ms
+
+ The drops fields indicate the number of packets that the
+ implementation had to drop (e.g. due to lack of buffers) since
+ the last reported drop count.
+
+ The fields of the extended header must be sorted by increasing
+ type. This is essential so that unknown types can be ignored and
+ the parser can jump to processing the payload.
diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 232ffa0..4992243 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -61,23 +61,29 @@
Interface org.bluez.GattCharacteristic1 [Experimental]
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY
-Methods array{byte} ReadValue()
+Methods array{byte} ReadValue(dict options)
Issues a request to read the value of the
characteristic and returns the value if the
operation was successful.
+ Possible options: "offset": uint16 offset
+ "device": Object Device (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
org.bluez.Error.NotPermitted
org.bluez.Error.NotAuthorized
org.bluez.Error.NotSupported
- void WriteValue(array{byte} value)
+ void WriteValue(array{byte} value, dict options)
Issues a request to write the value of the
characteristic.
+ Possible options: "offset": Start offset
+ "device": Device path (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
org.bluez.Error.NotPermitted
@@ -119,7 +125,7 @@
when a notification or indication is received, upon
which a PropertiesChanged signal will be emitted.
- boolean Notifying [read-only]
+ boolean Notifying [read-only, optional]
True, if notifications or indications on this
characteristic are currently enabled.
@@ -144,6 +150,8 @@
"encrypt-write"
"encrypt-authenticated-read"
"encrypt-authenticated-write"
+ "secure-read" (Server only)
+ "secure-write" (Server only)
Characteristic Descriptors hierarchy
====================================
@@ -154,23 +162,29 @@
Interface org.bluez.GattDescriptor1 [Experimental]
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ
-Methods array{byte} ReadValue()
+Methods array{byte} ReadValue(dict flags)
Issues a request to read the value of the
characteristic and returns the value if the
operation was successful.
+ Possible options: "offset": Start offset
+ "device": Device path (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
org.bluez.Error.NotPermitted
org.bluez.Error.NotAuthorized
org.bluez.Error.NotSupported
- void WriteValue(array{byte} value)
+ void WriteValue(array{byte} value, dict flags)
Issues a request to write the value of the
characteristic.
+ Possible options: "offset": Start offset
+ "device": Device path (Server only)
+
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
org.bluez.Error.NotPermitted
@@ -205,9 +219,11 @@
"encrypt-write"
"encrypt-authenticated-read"
"encrypt-authenticated-write"
+ "secure-read" (Server Only)
+ "secure-write" (Server Only)
-Profile hierarcy
-================
+GATT Profile hierarcy
+=====================
Local profile (GATT client) instance. By registering this type of object
an application effectively indicates support for a specific GATT profile
@@ -226,6 +242,10 @@
profile, because when this method gets called it has
already been unregistered.
+Properties array{string} UUIDs [read-only]
+
+ 128-bit GATT service UUIDs to auto connect.
+
GATT Manager hierarchy
======================
@@ -294,11 +314,12 @@
Methods void RegisterApplication(object application, dict options)
Registers a local GATT services hierarchy as described
- above.
+ above (GATT Server) and/or GATT profiles (GATT Client).
The application object path together with the D-Bus
system bus connection ID define the identification of
- the application registering a GATT based service.
+ the application registering a GATT based
+ service or profile.
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.AlreadyExists
@@ -312,27 +333,3 @@
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.DoesNotExist
-
- void RegisterProfile(object profile, array{string} UUIDs,
- dict options)
-
- Registers a GATT (client role) profile exported
- under interface GattProfile1. The array of UUIDs
- specifies the mandatory set of remote service
- UUIDs that should all be available for the
- remote device to match this profile. Matching
- devices will be added to the auto-connection
- list and connected whenever available.
-
- Possible errors: org.bluez.Error.InvalidArguments
- org.bluez.Error.AlreadyExists
-
- void UnregisterProfile(object profile)
-
- This unregisters the profile that has been
- previously registered. The object path parameter
- must match the same value that has been used
- on registration.
-
- Possible errors: org.bluez.Error.InvalidArguments
- org.bluez.Error.DoesNotExist
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index aea50a6..2c34ec4 100644
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -175,6 +175,7 @@
2803:value_handle:properties:uuid
Descriptor:
+ value:uuid
uuid
Sample Attributes section:
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 69fbc10..e37385f 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -302,6 +302,15 @@
const char *name, const char *format, va_list args);
void g_dbus_pending_property_error(GDBusPendingReply id, const char *name,
const char *format, ...);
+
+/*
+ * Note that when multiple properties for a given object path are changed
+ * in the same mainloop iteration, they will be grouped with the last
+ * property changed. If this behaviour is undesired, use
+ * g_dbus_emit_property_changed_full() with the
+ * G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH flag, causing the signal to ignore
+ * any grouping.
+ */
void g_dbus_emit_property_changed(DBusConnection *connection,
const char *path, const char *interface,
const char *name);
diff --git a/gdbus/object.c b/gdbus/object.c
index a220101..afb4587 100644
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -635,11 +635,19 @@
static void add_pending(struct generic_data *data)
{
- if (data->process_id > 0)
- return;
+ guint old_id = data->process_id;
data->process_id = g_idle_add(process_changes, data);
+ if (old_id > 0) {
+ /*
+ * If the element already had an old idler, remove the old one,
+ * no need to re-add it to the pending list.
+ */
+ g_source_remove(old_id);
+ return;
+ }
+
pending = g_slist_append(pending, data);
}
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 5f57f02..fd0b081 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -548,7 +548,7 @@
case 142:
return "Quintic Corp.";
case 143:
- return "Stollman E+V GmbH";
+ return "Telit Wireless Solutions GmbH (Formerly Stollman E+V GmbH)";
case 144:
return "Funai Electric Co., Ltd.";
case 145:
@@ -1953,6 +1953,112 @@
return "HM Electronics, Inc.";
case 845:
return "TASER International, Inc.";
+ case 846:
+ return "Safe Trust Inc.";
+ case 847:
+ return "Heartland Payment Systems";
+ case 848:
+ return "Bitstrata Systems Inc.";
+ case 849:
+ return "Pieps GmbH";
+ case 850:
+ return "iRiding(Xiamen)Technology Co.,Ltd.";
+ case 851:
+ return "Alpha Audiotronics, Inc.";
+ case 852:
+ return "TOPPAN FORMS CO.,LTD.";
+ case 853:
+ return "Sigma Designs, Inc.";
+ case 854:
+ return "Spectrum Brands, Inc.";
+ case 855:
+ return "Polymap Wireless";
+ case 856:
+ return "MagniWare Ltd.";
+ case 857:
+ return "Novotec Medical GmbH";
+ case 858:
+ return "Medicom Innovation Partner a/s";
+ case 859:
+ return "Matrix Inc.";
+ case 860:
+ return "Eaton Corporation";
+ case 861:
+ return "KYS";
+ case 862:
+ return "Naya Health, Inc.";
+ case 863:
+ return "Acromag";
+ case 864:
+ return "Insulet Corporation";
+ case 865:
+ return "Wellinks Inc.";
+ case 866:
+ return "ON Semiconductor";
+ case 867:
+ return "FREELAP SA";
+ case 868:
+ return "Favero Electronics Srl";
+ case 869:
+ return "BioMech Sensor LLC";
+ case 870:
+ return "BOLTT Sports technologies Private limited";
+ case 871:
+ return "Saphe International";
+ case 872:
+ return "Metormote AB";
+ case 873:
+ return "littleBits";
+ case 874:
+ return "SetPoint Medical";
+ case 875:
+ return "BRControls Products BV";
+ case 876:
+ return "Zipcar";
+ case 877:
+ return "AirBolt Pty Ltd";
+ case 878:
+ return "KeepTruckin Inc";
+ case 879:
+ return "Motiv, Inc.";
+ case 880:
+ return "Wazombi Labs OÜ";
+ case 881:
+ return "ORBCOMM";
+ case 882:
+ return "Nixie Labs, Inc.";
+ case 883:
+ return "AppNearMe Ltd";
+ case 884:
+ return "Holman Industries";
+ case 885:
+ return "Expain AS";
+ case 886:
+ return "Electronic Temperature Instruments Ltd";
+ case 887:
+ return "Plejd AB";
+ case 888:
+ return "Propeller Health";
+ case 889:
+ return "Shenzhen iMCO Electronic Technology Co.,Ltd";
+ case 890:
+ return "Algoria";
+ case 891:
+ return "Apption Labs Inc.";
+ case 892:
+ return "Cronologics Corporation";
+ case 893:
+ return "MICRODIA Ltd.";
+ case 894:
+ return "lulabytes S.L.";
+ case 895:
+ return "Nestec S.A.";
+ case 896:
+ return "LLC \"MEGA-F service\"";
+ case 897:
+ return "Sharp Corporation";
+ case 898:
+ return "Precision Outcomes Ltd";
case 65535:
return "internal use";
default:
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 852a6b2..eb27926 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -69,6 +69,7 @@
#define BT_SECURITY_LOW 1
#define BT_SECURITY_MEDIUM 2
#define BT_SECURITY_HIGH 3
+#define BT_SECURITY_FIPS 4
#define BT_DEFER_SETUP 7
diff --git a/lib/hci.c b/lib/hci.c
index 97b6ec3..fda1aca 100644
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -156,6 +156,10 @@
return "PCI";
case HCI_SDIO:
return "SDIO";
+ case HCI_SPI:
+ return "SPI";
+ case HCI_I2C:
+ return "I2C";
default:
return "UNKNOWN";
}
diff --git a/lib/hci.h b/lib/hci.h
index cbc55af..f76e205 100644
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -55,6 +55,8 @@
#define HCI_RS232 4
#define HCI_PCI 5
#define HCI_SDIO 6
+#define HCI_SPI 7
+#define HCI_I2C 8
/* HCI controller types */
#define HCI_BREDR 0x00
diff --git a/monitor/control.c b/monitor/control.c
index b3fc93d..26aecfd 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -35,6 +35,10 @@
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <fcntl.h>
#include "lib/bluetooth.h"
#include "lib/hci.h"
@@ -48,6 +52,7 @@
#include "packet.h"
#include "hcidump.h"
#include "ellisys.h"
+#include "tty.h"
#include "control.h"
static struct btsnoop *btsnoop_file = NULL;
@@ -965,7 +970,7 @@
data->buf, pktlen);
break;
case HCI_CHANNEL_MONITOR:
- btsnoop_write_hci(btsnoop_file, tv, index, opcode,
+ btsnoop_write_hci(btsnoop_file, tv, index, opcode, 0,
data->buf, pktlen);
ellisys_inject_hci(tv, index, opcode,
data->buf, pktlen);
@@ -1058,23 +1063,25 @@
data->offset += len;
- if (data->offset > MGMT_HDR_SIZE) {
+ while (data->offset >= MGMT_HDR_SIZE) {
struct mgmt_hdr *hdr = (struct mgmt_hdr *) data->buf;
uint16_t pktlen = le16_to_cpu(hdr->len);
+ uint16_t opcode, index;
- if (data->offset > pktlen + MGMT_HDR_SIZE) {
- uint16_t opcode = le16_to_cpu(hdr->opcode);
- uint16_t index = le16_to_cpu(hdr->index);
+ if (data->offset < pktlen + MGMT_HDR_SIZE)
+ return;
- packet_monitor(NULL, NULL, index, opcode,
+ opcode = le16_to_cpu(hdr->opcode);
+ index = le16_to_cpu(hdr->index);
+
+ packet_monitor(NULL, NULL, index, opcode,
data->buf + MGMT_HDR_SIZE, pktlen);
- data->offset -= pktlen + MGMT_HDR_SIZE;
+ data->offset -= pktlen + MGMT_HDR_SIZE;
- if (data->offset > 0)
- memmove(data->buf, data->buf +
- MGMT_HDR_SIZE + pktlen, data->offset);
- }
+ if (data->offset > 0)
+ memmove(data->buf, data->buf + MGMT_HDR_SIZE + pktlen,
+ data->offset);
}
}
@@ -1157,6 +1164,202 @@
server_fd = fd;
}
+static bool parse_drops(uint8_t **data, uint8_t *len, uint8_t *drops,
+ uint32_t *total)
+{
+ if (*len < 1)
+ return false;
+
+ *drops = **data;
+ *total += *drops;
+ (*data)++;
+ (*len)--;
+
+ return true;
+}
+
+static bool tty_parse_header(uint8_t *hdr, uint8_t len, struct timeval **tv,
+ struct timeval *ctv, uint32_t *drops)
+{
+ uint8_t cmd = 0;
+ uint8_t evt = 0;
+ uint8_t acl_tx = 0;
+ uint8_t acl_rx = 0;
+ uint8_t sco_tx = 0;
+ uint8_t sco_rx = 0;
+ uint8_t other = 0;
+ uint32_t total = 0;
+ uint32_t ts32;
+
+ while (len) {
+ uint8_t type = hdr[0];
+
+ hdr++; len--;
+
+ switch (type) {
+ case TTY_EXTHDR_COMMAND_DROPS:
+ if (!parse_drops(&hdr, &len, &cmd, &total))
+ return false;
+ break;
+ case TTY_EXTHDR_EVENT_DROPS:
+ if (!parse_drops(&hdr, &len, &evt, &total))
+ return false;
+ break;
+ case TTY_EXTHDR_ACL_TX_DROPS:
+ if (!parse_drops(&hdr, &len, &acl_tx, &total))
+ return false;
+ break;
+ case TTY_EXTHDR_ACL_RX_DROPS:
+ if (!parse_drops(&hdr, &len, &acl_rx, &total))
+ return false;
+ break;
+ case TTY_EXTHDR_SCO_TX_DROPS:
+ if (!parse_drops(&hdr, &len, &sco_tx, &total))
+ return false;
+ break;
+ case TTY_EXTHDR_SCO_RX_DROPS:
+ if (!parse_drops(&hdr, &len, &sco_rx, &total))
+ return false;
+ break;
+ case TTY_EXTHDR_OTHER_DROPS:
+ if (!parse_drops(&hdr, &len, &other, &total))
+ return false;
+ break;
+ case TTY_EXTHDR_TS32:
+ if (len < sizeof(ts32))
+ return false;
+ ts32 = get_le32(hdr);
+ hdr += sizeof(ts32); len -= sizeof(ts32);
+ /* ts32 is in units of 1/10th of a millisecond */
+ ctv->tv_sec = ts32 / 10000;
+ ctv->tv_usec = (ts32 % 10000) * 100;
+ *tv = ctv;
+ break;
+ default:
+ printf("Unknown extended header type %u\n", type);
+ return false;
+ }
+ }
+
+ if (total) {
+ *drops += total;
+ printf("* Drops: cmd %u evt %u acl_tx %u acl_rx %u sco_tx %u "
+ "sco_rx %u other %u\n", cmd, evt, acl_tx, acl_rx,
+ sco_tx, sco_rx, other);
+ }
+
+ return true;
+}
+
+static void tty_callback(int fd, uint32_t events, void *user_data)
+{
+ struct control_data *data = user_data;
+ ssize_t len;
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_remove_fd(data->fd);
+ return;
+ }
+
+ len = read(data->fd, data->buf + data->offset,
+ sizeof(data->buf) - data->offset);
+ if (len < 0)
+ return;
+
+ data->offset += len;
+
+ while (data->offset >= sizeof(struct tty_hdr)) {
+ struct tty_hdr *hdr = (struct tty_hdr *) data->buf;
+ uint16_t pktlen, opcode, data_len;
+ struct timeval *tv = NULL;
+ struct timeval ctv;
+ uint32_t drops = 0;
+
+ data_len = le16_to_cpu(hdr->data_len);
+
+ if (data->offset < 2 + data_len)
+ return;
+
+ if (data->offset < sizeof(*hdr) + hdr->hdr_len) {
+ fprintf(stderr, "Received corrupted data from TTY\n");
+ memmove(data->buf, data->buf + 2 + data_len,
+ data->offset);
+ return;
+ }
+
+ if (!tty_parse_header(hdr->ext_hdr, hdr->hdr_len,
+ &tv, &ctv, &drops))
+ fprintf(stderr, "Unable to parse extended header\n");
+
+ opcode = le16_to_cpu(hdr->opcode);
+ pktlen = data_len - 4 - hdr->hdr_len;
+
+ btsnoop_write_hci(btsnoop_file, tv, 0, opcode, drops,
+ hdr->ext_hdr + hdr->hdr_len, pktlen);
+ packet_monitor(tv, NULL, 0, opcode,
+ hdr->ext_hdr + hdr->hdr_len, pktlen);
+
+ data->offset -= 2 + data_len;
+
+ if (data->offset > 0)
+ memmove(data->buf, data->buf + 2 + data_len,
+ data->offset);
+ }
+}
+
+int control_tty(const char *path, unsigned int speed)
+{
+ struct control_data *data;
+ struct termios ti;
+ int fd, err;
+
+ fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0) {
+ err = -errno;
+ perror("Failed to open serial port");
+ return err;
+ }
+
+ if (tcflush(fd, TCIOFLUSH) < 0) {
+ err = -errno;
+ perror("Failed to flush serial port");
+ close(fd);
+ return err;
+ }
+
+ memset(&ti, 0, sizeof(ti));
+ /* Switch TTY to raw mode */
+ cfmakeraw(&ti);
+
+ ti.c_cflag |= (CLOCAL | CREAD);
+ ti.c_cflag &= ~CRTSCTS;
+
+ cfsetspeed(&ti, speed);
+
+ if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+ err = -errno;
+ perror("Failed to set serial port settings");
+ close(fd);
+ return err;
+ }
+
+ printf("--- %s opened ---\n", path);
+
+ data = malloc(sizeof(*data));
+ if (!data) {
+ close(fd);
+ return -ENOMEM;
+ }
+
+ memset(data, 0, sizeof(*data));
+ data->channel = HCI_CHANNEL_MONITOR;
+ data->fd = fd;
+
+ mainloop_add_fd(data->fd, EPOLLIN, tty_callback, data, free_data);
+
+ return 0;
+}
+
bool control_writer(const char *path)
{
btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR);
diff --git a/monitor/control.h b/monitor/control.h
index 28f16db..55384db 100644
--- a/monitor/control.h
+++ b/monitor/control.h
@@ -27,6 +27,7 @@
bool control_writer(const char *path);
void control_reader(const char *path);
void control_server(const char *path);
+int control_tty(const char *path, unsigned int speed);
int control_tracing(void);
void control_message(uint16_t opcode, const void *data, uint16_t size);
diff --git a/monitor/main.c b/monitor/main.c
index 4bf7ca1..a9fd90d 100644
--- a/monitor/main.c
+++ b/monitor/main.c
@@ -33,6 +33,7 @@
#include <getopt.h>
#include "src/shared/mainloop.h"
+#include "src/shared/tty.h"
#include "packet.h"
#include "lmp.h"
@@ -63,6 +64,8 @@
"\t-s, --server <socket> Start monitor server socket\n"
"\t-p, --priority <level> Show only priority or lower\n"
"\t-i, --index <num> Show only specified controller\n"
+ "\t-d, --tty <tty> Read data from TTY\n"
+ "\t-B, --tty-speed <rate> Set TTY speed (default 115200)\n"
"\t-t, --time Show time instead of time offset\n"
"\t-T, --date Show time and date information\n"
"\t-S, --sco Dump SCO traffic\n"
@@ -71,6 +74,8 @@
}
static const struct option main_options[] = {
+ { "tty", required_argument, NULL, 'd' },
+ { "tty-speed", required_argument, NULL, 'B' },
{ "read", required_argument, NULL, 'r' },
{ "write", required_argument, NULL, 'w' },
{ "analyze", required_argument, NULL, 'a' },
@@ -94,6 +99,8 @@
const char *writer_path = NULL;
const char *analyze_path = NULL;
const char *ellisys_server = NULL;
+ const char *tty = NULL;
+ unsigned int tty_speed = B115200;
unsigned short ellisys_port = 0;
const char *str;
int exit_status;
@@ -107,12 +114,22 @@
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "r:w:a:s:p:i:tTSE:vh",
+ opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:vh",
main_options, NULL);
if (opt < 0)
break;
switch (opt) {
+ case 'd':
+ tty= optarg;
+ break;
+ case 'B':
+ tty_speed = tty_get_speed(atoi(optarg));
+ if (!tty_speed) {
+ fprintf(stderr, "Unknown speed: %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
case 'r':
reader_path = optarg;
break;
@@ -213,7 +230,10 @@
if (ellisys_server)
ellisys_enable(ellisys_server, ellisys_port);
- if (control_tracing() < 0)
+ if (!tty && control_tracing() < 0)
+ return EXIT_FAILURE;
+
+ if (tty && control_tty(tty, tty_speed) < 0)
return EXIT_FAILURE;
exit_status = mainloop_run();
diff --git a/monitor/packet.c b/monitor/packet.c
index 0b21121..6c642a8 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -1870,7 +1870,9 @@
static void print_pk256(const char *label, const uint8_t *key)
{
- print_hex_field(label, key, 64);
+ print_field("%s:", label);
+ print_hex_field(" X", &key[0], 32);
+ print_hex_field(" Y", &key[32], 32);
}
static void print_dhkey(const uint8_t *dhkey)
diff --git a/monitor/tty.h b/monitor/tty.h
new file mode 100644
index 0000000..f0ba0c5
--- /dev/null
+++ b/monitor/tty.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2016 Intel Corporation
+ *
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+
+struct tty_hdr {
+ uint16_t data_len;
+ uint16_t opcode;
+ uint8_t flags;
+ uint8_t hdr_len;
+ uint8_t ext_hdr[0];
+} __attribute__ ((packed));
+
+#define TTY_EXTHDR_COMMAND_DROPS 1
+#define TTY_EXTHDR_EVENT_DROPS 2
+#define TTY_EXTHDR_ACL_TX_DROPS 3
+#define TTY_EXTHDR_ACL_RX_DROPS 4
+#define TTY_EXTHDR_SCO_TX_DROPS 5
+#define TTY_EXTHDR_SCO_RX_DROPS 6
+#define TTY_EXTHDR_OTHER_DROPS 7
+#define TTY_EXTHDR_TS32 8
diff --git a/monitor/uuid.c b/monitor/uuid.c
index 54adb0d..1ceaa6f 100644
--- a/monitor/uuid.c
+++ b/monitor/uuid.c
@@ -31,7 +31,7 @@
#include "uuid.h"
-static struct {
+static const struct {
uint16_t uuid;
const char *str;
} uuid16_table[] = {
@@ -540,6 +540,31 @@
{ }
};
+static const struct {
+ const char *uuid;
+ const char *str;
+} uuid128_table[] = {
+ { "a3c87500-8ed3-4bdf-8a39-a01bebede295",
+ "Eddystone Configuration Service" },
+ { "a3c87501-8ed3-4bdf-8a39-a01bebede295", "Capabilities" },
+ { "a3c87502-8ed3-4bdf-8a39-a01bebede295", "Active Slot" },
+ { "a3c87503-8ed3-4bdf-8a39-a01bebede295",
+ "Advertising Interval" },
+ { "a3c87504-8ed3-4bdf-8a39-a01bebede295", "Radio Tx Power" },
+ { "a3c87505-8ed3-4bdf-8a39-a01bebede295",
+ "(Advanced) Advertised Tx Power" },
+ { "a3c87506-8ed3-4bdf-8a39-a01bebede295", "Lock State" },
+ { "a3c87507-8ed3-4bdf-8a39-a01bebede295", "Unlock" },
+ { "a3c87508-8ed3-4bdf-8a39-a01bebede295", "Public ECDH Key" },
+ { "a3c87509-8ed3-4bdf-8a39-a01bebede295", "EID Identity Key" },
+ { "a3c8750a-8ed3-4bdf-8a39-a01bebede295", "ADV Slot Data" },
+ { "a3c8750b-8ed3-4bdf-8a39-a01bebede295",
+ "(Advanced) Factory reset" },
+ { "a3c8750c-8ed3-4bdf-8a39-a01bebede295",
+ "(Advanced) Remain Connectable" },
+ { }
+};
+
const char *uuid16_to_str(uint16_t uuid)
{
int i;
@@ -568,6 +593,7 @@
const char *uuidstr_to_str(const char *uuid)
{
uint32_t val;
+ int i;
if (!uuid)
return NULL;
@@ -575,6 +601,11 @@
if (strlen(uuid) != 36)
return NULL;
+ for (i = 0; uuid128_table[i].str; i++) {
+ if (strcasecmp(uuid128_table[i].uuid, uuid) == 0)
+ return uuid128_table[i].str;
+ }
+
if (strncasecmp(uuid + 8, "-0000-1000-8000-00805f9b34fb", 28))
return "Vendor specific";
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 22bf35b..2a43d32 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -293,8 +293,9 @@
static void auth_cb(DBusError *derr, void *user_data);
static gboolean process_queue(gpointer user_data);
static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
- uint8_t subunit, uint8_t *operands,
- size_t operand_count, void *user_data);
+ uint8_t subunit, uint8_t transaction,
+ uint8_t *operands, size_t operand_count,
+ void *user_data);
static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
{
@@ -706,8 +707,8 @@
if (p->err == 0 || req->func == NULL)
goto done;
- req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0,
- req->user_data);
+ req->func(session, AVC_CTYPE_REJECTED, req->subunit, p->transaction,
+ NULL, 0, req->user_data);
done:
g_free(req->operands);
@@ -829,9 +830,9 @@
continue;
if (req->func && req->func(control->session, avc->code,
- avc->subunit_type,
- operands, operand_count,
- req->user_data))
+ avc->subunit_type, p->transaction,
+ operands, operand_count,
+ req->user_data))
return;
control->processed = g_slist_remove(control->processed, p);
@@ -1724,8 +1725,9 @@
}
static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
- uint8_t subunit, uint8_t *operands,
- size_t operand_count, void *user_data)
+ uint8_t subunit, uint8_t transaction,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
{
if (code != AVC_CTYPE_ACCEPTED)
return FALSE;
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index 6c19ce4..68a2735 100644
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -132,8 +132,9 @@
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data);
typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
- uint8_t subunit, uint8_t *operands,
- size_t operand_count, void *user_data);
+ uint8_t subunit, uint8_t transaction,
+ uint8_t *operands, size_t operand_count,
+ void *user_data);
typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
uint8_t *operands, size_t operand_count,
void *user_data);
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 37bc291..6c8ed81 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2076,8 +2076,8 @@
}
}
-static gboolean avrcp_get_play_status_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_get_play_status_rsp(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -2140,8 +2140,8 @@
}
}
-static gboolean avrcp_player_value_rsp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_player_value_rsp(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -2210,8 +2210,8 @@
static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
- uint8_t *operands, size_t operand_count,
- void *user_data)
+ uint8_t transaction, uint8_t *operands,
+ size_t operand_count, void *user_data)
{
uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
struct avrcp *session = user_data;
@@ -2297,6 +2297,7 @@
static gboolean avrcp_get_element_attributes_rsp(struct avctp *conn,
uint8_t code, uint8_t subunit,
+ uint8_t transaction,
uint8_t *operands,
size_t operand_count,
void *user_data)
@@ -3112,7 +3113,7 @@
struct avrcp *session = user_data;
struct avrcp_player *player = session->controller->player;
struct media_player *mp = player->user_data;
- uint32_t num_of_items;
+ uint32_t num_of_items = 0;
if (pdu == NULL)
return -ETIMEDOUT;
@@ -3529,8 +3530,8 @@
player->uid_counter = get_be16(&pdu->params[1]);
}
-static gboolean avrcp_handle_event(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -3538,16 +3539,30 @@
struct avrcp_header *pdu = (void *) operands;
uint8_t event;
- if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) ||
- pdu == NULL)
+ if (!pdu)
return FALSE;
+ if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)) {
+ if (pdu->params[0] == AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED &&
+ code == AVC_CTYPE_REJECTED) {
+ int i;
+
+ /* Lookup event by transaction */
+ for (i = 0; i <= AVRCP_EVENT_LAST; i++) {
+ if (session->transaction_events[i] ==
+ transaction) {
+ event = i;
+ goto changed;
+ }
+ }
+ }
+ return FALSE;
+ }
+
event = pdu->params[0];
if (code == AVC_CTYPE_CHANGED) {
- session->registered_events ^= (1 << event);
- avrcp_register_notification(session, event);
- return FALSE;
+ goto changed;
}
switch (event) {
@@ -3578,8 +3593,15 @@
}
session->registered_events |= (1 << event);
+ session->transaction_events[event] = transaction;
return TRUE;
+
+changed:
+ session->registered_events ^= (1 << event);
+ session->transaction_events[event] = 0;
+ avrcp_register_notification(session, event);
+ return FALSE;
}
static void avrcp_register_notification(struct avrcp *session, uint8_t event)
@@ -3611,8 +3633,8 @@
avrcp_handle_event, session);
}
-static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -4126,8 +4148,8 @@
player_destroy(player);
}
-static gboolean avrcp_handle_set_volume(struct avctp *conn,
- uint8_t code, uint8_t subunit,
+static gboolean avrcp_handle_set_volume(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
uint8_t *operands, size_t operand_count,
void *user_data)
{
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 69070bf..23d1561 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1018,6 +1018,9 @@
{
struct media_player *mp = user_data;
+ if (!mp->name)
+ return "Player";
+
return mp->name;
}
diff --git a/profiles/deviceinfo/deviceinfo.c b/profiles/deviceinfo/deviceinfo.c
index d1f51a0..0c48f00 100644
--- a/profiles/deviceinfo/deviceinfo.c
+++ b/profiles/deviceinfo/deviceinfo.c
@@ -88,7 +88,7 @@
bt_string_to_uuid(&pnpid_uuid, PNPID_UUID);
if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
- &uuid)) {
+ NULL, &uuid)) {
error("Failed to obtain characteristic data");
return;
}
diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index 877c4fd..35b996c 100644
--- a/profiles/gap/gas.c
+++ b/profiles/gap/gas.c
@@ -181,7 +181,7 @@
bt_uuid_t uuid;
if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
- &uuid)) {
+ NULL, &uuid)) {
error("Failed to obtain characteristic data");
return;
}
diff --git a/profiles/scanparam/scan.c b/profiles/scanparam/scan.c
index 4015b3f..d3ca762 100644
--- a/profiles/scanparam/scan.c
+++ b/profiles/scanparam/scan.c
@@ -140,7 +140,7 @@
bt_uuid_t uuid, scan_interval_wind_uuid, scan_refresh_uuid;
if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
- &uuid)) {
+ NULL, &uuid)) {
error("Failed to obtain characteristic data");
return;
}
diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in
index 83e4732..f799f65 100644
--- a/src/bluetooth.service.in
+++ b/src/bluetooth.service.in
@@ -12,6 +12,8 @@
#Restart=on-failure
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
LimitNPROC=1
+ProtectHome=true
+ProtectSystem=full
[Install]
WantedBy=bluetooth.target
diff --git a/src/device.c b/src/device.c
index ed8ffba..b1ff1bb 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1277,6 +1277,9 @@
if (device->blocked)
return 0;
+ if (device->disconn_timer > 0)
+ g_source_remove(device->disconn_timer);
+
disconnect_all(device);
while (device->services != NULL) {
@@ -2001,6 +2004,7 @@
struct gatt_saver {
struct btd_device *device;
+ uint16_t ext_props;
GKeyFile *key_file;
};
@@ -2010,6 +2014,7 @@
GKeyFile *key_file = saver->key_file;
char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR];
const bt_uuid_t *uuid;
+ bt_uuid_t ext_uuid;
uint16_t handle_num;
handle_num = gatt_db_attribute_get_handle(attr);
@@ -2017,7 +2022,13 @@
uuid = gatt_db_attribute_get_type(attr);
bt_uuid_to_string(uuid, uuid_str, sizeof(uuid_str));
- sprintf(value, "%s", uuid_str);
+
+ bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID);
+ if (!bt_uuid_cmp(uuid, &ext_uuid) && saver->ext_props)
+ sprintf(value, "%04hx:%s", saver->ext_props, uuid_str);
+ else
+ sprintf(value, "%s", uuid_str);
+
g_key_file_set_string(key_file, "Attributes", handle, value);
}
@@ -2031,7 +2042,8 @@
bt_uuid_t uuid;
if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle,
- &properties, &uuid)) {
+ &properties, &saver->ext_props,
+ &uuid)) {
warn("Error storing characteristic - can't get data");
return;
}
@@ -2742,6 +2754,61 @@
return NULL;
}
+static void load_services(struct btd_device *device, char **uuids)
+{
+ char **uuid;
+
+ for (uuid = uuids; *uuid; uuid++) {
+ if (g_slist_find_custom(device->uuids, *uuid, bt_uuid_strcmp))
+ continue;
+
+ device->uuids = g_slist_insert_sorted(device->uuids,
+ g_strdup(*uuid),
+ bt_uuid_strcmp);
+ }
+
+ g_strfreev(uuids);
+}
+
+static void convert_info(struct btd_device *device, GKeyFile *key_file)
+{
+ char filename[PATH_MAX];
+ char adapter_addr[18];
+ char device_addr[18];
+ char **uuids;
+ char *str;
+ gsize length = 0;
+
+ /* Load device profile list from legacy properties */
+ uuids = g_key_file_get_string_list(key_file, "General", "SDPServices",
+ NULL, NULL);
+ if (uuids)
+ load_services(device, uuids);
+
+ uuids = g_key_file_get_string_list(key_file, "General", "GATTServices",
+ NULL, NULL);
+ if (uuids)
+ load_services(device, uuids);
+
+ if (!device->uuids)
+ return;
+
+ /* Remove old entries so they are not loaded again */
+ g_key_file_remove_key(key_file, "General", "SDPServices", NULL);
+ g_key_file_remove_key(key_file, "General", "GATTServices", NULL);
+
+ ba2str(btd_adapter_get_address(device->adapter), adapter_addr);
+ ba2str(&device->bdaddr, device_addr);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
+ device_addr);
+
+ str = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(filename, str, length, NULL);
+ g_free(str);
+
+ store_device_info(device);
+}
+
static void load_info(struct btd_device *device, const char *local,
const char *peer, GKeyFile *key_file)
{
@@ -2841,21 +2908,7 @@
uuids = g_key_file_get_string_list(key_file, "General", "Services",
NULL, NULL);
if (uuids) {
- char **uuid;
-
- for (uuid = uuids; *uuid; uuid++) {
- GSList *match;
-
- match = g_slist_find_custom(device->uuids, *uuid,
- bt_uuid_strcmp);
- if (match)
- continue;
-
- device->uuids = g_slist_insert_sorted(device->uuids,
- g_strdup(*uuid),
- bt_uuid_strcmp);
- }
- g_strfreev(uuids);
+ load_services(device, uuids);
/* Discovered services restored from storage */
device->bredr_state.svc_resolved = true;
@@ -2994,24 +3047,43 @@
*new_services = g_slist_append(*new_services, prim);
}
+static void load_desc_value(struct gatt_db_attribute *attrib,
+ int err, void *user_data)
+{
+ if (err)
+ warn("loading descriptor value to db failed");
+}
+
static int load_desc(char *handle, char *value,
struct gatt_db_attribute *service)
{
char uuid_str[MAX_LEN_UUID_STR];
struct gatt_db_attribute *att;
uint16_t handle_int;
- bt_uuid_t uuid;
+ uint16_t val;
+ bt_uuid_t uuid, ext_uuid;
if (sscanf(handle, "%04hx", &handle_int) != 1)
return -EIO;
- if (sscanf(value, "%s", uuid_str) != 1)
- return -EIO;
+ /* Check if there is any value stored, otherwise it is just the UUID */
+ if (sscanf(value, "%04hx:%s", &val, uuid_str) != 2) {
+ if (sscanf(value, "%s", uuid_str) != 1)
+ return -EIO;
+ val = 0;
+ }
+
+ DBG("loading descriptor handle: 0x%04x, value: 0x%04x, uuid: %s",
+ handle_int, val, uuid_str);
bt_string_to_uuid(&uuid, uuid_str);
+ bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID);
- DBG("loading descriptor handle: 0x%04x, uuid: %s", handle_int,
- uuid_str);
+ /* If it is CEP then it must contain the value */
+ if (!bt_uuid_cmp(&uuid, &ext_uuid) && !val) {
+ warn("cannot load CEP descriptor without value");
+ return -EIO;
+ }
att = gatt_db_service_insert_descriptor(service, handle_int, &uuid,
0, NULL, NULL, NULL);
@@ -3020,6 +3092,13 @@
return -EIO;
}
+ if (val) {
+ if (!gatt_db_attribute_write(att, 0, (uint8_t *)&val,
+ sizeof(val), 0, NULL,
+ load_desc_value, NULL))
+ return -EIO;
+ }
+
return 0;
}
@@ -3373,9 +3452,6 @@
store_gatt_db(device);
- g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE,
- "GattServices");
-
return FALSE;
}
@@ -3576,6 +3652,8 @@
src = btd_adapter_get_address(adapter);
ba2str(src, srcaddr);
+ convert_info(device, key_file);
+
load_info(device, srcaddr, address, key_file);
load_att_info(device, srcaddr, address);
@@ -3907,8 +3985,10 @@
g_slist_free(device->pending);
device->pending = NULL;
- if (btd_device_is_connected(device))
+ if (btd_device_is_connected(device)) {
+ g_source_remove(device->disconn_timer);
disconnect_all(device);
+ }
if (device->store_id > 0) {
g_source_remove(device->store_id);
@@ -4584,6 +4664,8 @@
btd_gatt_client_ready(device->client_dbus);
device_svc_resolved(device, device->bdaddr_type, 0);
+
+ store_gatt_db(device);
}
static void gatt_client_service_changed(uint16_t start_handle,
diff --git a/src/gatt-client.c b/src/gatt-client.c
index a018c8c..0cbacca 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <stdint.h>
+#include <errno.h>
#include <dbus/dbus.h>
@@ -71,7 +72,6 @@
bt_uuid_t uuid;
char *path;
struct queue *chrcs;
- struct queue *pending_ext_props;
};
struct characteristic {
@@ -192,33 +192,17 @@
return ret;
}
-static bool parse_value_arg(DBusMessage *msg, uint8_t **value,
- size_t *value_len)
+static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
{
- DBusMessageIter iter, array;
- uint8_t *val;
- int len;
+ DBusMessageIter array;
- if (!dbus_message_iter_init(msg, &iter))
- return false;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
- return false;
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, value, len);
- dbus_message_iter_recurse(&iter, &array);
- dbus_message_iter_get_fixed_array(&array, &val, &len);
- dbus_message_iter_next(&iter);
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
- return false;
-
- if (len < 0)
- return false;
-
- *value = val;
- *value_len = len;
-
- return true;
+ return 0;
}
typedef bool (*async_dbus_op_complete_t)(void *data);
@@ -391,12 +375,60 @@
return;
}
+static int parse_options(DBusMessageIter *iter, uint16_t *offset)
+{
+ DBusMessageIter dict;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "offset") == 0) {
+ if (var != DBUS_TYPE_UINT16)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, offset);
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int read_value(struct bt_gatt_client *gatt, uint16_t handle,
+ bt_gatt_client_read_callback_t callback,
+ struct async_dbus_op *op)
+{
+ if (op->offset)
+ return bt_gatt_client_read_long_value(gatt, handle, op->offset,
+ callback,
+ async_dbus_op_ref(op),
+ async_dbus_op_unref);
+ else
+ return bt_gatt_client_read_value(gatt, handle, callback,
+ async_dbus_op_ref(op),
+ async_dbus_op_unref);
+}
+
static DBusMessage *descriptor_read_value(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct descriptor *desc = user_data;
struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+ DBusMessageIter iter;
struct async_dbus_op *op;
+ uint16_t offset = 0;
if (!gatt)
return btd_error_failed(msg, "Not connected");
@@ -404,14 +436,17 @@
if (desc->read_id)
return btd_error_in_progress(msg);
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_options(&iter, &offset))
+ return btd_error_invalid_args(msg);
+
op = new0(struct async_dbus_op, 1);
op->msg = dbus_message_ref(msg);
op->data = desc;
+ op->offset = offset;
- desc->read_id = bt_gatt_client_read_value(gatt, desc->handle,
- desc_read_cb,
- async_dbus_op_ref(op),
- async_dbus_op_unref);
+ desc->read_id = read_value(gatt, desc->handle, desc_read_cb, op);
if (desc->read_id)
return NULL;
@@ -451,7 +486,6 @@
g_dbus_send_message(btd_get_dbus_connection(), reply);
}
-
static void write_cb(bool success, uint8_t att_ecode, void *user_data)
{
write_result_cb(success, false, att_ecode, user_data);
@@ -460,7 +494,8 @@
static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
struct bt_gatt_client *gatt,
bool reliable, const uint8_t *value,
- size_t value_len, void *data,
+ size_t value_len, uint16_t offset,
+ void *data,
async_dbus_op_complete_t complete)
{
struct async_dbus_op *op;
@@ -470,9 +505,10 @@
op->msg = dbus_message_ref(msg);
op->data = data;
op->complete = complete;
+ op->offset = offset;
- id = bt_gatt_client_write_long_value(gatt, reliable, handle,
- 0, value, value_len,
+ id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset,
+ value, value_len,
write_result_cb, op,
async_dbus_op_free);
@@ -523,8 +559,10 @@
{
struct descriptor *desc = user_data;
struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
+ DBusMessageIter iter;
uint8_t *value = NULL;
- size_t value_len = 0;
+ int value_len = 0;
+ uint16_t offset = 0;
if (!gatt)
return btd_error_failed(msg, "Not connected");
@@ -532,7 +570,12 @@
if (desc->write_id)
return btd_error_in_progress(msg);
- if (!parse_value_arg(msg, &value, &value_len))
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_value_arg(&iter, &value, &value_len))
+ return btd_error_invalid_args(msg);
+
+ if (parse_options(&iter, &offset))
return btd_error_invalid_args(msg);
/*
@@ -547,15 +590,15 @@
* Based on the value length and the MTU, either use a write or a long
* write.
*/
- if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3)
+ if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset)
desc->write_id = start_write_request(msg, desc->handle,
gatt, value,
value_len, desc,
desc_write_complete);
else
- desc->write_id = start_long_write(msg, desc->handle,
- gatt, false, value,
- value_len, desc,
+ desc->write_id = start_long_write(msg, desc->handle, gatt,
+ false, value,
+ value_len, offset, desc,
desc_write_complete);
if (!desc->write_id)
@@ -575,13 +618,15 @@
};
static const GDBusMethodTable descriptor_methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
- GDBUS_ARGS({ "value", "ay" }),
- descriptor_read_value) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
+ GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ descriptor_read_value) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
- GDBUS_ARGS({ "value", "ay" }),
- NULL,
- descriptor_write_value) },
+ GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL,
+ descriptor_write_value) },
{ }
};
@@ -707,6 +752,15 @@
return TRUE;
}
+static gboolean
+characteristic_notifying_exists(const GDBusPropertyTable *property, void *data)
+{
+ struct characteristic *chrc = data;
+
+ return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
+ chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+}
+
struct chrc_prop_data {
uint8_t prop;
char *str;
@@ -829,7 +883,9 @@
{
struct characteristic *chrc = user_data;
struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ DBusMessageIter iter;
struct async_dbus_op *op;
+ uint16_t offset = 0;
if (!gatt)
return btd_error_failed(msg, "Not connected");
@@ -837,14 +893,17 @@
if (chrc->read_id)
return btd_error_in_progress(msg);
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_options(&iter, &offset))
+ return btd_error_invalid_args(msg);
+
op = new0(struct async_dbus_op, 1);
op->msg = dbus_message_ref(msg);
op->data = chrc;
+ op->offset = offset;
- chrc->read_id = bt_gatt_client_read_value(gatt, chrc->value_handle,
- chrc_read_cb,
- async_dbus_op_ref(op),
- async_dbus_op_unref);
+ chrc->read_id = read_value(gatt, chrc->value_handle, chrc_read_cb, op);
if (chrc->read_id)
return NULL;
@@ -871,9 +930,11 @@
{
struct characteristic *chrc = user_data;
struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ DBusMessageIter iter;
uint8_t *value = NULL;
- size_t value_len = 0;
+ int value_len = 0;
bool supported = false;
+ uint16_t offset = 0;
if (!gatt)
return btd_error_failed(msg, "Not connected");
@@ -881,7 +942,12 @@
if (chrc->write_id)
return btd_error_in_progress(msg);
- if (!parse_value_arg(msg, &value, &value_len))
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_value_arg(&iter, &value, &value_len))
+ return btd_error_invalid_args(msg);
+
+ if (parse_options(&iter, &offset))
return btd_error_invalid_args(msg);
/*
@@ -898,7 +964,7 @@
if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) {
supported = true;
chrc->write_id = start_long_write(msg, chrc->value_handle, gatt,
- true, value, value_len,
+ true, value, value_len, offset,
chrc, chrc_write_complete);
if (chrc->write_id)
return NULL;
@@ -912,7 +978,7 @@
if (!mtu)
return btd_error_failed(msg, "No ATT transport");
- if (value_len <= (unsigned) mtu - 3)
+ if (value_len <= mtu - 3 && !offset)
chrc->write_id = start_write_request(msg,
chrc->value_handle,
gatt, value, value_len,
@@ -920,7 +986,7 @@
else
chrc->write_id = start_long_write(msg,
chrc->value_handle, gatt,
- false, value, value_len,
+ false, value, value_len, offset,
chrc, chrc_write_complete);
if (chrc->write_id)
@@ -1225,7 +1291,8 @@
{ "Value", "ay", characteristic_get_value, NULL,
characteristic_value_exists,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Notifying", "b", characteristic_get_notifying, NULL, NULL,
+ { "Notifying", "b", characteristic_get_notifying, NULL,
+ characteristic_notifying_exists,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ "Flags", "as", characteristic_get_flags, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
@@ -1233,17 +1300,19 @@
};
static const GDBusMethodTable characteristic_methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
- GDBUS_ARGS({ "value", "ay" }),
- characteristic_read_value) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
+ GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ characteristic_read_value) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
- GDBUS_ARGS({ "value", "ay" }),
- NULL,
- characteristic_write_value) },
+ GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL,
+ characteristic_write_value) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL,
- characteristic_start_notify) },
+ characteristic_start_notify) },
{ GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL,
- characteristic_stop_notify) },
+ characteristic_stop_notify) },
{ }
};
@@ -1273,7 +1342,9 @@
gatt_db_attribute_get_char_data(attr, &chrc->handle,
&chrc->value_handle,
- &chrc->props, &uuid);
+ &chrc->props,
+ &chrc->ext_props,
+ &uuid);
chrc->attr = gatt_db_get_attribute(service->client->db,
chrc->value_handle);
@@ -1387,7 +1458,6 @@
struct service *service = data;
queue_destroy(service->chrcs, NULL); /* List should be empty here */
- queue_destroy(service->pending_ext_props, NULL);
g_free(service->path);
free(service);
}
@@ -1401,7 +1471,6 @@
service = new0(struct service, 1);
service->chrcs = queue_new();
- service->pending_ext_props = queue_new();
service->client = client;
gatt_db_attribute_get_service_data(attr, &service->start_handle,
@@ -1473,44 +1542,6 @@
queue_push_tail(charac->descs, desc);
}
-static void read_ext_props_cb(bool success, uint8_t att_ecode,
- const uint8_t *value, uint16_t length,
- void *user_data)
-{
- struct characteristic *chrc = user_data;
- struct service *service = chrc->service;
-
- if (!success) {
- error("Failed to obtain extended properties - error: 0x%02x",
- att_ecode);
- return;
- }
-
- if (!value || length != 2) {
- error("Malformed extended properties value");
- return;
- }
-
- chrc->ext_props = get_le16(value);
- if (chrc->ext_props)
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- chrc->path,
- GATT_CHARACTERISTIC_IFACE,
- "Flags");
-
- queue_remove(service->pending_ext_props, chrc);
-}
-
-static void read_ext_props(void *data, void *user_data)
-{
- struct characteristic *chrc = data;
-
- bt_gatt_client_read_value(chrc->service->client->gatt,
- chrc->ext_props_handle,
- read_ext_props_cb,
- chrc, NULL);
-}
-
static bool create_descriptors(struct gatt_db_attribute *attr,
struct characteristic *charac)
{
@@ -1544,9 +1575,6 @@
queue_push_tail(service->chrcs, charac);
- if (charac->ext_props_handle)
- queue_push_tail(service->pending_ext_props, charac);
-
return;
fail:
@@ -1563,13 +1591,7 @@
gatt_db_service_foreach_char(attr, export_char, &data);
- if (data.failed)
- return false;
-
- /* Obtain extended properties */
- queue_foreach(service->pending_ext_props, read_ext_props, NULL);
-
- return true;
+ return !data.failed;
}
static void export_service(struct gatt_db_attribute *attr, void *user_data)
diff --git a/src/gatt-database.c b/src/gatt-database.c
index b8da955..e287b98 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -54,6 +54,7 @@
#endif
#define GATT_MANAGER_IFACE "org.bluez.GattManager1"
+#define GATT_PROFILE_IFACE "org.bluez.GattProfile1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHRC_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESC_IFACE "org.bluez.GattDescriptor1"
@@ -88,6 +89,7 @@
DBusMessage *reg;
GDBusClient *client;
bool failed;
+ struct queue *profiles;
struct queue *services;
struct queue *proxies;
};
@@ -103,10 +105,8 @@
};
struct external_profile {
- struct btd_gatt_database *database;
- char *owner;
- char *path; /* Path to GattProfile1 */
- unsigned int id;
+ struct gatt_app *app;
+ GDBusProxy *proxy;
struct queue *profiles; /* btd_profile list */
};
@@ -116,6 +116,7 @@
GDBusProxy *proxy;
uint8_t props;
uint8_t ext_props;
+ uint32_t perm;
struct gatt_db_attribute *attrib;
struct gatt_db_attribute *ccc;
struct queue *pending_reads;
@@ -135,6 +136,7 @@
};
struct pending_op {
+ struct btd_device *device;
unsigned int id;
struct gatt_db_attribute *attrib;
struct queue *owner_queue;
@@ -361,10 +363,47 @@
free(service);
}
+static void profile_remove(void *data)
+{
+ struct btd_profile *p = data;
+
+ DBG("Removed \"%s\"", p->name);
+
+ adapter_foreach(adapter_remove_profile, p);
+ btd_profile_unregister(p);
+
+ g_free((void *) p->name);
+ g_free((void *) p->remote_uuid);
+
+ free(p);
+}
+
+static void profile_release(struct external_profile *profile)
+{
+ DBG("Releasing \"%s\"", profile->app->owner);
+
+ g_dbus_proxy_method_call(profile->proxy, "Release", NULL, NULL, NULL,
+ NULL);
+}
+
+static void profile_free(void *data)
+{
+ struct external_profile *profile = data;
+
+ queue_destroy(profile->profiles, profile_remove);
+
+ profile_release(profile);
+
+ g_dbus_proxy_unref(profile->proxy);
+
+ free(profile);
+}
+
static void app_free(void *data)
{
struct gatt_app *app = data;
+ queue_destroy(app->profiles, profile_free);
queue_destroy(app->services, service_free);
queue_destroy(app->proxies, NULL);
@@ -385,53 +424,6 @@
free(app);
}
-static void profile_remove(void *data)
-{
- struct btd_profile *p = data;
-
- DBG("Removed \"%s\"", p->name);
-
- adapter_foreach(adapter_remove_profile, p);
- btd_profile_unregister(p);
-
- g_free((void *) p->name);
- g_free((void *) p->remote_uuid);
-
- free(p);
-}
-
-static void profile_release(struct external_profile *profile)
-{
- DBusMessage *msg;
-
- if (!profile->id)
- return;
-
- DBG("Releasing \"%s\"", profile->owner);
-
- g_dbus_remove_watch(btd_get_dbus_connection(), profile->id);
-
- msg = dbus_message_new_method_call(profile->owner, profile->path,
- "org.bluez.GattProfile1",
- "Release");
- if (msg)
- g_dbus_send_message(btd_get_dbus_connection(), msg);
-}
-
-static void profile_free(void *data)
-{
- struct external_profile *profile = data;
-
- queue_destroy(profile->profiles, profile_remove);
-
- profile_release(profile);
-
- g_free(profile->owner);
- g_free(profile->path);
-
- free(profile);
-}
-
static void gatt_database_free(void *data)
{
struct btd_gatt_database *database = data;
@@ -1122,7 +1114,7 @@
}
static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,
- uint8_t *ext_props)
+ uint8_t *ext_props, uint32_t *perm)
{
const char *flag;
@@ -1136,34 +1128,51 @@
if (!strcmp("broadcast", flag))
*props |= BT_GATT_CHRC_PROP_BROADCAST;
- else if (!strcmp("read", flag))
+ else if (!strcmp("read", flag)) {
*props |= BT_GATT_CHRC_PROP_READ;
- else if (!strcmp("write-without-response", flag))
+ *perm |= BT_ATT_PERM_READ;
+ } else if (!strcmp("write-without-response", flag)) {
*props |= BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP;
- else if (!strcmp("write", flag))
+ *perm |= BT_ATT_PERM_WRITE;
+ } else if (!strcmp("write", flag)) {
*props |= BT_GATT_CHRC_PROP_WRITE;
- else if (!strcmp("notify", flag))
+ *perm |= BT_ATT_PERM_WRITE;
+ } else if (!strcmp("notify", flag)) {
*props |= BT_GATT_CHRC_PROP_NOTIFY;
- else if (!strcmp("indicate", flag))
+ } else if (!strcmp("indicate", flag)) {
*props |= BT_GATT_CHRC_PROP_INDICATE;
- else if (!strcmp("authenticated-signed-writes", flag))
+ } else if (!strcmp("authenticated-signed-writes", flag)) {
*props |= BT_GATT_CHRC_PROP_AUTH;
- else if (!strcmp("reliable-write", flag))
+ *perm |= BT_ATT_PERM_WRITE;
+ } else if (!strcmp("reliable-write", flag)) {
*ext_props |= BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
- else if (!strcmp("writable-auxiliaries", flag))
+ *perm |= BT_ATT_PERM_WRITE;
+ } else if (!strcmp("writable-auxiliaries", flag)) {
*ext_props |= BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX;
- else if (!strcmp("encrypt-read", flag)) {
+ } else if (!strcmp("encrypt-read", flag)) {
*props |= BT_GATT_CHRC_PROP_READ;
*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_READ;
+ *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT;
} else if (!strcmp("encrypt-write", flag)) {
*props |= BT_GATT_CHRC_PROP_WRITE;
*ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_WRITE;
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT;
} else if (!strcmp("encrypt-authenticated-read", flag)) {
*props |= BT_GATT_CHRC_PROP_READ;
*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+ *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
} else if (!strcmp("encrypt-authenticated-write", flag)) {
*props |= BT_GATT_CHRC_PROP_WRITE;
*ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+ } else if (!strcmp("secure-read", flag)) {
+ *props |= BT_GATT_CHRC_PROP_READ;
+ *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ;
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_READ_SECURE;
+ } else if (!strcmp("secure-write", flag)) {
+ *props |= BT_GATT_CHRC_PROP_WRITE;
+ *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE;
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE;
} else {
error("Invalid characteristic flag: %s", flag);
return false;
@@ -1200,6 +1209,10 @@
*perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
else if (!strcmp("encrypt-authenticated-write", flag))
*perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
+ else if (!strcmp("secure-read", flag))
+ *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN;
+ else if (!strcmp("secure-write", flag))
+ *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN;
else {
error("Invalid descriptor flag: %s", flag);
return false;
@@ -1213,6 +1226,7 @@
uint32_t *perm)
{
DBusMessageIter iter, array;
+ const char *iface;
if (!g_dbus_proxy_get_property(proxy, "Flags", &iter))
return false;
@@ -1222,10 +1236,11 @@
dbus_message_iter_recurse(&iter, &array);
- if (perm)
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, GATT_DESC_IFACE))
return parse_desc_flags(&array, perm);
- return parse_chrc_flags(&array, props, ext_props);
+ return parse_chrc_flags(&array, props, ext_props, perm);
}
static struct external_chrc *chrc_create(struct gatt_app *app,
@@ -1273,7 +1288,7 @@
* are used to determine if any special descriptors should be
* created.
*/
- if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, NULL)) {
+ if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm)) {
error("Failed to parse characteristic properties");
goto fail;
}
@@ -1592,7 +1607,8 @@
free(op);
}
-static struct pending_op *pending_read_new(struct queue *owner_queue,
+static struct pending_op *pending_read_new(struct btd_device *device,
+ struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id)
{
@@ -1601,6 +1617,7 @@
op = new0(struct pending_op, 1);
op->owner_queue = owner_queue;
+ op->device = device;
op->attrib = attrib;
op->id = id;
queue_push_tail(owner_queue, op);
@@ -1608,33 +1625,75 @@
return op;
}
-static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
- struct queue *owner_queue,
- unsigned int id)
+static void append_options(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = user_data;
+ const char *path = device_get_path(op->device);
+
+ dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void read_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = 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);
+
+ append_options(&dict, op);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static struct pending_op *send_read(struct btd_device *device,
+ struct gatt_db_attribute *attrib,
+ GDBusProxy *proxy,
+ struct queue *owner_queue,
+ unsigned int id)
{
struct pending_op *op;
- uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
- op = pending_read_new(owner_queue, attrib, id);
+ op = pending_read_new(device, owner_queue, attrib, id);
- if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb,
- op, pending_op_free) == TRUE)
- return;
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb,
+ read_reply_cb, op, pending_op_free) == TRUE)
+ return op;
pending_op_free(op);
- gatt_db_attribute_read_result(attrib, id, ecode, NULL, 0);
+ return NULL;
}
static void write_setup_cb(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
- DBusMessageIter array;
+ DBusMessageIter array, dict;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
&op->data.iov_base, op->data.iov_len);
dbus_message_iter_close_container(iter, &array);
+
+ 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);
+
+ append_options(&dict, op);
+
+ dbus_message_iter_close_container(iter, &dict);
+
+ if (!op->owner_queue) {
+ gatt_db_attribute_write_result(op->attrib, op->id, 0);
+ pending_op_free(op);
+ }
}
static void write_reply_cb(DBusMessage *message, void *user_data)
@@ -1673,7 +1732,8 @@
gatt_db_attribute_write_result(op->attrib, op->id, ecode);
}
-static struct pending_op *pending_write_new(struct queue *owner_queue,
+static struct pending_op *pending_write_new(struct btd_device *device,
+ struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id,
const uint8_t *value,
@@ -1686,6 +1746,7 @@
op->data.iov_base = (uint8_t *) value;
op->data.iov_len = len;
+ op->device = device;
op->owner_queue = owner_queue;
op->attrib = attrib;
op->id = id;
@@ -1694,55 +1755,25 @@
return op;
}
-static void send_write(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
+static struct pending_op *send_write(struct btd_device *device,
+ struct gatt_db_attribute *attrib,
+ GDBusProxy *proxy,
struct queue *owner_queue,
unsigned int id,
const uint8_t *value, size_t len)
{
struct pending_op *op;
- uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
- op = pending_write_new(owner_queue, attrib, id, value, len);
+ op = pending_write_new(device, owner_queue, attrib, id, value, len);
if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
- write_reply_cb, op,
- pending_op_free) == TRUE)
- return;
+ owner_queue ? write_reply_cb : NULL,
+ op, pending_op_free) == TRUE)
+ return op;
pending_op_free(op);
- gatt_db_attribute_write_result(attrib, id, ecode);
-}
-
-static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props)
-{
- uint32_t perm = 0;
-
- if (props & BT_GATT_CHRC_PROP_WRITE ||
- props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP ||
- ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE ||
- ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE ||
- ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
- perm |= BT_ATT_PERM_WRITE;
-
- if (props & BT_GATT_CHRC_PROP_READ ||
- ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ ||
- ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
- perm |= BT_ATT_PERM_READ;
-
- if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ)
- perm |= BT_ATT_PERM_READ_ENCRYPT;
-
- if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE)
- perm |= BT_ATT_PERM_WRITE_ENCRYPT;
-
- if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ)
- perm |= BT_ATT_PERM_READ_AUTHEN;
-
- if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE)
- perm |= BT_ATT_PERM_WRITE_AUTHEN;
-
- return perm;
+ return NULL;
}
static uint8_t ccc_write_cb(uint16_t value, void *user_data)
@@ -1895,19 +1926,65 @@
return true;
}
+static struct btd_device *att_get_device(struct bt_att *att)
+{
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+ bdaddr_t src, dst;
+ uint8_t dst_type;
+ struct btd_adapter *adapter;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return NULL;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST_TYPE, &dst_type,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return NULL;
+ }
+
+ g_io_channel_unref(io);
+
+ adapter = adapter_find(&src);
+ if (!adapter) {
+ error("Unable to find adapter object");
+ return NULL;
+ }
+
+ return btd_adapter_find_device(adapter, &dst, dst_type);
+}
+
static void desc_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct external_desc *desc = user_data;
+ struct btd_device *device;
if (desc->attrib != attrib) {
error("Read callback called with incorrect attribute");
- return;
+ goto fail;
}
- send_read(attrib, desc->proxy, desc->pending_reads, id);
+ device = att_get_device(att);
+ if (!device) {
+ error("Unable to find device object");
+ goto fail;
+ }
+
+ if (send_read(device, attrib, desc->proxy, desc->pending_reads, id))
+ return;
+
+fail:
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
}
static void desc_write_cb(struct gatt_db_attribute *attrib,
@@ -1917,13 +1994,26 @@
void *user_data)
{
struct external_desc *desc = user_data;
+ struct btd_device *device;
if (desc->attrib != attrib) {
error("Read callback called with incorrect attribute");
- return;
+ goto fail;
}
- send_write(attrib, desc->proxy, desc->pending_writes, id, value, len);
+ device = att_get_device(att);
+ if (!device) {
+ error("Unable to find device object");
+ goto fail;
+ }
+
+ if (send_write(device, attrib, desc->proxy, desc->pending_writes, id,
+ value, len))
+ return;
+
+fail:
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
}
static bool database_add_desc(struct external_service *service,
@@ -1956,43 +2046,25 @@
void *user_data)
{
struct external_chrc *chrc = user_data;
+ struct btd_device *device;
if (chrc->attrib != attrib) {
error("Read callback called with incorrect attribute");
- return;
+ goto fail;
}
- send_read(attrib, chrc->proxy, chrc->pending_reads, id);
-}
+ device = att_get_device(att);
+ if (!device) {
+ error("Unable to find device object");
+ goto fail;
+ }
-static void write_without_response_setup_cb(DBusMessageIter *iter,
- void *user_data)
-{
- struct iovec *iov = user_data;
- DBusMessageIter array;
+ if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id))
+ return;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
- dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
- &iov->iov_base, iov->iov_len);
- dbus_message_iter_close_container(iter, &array);
-}
-
-static void send_write_without_response(struct gatt_db_attribute *attrib,
- GDBusProxy *proxy, unsigned int id,
- const uint8_t *value, size_t len)
-{
- struct iovec iov;
- uint8_t ecode = 0;
-
- iov.iov_base = (uint8_t *) value;
- iov.iov_len = len;
-
- if (!g_dbus_proxy_method_call(proxy, "WriteValue",
- write_without_response_setup_cb,
- NULL, &iov, NULL))
- ecode = BT_ATT_ERROR_UNLIKELY;
-
- gatt_db_attribute_write_result(attrib, id, ecode);
+fail:
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
}
static void chrc_write_cb(struct gatt_db_attribute *attrib,
@@ -2002,26 +2074,37 @@
void *user_data)
{
struct external_chrc *chrc = user_data;
+ struct btd_device *device;
+ struct queue *queue;
if (chrc->attrib != attrib) {
error("Write callback called with incorrect attribute");
- return;
+ goto fail;
}
- if (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) {
- send_write_without_response(attrib, chrc->proxy, id, value,
- len);
- return;
+ device = att_get_device(att);
+ if (!device) {
+ error("Unable to find device object");
+ goto fail;
}
- send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len);
+ if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
+ queue = chrc->pending_writes;
+ else
+ queue = NULL;
+
+ if (send_write(device, attrib, chrc->proxy, queue, id, value, len))
+ return;
+
+fail:
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
}
static bool database_add_chrc(struct external_service *service,
struct external_chrc *chrc)
{
bt_uuid_t uuid;
- uint32_t perm;
const struct queue_entry *entry;
if (!parse_uuid(chrc->proxy, &uuid)) {
@@ -2034,14 +2117,8 @@
return false;
}
- /*
- * TODO: Once shared/gatt-server properly supports permission checks,
- * set the permissions based on a D-Bus property of the external
- * characteristic.
- */
- perm = permissions_from_props(chrc->props, chrc->ext_props);
chrc->attrib = gatt_db_service_add_characteristic(service->attrib,
- &uuid, perm,
+ &uuid, chrc->perm,
chrc->props, chrc_read_cb,
chrc_write_cb, chrc);
if (!chrc->attrib) {
@@ -2134,9 +2211,6 @@
{
const struct queue_entry *entry;
- if (queue_isempty(app->services))
- return false;
-
entry = queue_get_entries(app->services);
while (entry) {
if (!database_add_service(entry->data)) {
@@ -2150,6 +2224,131 @@
return true;
}
+static int profile_device_probe(struct btd_service *service)
+{
+ struct btd_profile *p = btd_service_get_profile(service);
+
+ DBG("%s probed", p->name);
+
+ return 0;
+}
+
+static void profile_device_remove(struct btd_service *service)
+{
+ struct btd_profile *p = btd_service_get_profile(service);
+
+ DBG("%s removed", p->name);
+}
+
+static int profile_add(struct external_profile *profile, const char *uuid)
+{
+ struct btd_profile *p;
+
+ p = new0(struct btd_profile, 1);
+
+ /* Assign directly to avoid having extra fields */
+ p->name = (const void *) g_strdup_printf("%s%s/%s", profile->app->owner,
+ g_dbus_proxy_get_path(profile->proxy), uuid);
+ if (!p->name) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ p->remote_uuid = (const void *) g_strdup(uuid);
+ if (!p->remote_uuid) {
+ g_free((void *) p->name);
+ free(p);
+ return -ENOMEM;
+ }
+
+ p->device_probe = profile_device_probe;
+ p->device_remove = profile_device_remove;
+ p->auto_connect = true;
+ p->external = true;
+
+ queue_push_tail(profile->profiles, p);
+
+ DBG("Added \"%s\"", p->name);
+
+ return 0;
+}
+
+static void add_profile(void *data, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ btd_profile_register(data);
+ adapter_add_profile(adapter, data);
+}
+
+static struct external_profile *create_profile(struct gatt_app *app,
+ GDBusProxy *proxy,
+ const char *path)
+{
+ struct external_profile *profile;
+ DBusMessageIter iter, array;
+
+ if (!path || !g_str_has_prefix(path, "/"))
+ return NULL;
+
+ profile = new0(struct external_profile, 1);
+
+ profile->app = app;
+ profile->proxy = g_dbus_proxy_ref(proxy);
+ profile->profiles = queue_new();
+
+ if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) {
+ DBG("UUIDs property not found");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+ const char *uuid;
+
+ dbus_message_iter_get_basic(&array, &uuid);
+
+ if (profile_add(profile, uuid) < 0)
+ goto fail;
+
+ dbus_message_iter_next(&array);
+ }
+
+ if (queue_isempty(profile->profiles))
+ goto fail;
+
+ queue_foreach(profile->profiles, add_profile, app->database->adapter);
+ queue_push_tail(app->profiles, profile);
+
+ return profile;
+
+fail:
+ profile_free(profile);
+ return NULL;
+}
+
+static void register_profile(void *data, void *user_data)
+{
+ struct gatt_app *app = user_data;
+ GDBusProxy *proxy = data;
+ const char *iface = g_dbus_proxy_get_interface(proxy);
+ const char *path = g_dbus_proxy_get_path(proxy);
+
+ if (app->failed)
+ return;
+
+ if (g_strcmp0(iface, GATT_PROFILE_IFACE) == 0) {
+ struct external_profile *profile;
+
+ profile = create_profile(app, proxy, path);
+ if (!profile) {
+ app->failed = true;
+ return;
+ }
+ }
+}
+
static void register_service(void *data, void *user_data)
{
struct gatt_app *app = user_data;
@@ -2232,11 +2431,13 @@
goto reply;
}
+ queue_foreach(app->proxies, register_profile, app);
queue_foreach(app->proxies, register_service, app);
queue_foreach(app->proxies, register_characteristic, app);
queue_foreach(app->proxies, register_descriptor, app);
- if (!app->services || app->failed) {
+ if ((queue_isempty(app->services) && queue_isempty(app->profiles)) ||
+ app->failed) {
error("No valid external GATT objects found");
fail = true;
reply = btd_error_failed(app->reg,
@@ -2289,6 +2490,7 @@
goto fail;
app->services = queue_new();
+ app->profiles = queue_new();
app->proxies = queue_new();
app->reg = dbus_message_ref(msg);
@@ -2375,203 +2577,6 @@
return dbus_message_new_method_return(msg);
}
-static void profile_exited(DBusConnection *conn, void *user_data)
-{
- struct external_profile *profile = user_data;
-
- DBG("\"%s\" exited", profile->owner);
-
- profile->id = 0;
-
- queue_remove(profile->database->profiles, profile);
-
- profile_free(profile);
-}
-
-static int profile_device_probe(struct btd_service *service)
-{
- struct btd_profile *p = btd_service_get_profile(service);
-
- DBG("%s probed", p->name);
-
- return 0;
-}
-
-static void profile_device_remove(struct btd_service *service)
-{
- struct btd_profile *p = btd_service_get_profile(service);
-
- DBG("%s removed", p->name);
-}
-
-static int profile_add(struct external_profile *profile, const char *uuid)
-{
- struct btd_profile *p;
-
- p = new0(struct btd_profile, 1);
-
- /* Assign directly to avoid having extra fields */
- p->name = (const void *) g_strdup_printf("%s%s/%s", profile->owner,
- profile->path, uuid);
- if (!p->name) {
- free(p);
- return -ENOMEM;
- }
-
- p->remote_uuid = (const void *) g_strdup(uuid);
- if (!p->remote_uuid) {
- g_free((void *) p->name);
- free(p);
- return -ENOMEM;
- }
-
- p->device_probe = profile_device_probe;
- p->device_remove = profile_device_remove;
- p->auto_connect = true;
- p->external = true;
-
- queue_push_tail(profile->profiles, p);
-
- DBG("Added \"%s\"", p->name);
-
- return 0;
-}
-
-static void add_profile(void *data, void *user_data)
-{
- struct btd_adapter *adapter = user_data;
-
- btd_profile_register(data);
- adapter_add_profile(adapter, data);
-}
-
-static int profile_create(DBusConnection *conn,
- struct btd_gatt_database *database,
- const char *sender, const char *path,
- DBusMessageIter *iter)
-{
- struct external_profile *profile;
- DBusMessageIter uuids;
-
- if (!path || !g_str_has_prefix(path, "/"))
- return -EINVAL;
-
- profile = new0(struct external_profile, 1);
-
- profile->owner = g_strdup(sender);
- if (!profile->owner)
- goto fail;
-
- profile->path = g_strdup(path);
- if (!profile->path)
- goto fail;
-
- profile->profiles = queue_new();
- profile->database = database;
- profile->id = g_dbus_add_disconnect_watch(conn, sender, profile_exited,
- profile, NULL);
-
- dbus_message_iter_recurse(iter, &uuids);
-
- while (dbus_message_iter_get_arg_type(&uuids) == DBUS_TYPE_STRING) {
- const char *uuid;
-
- dbus_message_iter_get_basic(&uuids, &uuid);
-
- if (profile_add(profile, uuid) < 0)
- goto fail;
-
- dbus_message_iter_next(&uuids);
- }
-
- if (queue_isempty(profile->profiles))
- goto fail;
-
- queue_foreach(profile->profiles, add_profile, database->adapter);
- queue_push_tail(database->profiles, profile);
-
- return 0;
-
-fail:
- profile_free(profile);
- return -EINVAL;
-}
-
-static bool match_profile(const void *a, const void *b)
-{
- const struct external_profile *profile = a;
- const struct svc_match_data *data = b;
-
- return g_strcmp0(profile->path, data->path) == 0 &&
- g_strcmp0(profile->owner, data->sender) == 0;
-}
-
-static DBusMessage *manager_register_profile(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
-{
- struct btd_gatt_database *database = user_data;
- const char *sender = dbus_message_get_sender(msg);
- DBusMessageIter args;
- const char *path;
- struct svc_match_data match_data;
-
- DBG("sender %s", sender);
-
- if (!dbus_message_iter_init(msg, &args))
- return btd_error_invalid_args(msg);
-
- if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&args, &path);
-
- match_data.path = path;
- match_data.sender = sender;
-
- if (queue_find(database->profiles, match_profile, &match_data))
- return btd_error_already_exists(msg);
-
- dbus_message_iter_next(&args);
- if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
- return btd_error_invalid_args(msg);
-
- if (profile_create(conn, database, sender, path, &args) < 0)
- return btd_error_failed(msg, "Failed to register profile");
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *manager_unregister_profile(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
-{
- struct btd_gatt_database *database = user_data;
- const char *sender = dbus_message_get_sender(msg);
- const char *path;
- DBusMessageIter args;
- struct external_profile *profile;
- struct svc_match_data match_data;
-
- if (!dbus_message_iter_init(msg, &args))
- return btd_error_invalid_args(msg);
-
- if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&args, &path);
-
- match_data.path = path;
- match_data.sender = sender;
-
- profile = queue_remove_if(database->profiles, match_profile,
- &match_data);
- if (!profile)
- return btd_error_does_not_exist(msg);
-
- profile_free(profile);
-
- return dbus_message_new_method_return(msg);
-}
-
static const GDBusMethodTable manager_methods[] = {
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterApplication",
GDBUS_ARGS({ "application", "o" },
@@ -2580,13 +2585,6 @@
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterApplication",
GDBUS_ARGS({ "application", "o" }),
NULL, manager_unregister_app) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterProfile",
- GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
- { "options", "a{sv}" }), NULL,
- manager_register_profile) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterProfile",
- GDBUS_ARGS({ "profile", "o" }),
- NULL, manager_unregister_profile) },
{ }
};
diff --git a/src/profile.c b/src/profile.c
index 5a4f09c..c81a9f9 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -1047,10 +1047,11 @@
conn);
}
- if (conn->service && service_accept(conn->service) == 0) {
- if (send_new_connection(ext, conn))
- return;
- }
+ if (conn->service && service_accept(conn->service) < 0)
+ goto drop;
+
+ if (send_new_connection(ext, conn))
+ return;
drop:
if (conn->service)
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index c3062c0..4a9b67f 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -31,6 +31,7 @@
#define BT_ATT_SECURITY_LOW 1
#define BT_ATT_SECURITY_MEDIUM 2
#define BT_ATT_SECURITY_HIGH 3
+#define BT_ATT_SECURITY_FIPS 4
#define BT_ATT_DEFAULT_LE_MTU 23
#define BT_ATT_MAX_LE_MTU 517
@@ -123,6 +124,10 @@
BT_ATT_PERM_WRITE_AUTHEN)
#define BT_ATT_PERM_AUTHOR 0x40
#define BT_ATT_PERM_NONE 0x80
+#define BT_ATT_PERM_READ_SECURE 0x0100
+#define BT_ATT_PERM_WRITE_SECURE 0x0200
+#define BT_ATT_PERM_SECURE (BT_ATT_PERM_READ_SECURE | \
+ BT_ATT_PERM_WRITE_SECURE)
/* GATT Characteristic Properties Bitfield values */
#define BT_GATT_CHRC_PROP_BROADCAST 0x01
diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index cec1b21..e20d1b3 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -198,7 +198,8 @@
}
bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
- uint32_t flags, const void *data, uint16_t size)
+ uint32_t flags, uint32_t drops, const void *data,
+ uint16_t size)
{
struct btsnoop_pkt pkt;
uint64_t ts;
@@ -212,7 +213,7 @@
pkt.size = htobe32(size);
pkt.len = htobe32(size);
pkt.flags = htobe32(flags);
- pkt.drops = htobe32(0);
+ pkt.drops = htobe32(drops);
pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);
written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
@@ -254,8 +255,8 @@
}
bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
- uint16_t index, uint16_t opcode,
- const void *data, uint16_t size)
+ uint16_t index, uint16_t opcode, uint32_t drops,
+ const void *data, uint16_t size)
{
uint32_t flags;
@@ -283,7 +284,7 @@
return false;
}
- return btsnoop_write(btsnoop, tv, flags, data, size);
+ return btsnoop_write(btsnoop, tv, flags, drops, data, size);
}
bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
@@ -303,7 +304,7 @@
return false;
}
- return btsnoop_write(btsnoop, tv, flags, data, size);
+ return btsnoop_write(btsnoop, tv, flags, 0, data, size);
}
static bool pklg_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index d94dbae..dab2b2f 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -62,6 +62,8 @@
#define BTSNOOP_BUS_RS232 4
#define BTSNOOP_BUS_PCI 5
#define BTSNOOP_BUS_SDIO 6
+#define BTSNOOP_BUS_SPI 7
+#define BTSNOOP_BUS_I2C 8
struct btsnoop_opcode_new_index {
uint8_t type;
@@ -99,11 +101,11 @@
uint32_t btsnoop_get_format(struct btsnoop *btsnoop);
-bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
- uint32_t flags, const void *data, uint16_t size);
+bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, uint32_t flags,
+ uint32_t drops, const void *data, uint16_t size);
bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
- uint16_t index, uint16_t opcode,
- const void *data, uint16_t size);
+ uint16_t index, uint16_t opcode, uint32_t drops,
+ const void *data, uint16_t size);
bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
uint16_t frequency, const void *data, uint16_t size);
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 34b6cc7..adabfe3 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -243,9 +243,9 @@
if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr)))
return NULL;
- if (!gatt_db_attribute_get_char_data(attr, NULL, NULL,
- &properties, NULL))
- return NULL;
+ if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, &properties,
+ NULL, NULL))
+ return NULL;
chrc = new0(struct notify_chrc, 1);
@@ -316,6 +316,7 @@
struct queue *pending_svcs;
struct queue *pending_chrcs;
struct queue *svcs;
+ struct queue *ext_prop_desc;
struct gatt_db_attribute *cur_svc;
bool success;
uint16_t start;
@@ -331,6 +332,7 @@
queue_destroy(op->pending_svcs, NULL);
queue_destroy(op->pending_chrcs, free);
queue_destroy(op->svcs, NULL);
+ queue_destroy(op->ext_prop_desc, NULL);
free(op);
}
@@ -360,6 +362,7 @@
op->pending_svcs = queue_new();
op->pending_chrcs = queue_new();
op->svcs = queue_new();
+ op->ext_prop_desc = queue_new();
op->client = client;
op->complete_func = complete_func;
op->failure_func = failure_func;
@@ -604,6 +607,107 @@
return false;
}
+static void ext_prop_write_cb(struct gatt_db_attribute *attrib,
+ int err, void *user_data)
+{
+ struct bt_gatt_client *client = user_data;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Value set status: %d", err);
+}
+
+static void ext_prop_read_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data);
+
+static bool read_ext_prop_desc(struct discovery_op *op)
+{
+ struct bt_gatt_client *client = op->client;
+ uint16_t handle;
+ struct gatt_db_attribute *attr;
+
+ attr = queue_peek_head(op->ext_prop_desc);
+ if (!attr)
+ return false;
+
+ handle = gatt_db_attribute_get_handle(attr);
+ bt_gatt_client_read_value(client, handle, ext_prop_read_cb,
+ discovery_op_ref(op),
+ discovery_op_unref);
+
+ return true;
+}
+
+static void ext_prop_read_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct discovery_op *op = user_data;
+ struct bt_gatt_client *client = op->client;
+ bool discovering;
+ struct gatt_db_attribute *desc_attr = NULL;
+ struct gatt_db_attribute *next_srv;
+ uint16_t start, end;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Ext. prop value: 0x%04x", (uint16_t)value[0]);
+
+ desc_attr = queue_pop_head(op->ext_prop_desc);
+ if (!desc_attr)
+ goto failed;
+
+ if (!gatt_db_attribute_write(desc_attr, 0, value, length, 0, NULL,
+ ext_prop_write_cb, client))
+ goto failed;
+
+ /* Any other descriptor to read? */
+ if (read_ext_prop_desc(op))
+ return;
+
+ /* Continue with discovery */
+ do {
+ if (!discover_descs(op, &discovering))
+ goto failed;
+
+ if (discovering)
+ return;
+
+ /* Done with the current service */
+ gatt_db_service_set_active(op->cur_svc, true);
+
+ next_srv = queue_pop_head(op->svcs);
+ if (!next_srv)
+ goto done;
+
+ if (!gatt_db_attribute_get_service_handles(next_srv, &start,
+ &end))
+ goto failed;
+
+ } while (start == end);
+
+ /* Move on to the next service */
+ op->cur_svc = next_srv;
+
+ client->discovery_req = bt_gatt_discover_characteristics(client->att,
+ start, end,
+ discover_chrcs_cb,
+ discovery_op_ref(op),
+ discovery_op_unref);
+ if (client->discovery_req)
+ return;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to start characteristic discovery");
+
+ discovery_op_unref(op);
+
+failed:
+ success = false;
+
+done:
+ discovery_op_complete(op, success, att_ecode);
+}
+
static void discover_descs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
@@ -618,6 +722,7 @@
char uuid_str[MAX_LEN_UUID_STR];
unsigned int desc_count;
bool discovering;
+ bt_uuid_t ext_prop_uuid;
discovery_req_clear(client);
@@ -640,6 +745,8 @@
util_debug(client->debug_callback, client->debug_data,
"Descriptors found: %u", desc_count);
+ bt_uuid16_create(&ext_prop_uuid, GATT_CHARAC_EXT_PROPER_UUID);
+
while (bt_gatt_iter_next_descriptor(&iter, &handle, u128.data)) {
bt_uuid128_create(&uuid, u128);
@@ -657,8 +764,15 @@
if (gatt_db_attribute_get_handle(attr) != handle)
goto failed;
+
+ if (!bt_uuid_cmp(&ext_prop_uuid, &uuid))
+ queue_push_tail(op->ext_prop_desc, attr);
}
+ /* If we got extended prop descriptor, lets read it right away */
+ if (read_ext_prop_desc(op))
+ return;
+
next:
if (!discover_descs(op, &discovering))
goto failed;
@@ -1631,7 +1745,7 @@
queue_foreach(client->notify_list, notify_handler, &pdu_data);
- if (opcode == BT_ATT_OP_HANDLE_VAL_IND)
+ if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent)
bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0,
NULL, NULL, NULL);
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index cc49458..513451f 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -52,6 +52,8 @@
.value.u16 = GATT_CHARAC_UUID };
static const bt_uuid_t included_service_uuid = { .type = BT_UUID16,
.value.u16 = GATT_INCLUDE_UUID };
+static const bt_uuid_t ext_desc_uuid = { .type = BT_UUID16,
+ .value.u16 = GATT_CHARAC_EXT_PROPER_UUID };
struct gatt_db {
int ref_count;
@@ -1456,10 +1458,68 @@
return le_to_uuid(decl->value, decl->value_len, uuid);
}
+static void read_ext_prop_value(struct gatt_db_attribute *attrib,
+ int err, const uint8_t *value,
+ size_t length, void *user_data)
+{
+ uint16_t *ext_prop = user_data;
+
+ if (err || (length != sizeof(uint16_t)))
+ return;
+
+ *ext_prop = (uint16_t) value[0];
+}
+
+static void read_ext_prop(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ uint16_t *ext_prop = user_data;
+
+ /*
+ * If ext_prop is set that means extended properties descriptor
+ * has been already found
+ */
+ if (*ext_prop != 0)
+ return;
+
+ if (bt_uuid_cmp(&ext_desc_uuid, &attrib->uuid))
+ return;
+
+ gatt_db_attribute_read(attrib, 0, BT_ATT_OP_READ_REQ, NULL,
+ read_ext_prop_value, ext_prop);
+}
+
+static uint8_t get_char_extended_prop(const struct gatt_db_attribute *attrib)
+{
+ uint16_t ext_prop;
+
+ if (!attrib)
+ return 0;
+
+ if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid))
+ return 0;
+
+ /* Check properties first */
+ if (!(attrib->value[0] & BT_GATT_CHRC_PROP_EXT_PROP))
+ return 0;
+
+ ext_prop = 0;
+
+ /*
+ * Cast needed for foreach function. We do not change attrib during
+ * this call
+ */
+ gatt_db_service_foreach_desc((struct gatt_db_attribute *) attrib,
+ read_ext_prop, &ext_prop);
+
+ return ext_prop;
+}
+
bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib,
uint16_t *handle,
uint16_t *value_handle,
uint8_t *properties,
+ uint16_t *ext_prop,
bt_uuid_t *uuid)
{
if (!attrib)
@@ -1484,6 +1544,9 @@
if (properties)
*properties = attrib->value[0];
+ if (ext_prop)
+ *ext_prop = get_char_extended_prop(attrib);
+
if (value_handle)
*value_handle = get_le16(attrib->value + 1);
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 96cceb9..134ec63 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -199,6 +199,7 @@
uint16_t *handle,
uint16_t *value_handle,
uint8_t *properties,
+ uint16_t *ext_prop,
bt_uuid_t *uuid);
bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib,
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index f1fca92..79e01c8 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -72,6 +72,8 @@
uint16_t handle;
uint16_t offset;
uint16_t length;
+
+ bool reliable_supported;
};
static void prep_write_data_destroy(void *user_data)
@@ -396,6 +398,9 @@
return 0;
security = bt_att_get_security(server->att);
+ if (perm & BT_ATT_PERM_SECURE && security < BT_ATT_SECURITY_FIPS)
+ return BT_ATT_ERROR_AUTHENTICATION;
+
if (perm & BT_ATT_PERM_AUTHEN && security < BT_ATT_SECURITY_HIGH)
return BT_ATT_ERROR_AUTHENTICATION;
@@ -1111,6 +1116,23 @@
return true;
}
+static bool is_reliable_write_supported(const struct bt_gatt_server *server,
+ uint16_t handle)
+{
+ struct gatt_db_attribute *attr;
+ uint16_t ext_prop;
+
+ attr = gatt_db_get_attribute(server->db, handle);
+ if (!attr)
+ return false;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, NULL, &ext_prop,
+ NULL))
+ return false;
+
+ return (ext_prop & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE);
+}
+
static bool prep_data_new(struct bt_gatt_server *server,
uint16_t handle, uint16_t offset,
uint16_t length, uint8_t *value)
@@ -1128,6 +1150,13 @@
prep_data->handle = handle;
prep_data->offset = offset;
+ /*
+ * Handle is the value handle. We need characteristic declaration
+ * handle which in BlueZ is handle_value -1
+ */
+ prep_data->reliable_supported = is_reliable_write_supported(server,
+ handle - 1);
+
queue_push_tail(server->prep_queue, prep_data);
return true;
@@ -1258,6 +1287,14 @@
ehandle, err);
}
+static bool find_no_reliable_characteristic(const void *data,
+ const void *match_data)
+{
+ const struct prep_write_data *prep_data = data;
+
+ return !prep_data->reliable_supported;
+}
+
static void exec_write_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
@@ -1265,6 +1302,7 @@
uint8_t flags;
uint8_t ecode;
bool write;
+ uint16_t ehandle = 0;
if (length != 1) {
ecode = BT_ATT_ERROR_INVALID_PDU;
@@ -1293,6 +1331,19 @@
return;
}
+ /* If there is more than one prep request, we are in reliable session */
+ if (queue_length(server->prep_queue) > 1) {
+ struct prep_write_data *prep_data;
+
+ prep_data = queue_find(server->prep_queue,
+ find_no_reliable_characteristic, NULL);
+ if (prep_data) {
+ ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ ehandle = prep_data->handle;
+ goto error;
+ }
+ }
+
exec_next_prep_write(server, 0, 0);
return;
@@ -1300,7 +1351,7 @@
error:
queue_remove_all(server->prep_queue, NULL, NULL,
prep_write_data_destroy);
- bt_att_send_error_rsp(server->att, opcode, 0, ecode);
+ bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
}
static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
diff --git a/src/shared/tty.h b/src/shared/tty.h
new file mode 100644
index 0000000..66ec09f
--- /dev/null
+++ b/src/shared/tty.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <termios.h>
+
+static inline unsigned int tty_get_speed(int speed)
+{
+ switch (speed) {
+ case 9600:
+ return B9600;
+ case 19200:
+ return B19200;
+ case 38400:
+ return B38400;
+ case 57600:
+ return B57600;
+ case 115200:
+ return B115200;
+ case 230400:
+ return B230400;
+ case 460800:
+ return B460800;
+ case 500000:
+ return B500000;
+ case 576000:
+ return B576000;
+ case 921600:
+ return B921600;
+ case 1000000:
+ return B1000000;
+ case 1152000:
+ return B1152000;
+ case 1500000:
+ return B1500000;
+ case 2000000:
+ return B2000000;
+#ifdef B2500000
+ case 2500000:
+ return B2500000;
+#endif
+#ifdef B3000000
+ case 3000000:
+ return B3000000;
+#endif
+#ifdef B3500000
+ case 3500000:
+ return B3500000;
+#endif
+#ifdef B3710000
+ case 3710000:
+ return B3710000;
+#endif
+#ifdef B4000000
+ case 4000000:
+ return B4000000;
+#endif
+ }
+
+ return 0;
+}
diff --git a/test/example-gatt-client b/test/example-gatt-client
index b77a627..4d1df2f 100755
--- a/test/example-gatt-client
+++ b/test/example-gatt-client
@@ -113,7 +113,7 @@
def start_client():
# Read the Body Sensor Location value and print it asynchronously.
- body_snsr_loc_chrc[0].ReadValue(reply_handler=body_sensor_val_cb,
+ body_snsr_loc_chrc[0].ReadValue({}, reply_handler=body_sensor_val_cb,
error_handler=generic_error_cb,
dbus_interface=GATT_CHRC_IFACE)
diff --git a/test/example-gatt-server b/test/example-gatt-server
index 93cf068..71aeb1b 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -166,13 +166,15 @@
return self.get_properties[GATT_CHRC_IFACE]
- @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
- def ReadValue(self):
+ @dbus.service.method(GATT_CHRC_IFACE,
+ in_signature='a{sv}',
+ out_signature='ay')
+ def ReadValue(self, options):
print('Default ReadValue called, returning error')
raise NotSupportedException()
- @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
- def WriteValue(self, value):
+ @dbus.service.method(GATT_CHRC_IFACE, in_signature='aya{sv}')
+ def WriteValue(self, value, options):
print('Default WriteValue called, returning error')
raise NotSupportedException()
@@ -222,13 +224,15 @@
return self.get_properties[GATT_CHRC_IFACE]
- @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
- def ReadValue(self):
+ @dbus.service.method(GATT_DESC_IFACE,
+ in_signature='a{sv}',
+ out_signature='ay')
+ def ReadValue(self, options):
print ('Default ReadValue called, returning error')
raise NotSupportedException()
- @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
- def WriteValue(self, value):
+ @dbus.service.method(GATT_DESC_IFACE, in_signature='aya{sv}')
+ def WriteValue(self, value, options):
print('Default WriteValue called, returning error')
raise NotSupportedException()
@@ -317,7 +321,7 @@
['read'],
service)
- def ReadValue(self):
+ def ReadValue(self, options):
# Return 'Chest' as the sensor location.
return [ 0x01 ]
@@ -331,7 +335,7 @@
['write'],
service)
- def WriteValue(self, value):
+ def WriteValue(self, value, options):
print('Heart Rate Control Point WriteValue called')
if len(value) != 1:
@@ -393,7 +397,7 @@
self.notify_battery_level()
return True
- def ReadValue(self):
+ def ReadValue(self, options):
print('Battery Level read: ' + repr(self.battery_lvl))
return [dbus.Byte(self.battery_lvl)]
@@ -425,6 +429,7 @@
Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
self.add_characteristic(TestCharacteristic(bus, 0, self))
self.add_characteristic(TestEncryptCharacteristic(bus, 1, self))
+ self.add_characteristic(TestSecureCharacteristic(bus, 2, self))
class TestCharacteristic(Characteristic):
"""
@@ -445,11 +450,11 @@
self.add_descriptor(
CharacteristicUserDescriptionDescriptor(bus, 1, self))
- def ReadValue(self):
+ def ReadValue(self, options):
print('TestCharacteristic Read: ' + repr(self.value))
return self.value
- def WriteValue(self, value):
+ def WriteValue(self, value, options):
print('TestCharacteristic Write: ' + repr(value))
self.value = value
@@ -468,7 +473,7 @@
['read', 'write'],
characteristic)
- def ReadValue(self):
+ def ReadValue(self, options):
return [
dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
]
@@ -491,10 +496,10 @@
['read', 'write'],
characteristic)
- def ReadValue(self):
+ def ReadValue(self, options):
return self.value
- def WriteValue(self, value):
+ def WriteValue(self, value, options):
if not self.writable:
raise NotPermittedException()
self.value = value
@@ -517,11 +522,11 @@
self.add_descriptor(
CharacteristicUserDescriptionDescriptor(bus, 3, self))
- def ReadValue(self):
+ def ReadValue(self, options):
print('TestCharacteristic Read: ' + repr(self.value))
return self.value
- def WriteValue(self, value):
+ def WriteValue(self, value, options):
print('TestCharacteristic Write: ' + repr(value))
self.value = value
@@ -539,7 +544,54 @@
['encrypt-read', 'encrypt-write'],
characteristic)
- def ReadValue(self):
+ def ReadValue(self, options):
+ return [
+ dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+ ]
+
+
+class TestSecureCharacteristic(Characteristic):
+ """
+ Dummy test characteristic requiring secure connection.
+
+ """
+ TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef5'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.TEST_CHRC_UUID,
+ ['secure-read', 'secure-write'],
+ service)
+ self.value = []
+ self.add_descriptor(TestEncryptDescriptor(bus, 2, self))
+ self.add_descriptor(
+ CharacteristicUserDescriptionDescriptor(bus, 3, self))
+
+ def ReadValue(self, options):
+ print('TestCharacteristic Read: ' + repr(self.value))
+ return self.value
+
+ def WriteValue(self, value, options):
+ print('TestCharacteristic Write: ' + repr(value))
+ self.value = value
+
+
+class TestSecureDescriptor(Descriptor):
+ """
+ Dummy test descriptor requiring secure connection. Returns a static value.
+
+ """
+ TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef6'
+
+ def __init__(self, bus, index, characteristic):
+ Descriptor.__init__(
+ self, bus, index,
+ self.TEST_DESC_UUID,
+ ['secure-read', 'secure-write'],
+ characteristic)
+
+ def ReadValue(self, options):
return [
dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
]
diff --git a/test/test-gatt-profile b/test/test-gatt-profile
index ad320b1..995a659 100755
--- a/test/test-gatt-profile
+++ b/test/test-gatt-profile
@@ -15,46 +15,116 @@
import gobject as GObject
import bluezutils
-class GattProfile(dbus.service.Object):
- @dbus.service.method("org.bluez.GattProfile1",
- in_signature="", out_signature="")
- def Release(self):
- print("Release")
- mainloop.quit()
+BLUEZ_SERVICE_NAME = 'org.bluez'
+GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+GATT_PROFILE_IFACE = 'org.bluez.GattProfile1'
+
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+
+class Application(dbus.service.Object):
+ def __init__(self, bus):
+ self.path = '/'
+ self.profiles = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_profile(self, profile):
+ self.profiles.append(profile)
+
+ @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+ def GetManagedObjects(self):
+ response = {}
+ print('GetManagedObjects')
+
+ for profile in self.profiles:
+ response[profile.get_path()] = profile.get_properties()
+
+ return response
+
+
+class Profile(dbus.service.Object):
+ PATH_BASE = '/org/bluez/example/profile'
+
+ def __init__(self, bus, uuids):
+ self.path = self.PATH_BASE
+ self.bus = bus
+ self.uuids = uuids
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_PROFILE_IFACE: {
+ 'UUIDs': self.uuids,
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ @dbus.service.method(GATT_PROFILE_IFACE,
+ in_signature="",
+ out_signature="")
+ def Release(self):
+ print("Release")
+ mainloop.quit()
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_PROFILE_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_PROFILE_IFACE]
+
+
+def register_app_cb():
+ print('GATT application registered')
+
+
+def register_app_error_cb(error):
+ print('Failed to register application: ' + str(error))
+ mainloop.quit()
if __name__ == '__main__':
- dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
- bus = dbus.SystemBus()
+ bus = dbus.SystemBus()
- path = bluezutils.find_adapter().object_path
+ path = bluezutils.find_adapter().object_path
- manager = dbus.Interface(bus.get_object("org.bluez", path),
- "org.bluez.GattManager1")
+ manager = dbus.Interface(bus.get_object("org.bluez", path),
+ GATT_MANAGER_IFACE)
- option_list = [
- make_option("-u", "--uuid", action="store",
- type="string", dest="uuid",
- default=None),
- make_option("-p", "--path", action="store",
- type="string", dest="path",
- default="/foo/bar/profile"),
- ]
+ option_list = [make_option("-u", "--uuid", action="store",
+ type="string", dest="uuid",
+ default=None),
+ ]
- opts = dbus.Dictionary({ }, signature='sv')
+ opts = dbus.Dictionary({}, signature='sv')
- parser = OptionParser(option_list=option_list)
+ parser = OptionParser(option_list=option_list)
- (options, args) = parser.parse_args()
+ (options, args) = parser.parse_args()
- profile = GattProfile(bus, options.path)
+ mainloop = GObject.MainLoop()
- mainloop = GObject.MainLoop()
+ if not options.uuid:
+ options.uuid = str(uuid.uuid4())
- if not options.uuid:
- options.uuid = str(uuid.uuid4())
+ app = Application(bus)
+ profile = Profile(bus, [options.uuid])
+ app.add_profile(profile)
+ manager.RegisterApplication(app.get_path(), {},
+ reply_handler=register_app_cb,
+ error_handler=register_app_error_cb)
- uuids = { options.uuid }
- manager.RegisterProfile(options.path, uuids, opts)
-
- mainloop.run()
+ mainloop.run()
diff --git a/tools/bccmd.c b/tools/bccmd.c
index 4d15f3f..84f1a4a 100644
--- a/tools/bccmd.c
+++ b/tools/bccmd.c
@@ -36,6 +36,8 @@
#include "lib/hci.h"
#include "lib/hci_lib.h"
+#include "src/shared/tty.h"
+
#include "csr.h"
#define CSR_TRANSPORT_UNKNOWN 0
@@ -1193,34 +1195,8 @@
device = strdup(optarg);
break;
case 'b':
- switch (atoi(optarg)) {
- case 9600: bcsp_rate = B9600; break;
- case 19200: bcsp_rate = B19200; break;
- case 38400: bcsp_rate = B38400; break;
- case 57600: bcsp_rate = B57600; break;
- case 115200: bcsp_rate = B115200; break;
- case 230400: bcsp_rate = B230400; break;
- case 460800: bcsp_rate = B460800; break;
- case 500000: bcsp_rate = B500000; break;
- case 576000: bcsp_rate = B576000; break;
- case 921600: bcsp_rate = B921600; break;
- case 1000000: bcsp_rate = B1000000; break;
- case 1152000: bcsp_rate = B1152000; break;
- case 1500000: bcsp_rate = B1500000; break;
- case 2000000: bcsp_rate = B2000000; break;
-#ifdef B2500000
- case 2500000: bcsp_rate = B2500000; break;
-#endif
-#ifdef B3000000
- case 3000000: bcsp_rate = B3000000; break;
-#endif
-#ifdef B3500000
- case 3500000: bcsp_rate = B3500000; break;
-#endif
-#ifdef B4000000
- case 4000000: bcsp_rate = B4000000; break;
-#endif
- default:
+ bcsp_rate = tty_get_speed(atoi(optarg));
+ if (!bcsp_rate) {
printf("Unknown BCSP baud rate specified, defaulting to 38400bps\n");
bcsp_rate = B38400;
}
diff --git a/tools/bluemoon.c b/tools/bluemoon.c
index 650ab0c..5fc6269 100644
--- a/tools/bluemoon.c
+++ b/tools/bluemoon.c
@@ -620,6 +620,7 @@
{ 0x09, "iBT 1.5 (AG610)" },
{ 0x0a, "iBT 2.1 (AG620)" },
{ 0x0b, "iBT 3.0 (LnP)" },
+ { 0x0c, "iBT 3.0 (WsP)" },
{ }
};
diff --git a/tools/btattach.c b/tools/btattach.c
index 45962e8..ec63ec9 100644
--- a/tools/btattach.c
+++ b/tools/btattach.c
@@ -46,6 +46,7 @@
#include "src/shared/mainloop.h"
#include "src/shared/timeout.h"
#include "src/shared/util.h"
+#include "src/shared/tty.h"
#include "src/shared/hci.h"
static int open_serial(const char *path, unsigned int speed)
@@ -219,56 +220,6 @@
{ }
};
-static unsigned int get_speed(const char *str)
-{
- switch (atoi(str)) {
- case 57600:
- return B57600;
- case 115200:
- return B115200;
- case 230400:
- return B230400;
- case 460800:
- return B460800;
- case 500000:
- return B500000;
- case 576000:
- return B576000;
- case 921600:
- return B921600;
- case 1000000:
- return B1000000;
- case 1152000:
- return B1152000;
- case 1500000:
- return B1500000;
- case 2000000:
- return B2000000;
-#ifdef B2500000
- case 2500000:
- return B2500000;
-#endif
-#ifdef B3000000
- case 3000000:
- return B3000000;
-#endif
-#ifdef B3500000
- case 3500000:
- return B3500000;
-#endif
-#ifdef B3710000
- case 3710000:
- return B3710000;
-#endif
-#ifdef B4000000
- case 4000000:
- return B4000000;
-#endif
- }
-
- return 0;
-}
-
int main(int argc, char *argv[])
{
const char *bredr_path = NULL, *amp_path = NULL, *proto = NULL;
@@ -296,7 +247,7 @@
proto = optarg;
break;
case 'S':
- speed = get_speed(optarg);
+ speed = tty_get_speed(atoi(optarg));
if (!speed) {
fprintf(stderr, "Invalid speed: %s\n", optarg);
return EXIT_FAILURE;
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 2153bec..4c8c9dd 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -297,18 +297,20 @@
{
uint16_t handle, value_handle;
uint8_t properties;
+ uint16_t ext_prop;
bt_uuid_t uuid;
if (!gatt_db_attribute_get_char_data(attr, &handle,
&value_handle,
&properties,
+ &ext_prop,
&uuid))
return;
printf("\t " COLOR_YELLOW "charac" COLOR_OFF
- " - start: 0x%04x, value: 0x%04x, "
- "props: 0x%02x, uuid: ",
- handle, value_handle, properties);
+ " - start: 0x%04x, value: 0x%04x, "
+ "props: 0x%02x, ext_props: 0x%04x, uuid: ",
+ handle, value_handle, properties, ext_prop);
print_uuid(&uuid);
gatt_db_service_foreach_desc(attr, print_desc, NULL);
diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c
index 292b584..fadaff2 100644
--- a/tools/btgatt-server.c
+++ b/tools/btgatt-server.c
@@ -419,7 +419,8 @@
bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
gatt_db_service_add_characteristic(service, &uuid,
BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
- BT_GATT_CHRC_PROP_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_EXT_PROP,
gap_device_name_read_cb,
gap_device_name_write_cb,
server);
@@ -931,18 +932,20 @@
{
uint16_t handle, value_handle;
uint8_t properties;
+ uint16_t ext_prop;
bt_uuid_t uuid;
if (!gatt_db_attribute_get_char_data(attr, &handle,
&value_handle,
&properties,
+ &ext_prop,
&uuid))
return;
printf("\t " COLOR_YELLOW "charac" COLOR_OFF
- " - start: 0x%04x, value: 0x%04x, "
- "props: 0x%02x, uuid: ",
- handle, value_handle, properties);
+ " - start: 0x%04x, value: 0x%04x, "
+ "props: 0x%02x, ext_prop: 0x%04x, uuid: ",
+ handle, value_handle, properties, ext_prop);
print_uuid(&uuid);
gatt_db_service_foreach_desc(attr, print_desc, NULL);
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
index 329d1af..0c78c4d 100644
--- a/tools/gatt-service.c
+++ b/tools/gatt-service.c
@@ -29,6 +29,7 @@
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
+#include <string.h>
#include <sys/signalfd.h>
#include <glib.h>
@@ -127,32 +128,48 @@
return desc_read(desc, iter);
}
-static void desc_write(struct descriptor *desc, DBusMessageIter *iter)
+static void desc_write(struct descriptor *desc, const uint8_t *value, int len)
{
- DBusMessageIter array;
- const uint8_t *value;
- int vlen;
-
- dbus_message_iter_recurse(iter, &array);
- dbus_message_iter_get_fixed_array(&array, &value, &vlen);
-
g_free(desc->value);
- desc->value = g_memdup(value, vlen);
- desc->vlen = vlen;
+ desc->value = g_memdup(value, len);
+ desc->vlen = len;
g_dbus_emit_property_changed(connection, desc->path,
GATT_DESCRIPTOR_IFACE, "Value");
}
+static int parse_value(DBusMessageIter *iter, const uint8_t **value, int *len)
+{
+ DBusMessageIter array;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, value, len);
+
+ return 0;
+}
+
static void desc_set_value(const GDBusPropertyTable *property,
DBusMessageIter *iter,
GDBusPendingPropertySet id, void *user_data)
{
struct descriptor *desc = user_data;
+ const uint8_t *value;
+ int len;
printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);
- desc_write(desc, iter);
+ if (parse_value(iter, &value, &len)) {
+ printf("Invalid value for Set('Value'...)\n");
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ desc_write(desc, value, len);
g_dbus_pending_property_success(id);
}
@@ -249,15 +266,8 @@
return TRUE;
}
-static void chr_write(struct characteristic *chr, DBusMessageIter *iter)
+static void chr_write(struct characteristic *chr, const uint8_t *value, int len)
{
- DBusMessageIter array;
- uint8_t *value;
- int len;
-
- dbus_message_iter_recurse(iter, &array);
- dbus_message_iter_get_fixed_array(&array, &value, &len);
-
g_free(chr->value);
chr->value = g_memdup(value, len);
chr->vlen = len;
@@ -271,10 +281,12 @@
GDBusPendingPropertySet id, void *user_data)
{
struct characteristic *chr = user_data;
+ const uint8_t *value;
+ int len;
printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid);
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ if (!parse_value(iter, &value, &len)) {
printf("Invalid value for Set('Value'...)\n");
g_dbus_pending_property_error(id,
ERROR_INTERFACE ".InvalidArguments",
@@ -282,7 +294,7 @@
return;
}
- chr_write(chr, iter);
+ chr_write(chr, value, len);
g_dbus_pending_property_success(id);
}
@@ -368,12 +380,53 @@
g_free(desc);
}
+static int parse_options(DBusMessageIter *iter, const char **device)
+{
+ DBusMessageIter dict;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "device") == 0) {
+ if (var != DBUS_TYPE_OBJECT_PATH)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, device);
+ printf("Device: %s\n", *device);
+ }
+ }
+
+ return 0;
+}
+
static DBusMessage *chr_read_value(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
struct characteristic *chr = user_data;
DBusMessage *reply;
DBusMessageIter iter;
+ const char *device;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
reply = dbus_message_new_method_return(msg);
if (!reply)
@@ -392,10 +445,21 @@
{
struct characteristic *chr = user_data;
DBusMessageIter iter;
+ const uint8_t *value;
+ int len;
+ const char *device;
dbus_message_iter_init(msg, &iter);
- chr_write(chr, &iter);
+ if (parse_value(&iter, &value, &len))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ chr_write(chr, value, len);
return dbus_message_new_method_return(msg);
}
@@ -415,10 +479,12 @@
}
static const GDBusMethodTable chr_methods[] = {
- { GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
- chr_read_value) },
- { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
- NULL, chr_write_value) },
+ { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ chr_read_value) },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL, chr_write_value) },
{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chr_start_notify) },
{ GDBUS_METHOD("StopNotify", NULL, NULL, chr_stop_notify) },
{ }
@@ -430,6 +496,15 @@
struct descriptor *desc = user_data;
DBusMessage *reply;
DBusMessageIter iter;
+ const char *device;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
reply = dbus_message_new_method_return(msg);
if (!reply)
@@ -438,6 +513,10 @@
dbus_message_iter_init_append(reply, &iter);
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
desc_read(desc, &iter);
return reply;
@@ -448,20 +527,34 @@
{
struct descriptor *desc = user_data;
DBusMessageIter iter;
+ const char *device;
+ const uint8_t *value;
+ int len;
- dbus_message_iter_init(msg, &iter);
+ if (!dbus_message_iter_init(msg, &iter))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
- desc_write(desc, &iter);
+ if (parse_value(&iter, &value, &len))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ if (parse_options(&iter, &device))
+ return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments");
+
+ desc_write(desc, value, len);
return dbus_message_new_method_return(msg);
}
static const GDBusMethodTable desc_methods[] = {
- { GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
- desc_read_value) },
- { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
- NULL,
- desc_write_value) },
+ { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+ GDBUS_ARGS({ "value", "ay" }),
+ desc_read_value) },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+ { "options", "a{sv}" }),
+ NULL, desc_write_value) },
{ }
};
diff --git a/tools/hciattach.c b/tools/hciattach.c
index 59a76a7..fad176c 100644
--- a/tools/hciattach.c
+++ b/tools/hciattach.c
@@ -46,6 +46,8 @@
#include "lib/hci.h"
#include "lib/hci_lib.h"
+#include "src/shared/tty.h"
+
#include "hciattach.h"
struct uart_t {
@@ -84,68 +86,12 @@
exit(1);
}
-int uart_speed(int s)
-{
- switch (s) {
- case 9600:
- return B9600;
- case 19200:
- return B19200;
- case 38400:
- return B38400;
- case 57600:
- return B57600;
- case 115200:
- return B115200;
- case 230400:
- return B230400;
- case 460800:
- return B460800;
- case 500000:
- return B500000;
- case 576000:
- return B576000;
- case 921600:
- return B921600;
- case 1000000:
- return B1000000;
- case 1152000:
- return B1152000;
- case 1500000:
- return B1500000;
- case 2000000:
- return B2000000;
-#ifdef B2500000
- case 2500000:
- return B2500000;
-#endif
-#ifdef B3000000
- case 3000000:
- return B3000000;
-#endif
-#ifdef B3500000
- case 3500000:
- return B3500000;
-#endif
-#ifdef B3710000
- case 3710000:
- return B3710000;
-#endif
-#ifdef B4000000
- case 4000000:
- return B4000000;
-#endif
- default:
- return B57600;
- }
-}
-
int set_speed(int fd, struct termios *ti, int speed)
{
- if (cfsetospeed(ti, uart_speed(speed)) < 0)
+ if (cfsetospeed(ti, tty_get_speed(speed)) < 0)
return -errno;
- if (cfsetispeed(ti, uart_speed(speed)) < 0)
+ if (cfsetispeed(ti, tty_get_speed(speed)) < 0)
return -errno;
if (tcsetattr(fd, TCSANOW, ti) < 0)
@@ -646,7 +592,7 @@
fprintf(stderr, "Speed %d too high. Remaining at %d baud\n",
u->speed, u->init_speed);
u->speed = u->init_speed;
- } else if (u->speed != 57600 && uart_speed(u->speed) == B57600) {
+ } else if (!tty_get_speed(u->speed)) {
/* Unknown speed. Why oh why can't we just pass an int to the kernel? */
fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n",
u->speed, u->init_speed);
diff --git a/tools/parse_companies.pl b/tools/parse_companies.pl
index 1746222..f755390 100755
--- a/tools/parse_companies.pl
+++ b/tools/parse_companies.pl
@@ -10,6 +10,8 @@
'nbsp' => ' ',
'eacute' => 'é',
'auml' => 'ä',
+ 'uuml' => 'ü',
+ 'Uuml' => 'Ü',
);
# better to use URI::Encode if you have it
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 42c2c1a..9b54426 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -5,18 +5,18 @@
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
*
*
- * 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 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 program is distributed in the hope that it will be useful,
+ * 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 General Public License for more details.
+ * 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 General Public License
- * along with this program; if not, write to the Free Software
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 0912348..8129719 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -257,7 +257,7 @@
raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00, \
0x2a), \
raw_pdu(0x08, 0x13, 0xf0, 0x18, 0xf0, 0x03, 0x28), \
- raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef, \
+ raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef, \
0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00, \
0x00, 0x00, 0x00, 0x09, 0xB0, 0x00, 0x00), \
raw_pdu(0x08, 0x15, 0xf0, 0x18, 0xf0, 0x03, 0x28), \
@@ -267,8 +267,10 @@
raw_pdu(0x01, 0x08, 0x18, 0xf0, 0x0a), \
raw_pdu(0x04, 0x16, 0xf0, 0x16, 0xf0), \
raw_pdu(0x05, 0x01, 0x16, 0xf0, 0x00, 0x29), \
+ raw_pdu(0x0a, 0x16, 0xf0), \
+ raw_pdu(0x0b, 0x01, 0x00), \
raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), \
- raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29, \
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29, \
0x2a), \
raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), \
raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a)
@@ -526,9 +528,9 @@
bt_uuid_t a_uuid, b_uuid;
gatt_db_attribute_get_char_data(a, &a_handle, &a_value_handle,
- &a_properties, &a_uuid);
+ &a_properties, NULL, &a_uuid);
gatt_db_attribute_get_char_data(b, &b_handle, &b_value_handle,
- &b_properties, &b_uuid);
+ &b_properties, NULL, &b_uuid);
return a_handle == b_handle && a_value_handle == b_value_handle &&
a_properties == b_properties &&
@@ -1556,7 +1558,8 @@
BT_ATT_PERM_WRITE,
BT_GATT_CHRC_PROP_READ |
BT_GATT_CHRC_PROP_NOTIFY |
- BT_GATT_CHRC_PROP_INDICATE,
+ BT_GATT_CHRC_PROP_INDICATE |
+ BT_GATT_CHRC_PROP_EXT_PROP,
"BlueZ"),
DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID, BT_ATT_PERM_READ |
BT_ATT_PERM_WRITE, 0x00, 0x00),
@@ -1572,7 +1575,8 @@
"BlueZ Unit Tester"),
CHARACTERISTIC(0000B009-0000-0000-0123-456789abcdef,
BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
- BT_GATT_CHRC_PROP_READ, 0x09),
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_EXT_PROP, 0x09),
DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01,
0x00),
CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ,
@@ -1617,7 +1621,8 @@
PRIMARY_SERVICE(0x0080, "a00b", 7),
CHARACTERISTIC(0xb008, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
BT_GATT_CHRC_PROP_READ |
- BT_GATT_CHRC_PROP_WRITE,
+ BT_GATT_CHRC_PROP_WRITE |
+ BT_GATT_CHRC_PROP_EXT_PROP,
0x08),
DESCRIPTOR(0xb015, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x01),
DESCRIPTOR(0xb016, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x02),
@@ -2546,7 +2551,7 @@
raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,
0x2a),
raw_pdu(0x08, 0x13, 0xf0, 0x18, 0xf0, 0x03, 0x28),
- raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,
+ raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef,
0xcd, 0xab, 0x89, 0x67, 0x45, 0x23,
0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
0xb0, 0x00, 0x00),
@@ -2560,7 +2565,7 @@
ts_small_db, NULL,
raw_pdu(0x03, 0x00, 0x02),
raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29,
0x2a),
raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28),
raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a));
@@ -2599,7 +2604,7 @@
raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,
0x2a),
raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28),
- raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,
+ raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef,
0xcd, 0xab, 0x89, 0x67, 0x45, 0x23,
0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
0xb0, 0x00, 0x00),
@@ -2613,7 +2618,7 @@
ts_small_db, NULL,
raw_pdu(0x03, 0x00, 0x02),
raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29,
0x2a),
raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28),
raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a));
@@ -4434,5 +4439,19 @@
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
raw_pdu(0x01, 0x16, 0x04, 0x00, 0x03));
+ define_test_server("/robustness/no-reliable-characteristic",
+ test_server, ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x16, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x16, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x17, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x16, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x17, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x01, 0x18, 0x25, 0x00, 0x06));
+
return tester_run();
}