+ver 5.30:
+ Fix compilation error in C++ due to inline function.
+ Fix issue with missing storage of device information.
+ Fix issue with GATT client and gaps in service handles.
+ Fix issue with AVDTP discovery callback crashing.
+ Fix issue with AVCTP channel handling in case of conflicts.
+ Fix issue with AVRCP target and get capabilities command.
+ Add experimental support for LE advertising manager API.
+ Add support for Android 5.1 GATT MTU exchange API.
+
+ver 5.29:
+ Fix issue with AVCTP initial key repeat timeout.
+ Fix issue with Android application disconnect handling.
+ Fix issue with Android support and service notifications.
+ Fix issue with Android support and Exchange MTU Request.
+ Fix issue with Android HFP support and AT+CMER handling.
+ Fix issue with Android HFP support and SLC setup.
+ Fix issue with Android HFP support and call hold status.
+ Fix issue with Android HFP support and indicator handling.
+ Fix issue with Android HFP support and SCO/eSCO disconnection.
+ Fix issue with Android HID over GATT support and battery service.
+ Fix issue with GATT sending Exchange MTU Request for BR/EDR.
+ Fix issue with GATT notification support without CCC.
+ Fix issue with GATT object life-time after disconnects.
+ Fix issue with GATT notification handling API.
+ Add experimental support for GATT client D-Bus API.
+ Add experimental support for GATT server D-Bus API.
+ Add support for Multi Profile Specification.
+ Update Android qualification documentation to PTS 6.0 release.
+
ver 5.28:
Fix issue with GATT device discovery and probing.
Fix issue with bearer selection for dual-mode devices.
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:1:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:3:18
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
endif
attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
attrib/gatt.h attrib/gatt.c \
- attrib/gattrib.h attrib/gattrib.c \
+ attrib/gattrib.h attrib/gattrib.c \
attrib/gatt-service.h attrib/gatt-service.c
btio_sources = btio/btio.h btio/btio.c
src/uinput.h \
src/plugin.h src/plugin.c \
src/storage.h src/storage.c \
+ src/advertising.h src/advertising.c \
src/agent.h src/agent.c \
src/error.h src/error.c \
src/adapter.h src/adapter.c \
doc/adapter-api.txt doc/device-api.txt \
doc/agent-api.txt doc/profile-api.txt \
doc/network-api.txt doc/media-api.txt \
- doc/health-api.txt doc/sap-api.txt
+ doc/health-api.txt doc/sap-api.txt \
+ doc/input-api.txt
EXTRA_DIST += doc/alert-api.txt \
doc/proximity-api.txt doc/heartrate-api.txt \
- doc/thermometer-api.txt doc/cyclingspeed-api.txt
+ doc/thermometer-api.txt doc/cyclingspeed-api.txt \
+ doc/gatt-api.txt doc/advertising-api.txt
EXTRA_DIST += doc/obex-api.txt doc/obex-agent-api.txt
unit_test_gatt_LDADD = src/libshared-glib.la \
lib/libbluetooth-internal.la @GLIB_LIBS@
+unit_tests += unit/test-hog
+
+unit_test_hog_SOURCES = unit/test-hog.c \
+ $(btio_sources) \
+ android/hog.h android/hog.c \
+ android/scpp.h android/scpp.c \
+ android/bas.h android/bas.c \
+ android/dis.h android/dis.c \
+ src/log.h src/log.c \
+ attrib/att.h attrib/att.c \
+ attrib/gatt.h attrib/gatt.c \
+ attrib/gattrib.h attrib/gattrib.c
+unit_test_hog_LDADD = src/libshared-glib.la \
+ lib/libbluetooth-internal.la @GLIB_LIBS@
+
unit_tests += unit/test-gattrib
unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c $(btio_sources) src/log.h src/log.c
endif
endif
+if TIZEN_HEALTH_PLUGIN
builtin_modules += gap
builtin_sources += profiles/gap/gas.c
builtin_modules += deviceinfo
builtin_sources += profiles/deviceinfo/deviceinfo.c
+endif
if EXPERIMENTAL
if TIZEN_UNUSED_PLUGIN
bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
tools/rfcomm tools/rctest tools/l2test tools/l2ping \
tools/sdptool tools/ciptool tools/bccmd \
- tools/bluemoon tools/hex2hcd tools/mpris-proxy
+ tools/bluemoon tools/hex2hcd tools/mpris-proxy \
+ tools/btsnoop
tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
tools/hciattach_st.c \
profiles/health/mcap.h profiles/health/mcap.c
tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lrt
+tools_btsnoop_SOURCES = tools/btsnoop.c
+tools_btsnoop_LDADD = src/libshared-mainloop.la
+
dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
tools/hcitool.1 tools/hcidump.1 \
tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
tools/scotest tools/amptest tools/hwdb \
tools/hcieventmask tools/hcisecfilter \
tools/btmgmt tools/btinfo tools/btattach \
- tools/btsnoop tools/btproxy \
- tools/btiotest tools/mcaptest tools/cltest \
- tools/oobtest tools/seq2bseq tools/ibeacon \
- tools/btgatt-client tools/btgatt-server
+ tools/btproxy \
+ tools/btiotest tools/bneptest tools/mcaptest \
+ tools/cltest tools/oobtest tools/seq2bseq \
+ tools/ibeacon tools/btgatt-client tools/btgatt-server
tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
tools_btattach_SOURCES = tools/btattach.c monitor/bt.h
tools_btattach_LDADD = src/libshared-mainloop.la
-tools_btsnoop_SOURCES = tools/btsnoop.c
-tools_btsnoop_LDADD = src/libshared-mainloop.la
-
tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h
tools_btproxy_LDADD = src/libshared-mainloop.la
profiles/health/mcap.h profiles/health/mcap.c
tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+tools_bneptest_SOURCES = tools/bneptest.c \
+ btio/btio.h btio/btio.c \
+ src/log.h src/log.c \
+ profiles/network/bnep.h profiles/network/bnep.c
+tools_bneptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
tools_cltest_SOURCES = tools/cltest.c
tools_cltest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
test/service-ftp.xml test/simple-player test/test-nap \
test/test-heartrate test/test-alert test/test-hfp \
test/test-cyclingspeed test/opp-client test/ftp-client \
- test/pbap-client test/map-client
+ test/pbap-client test/map-client test/advertisement-example
include $(BUILD_EXECUTABLE)
#
+# bneptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/src/log.c \
+ bluez/btio/btio.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+ bluez/profiles/network/bnep.c \
+ bluez/tools/bneptest.c \
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, glib) \
+ $(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := bneptest
+
+include $(BUILD_EXECUTABLE)
+
+#
# avdtptest
#
if ANDROID
-AM_CFLAGS += -DANDROID_VERSION=0x050000
+AM_CFLAGS += -DANDROID_VERSION=0x050100
android_plugindir = $(abs_top_srcdir)/android/.libs
android/pics-dis.txt \
android/pics-avdtp.txt \
android/pics-gavdp.txt \
+ android/pics-bnep.txt \
android/pixit-l2cap.txt \
android/pixit-gap.txt \
android/pixit-did.txt \
android/pixit-avdtp.txt \
android/pixit-gavdp.txt \
android/pixit-sdp.txt \
+ android/pixit-bnep.txt \
android/pts-rfcomm.txt \
android/pts-spp.txt \
android/pts-l2cap.txt \
android/pts-dis.txt \
android/pts-avdtp.txt \
android/pts-gavdp.txt \
- android/pts-sdp.txt
+ android/pts-sdp.txt \
+ android/pts-bnep.txt
};
struct avctp {
+ unsigned int ref;
int uinput;
unsigned int passthrough_id;
}
+static struct avctp *avctp_ref(struct avctp *session)
+{
+ __sync_fetch_and_add(&session->ref, 1);
+
+ DBG("%p: ref=%d", session, session->ref);
+
+ return session;
+}
+
+static void avctp_unref(struct avctp *session)
+{
+ DBG("%p: ref=%d", session, session->ref);
+
+ if (__sync_sub_and_fetch(&session->ref, 1))
+ return;
+
+ if (session->browsing)
+ avctp_channel_destroy(session->browsing);
+
+ if (session->control)
+ avctp_channel_destroy(session->control);
+
+ if (session->destroy)
+ session->destroy(session->data);
+
+ g_free(session->handler);
+
+ if (session->key.timer > 0)
+ g_source_remove(session->key.timer);
+
+ if (session->uinput >= 0) {
+ DBG("AVCTP: closing uinput");
+
+ ioctl(session->uinput, UI_DEV_DESTROY);
+ close(session->uinput);
+ session->uinput = -1;
+ }
+
+ g_free(session);
+}
+
static void control_response(struct avctp_channel *control,
struct avctp_header *avctp,
struct avc_header *avc,
control);
}
+ avctp_ref(control->session);
+
for (l = control->processed; l; l = l->next) {
p = l->data;
req = p->data;
avc->subunit_type,
operands, operand_count,
req->user_data))
- return;
+ break;
control->processed = g_slist_remove(control->processed, p);
pending_destroy(p, NULL);
- return;
+ break;
}
+
+ avctp_unref(control->session);
}
static void browsing_response(struct avctp_channel *browsing,
browsing);
}
+ avctp_ref(browsing->session);
+
for (l = browsing->processed; l; l = l->next) {
p = l->data;
req = p->data;
if (req->func && req->func(browsing->session, operands,
operand_count, req->user_data))
- return;
+ break;
browsing->processed = g_slist_remove(browsing->processed, p);
pending_destroy(p, NULL);
- return;
+ break;
}
+
+ avctp_unref(browsing->session);
}
static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
control->watch = g_io_add_watch(session->control->io, cond,
(GIOFunc) session_cb, session);
- return session;
+ return avctp_ref(session);
}
int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
if (!session)
return;
- if (session->browsing)
- avctp_channel_destroy(session->browsing);
-
- if (session->control)
- avctp_channel_destroy(session->control);
-
- if (session->destroy)
- session->destroy(session->data);
-
- g_free(session->handler);
-
- if (session->key.timer > 0)
- g_source_remove(session->key.timer);
-
- if (session->uinput >= 0) {
- DBG("AVCTP: closing uinput");
-
- ioctl(session->uinput, UI_DEV_DESTROY);
- close(session->uinput);
- session->uinput = -1;
- }
-
- g_free(session);
+ avctp_unref(session);
}
struct avdtp_local_sep *lsep;
struct avdtp_error err;
+ if (!session->req->timeout)
+ /* Request is in process */
+ return;
+
if (session->req->signal_id == AVDTP_ABORT) {
/* Avoid freeing the Abort request here */
DBG("handle_unanswered_req: Abort req, returning");
break;
}
+ /* Take a reference to protect against callback destroying session */
+ avdtp_ref(session);
+
if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
if (!avdtp_parse_cmd(session, session->in.transaction,
session->in.signal_id,
goto next;
}
+ avdtp_unref(session);
return TRUE;
}
if (session->req == NULL) {
error("No pending request, ignoring message");
+ avdtp_unref(session);
return TRUE;
}
if (header->transaction != session->req->transaction) {
error("Transaction label doesn't match");
+ avdtp_unref(session);
return TRUE;
}
if (session->in.signal_id != session->req->signal_id) {
error("Response signal doesn't match");
+ avdtp_unref(session);
return TRUE;
}
pending_req_free(session->req);
session->req = NULL;
- process_queue(session);
+ if (session->ref > 1)
+ process_queue(session);
+
+ avdtp_unref(session);
return TRUE;
{
struct avdtp_local_sep *sep = stream->lsep;
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
if (sep->cfm && sep->cfm->set_configuration)
sep->cfm->set_configuration(session, sep, stream, NULL,
sep->user_data);
- avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
-
return TRUE;
}
error("START request rejected: %s (%d)",
avdtp_strerror(&err), err.err.error_code);
if (sep && sep->cfm && sep->cfm->start) {
+ stream->starting = FALSE;
sep->cfm->start(session, sep, stream, &err,
sep->user_data);
- stream->starting = FALSE;
}
return TRUE;
case AVDTP_SUSPEND:
avctp_unregister_passthrough_handler(session->conn,
session->passthrough_id);
+ if (session->browsing_id > 0)
+ avctp_unregister_browsing_pdu_handler(session->conn,
+ session->browsing_id);
+
/* clear destroy callback that would call shutdown again */
avctp_set_destroy_cb(session->conn, NULL, NULL);
avctp_shutdown(session->conn);
if (item->len > 0) {
text[i] = g_strndup(item->data, item->len);
+ attrs[i] = item->attr;
params_len -= item->len;
params += item->len;
+ } else {
+ text[i] = NULL;
+ attrs[i] = 0;
}
}
return -EPROTO;
}
+static void free_attribute_list(uint8_t number, char **text)
+{
+ while(number--)
+ g_free(text[number]);
+}
+
static int parse_elements(struct avrcp_header *pdu, uint8_t *number,
uint32_t *attrs, char **text)
{
player->cfm->get_element_attributes(session, err, number, attrs, text,
player->user_data);
+ if (err == 0)
+ free_attribute_list(number, text);
+
return FALSE;
}
player->cfm->get_item_attributes(session, err, number, attrs, text,
player->user_data);
+ if (err == 0)
+ free_attribute_list(number, text);
+
return FALSE;
}
#define AVRCP_MEDIA_SEARCH 0x02
#define AVRCP_MEDIA_NOW_PLAYING 0x03
+/* SDP features */
+#define AVRCP_FEATURE_CATEGORY_1 0x0001
+#define AVRCP_FEATURE_CATEGORY_2 0x0002
+#define AVRCP_FEATURE_CATEGORY_3 0x0004
+#define AVRCP_FEATURE_CATEGORY_4 0x0008
+#define AVRCP_FEATURE_PLAYER_SETTINGS 0x0010
+#define AVRCP_FEATURE_GROUP_NAVIGATION 0x0020
+#define AVRCP_FEATURE_BROWSING 0x0040
+#define AVRCP_FEATURE_MULTIPLE_PLAYERS 0x0080
+
/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG 0x001958
#define L2CAP_PSM_AVCTP 0x17
-#define AVRCP_FEATURE_CATEGORY_1 0x0001
-#define AVRCP_FEATURE_CATEGORY_2 0x0002
-#define AVRCP_FEATURE_CATEGORY_3 0x0004
-#define AVRCP_FEATURE_CATEGORY_4 0x0008
-
static bdaddr_t adapter_addr;
static uint32_t record_tg_id = 0;
static uint32_t record_ct_id = 0;
struct sockaddr_hci addr;
int opt = 1;
+#ifdef __TIZEN_PATCH__
+ snoop = btsnoop_create(path, BTSNOOP_TYPE_HCI, -1, -1);
+#else
snoop = btsnoop_create(path, BTSNOOP_TYPE_HCI);
+#endif
if (!snoop)
return -1;
return true;
}
+static void notify_client_mtu_change(struct app_connection *conn, bool success)
+{
+ struct hal_ev_gatt_client_configure_mtu ev;
+ size_t mtu;
+
+ g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+ ev.conn_id = conn->id;
+ ev.status = success ? GATT_SUCCESS : GATT_FAILURE;
+ ev.mtu = mtu;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_CONFIGURE_MTU, sizeof(ev), &ev);
+}
+
+static void notify_server_mtu(struct app_connection *conn)
+{
+ struct hal_ev_gatt_server_mtu_changed ev;
+ size_t mtu;
+
+ g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+ ev.conn_id = conn->id;
+ ev.mtu = mtu;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_MTU_CHANGED, sizeof(ev), &ev);
+}
+
+static void notify_mtu_change(void *data, void *user_data)
+{
+ struct gatt_device *device = user_data;
+ struct app_connection *conn = data;
+
+ if (conn->device != device)
+ return;
+
+ switch (conn->app->type) {
+ case GATT_CLIENT:
+ notify_client_mtu_change(conn, true);
+ break;
+ case GATT_SERVER:
+ notify_server_mtu(conn);
+ break;
+ default:
+ break;
+ }
+}
+
static bool update_mtu(struct gatt_device *device, uint16_t rmtu)
{
uint16_t mtu, lmtu;
return false;
}
+ queue_foreach(app_connections, notify_mtu_change, device);
+
return true;
}
data.status = status;
queue_foreach(app_connections, notify_app_connect_status_by_device,
&data);
+
+ /* For BR/EDR notify about MTU since it is not negotiable*/
+ if (cid != ATT_CID)
+ queue_foreach(app_connections, notify_mtu_change, dev);
+
device_unref(dev);
/* Check if we should restart scan */
static void handle_client_configure_mtu(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_configure_mtu *cmd = buf;
+ static struct app_connection *conn;
+ uint8_t status;
- DBG("conn_id %u", cmd->conn_id);
+ DBG("conn_id %u mtu %d", cmd->conn_id, cmd->mtu);
- /* TODO */
+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ /*
+ * currently MTU is always exchanged on connection, just report current
+ * value
+ *
+ * TODO figure out when send failed status in notification
+ * TODO should we fail for BR/EDR?
+ */
+ notify_client_mtu_change(conn, false);
+ status = HAL_STATUS_SUCCESS;
+
+failed:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_CLIENT_CONFIGURE_MTU,
- HAL_STATUS_UNSUPPORTED);
+ status);
}
static void handle_client_conn_param_update(const void *buf, uint16_t len)
#endif
}
+static void handle_server_mtu_changed(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 1, 0)
+ struct hal_ev_gatt_server_mtu_changed *ev = buf;
+
+ if (cbs->server->mtu_changed_cb)
+ cbs->server->mtu_changed_cb(ev->conn_id, ev->mtu);
+#endif
+}
+
/*
* handlers will be called from notification thread context,
* index in table equals to 'opcode - HAL_MINIMUM_EVENT'
/* HAL_EV_GATT_SERVER_CONGESTION */
{ handle_server_congestion, false,
sizeof(struct hal_ev_gatt_server_congestion) },
+ /* HAL_EV_GATT_SERVER_MTU_CHANGED */
+ { handle_server_mtu_changed, false,
+ sizeof(struct hal_ev_gatt_server_mtu_changed) },
};
/* Client API */
Notification parameters: Connection ID (4 octets)
Congested (1 octet)
+ Opcode 0xb0 - Server MTU Changed notification
+
+ Notification parameters: Connection ID (4 octets)
+ MTU (4 octets)
+
Bluetooth Handsfree Client HAL (ID 10)
======================================
uint8_t congested;
} __attribute__((packed));
+#define HAL_EV_GATT_SERVER_MTU_CHANGED 0xb0
+struct hal_ev_gatt_server_mtu_changed {
+ int32_t conn_id;
+ int32_t mtu;
+} __attribute__((packed));
+
#define HAL_GATT_PERMISSION_READ 0x0001
#define HAL_GATT_PERMISSION_READ_ENCRYPTED 0x0002
#define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM 0x0004
size_t bytes)
{
struct sco_stream_in *in = (struct sco_stream_in *) stream;
-#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
- size_t frame_size = audio_stream_in_frame_size(&in->stream);
-#else
- size_t frame_size = audio_stream_frame_size(&stream->common);
-#endif
- size_t frame_num = bytes / frame_size;
- size_t input_frame_num = frame_num;
+ size_t frame_size, frame_num, input_frame_num;
void *read_buf = buffer;
size_t total = bytes;
int ret;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ frame_size = audio_stream_in_frame_size(&in->stream);
+#else
+ frame_size = audio_stream_frame_size(&stream->common);
+#endif
+
+ if (!frame_size)
+ return -1;
+
+ frame_num = bytes / frame_size;
+ input_frame_num = frame_num;
+
DBG("Read from fd %d bytes %zu", sco_fd, bytes);
if (ipc_get_sco_fd(&in->bd_addr) != SCO_STATUS_SUCCESS)
*/
typedef void (*congestion_callback)(int conn_id, bool congested);
+/** Callback invoked when the MTU for a given connection changes */
+typedef void (*mtu_changed_callback)(int conn_id, int mtu);
+
typedef struct {
register_server_callback register_server_cb;
connection_callback connection_cb;
response_confirmation_callback response_confirmation_cb;
indication_sent_callback indication_sent_cb;
congestion_callback congestion_cb;
+ mtu_changed_callback mtu_changed_cb;
} btgatt_server_callbacks_t;
/** Represents the standard BT-GATT server interface. */
if (!dev->hog) {
/* TODO: Get device details and primary */
- dev->hog = bt_hog_new("bluez-input-device", dev->vendor,
+ dev->hog = bt_hog_new_default("bluez-input-device", dev->vendor,
dev->product, dev->version, NULL);
if (!dev->hog) {
error("HoG: unable to create session");
GAttrib *attrib;
GSList *reports;
struct bt_uhid *uhid;
+ int uhid_fd;
gboolean has_report_id;
uint16_t bcdhid;
uint8_t bcountrycode;
g_free(hog);
}
-struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
- uint16_t version, void *primary)
+struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *primary)
+{
+ return bt_hog_new(-1, name, vendor, product, version, primary);
+}
+
+struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *primary)
{
struct bt_hog *hog;
return NULL;
hog->gatt_op = queue_new();
- if (!hog->gatt_op) {
- hog_free(hog);
- return NULL;
- }
-
hog->bas = queue_new();
- if (!hog->bas) {
- queue_destroy(hog->gatt_op, NULL);
- hog_free(hog);
- return NULL;
- }
- hog->uhid = bt_uhid_new_default();
- if (!hog->uhid) {
+ if (fd < 0)
+ hog->uhid = bt_uhid_new_default();
+ else
+ hog->uhid = bt_uhid_new(fd);
+
+ hog->uhid_fd = fd;
+
+ if (!hog->gatt_op || !hog->bas || !hog->uhid) {
hog_free(hog);
- queue_destroy(hog->gatt_op, NULL);
- queue_destroy(hog->bas, NULL);
return NULL;
}
return;
}
- instance = bt_hog_new(hog->name, hog->vendor, hog->product,
- hog->version, primary);
+ instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
+ hog->product, hog->version, primary);
if (!instance)
return;
struct bt_hog;
-struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
- uint16_t version, void *primary);
+struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *primary);
+
+struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *primary);
struct bt_hog *bt_hog_ref(struct bt_hog *hog);
void bt_hog_unref(struct bt_hog *hog);
if (!dev->session)
goto fail;
- perr = bnep_connect(dev->session, bnep_conn_cb, dev);
+ perr = bnep_connect(dev->session, bnep_conn_cb, bnep_disconn_cb, dev,
+ dev);
if (perr < 0) {
error("bnep connect req failed: %s", strerror(-perr));
goto fail;
}
- bnep_set_disconnect(dev->session, bnep_disconn_cb, dev);
-
if (dev->io) {
g_io_channel_unref(dev->io);
dev->io = NULL;
{
struct pan_device *dev = user_data;
uint8_t packet[BNEP_MTU];
- struct bnep_setup_conn_req *req = (void *) packet;
- uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
int sk, n, err;
if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
sk = g_io_channel_unix_get_fd(chan);
+#ifndef __TIZEN_PATCH__
/* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
n = read(sk, packet, sizeof(packet));
+#else
+ /*
+ * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case
+ * of kernel setup connection msg handling.
+ */
+ n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+#endif
+
if (n < 0) {
error("read(): %s(%d)", strerror(errno), errno);
goto failed;
}
- /* Highest known control command id BNEP_FILTER_MULT_ADDR_RSP 0x06 */
- if (req->type == BNEP_CONTROL &&
- req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
- error("cmd not understood");
- bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_CMD_NOT_UNDERSTOOD,
- req->ctrl);
- goto failed;
- }
-
- if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) {
- error("cmd is not BNEP_SETUP_CONN_REQ %02X %02X", req->type,
- req->ctrl);
- goto failed;
- }
-
- rsp = bnep_setup_decode(req, &dst_role, &src_role);
- if (rsp != BNEP_SUCCESS) {
- error("bnep_setup_decode failed");
+ if (n < 3) {
+ error("pan: to few setup connection request data received");
goto failed;
}
err = nap_create_bridge();
- if (err < 0) {
+ if (err < 0)
error("pan: Failed to create bridge: %s (%d)", strerror(-err),
-err);
- goto failed;
- }
- if (bnep_server_add(sk, dst_role, BNEP_BRIDGE, dev->iface,
- &dev->dst) < 0) {
+ if (bnep_server_add(sk, (err < 0) ? NULL : BNEP_BRIDGE, dev->iface,
+ &dev->dst, packet, n) < 0) {
error("pan: server_connadd failed");
- rsp = BNEP_CONN_NOT_ALLOWED;
goto failed;
}
- rsp = BNEP_SUCCESS;
- bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
-
dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
nap_watchdog_cb, dev);
g_io_channel_unref(dev->io);
return FALSE;
failed:
- bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
pan_device_remove(dev);
return FALSE;
--- /dev/null
+BNEP PICS for the PTS tool.
+
+PTS version: 6.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+ Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_BNEP_1_1 True BNEP Connection Setup (M)
+TSPC_BNEP_1_2 True (*) BNEP Data Packet Reception (M)
+TSPC_BNEP_1_3 True (*) BNEP Data Packet Transmission (M)
+TSPC_BNEP_1_3a True (*) BNEP Compressed Packet Transmission (O)
+TSPC_BNEP_1_3b True (*) BNEP Compressed Packet Transmission Source Only
+ (O)
+TSPC_BNEP_1_4 True BNEP Control Message Processing (M)
+TSPC_BNEP_1_5 True BNEP Extension Header Processing (M)
+TSPC_BNEP_1_6 True Network Protocol Filter Message Transmission (O)
+TSPC_BNEP_1_7 True Multicast Address Filter Message Transmission
+ (O)
+-------------------------------------------------------------------------------
--- /dev/null
+BNEP PIXIT for the PTS tool.
+
+PTS version: 6.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name Value
+-------------------------------------------------------------------------------
+TSPX_class_of_device 04041C
+TSPX_security_control_data
+TSPX_content_protection_data
+TSPX_bd_addr_iut 112233445566 (*&)
+TSPX_delete_link_key FALSE
+TSPX_pin_code 1234
+TSPX_security_enabled FALSE
+TSPX_time_guard 300000
+TSPX_use_implicit_send TRUE
+TSPX_auth_password 0000
+TSPX_auth_user_id PTS
+TSPX_l2cap_psm 000F
+TSPX_rfcomm_channel 8
+TSPX_no_confirmations FALSE
+TSPX_UUID_dest_address 1116
+TSPX_UUID_source_address 1115
+TSPX_MAC_dest_address 000000000000 (*&)
+TSPX_MAC_source_address 000000000000 (*&)
--- /dev/null
+PTS test results for BNEP
+
+PTS version: 6.0
+Tested: 12-March-2015
+Android version: 5.0
+Kernel version: 3.20
+
+Results:
+PASS test passed
+FAIL test failed
+INC test is inconclusive
+N/A test is disabled due to PICS setup
+
+--------------------------------------------------------------------------------
+Test Name Result Notes
+--------------------------------------------------------------------------------
+TC_CTRL_BV_01_C PASS bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_02_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+TC_CTRL_BV_03_C PASS bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_04_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_05_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_06_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_07_C PASS PTS issue #13169
+ bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -t 3 -d 0 -e 1500 -y 1
+TC_CTRL_BV_08_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_09_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -t 5 -g 00:00:00:00:00:00
+ -j ff:ff:ff:ff:ff:ff -y 1
+TC_CTRL_BV_10_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_19_C INC JIRA #BA-343
+ bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_11_C PASS bneptest -s -b <bridge> -n <iface>
+TC_RX_C_BV_12_C PASS bneptest -s -b <bridge> -n <iface>
+TC_RX_C_S_BV_13_C PASS bneptest -s -b <bridge> -n <iface>
+TC_RX_C_S_BV_14_C PASS bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_15_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_16_C PASS PTS issue #13171
+ bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_17_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_18_C PASS PTS issue #13171
+ bneptest -s -b <bridge> -n <iface>
+TC_TX_TYPE_0_BV_20_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -w 0 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_BV_21_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -w 2 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_S_BV_22_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -w 3 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_D_BV_23_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -w 4 -k <src hw addr> -f <dst hw addr>
/* API not available in bluez 5.25
* att_put_uuid(info->uuid, &atval[0]);*/
- a = attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
- atval, info->uuid.type / 8);
+ a = attrib_db_add(adapter, h++, &bt_uuid, ATT_AUTHENTICATION,
+ ATT_AUTHENTICATION, atval, info->uuid.type / 8);
if (a == NULL) {
return FALSE;
GAttribResultFunc func, gpointer user_data);
guint gatt_read_char_by_offset(GAttrib *attrib, uint16_t handle, uint16_t offset,
GAttribResultFunc func, gpointer user_data);
-gboolean gatt_register_internet_protocol_service(struct btd_adapter *adapter);
-gboolean gatt_unregister_internet_protocol_service(struct btd_adapter *adapter);
-#endif
\ No newline at end of file
+#endif
}
#endif
-gboolean g_attrib_set_destroy_function(GAttrib *attrib,
- GDestroyNotify destroy, gpointer user_data)
+gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy,
+ gpointer user_data)
{
if (!attrib)
return FALSE;
uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
{
+ uint16_t mtu;
+
if (!attrib || !len)
return NULL;
+ mtu = bt_att_get_mtu(attrib->att);
+
+ /*
+ * Clients of this expect a buffer to use.
+ *
+ * Pdu encoding in shared/att verifies if whole buffer fits the mtu,
+ * thus we should set the buflen also when mtu is reduced. But we
+ * need to reallocate the buffer only if mtu is larger.
+ */
+ if (mtu > attrib->buflen)
+ attrib->buf = g_realloc(attrib->buf, mtu);
+
+ attrib->buflen = mtu;
*len = attrib->buflen;
return attrib->buf;
}
#include <stdlib.h>
#include <stdbool.h>
#include <sys/uio.h>
+#include <wordexp.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "display.h"
#include "gatt.h"
+#define PROFILE_PATH "/org/bluez/profile"
+#define PROFILE_INTERFACE "org.bluez.GattProfile1"
+
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
static GList *services;
static GList *characteristics;
static GList *descriptors;
+static GList *managers;
static void print_service(GDBusProxy *proxy, const char *description)
{
if (*entry == '\0')
continue;
-
+#ifndef __TIZEN_PATCH__
if (i > 512) {
rl_printf("Too much data\n");
return;
}
-
+#else
+ if (i >= 512) {
+ rl_printf("Too much data\n");
+ return;
+ }
+#endif
val = strtol(entry, &endptr, 0);
if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
rl_printf("Invalid value at index %d\n", i);
rl_printf("Unable to notify attribute %s\n",
g_dbus_proxy_get_path(proxy));
}
+
+static void register_profile_setup(DBusMessageIter *iter, void *user_data)
+{
+ wordexp_t *w = user_data;
+ DBusMessageIter uuids, opt;
+ const char *path = PROFILE_PATH;
+ size_t i;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &uuids);
+ for (i = 0; i < w->we_wordc; i++)
+ dbus_message_iter_append_basic(&uuids, DBUS_TYPE_STRING,
+ &w->we_wordv[i]);
+ dbus_message_iter_close_container(iter, &uuids);
+
+ 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,
+ &opt);
+ dbus_message_iter_close_container(iter, &opt);
+
+}
+
+static void register_profile_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to register profile: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ rl_printf("Profile registered\n");
+}
+
+void gatt_add_manager(GDBusProxy *proxy)
+{
+ managers = g_list_append(managers, proxy);
+}
+
+void gatt_remove_manager(GDBusProxy *proxy)
+{
+ managers = g_list_remove(managers, proxy);
+}
+
+static int match_proxy(const void *a, const void *b)
+{
+ GDBusProxy *proxy1 = (void *) a;
+ GDBusProxy *proxy2 = (void *) b;
+
+ return strcmp(g_dbus_proxy_get_path(proxy1),
+ g_dbus_proxy_get_path(proxy2));
+}
+
+static DBusMessage *release_profile(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+ { GDBUS_METHOD("Release", NULL, NULL, release_profile) },
+ { }
+};
+
+void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
+ wordexp_t *w)
+{
+ GList *l;
+
+ l = g_list_find_custom(managers, proxy, match_proxy);
+ if (!l) {
+ rl_printf("Unable to find GattManager proxy\n");
+ return;
+ }
+
+ if (g_dbus_register_interface(conn, PROFILE_PATH,
+ PROFILE_INTERFACE, methods,
+ NULL, NULL, NULL, NULL) == FALSE) {
+ rl_printf("Failed to register profile object\n");
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(l->data, "RegisterProfile",
+ register_profile_setup,
+ register_profile_reply, w,
+ NULL) == FALSE) {
+ rl_printf("Failed register profile\n");
+ return;
+ }
+}
+
+static void unregister_profile_reply(DBusMessage *message, void *user_data)
+{
+ DBusConnection *conn = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to unregister profile: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ rl_printf("Profile unregistered\n");
+
+ g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+}
+
+static void unregister_profile_setup(DBusMessageIter *iter, void *user_data)
+{
+ const char *path = PROFILE_PATH;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+void gatt_unregister_profile(DBusConnection *conn, GDBusProxy *proxy)
+{
+ GList *l;
+
+ l = g_list_find_custom(managers, proxy, match_proxy);
+ if (!l) {
+ rl_printf("Unable to find GattManager proxy\n");
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(l->data, "UnregisterProfile",
+ unregister_profile_setup,
+ unregister_profile_reply, conn,
+ NULL) == FALSE) {
+ rl_printf("Failed unregister profile\n");
+ return;
+ }
+}
void gatt_read_attribute(GDBusProxy *proxy);
void gatt_write_attribute(GDBusProxy *proxy, const char *arg);
void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
+
+void gatt_add_manager(GDBusProxy *proxy);
+void gatt_remove_manager(GDBusProxy *proxy);
+
+void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
+ wordexp_t *w);
+void gatt_unregister_profile(DBusConnection *conn, GDBusProxy *proxy);
#include <stdbool.h>
#include <signal.h>
#include <sys/signalfd.h>
+#include <wordexp.h>
#include <readline/readline.h>
#include <readline/history.h>
gatt_add_characteristic(proxy);
} else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
gatt_add_descriptor(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattManager1")) {
+ gatt_add_manager(proxy);
}
}
if (default_attr == proxy)
set_default_attribute(NULL);
+ } else if (!strcmp(interface, "org.bluez.GattManager1")) {
+ gatt_remove_manager(proxy);
}
}
gatt_notify_attribute(default_attr, enable ? true : false);
}
+static void cmd_register_profile(const char *arg)
+{
+ wordexp_t w;
+
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ if (wordexp(arg, &w, WRDE_NOCMD)) {
+ rl_printf("Invalid argument\n");
+ return;
+ }
+
+ if (w.we_wordc == 0) {
+ rl_printf("Missing argument\n");
+ return;
+ }
+
+ gatt_register_profile(dbus_conn, default_ctrl, &w);
+
+ wordfree(&w);
+}
+
+static void cmd_unregister_profile(const char *arg)
+{
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ gatt_unregister_profile(dbus_conn, default_ctrl);
+}
+
static void cmd_version(const char *arg)
{
rl_printf("Version %s\n", VERSION);
{ "write", "<data=[xx xx ...]>", cmd_write,
"Write attribute value" },
{ "notify", "<on/off>", cmd_notify, "Notify attribute value" },
+ { "register-profile", "<UUID ...>", cmd_register_profile,
+ "Register profile to connect" },
+ { "unregister-profile", NULL, cmd_unregister_profile,
+ "Unregister profile" },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
AC_PREREQ(2.60)
-AC_INIT(bluez, 5.28)
+AC_INIT(bluez, 5.30)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
AM_CONDITIONAL(MANPAGES, test "${enable_manpages}" = "yes")
AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
- [enable experimental plugins (NFC, ...)]),
+ [enable experimental plugins (SAP, NFC, ...)]),
[enable_experimental=${enableval}])
AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes")
This process will start creating Device objects as
new devices are discovered.
+ During discovery RSSI delta-threshold is imposed.
+
Possible errors: org.bluez.Error.NotReady
org.bluez.Error.Failed
org.bluez.Error.Failed
org.bluez.Error.NotAuthorized
+ void RemoveDevice(object device)
+
+ This removes the remote device object at the given
+ path. It will remove also the pairing information.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void SetDiscoveryFilter(dict filter) [Experimental]
+
+ This method sets the device discovery filter for the
+ caller. When this method is called with no filter
+ parameter, filter is removed.
+
+ Parameters that may be set in the filter dictionary
+ include the following:
+
+ array{string} UUIDs : filtered service UUIDs
+ int16 RSSI : RSSI threshold value
+ uint16 Pathloss : Pathloss threshold value
+ string Transport : type of scan to run
+
+ When a remote device is found that advertises any UUID
+ from UUIDs, it will be reported if:
+ - Pathloss and RSSI are both empty,
+ - only Pathloss param is set, device advertise TX pwer,
+ and computed pathloss is less than Pathloss param,
+ - only RSSI param is set, and received RSSI is higher
+ than RSSI param,
+
+ Transport parameter determines the type of scan:
+ "auto" - interleaved scan, default value
+ "bredr" - BR/EDR inquiry
+ "le" - LE scan only
+
+ If "le" or "bredr" Transport is requested, and the
+ controller doesn't support it, org.bluez.Error.Failed
+ error will be returned. If "auto" transport is
+ requested, scan will use LE, BREDR, or both, depending
+ on what's currently enabled on the controller.
+
+ When discovery filter is set, Device objects will be
+ created as new devices with matching criteria are
+ discovered. PropertiesChanged signals will be emitted
+ for already existing Device objects, with updated RSSI
+ value. If one or more discovery filters have been set,
+ the RSSI delta-threshold, that is imposed by
+ StartDiscovery by default, will not be applied.
+
+ When multiple clients call SetDiscoveryFilter, their
+ filters are internally merged, and notifications about
+ new devices are sent to all clients. Therefore, each
+ client must check that device updates actually match
+ its filter.
+
+ When SetDiscoveryFilter is called multiple times by the
+ same client, last filter passed will be active for
+ given client.
+
+ SetDiscoveryFilter can be called before StartDiscovery.
+ It is useful when client will create first discovery
+ session, to ensure that proper scan will be started
+ right after call to StartDiscovery.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+
#ifdef __TIZEN_PATCH__
void StartCustomDiscovery(string pattern)
Possible errors: org.bluez.Error.InvalidArguments
#endif
- void RemoveDevice(object device)
-
- This removes the remote device object at the given
- path. It will remove also the pairing information.
-
- Possible errors: org.bluez.Error.InvalidArguments
- org.bluez.Error.Failed
-
Properties string Address [readonly]
The Bluetooth device address.
--- /dev/null
+BlueZ D-Bus LE Advertising API Description
+******************************************
+
+Advertising packets are structured data which is broadcast on the LE Advertising
+channels and available for all devices in range. Because of the limited space
+available in LE Advertising packets (32 bytes), each packet's contents must be
+carefully controlled.
+
+BlueZ acts as a store for the Advertisement Data which is meant to be sent.
+It constructs the correct Advertisement Data from the structured
+data and configured the kernel to send the correct advertisement.
+
+Advertisement Data objects are registered freely and then referenced by BlueZ
+when constructing the data sent to the kernel.
+
+LE Advertisement Data hierarchy
+===============================
+
+Specifies the Advertisement Data to be broadcast and some advertising
+parameters. Properties which are not present will not be included in the
+data. Required advertisement data types will always be included.
+All UUIDs are 128-bit versions in the API, and 16 or 32-bit
+versions of the same UUID will be used in the advertising data as appropriate.
+
+Service org.bluez
+Interface org.bluez.LEAdvertisement1
+Object path freely definable
+
+Methods void Release() [noreply]
+
+ This method gets called when the service daemon
+ removes the Advertisement. A client can use it to do
+ cleanup tasks. There is no need to call
+ UnregisterAdvertisement because when this method gets
+ called it has already been unregistered.
+
+Properties string Type
+
+ Determines the type of advertising packet requested.
+
+ Possible values: "broadcast" or "peripheral"
+
+ array{string} ServiceUUIDs
+
+ List of UUIDs to include in the "Service UUID" field of
+ the Advertising Data.
+
+ dict ManufacturerData
+
+ Manufactuer Data fields to include in
+ the Advertising Data. Keys are the Manufacturer ID
+ to associate with the data.
+
+ array{string} SolicitUUIDs
+
+ Array of UUIDs to include in "Service Solicitation"
+ Advertisement Data.
+
+ dict ServiceData
+
+ Service Data elements to include. The keys are the
+ UUID to associate with the data.
+
+
+LE Advertising Manager hierarchy
+================================
+
+The Advertising Manager allows external applications to register Advertisement
+Data which should be broadcast to devices. Advertisement Data elements must
+follow the API for LE Advertisement Data described above.
+
+Service org.bluez
+Interface org.bluez.LEAdvertisingManager1 [Experimental]
+Object path /org/bluez/{hci0,hci1,...}
+
+Methods RegisterAdvertisement(object advertisement, dict options)
+
+ Registers an advertisement object to be sent over the LE
+ Advertising channel. The service must be exported
+ under interface LEAdvertisement1. InvalidArguments
+ indicates that the object has invalid or conflicting
+ properties. InvalidLength indicates that the data
+ provided generates a data packet which is too long.
+ The properties of this object are parser when it is
+ registered, and any changes are ignored.
+ Currently only one advertisement at a time is supported,
+ attempting to register two advertisements will result in
+ an AlreadyExists error.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+ org.bluez.Error.InvalidLength
+
+ UnregisterAdvertisement(object advertisement)
+
+ This unregisters an advertisement that has been
+ prevously 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
descriptor objects will become available via
ObjectManager as soon as they get discovered.
+#ifdef __TIZEN_PATCH__
+ string Unicast [read-only]
+
+ 12:34:56:78:9A:BC remote device address, if address is set then
+ notifications or indications shall be sent to only "XX_XX_XX_XX_XX_XX"
+ device otherwise notification or indication shall be multicasted.
+#endif
Characteristic Descriptors hierarchy
====================================
DescriptorX /serviceX/CharacterisitcX/DescriptorX
Possible errors: org.bluez.Error.InvalidArguments
-#endif
\ No newline at end of file
+#endif
Linux kernel v3.16 Version 1.6
Linux kernel v3.17 Version 1.7
Linux kernel v3.19 Version 1.8
+Linux kernel v4.1 Version 1.9 (not yet released)
Version 1.1 introduces Set Device ID command.
Version 1.8 introduces Start Service Discovery command. It also adds new
Long Term Key types for LE Secure Connection feature.
+Version 1.9 introduces a new static address setting and allows the usage
+of Set Fast Connectable when controller is powered off. The existing Set
+Advertising command gained an extra setting for enabling undirected
+connectable advertising.
+
Example
=======
0x0F Not Powered
0x10 Cancelled
0x11 Invalid Index
+0x12 RFKilled
+0x13 Already Paired
+0x14 Permission Denied
As a general rule all commands generate the events as specified below,
however invalid lengths or unknown commands will always generate a
(e.g. not for single-mode LE ones). It will return Not Supported
otherwise.
- This command can only be used when the controller is powered on
- and will return Not Powerd otherwise.
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
- If connectable is not set, then this command will fail with
- Rejected error.
+ The setting will be remembered during power down/up toggles.
This command generates a Command Complete event on success or
a Command Status event on failure.
Possible errors: Failed
Busy
- Rejected
Not Supported
Invalid Parameters
- Not Powered
Invalid Index
Invalid Parameters
Not Powered
Invalid Index
+ Already Paired
Cancel Pair Device Command
Secure Connections is disabled, then of course this is the
same as not providing any data at all.
- When providing data for remote LE devices, then the Hash_192 field
- is used to provide the Security Manager TK Value. The Randomizer_192
- field is not used and shall be set to zero. The Hash_192 value can
- also be set to zero and that means that no Out Of Band data for
- LE legacy pairing is provided.
+ When providing data for remote LE devices, then the Hash_192 and
+ and Randomizer_192 fields are not used and shell be set to zero.
The Hash_256 and Randomizer_256 fields can be used for LE secure
connections Out Of Band data. If only LE secure connections data
is provided the Hash_P192 and Randomizer_P192 fields can be set
- to zero.
+ to zero. Currently there is no support for providing the Security
+ Manager TK Value for LE legacy pairing.
If Secure Connections Only mode has been enabled, then providing
Hash_P192 and Randomizer_P192 is not allowed. They are required
0x0000 Disable Device ID
0x0001 Bluetooth SIG
- 0x0002 USB Implementer?\99s Forum
+ 0x0002 USB Implementerâ\80\99s Forum
The information are put into the EIR data. If the controller does
not support EIR or if SSP is disabled, this command will still
Return Parameters: Current_Settings (4 Octets)
This command is used to enable LE advertising on a controller
- that supports it. The allowed values for the Advertising
- command parameter are 0x00 and 0x01. All other values will
- return Invalid Parameters.
+ that supports it. The allowed values for the Advertising command
+ parameter are 0x00, 0x01 and 0x02. All other values will return
+ Invalid Parameters.
- A pre-requisite is that LE is already enabled, otherwise
- this command will return a "rejected" response.
+ The value 0x00 disables advertising, the value 0x01 enables
+ advertising with considering of connectable setting and the
+ value 0x02 enables advertising in connectable mode.
+
+ Using value 0x01 means that when connectable setting is disabled,
+ the advertising happens with undirected non-connectable advertising
+ packets and a non-resovable random address is used. If connectable
+ setting is enabled, then undirected connectable advertising packets
+ and the identity address or resolvable private address are used.
+
+ LE Devices configured via Add Device command with Action 0x01
+ have no effect when using Advertising value 0x01 since only the
+ connectable setting is taken into account.
+
+ To utilize undirected connectable advertising without changing the
+ connectable setting, the value 0x02 can be utilized. It makes the
+ device connectable via LE without the requirement for being
+ connectable on BR/EDR (and/or LE).
+
+ The value 0x02 should be the preferred mode of operation when
+ implementing peripheral mode.
+
+ Using this command will temporarily deactive any configuration
+ made by the Add Advertising command. This command takes precedence.
+
+ A pre-requisite is that LE is already enabled, otherwise this
+ command will return a "rejected" response.
This command generates a Command Complete event on success or a
Command Status event on failure.
LE Bluetooth Device Address
LE Role
- Security Manager TK Value (optional)
LE Secure Connections Confirmation Value (optional)
LE Secure Connections Random Value (optional)
Appearance (optional)
Random Value fields are only included when secure connections has been
enabled.
+ The Security Manager TK Value from the Bluetooth specification can
+ not be provided by this command. The Out Of Band information here are
+ for asymmetric exchanges based on Diffie-Hellman key exchange. The
+ Security Manager TK Value is a symmetric random number that has to
+ be acquired and agreed upon differently.
+
The returned information from BR/EDR controller and LE controller
types are not related to each other. Once they have been used
over an Out Of Band link, a new set of information shall be
requested.
When Secure Connections Only mode has been enabled, then the fields
- for Simple Pairing Hash C-192, Simple Pairing Randomizer R-192 and
- Security Manager TK Value are not returned. Only the fields for
- the strong secure connections pairing are included.
+ for Simple Pairing Hash C-192 and Simple Pairing Randomizer R-192
+ are not returned. Only the fields for the strong secure connections
+ pairing are included.
This command can only be used when the controller is powered.
Return Parameters: Num_Controllers (2 Octets)
Controller_Index[i] (2 Octets)
Controller_Type[i] (1 Octet)
+ Controller_Bus[i] (1 Octet)
This command returns the list of currently known controllers. It
includes configured, unconfigured and alternate controllers.
Alternate MAC/PHY controllers will be listed as 0x02. They do not
support the difference between configured and unconfigured state.
+ The Controller_Bus parameter has these values:
+
+ 0x00 Virtual
+ 0x01 USB
+ 0x02 PCMCIA
+ 0x03 UART
+ 0x04 RS232
+ 0x05 PCI
+ 0x06 SDIO
+
Controllers marked as RAW only operation are currently not listed
by this command.
a Command Status event on failure.
+Read Advertising Features Command
+=================================
+
+ Command Code: 0x003d
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Supported_Flags (4 Octets)
+ Max_Adv_Data_Len (1 Octet)
+ Max_Scan_Rsp_Len (1 Octet)
+ Max_Instances (1 Octet)
+ Num_Instances (1 Octet)
+ Instance[i] (1 Octet)
+
+ This command is used to read the advertising features supported
+ by the controller and stack.
+
+ With the Supported_Flags field the possible values for the Flags
+ field in Add Advertising command provided:
+
+ 0 Switch into Connectable mode
+ 1 Advertise as Discoverable
+ 2 Advertise as Limited Discoverable
+ 3 Add Flags field to Adv_Data
+ 4 Add TX Power field to Adv_Data
+ 5 Add Appearance field to Scan_Rsp
+ 6 Add Local Name in Scan_Rsp
+
+ The Flags bit 0 indicates support for connectable advertising
+ and for switching to connectable advertising independent of the
+ connectable global setting. When this flag is not supported, then
+ the global connectable setting determines if undirected connectable,
+ undirected scannable or undirected non-connectable advertising is
+ used. It also determines the use of non-resolvable random address
+ versus identity address or resolvable private address.
+
+ The Flags bit 1 indicates support for advertising with discoverable
+ mode enabled. Users of this flag will decrease the Max_Adv_Data_Len
+ by 3 octets. In this case the advertising data flags are managed
+ and added in front of the provided advertising data.
+
+ The Flags bit 2 indicates support for advertising with limited
+ discoverable mode enabled. Users of this flag will decrease the
+ Max_Adv_Data_Len by 3 octets. In this case the advertising data
+ flags are managed and added in front of the provided advertising
+ data.
+
+ The Flags bit 3 indicates support for automatically keeping the
+ Flags field of the advertising data updated. Users of this flag
+ will decrease the Max_Adv_Data_Len by 3 octets and need to keep
+ that in mind. The Flags field will be added in front of the
+ advertising data provided by the user. Note that with Flags bit 1
+ and Flags bit 2, this one will be implicitly used even if it is
+ not marked as supported.
+
+ The Flags bit 4 indicates support for automatically adding the
+ TX Power value to the advertising data. Users of this flag will
+ decrease the Max_Adv_Data_Len by 3 octets. The TX Power field will
+ be added at the end of the user provided advertising data. If the
+ controller does not support TX Power information, then this bit will
+ not be set.
+
+ The Flags bit 5 indicates support for automatically adding the
+ Apperance value to the scan response data. Users of this flag
+ will decrease the Max_Scan_Rsp_len by 4 octets. The Appearance
+ field will be added in front of the scan response data provided
+ by the user. If the appearance value is not supported, then this
+ bit will not be set.
+
+ The Flags bit 6 indicates support for automatically adding the
+ Local Name value to the scan response data. This flag indicates
+ an opportunistic approach for the Local Name. If enough space
+ in the scan response data is available, it will be added. If the
+ space is limited a short version or no name information. The
+ Local Name will be added at the end of the scan response data.
+
+ The valid range for Instance identifiers is 1-254. The value 0
+ is reserved for internal use and the value 255 is reserved for
+ future extensions. However the Max_Instances value for indicating
+ the number of supported Instances can be also 0 if the controller
+ does not support any advertising.
+
+ The Max_Adv_Data_Len and Max_Scan_Rsp_Len provides extra
+ information about the maximum length of the data fields. For
+ now this will always return the value 31. Different flags
+ however might decrease the actual available length in these
+ data fields.
+
+ With Num_Instances and Instance array the current occupied
+ Instance identifiers can be retrieved.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+ Possible errors: Invalid Parameters
+ Invalid Index
+
+
+Add Advertising Command
+=======================
+
+ Command Code: 0x003e
+ Controller Index: <controller id>
+ Command Parameters: Instance (1 Octet)
+ Flags (4 Octets)
+ Duration (2 Octets)
+ Timeout (2 Octets)
+ Adv_Data_Len (1 Octet)
+ Scan_Rsp_len (1 Octet)
+ Adv_Data (0-255 Octets)
+ Scan_Rsp (0-255 Octets)
+ Return Parameters: Instance (1 Octet)
+
+ This command is used to configure an advertising instance that
+ can be used to switch a Bluetooth Low Energy controller into
+ advertising mode.
+
+ Added advertising information with this command will be ignored
+ when using the Set Advertising command to enable advertising. The
+ usage of Set Advertising command take precedence over this command.
+
+ The Instance identifier is a value between 1 and the number of
+ supported instances. The value 0 is reserved.
+
+ With the Flags value the type of advertising is controlled and
+ the following flags are defined:
+
+ 0 Switch into Connectable mode
+ 1 Advertise as Discoverable
+ 2 Advertise as Limited Discoverable
+ 3 Add Flags field to Adv_Data
+ 4 Add TX Power field to Adv_Data
+ 5 Add Appearance field to Scan_Rsp
+ 6 Add Local Name in Scan_Rsp
+
+ When the connectable flag is set, then the controller will use
+ undirected connectable advertising. The value of the connectable
+ setting can be overwritten this way. This is useful to switch a
+ controller into connectable mode only for LE operation. This is
+ similar to the mode 0x02 from the Set Advertising command.
+
+ When the connectable flag is not set, then the controller will
+ use advertising based on the connectable setting. When using
+ non-connectable or scannable advertising, the controller will
+ be programmed with a non-resolvable random address. When the
+ system is connectable, then the identity address or resolvable
+ private address will be used.
+
+ Using the connectable flag is useful for peripheral mode support
+ where BR/EDR (and/or LE) is controlled by Add Device. This allows
+ making the peripheral connectable without having to interfere
+ with the global connectable setting.
+
+ If Scan_Rsp_Len is zero and connectable flag is not set and
+ the global connectable setting is off, then non-connectable
+ advertising is used. If Scan_Rsp_Len is larger than zero and
+ connectable flag is not set and the global advertising is off,
+ then scannable advertising is used. This small difference is
+ supported to provide less air traffic for devices implementing
+ broadcaster role.
+
+ The Duration parameter configures the length of an Instance. The
+ value is in seconds and a value of 0 indicates an automatic choice
+ for the Duration. If only one advertising Instance has been added,
+ then the Duration value will be ignored. It only applies for the
+ case where multiple Instances are configured. In that case every
+ Instance will be available for the Duration time and after that
+ it switches to the next one. This is a simple round-robin based
+ approach.
+
+ The Timeout parameter configures the life-time of an Instance. In
+ case the value 0 is used it indicates no expiration time. If a
+ timeout value is provided, then the advertising Instace will be
+ automatically removed when the timeout passes. The value for the
+ timeout is in seconds. Powering down a controller will invalidate
+ all advertising Instances and it is not possible to add a new
+ Instance with a timeout when the controller is powered down.
+
+ When a Timeout is provided, then the Duration substracts from
+ the actual Timeout value of that Instance. For example an Instance
+ with Timeout of 6 and Duration of 2 will be scheduled exactly 3
+ times. Other Instances have no influence on the Timeout.
+
+ A pre-requisite is that LE is already enabled, otherwise this
+ command will return a "rejected" response.
+
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ This command generates a Command Complete event on success or a
+ Command Status event on failure.
+
+ Possible errors: Failed
+ Rejected
+ Not Supported
+ Invalid Parameters
+ Invalid Index
+
+
+Remove Advertising Command
+==========================
+
+ Command Code: 0x003f
+ Controller Index: <controller id>
+ Command Parameters: Instance (1 Octet)
+ Return Parameters: Instance (1 Octet)
+
+ This command is used to remove an advertising instance that
+ can be used to switch a Bluetooth Low Energy controller into
+ advertising mode.
+
+ When the Instance parameter is zero, then all previously added
+ advertising Instances will be removed.
+
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+ Possible errors: Invalid Parameters
+ Invalid Index
+
+
Command Complete Event
======================
Event Code: 0x0020
Controller Index: <controller id>
Event Parameters: Controller_Type (1 Octet)
+ Controller_Bus (1 Octet)
This event indicates that a new controller index has been
added to the system.
This event will only be used after Read Extended Controller Index
List has been used at least once. If it has not been used, then
- Index Added and Unconfigured Index Added are send instead.
+ Index Added and Unconfigured Index Added are sent instead.
Extended Index Removed Event
Event Code: 0x0021
Controller Index: <controller id>
Event Parameters: Controller_Type (1 Octet)
+ Controller_Bus (1 Octet)
This event indicates that an existing controller index has been
removed from the system.
This event will only be used after Read Extended Controller Index
List has been used at least once. If it has not been used, then
- Index Removed and Unconfigured Index Removed are send instead.
+ Index Removed and Unconfigured Index Removed are sent instead.
+
+
+Local Out Of Band Extended Data Updated Event
+=============================================
+
+ Event Code: 0x0022
+ Controller Index: <controller id>
+ Event Parameters: Address_Type (1 Octet)
+ EIR_Data_Length (2 Octets)
+ EIR_Data (0-65535 Octets)
+
+ This event is used when the Read Local Out Of Band Extended Data
+ command has been used and some other user requested a new set
+ of local out-of-band data. This allows for the original caller
+ to adjust the data.
+
+ Possible values for the Address_Type parameter are a bit-wise or
+ of the following bits:
+
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
+
+ By combining these e.g. the following values are possible:
+
+ 1 BR/EDR
+ 6 LE (public & random)
+ 7 Reserved (not in use)
+
+ The value for EIR_Data_Length and content for EIR_Data is the
+ same as described in Read Local Out Of Band Extended Data command.
+
+ When LE Privacy is used and LE Secure Connections out-of-band
+ data has been requested, then this event will be emitted every
+ time the Resolvable Private Address (RPA) gets changed. The new
+ RPA will be included in the EIR_Data.
+
+ The event will only be sent to management sockets other than the
+ one through which the command was sent. It will additionally also
+ only be sent to sockets that have used the command at least once.
+
+
+Advertising Added Event
+=======================
+
+ Event Code: 0x0023
+ Controller Index: <controller id>
+ Event Parameters: Instance (1 Octet)
+
+ This event indicates that an advertising instance has been added
+ using the Add Advertising command.
+
+ The event will only be sent to management sockets other than the
+ one through which the command was sent.
+
+
+Advertising Removed Event
+=========================
+
+ Event Code: 0x0024
+ Controller Index: <controller id>
+ Event Parameters: Instance (1 Octet)
+
+ This event indicates that an advertising instance has been removed
+ using the Remove Advertising command.
+
+ The event will only be sent to management sockets other than the
+ one through which the command was sent.
IdentityAddress String Identity Address of the device
IdentityAddressType String Type of Identity Address of the device
-#endif
\ No newline at end of file
+#endif
test-gobex-apparam 18 OBEX apparam handling
test-gobex-transfer 36 OBEX transfer handling
test-gdbus-client 13 D-Bus client handling
-test-gatt 131 GATT qualification test cases
+test-gatt 159 GATT qualification test cases
+test-hog 6 HID Over GATT qualification test cases
-----
- 706
+ 740
Automated end-to-end testing
Application Count Description
-------------------------------------------
-mgmt-tester 243 Kernel management interface testing
+mgmt-tester 293 Kernel management interface testing
l2cap-tester 27 Kernel L2CAP implementation testing
rfcomm-tester 9 Kernel RFCOMM implementation testing
smp-tester 5 Kernel SMP implementation testing
gap-tester 1 Daemon D-Bus API testing
hci-tester 14 Controller hardware testing
-----
- 307
+ 357
Android end-to-end testing
btdev->commands[15] |= 0x02; /* Read BD ADDR */
}
-static void set_bredr_commands(struct btdev *btdev)
+static void set_common_commands_bredr20(struct btdev *btdev)
{
- set_common_commands_all(btdev);
- set_common_commands_bredrle(btdev);
-
btdev->commands[0] |= 0x01; /* Inquiry */
btdev->commands[0] |= 0x02; /* Inquiry Cancel */
btdev->commands[0] |= 0x10; /* Create Connection */
btdev->commands[14] |= 0x40; /* Read Local Extended Features */
btdev->commands[15] |= 0x01; /* Read Country Code */
btdev->commands[16] |= 0x04; /* Enable Device Under Test Mode */
+}
+
+static void set_bredr_commands(struct btdev *btdev)
+{
+ set_common_commands_all(btdev);
+ set_common_commands_bredrle(btdev);
+ set_common_commands_bredr20(btdev);
+
btdev->commands[16] |= 0x08; /* Setup Synchronous Connection */
btdev->commands[17] |= 0x01; /* Read Extended Inquiry Response */
btdev->commands[17] |= 0x02; /* Write Extended Inquiry Response */
btdev->commands[30] |= 0x08; /* Get MWS Transport Layer Config */
}
+static void set_bredr20_commands(struct btdev *btdev)
+{
+ set_common_commands_all(btdev);
+ set_common_commands_bredrle(btdev);
+ set_common_commands_bredr20(btdev);
+}
+
static void set_le_commands(struct btdev *btdev)
{
set_common_commands_all(btdev);
btdev->max_page = 1;
}
+static void set_bredr20_features(struct btdev *btdev)
+{
+ btdev->features[0] |= 0x04; /* Encryption */
+ btdev->features[0] |= 0x20; /* Role switch */
+ btdev->features[0] |= 0x80; /* Sniff mode */
+ btdev->features[1] |= 0x08; /* SCO link */
+ btdev->features[3] |= 0x40; /* RSSI with inquiry results */
+ btdev->features[3] |= 0x80; /* Extended SCO link */
+ btdev->features[4] |= 0x08; /* AFH capable slave */
+ btdev->features[4] |= 0x10; /* AFH classification slave */
+ btdev->features[5] |= 0x02; /* Sniff subrating */
+ btdev->features[5] |= 0x04; /* Pause encryption */
+ btdev->features[5] |= 0x08; /* AFH capable master */
+ btdev->features[5] |= 0x10; /* AFH classification master */
+ btdev->features[7] |= 0x80; /* Extended features */
+
+ btdev->max_page = 1;
+}
+
static void set_le_features(struct btdev *btdev)
{
btdev->features[4] |= 0x20; /* BR/EDR Not Supported */
btdev->type = type;
btdev->manufacturer = 63;
-
- if (type == BTDEV_TYPE_BREDR)
- btdev->version = 0x05;
- else
- btdev->version = 0x08;
-
btdev->revision = 0x0000;
switch (btdev->type) {
case BTDEV_TYPE_BREDRLE:
+ btdev->version = 0x08;
set_bredrle_features(btdev);
set_bredrle_commands(btdev);
break;
case BTDEV_TYPE_BREDR:
+ btdev->version = 0x05;
set_bredr_features(btdev);
set_bredr_commands(btdev);
break;
case BTDEV_TYPE_LE:
+ btdev->version = 0x08;
set_le_features(btdev);
set_le_commands(btdev);
break;
case BTDEV_TYPE_AMP:
+ btdev->version = 0x01;
set_amp_features(btdev);
set_amp_commands(btdev);
break;
+ case BTDEV_TYPE_BREDR20:
+ btdev->version = 0x04;
+ set_bredr20_features(btdev);
+ set_bredr20_commands(btdev);
+ break;
}
btdev->page_scan_interval = 0x0800;
BTDEV_TYPE_BREDR,
BTDEV_TYPE_LE,
BTDEV_TYPE_AMP,
+ BTDEV_TYPE_BREDR20,
};
enum btdev_hook_type {
case HCIEMU_TYPE_LE:
hciemu->btdev_type = BTDEV_TYPE_LE;
break;
+ case HCIEMU_TYPE_LEGACY:
+ hciemu->btdev_type = BTDEV_TYPE_BREDR20;
+ break;
default:
return NULL;
}
HCIEMU_TYPE_BREDRLE,
HCIEMU_TYPE_BREDR,
HCIEMU_TYPE_LE,
+ HCIEMU_TYPE_LEGACY,
};
enum hciemu_hook_type {
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/uio.h>
#include "lib/bluetooth.h"
#include "lib/hci.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/uio.h>
#include "lib/bluetooth.h"
#include "lib/hci.h"
if (!client->connected)
return;
- if (!client->proxy_added && !client->proxy_removed) {
+ if ((!client->proxy_added && !client->proxy_removed) ||
+ !client->root_path) {
refresh_properties(client);
return;
}
GDBusClient *client;
unsigned int i;
- if (!connection || !service || !root_path)
+ if (!connection || !service)
return NULL;
client = g_try_new0(GDBusClient, 1);
service_connect,
service_disconnect,
client, NULL);
+
+ if (!root_path)
+ return g_dbus_client_ref(client);
+
client->added_watch = g_dbus_add_signal_watch(connection, service,
client->root_path,
DBUS_INTERFACE_OBJECT_MANAGER,
const char *path, const char *interface,
const char *name, int type, va_list args);
+#ifdef GATT_NO_RELAY
+gboolean g_dbus_emit_signal_to_dest(DBusConnection *connection,
+ const char *dest, const char *path,
+ const char *interface, const char *name, int type, ...);
+#endif
+
guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
GDBusWatchFunction connect,
GDBusWatchFunction disconnect,
return result;
}
+#ifdef GATT_NO_RELAY
+static gboolean g_dbus_emit_signal_valist_to_dest(DBusConnection *connection,
+ const char *dest, const char *path, const char *interface,
+ const char *name, int type, va_list args)
+{
+ DBusMessage *signal;
+ dbus_bool_t ret;
+ const GDBusArgInfo *args_info;
+
+ if (!check_signal(connection, path, interface, name, &args_info))
+ return FALSE;
+
+ signal = dbus_message_new_signal(path, interface, name);
+ if (signal == NULL) {
+ error("Unable to allocate new %s.%s signal", interface, name);
+ return FALSE;
+ }
+
+ ret = dbus_message_append_args_valist(signal, type, args);
+ if (!ret)
+ goto fail;
+
+ if (g_dbus_args_have_signature(args_info, signal) == FALSE) {
+ error("%s.%s: got unexpected signature '%s'", interface, name,
+ dbus_message_get_signature(signal));
+ ret = FALSE;
+ goto fail;
+ }
+
+ ret = dbus_message_set_destination(signal, dest);
+ if (!ret)
+ error("Fail to set destination");
+
+ return g_dbus_send_message(connection, signal);
+
+fail:
+ dbus_message_unref(signal);
+
+ return ret;
+}
+
+gboolean g_dbus_emit_signal_to_dest(DBusConnection *connection,
+ const char *dest, const char *path,
+ const char *interface, const char *name, int type, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, type);
+
+ result = g_dbus_emit_signal_valist_to_dest(connection, dest, path,
+ interface, name, type, args);
+
+ va_end(args);
+
+ return result;
+}
+#endif
+
gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
const char *path, const char *interface,
const char *name, int type, va_list args)
return "IPS Group Inc.";
case 488:
return "STIR";
+ case 489:
+ return "Sano, Inc";
+ case 490:
+ return "Advanced Application Design, Inc.";
+ case 491:
+ return "AutoMap LLC";
+ case 492:
+ return "Spreadtrum Communications Shanghai Ltd";
+ case 493:
+ return "CuteCircuit LTD";
+ case 494:
+ return "Valeo Service";
+ case 495:
+ return "Fullpower Technologies, Inc.";
+ case 496:
+ return "KloudNation";
+ case 497:
+ return "Zebra Technologies Corporation";
+ case 498:
+ return "Itron, Inc.";
+ case 499:
+ return "The University of Tokyo";
+ case 500:
+ return "UTC Fire and Security";
case 65535:
return "internal use";
default:
static inline void bswap_128(const void *src, void *dst)
{
- const uint8_t *s = src;
- uint8_t *d = dst;
+ const uint8_t *s = (const uint8_t *) src;
+ uint8_t *d = (uint8_t *) dst;
int i;
for (i = 0; i < 16; i++)
uint8_t list[0];
} __attribute__((packed));
+struct bnep_ctrl_cmd_not_understood_cmd {
+ uint8_t type;
+ uint8_t ctrl;
+ uint8_t unkn_ctrl;
+} __attribute__((packed));
+
struct bnep_control_rsp {
uint8_t type;
uint8_t ctrl;
#define BNEPCONNDEL _IOW('B', 201, int)
#define BNEPGETCONNLIST _IOR('B', 210, int)
#define BNEPGETCONNINFO _IOR('B', 211, int)
+#ifdef __TIZEN_PATCH__
+#define BNEPGETSUPPFEAT _IOR('B', 212, int)
+
+#define BNEP_SETUP_RESPONSE 0
+#endif
struct bnep_connadd_req {
int sock; /* Connected socket */
int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to);
int hci_le_set_address_resolution_enable(int dev_id, uint8_t enable, int to);
int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
-
+#ifdef __TIZEN_PATCH__
+int hci_le_read_maximum_data_length(
+ int dd, uint8_t *status, uint16_t *tx_octets,
+ uint16_t *tx_time, uint16_t *rx_octets,
+ uint16_t *rx_time, int to );
+int hci_le_write_host_suggested_data_length(int dd, uint16_t *def_tx_octets,
+ uint16_t *def_tx_time, int to);
+int hci_le_read_host_suggested_data_length(int dd, uint8_t *status,
+ uint16_t *def_tx_octets, uint16_t *def_tx_time, int to);
+int hci_le_set_data_length(int dd, const bdaddr_t *bdaddr,
+ uint16_t *max_tx_octets, uint16_t *max_tx_time, int to);
+#endif
int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg);
int hci_get_route(bdaddr_t *bdaddr);
#define MGMT_STATUS_CANCELLED 0x10
#define MGMT_STATUS_INVALID_INDEX 0x11
#define MGMT_STATUS_RFKILLED 0x12
+#define MGMT_STATUS_ALREADY_PAIRED 0x13
+#define MGMT_STATUS_PERMISSION_DENIED 0x14
struct mgmt_hdr {
uint16_t opcode;
#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020
struct mgmt_rp_read_local_oob_data {
- uint8_t hash[16];
- uint8_t randomizer[16];
-} __packed;
-struct mgmt_rp_read_local_oob_ext_data {
uint8_t hash192[16];
- uint8_t randomizer192[16];
+ uint8_t rand192[16];
uint8_t hash256[16];
- uint8_t randomizer256[16];
+ uint8_t rand256[16];
} __packed;
#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021
uint16_t uuid_count;
uint8_t uuids[0][16];
} __packed;
-#define MGMT_START_SERVICE_DISCOVERY_SIZE 4
+
+#define MGMT_OP_READ_LOCAL_OOB_EXT_DATA 0x003B
+struct mgmt_cp_read_local_oob_ext_data {
+ uint8_t type;
+} __packed;
+struct mgmt_rp_read_local_oob_ext_data {
+ uint8_t type;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+
+#define MGMT_OP_READ_EXT_INDEX_LIST 0x003C
+struct mgmt_rp_read_ext_index_list {
+ uint16_t num_controllers;
+ struct {
+ uint16_t index;
+ uint8_t type;
+ uint8_t bus;
+ } entry[0];
+} __packed;
+
+#define MGMT_OP_READ_ADV_FEATURES 0x003D
+struct mgmt_rp_read_adv_features {
+ uint32_t supported_flags;
+ uint8_t max_adv_data_len;
+ uint8_t max_scan_rsp_len;
+ uint8_t max_instances;
+ uint8_t num_instances;
+ uint8_t instance[0];
+} __packed;
+
+#define MGMT_OP_ADD_ADVERTISING 0x003E
+struct mgmt_cp_add_advertising {
+ uint8_t instance;
+ uint32_t flags;
+ uint16_t duration;
+ uint16_t timeout;
+ uint8_t adv_data_len;
+ uint8_t scan_rsp_len;
+ uint8_t data[0];
+} __packed;
+struct mgmt_rp_add_advertising {
+ uint8_t instance;
+} __packed;
+
+#define MGMT_ADV_FLAG_CONNECTABLE (1 << 0)
+#define MGMT_ADV_FLAG_DISCOV (1 << 1)
+#define MGMT_ADV_FLAG_LIMITED_DISCOV (1 << 2)
+#define MGMT_ADV_FLAG_MANAGED_FLAGS (1 << 3)
+#define MGMT_ADV_FLAG_TX_POWER (1 << 4)
+#define MGMT_ADV_FLAG_APPEARANCE (1 << 5)
+#define MGMT_ADV_FLAG_LOCAL_NAME (1 << 6)
+
+#define MGMT_OP_REMOVE_ADVERTISING 0x003F
+struct mgmt_cp_remove_advertising {
+ uint8_t instance;
+} __packed;
+#define MGMT_REMOVE_ADVERTISING_SIZE 1
+struct mgmt_rp_remove_advertising {
+ uint8_t instance;
+} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
#define MGMT_EV_CLASS_OF_DEV_CHANGED 0x0007
struct mgmt_ev_class_of_dev_changed {
- uint8_t class_of_dev[3];
+ uint8_t dev_class[3];
} __packed;
#define MGMT_EV_LOCAL_NAME_CHANGED 0x0008
#define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f
+#define MGMT_EV_EXT_INDEX_ADDED 0x0020
+struct mgmt_ev_ext_index_added {
+ uint8_t type;
+ uint8_t bus;
+} __packed;
+
+#define MGMT_EV_EXT_INDEX_REMOVED 0x0021
+struct mgmt_ev_ext_index_removed {
+ uint8_t type;
+ uint8_t bus;
+} __packed;
+
+#define MGMT_EV_LOCAL_OOB_DATA_UPDATED 0x0022
+struct mgmt_ev_local_oob_data_updated {
+ uint8_t type;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+
+#define MGMT_EV_ADVERTISING_ADDED 0x0023
+struct mgmt_ev_advertising_added {
+ uint8_t instance;
+} __packed;
+
+#define MGMT_EV_ADVERTISING_REMOVED 0x0024
+struct mgmt_ev_advertising_removed {
+ uint8_t instance;
+} __packed;
+
static const char *mgmt_op[] = {
"<0x0000>",
"Read Version",
"Set Powered",
"Set Discoverable",
"Set Connectable",
- "Set Fast Connectable", /* 0x0008 */
+ "Set Fast Connectable", /* 0x0008 */
"Set Bondable",
"Set Link Security",
"Set Secure Simple Pairing",
"Set Low Energy",
"Set Dev Class",
"Set Local Name",
- "Add UUID", /* 0x0010 */
+ "Add UUID", /* 0x0010 */
"Remove UUID",
"Load Link Keys",
"Load Long Term Keys",
"Get Connections",
"PIN Code Reply",
"PIN Code Neg Reply",
- "Set IO Capability", /* 0x0018 */
+ "Set IO Capability", /* 0x0018 */
"Pair Device",
"Cancel Pair Device",
"Unpair Device",
"User Confirm Neg Reply",
"User Passkey Reply",
"User Passkey Neg Reply",
- "Read Local OOB Data", /* 0x0020 */
+ "Read Local OOB Data", /* 0x0020 */
"Add Remote OOB Data",
"Remove Remove OOB Data",
"Start Discovery",
"Confirm Name",
"Block Device",
"Unblock Device",
- "Set Device ID",
+ "Set Device ID", /* 0x0028 */
"Set Advertising",
"Set BR/EDR",
"Set Static Address",
"Set Secure Connections",
"Set Debug Keys",
"Set Privacy",
- "Load Identity Resolving Keys",
+ "Load Identity Resolving Keys", /* 0x0030 */
"Get Connection Information",
"Get Clock Information",
"Add Device",
"Load Connection Parameters",
"Read Unconfigured Index List",
"Read Controller Configuration Information",
- "Set External Configuration",
+ "Set External Configuration", /* 0x0038 */
"Set Public Address",
"Start Service Discovery",
+ "Read Local Out Of Band Extended Data",
+ "Read Extended Controller Index List",
+ "Read Advertising Features",
+ "Add Advertising",
+ "Remove Advertising",
};
static const char *mgmt_ev[] = {
"Unconfigured Index Added",
"Unconfigured Index Removed",
"New Configuration Options",
+ "Extended Index Added",
+ "Extended Index Removed",
+ "Local Out Of Band Extended Data Updated",
+ "Advertising Added",
+ "Advertising Removed",
};
static const char *mgmt_status[] = {
"Cancelled",
"Invalid Index",
"Blocked through rfkill",
+ "Already Paired",
+ "Permission Denied",
};
#ifdef __TIZEN_PATCH__
} __packed;
#define MGMT_LE_SET_DATA_LENGTH_SIZE 10
+#define MGMT_OP_SET_DEV_RPA_RES_SUPPORT (TIZEN_OP_CODE_BASE + 0x19)
+struct mgmt_cp_set_dev_rpa_res_support {
+ struct mgmt_addr_info addr;
+ uint8_t res_support;
+} __packed;
+
/* Currently there is no support in kernel for below MGMT cmd opcodes. */
#if 0 // Not defined in kernel
#define MGMT_OP_READ_RSSI (TIZEN_OP_CODE_BASE + 0x11)
uint8_t connected;
} __packed;
+
#define MGMT_EV_LE_DATA_LENGTH_CHANGED (TIZEN_EV_BASE + 0x0d)
struct mgmt_ev_le_data_length_changed {
struct mgmt_addr_info addr;
#define GATT_CHARAC_SOFTWARE_REVISION_STRING 0x2A28
#define GATT_CHARAC_MANUFACTURER_NAME_STRING 0x2A29
#define GATT_CHARAC_PNP_ID 0x2A50
+#ifdef __TIZEN_PATCH__
+#define GATT_CHARAC_CENTRAL_RPA_RESOLUTION 0x2AA6
+#endif
/* GATT Characteristic Descriptors */
#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
static int btsnoop_fd = -1;
static uint16_t btsnoop_index = 0xffff;
+#ifdef __TIZEN_PATCH__
+static char *btsnoop_path = NULL;
+static int16_t btsnoop_rotate = -1;
+static ssize_t btsnoop_size = -1;
+
+void btsnoop_create(const char *path, uint32_t type,
+ int16_t rotate_count, ssize_t file_size)
+#else
void btsnoop_create(const char *path, uint32_t type)
+#endif
{
struct btsnoop_hdr hdr;
ssize_t written;
btsnoop_fd = -1;
return;
}
+
+#ifdef __TIZEN_PATCH__
+ if (rotate_count > 0 && file_size > 0) {
+ btsnoop_path = strdup(path);
+ btsnoop_rotate = rotate_count;
+ btsnoop_size = file_size;
+ }
+#endif
+}
+
+#ifdef __TIZEN_PATCH__
+static void btsnoop_create_2(void)
+{
+ struct btsnoop_hdr hdr;
+ ssize_t written;
+
+ if (btsnoop_fd >= 0)
+ close(btsnoop_fd);
+
+ btsnoop_fd = open(btsnoop_path,
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (btsnoop_fd < 0)
+ goto fail;
+
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htobe32(btsnoop_version);
+ hdr.type = htobe32(btsnoop_type);
+
+ written = write(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
+ if (written < 0) {
+ close(btsnoop_fd);
+ btsnoop_fd = -1;
+ goto fail;
+ }
+
+ return;
+
+fail:
+ free(btsnoop_path);
+ btsnoop_rotate = -1;
+ btsnoop_size = -1;
+ btsnoop_index = 0xffff;
+
+ return;
}
+static void btsnoop_rotate_files(void)
+{
+ char *filename = NULL;
+ char *new_filename = NULL;
+ int i;
+ int postfix_width = 0;
+ int err;
+
+ if (btsnoop_rotate <= 1)
+ return;
+
+ for (i = btsnoop_rotate / 10; i; i /= 10)
+ postfix_width++;
+
+ for (i = btsnoop_rotate - 2; i >= 0; i--) {
+ if (i == 0) {
+ filename = strdup(btsnoop_path);
+ err = (filename == NULL) ? -1 : 0;
+ } else {
+ err = asprintf(&filename, "%s.%0*d",
+ btsnoop_path, postfix_width, i);
+ }
+
+ if (err < 0 || access(filename, F_OK) < 0)
+ goto done;
+
+ err = asprintf(&new_filename, "%s.%0*d",
+ btsnoop_path, postfix_width, i + 1);
+ if (err < 0)
+ goto done;
+
+ err = rename(filename, new_filename)
+
+done:
+ if (new_filename) {
+ free(new_filename);
+ new_filename = NULL;
+ }
+
+ if (filename) {
+ free(filename);
+ filename = NULL;
+ }
+
+ if (err < 0)
+ break;
+ }
+}
+#endif
+
void btsnoop_write(struct timeval *tv, uint32_t flags,
const void *data, uint16_t size)
{
pkt.drops = htobe32(0);
pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);
+#ifdef __TIZEN_PATCH__
+ if ((btsnoop_rotate > 0 && btsnoop_size > 0) &&
+ lseek(btsnoop_fd, 0x00, SEEK_CUR) +
+ BTSNOOP_PKT_SIZE + size > btsnoop_size) {
+ btsnoop_rotate_files();
+ btsnoop_create_2();
+ if (btsnoop_fd < 0)
+ return;
+ }
+#endif
+
written = write(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
if (written < 0)
return;
if (btsnoop_fd < 0)
return;
+#ifdef __TIZEN_PATCH__
+ if (btsnoop_path) {
+ free(btsnoop_path);
+ btsnoop_path = NULL;
+ }
+ btsnoop_rotate = -1;
+ btsnoop_size = -1;
+#endif
+
close(btsnoop_fd);
btsnoop_fd = -1;
char name[8];
} __attribute__((packed));
+#ifdef __TIZEN_PATCH__
+void btsnoop_create(const char *path, uint32_t type,
+ int16_t rotate_count, ssize_t file_size);
+#else
void btsnoop_create(const char *path, uint32_t type);
+#endif
void btsnoop_write(struct timeval *tv, uint32_t flags,
const void *data, uint16_t size);
void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
packet_hexdump(buf, len);
}
+static void mgmt_ext_index_added(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_ext_index_added *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Extended Index Added control\n");
+ return;
+ }
+
+ printf("@ Extended Index Added: %u (%u)\n", ev->type, ev->bus);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_ext_index_removed(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_ext_index_removed *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Extended Index Removed control\n");
+ return;
+ }
+
+ printf("@ Extended Index Removed: %u (%u)\n", ev->type, ev->bus);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
static void mgmt_controller_error(uint16_t len, const void *buf)
{
const struct mgmt_ev_controller_error *ev = buf;
}
printf("@ Class of Device Changed: 0x%2.2x%2.2x%2.2x\n",
- ev->class_of_dev[2],
- ev->class_of_dev[1],
- ev->class_of_dev[0]);
+ ev->dev_class[2],
+ ev->dev_class[1],
+ ev->dev_class[0]);
buf += sizeof(*ev);
len -= sizeof(*ev);
packet_hexdump(buf, len);
}
+static void mgmt_advertising_added(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_advertising_added *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Advertising Added control\n");
+ return;
+ }
+
+ printf("@ Advertising Added: %u\n", ev->instance);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_advertising_removed(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_advertising_removed *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Advertising Removed control\n");
+ return;
+ }
+
+ printf("@ Advertising Removed: %u\n", ev->instance);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
void control_message(uint16_t opcode, const void *data, uint16_t size)
{
switch (opcode) {
case MGMT_EV_NEW_CONFIG_OPTIONS:
mgmt_new_config_options(size, data);
break;
+ case MGMT_EV_EXT_INDEX_ADDED:
+ mgmt_ext_index_added(size, data);
+ break;
+ case MGMT_EV_EXT_INDEX_REMOVED:
+ mgmt_ext_index_removed(size, data);
+ break;
+ case MGMT_EV_ADVERTISING_ADDED:
+ mgmt_advertising_added(size, data);
+ break;
+ case MGMT_EV_ADVERTISING_REMOVED:
+ mgmt_advertising_removed(size, data);
+ break;
default:
printf("* Unknown control (code %d len %d)\n", opcode, size);
packet_hexdump(data, size);
server_fd = fd;
}
+#ifdef __TIZEN_PATCH__
+bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size)
+#else
bool control_writer(const char *path)
+#endif
{
+#ifdef __TIZEN_PATCH__
+ btsnoop_file = btsnoop_create(path, BTSNOOP_TYPE_MONITOR,
+ rotate_count, file_size);
+#else
btsnoop_file = btsnoop_create(path, BTSNOOP_TYPE_MONITOR);
+#endif
return !!btsnoop_file;
}
#include <stdint.h>
+#ifdef __TIZEN_PATCH__
+bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size);
+#else
bool control_writer(const char *path);
+#endif
void control_reader(const char *path);
void control_server(const char *path);
int control_tracing(void);
case 0x11:
str = "Insufficient Resources";
break;
- case 0xfd:
- str = "CCC Improperly Configured";
- break;
- case 0xfe:
- str = "Procedure Already in Progress";
- break;
- case 0xff:
- str = "Out of Range";
- break;
default:
str = "Reserved";
break;
"\t-T, --date Show time and date information\n"
"\t-S, --sco Dump SCO traffic\n"
"\t-E, --ellisys [ip] Send Ellisys HCI Injection\n"
+#ifdef __TIZEN_PATCH__
+ "\t-C, --count <num> Save traces by <num> rotation\n"
+ "\t-W, --size <num> Save traces at most <num> size\n"
+#endif
"\t-h, --help Show help options\n");
}
{ "date", no_argument, NULL, 'T' },
{ "sco", no_argument, NULL, 'S' },
{ "ellisys", required_argument, NULL, 'E' },
+#ifdef __TIZEN_PATCH__
+ { "count", required_argument, NULL, 'C' },
+ { "size", required_argument, NULL, 'W' },
+#endif
{ "todo", no_argument, NULL, '#' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
const char *analyze_path = NULL;
const char *ellisys_server = NULL;
unsigned short ellisys_port = 0;
+#ifdef __TIZEN_PATCH__
+ int16_t rotate_count = -1;
+ ssize_t file_size = -1;
+#endif
const char *str;
int exit_status;
sigset_t mask;
for (;;) {
int opt;
+#ifdef __TIZEN_PATCH__
+ opt = getopt_long(argc, argv, "r:w:a:s:i:tTSE:C:W:vh",
+ main_options, NULL);
+#else
opt = getopt_long(argc, argv, "r:w:a:s:i:tTSE:vh",
main_options, NULL);
+#endif
if (opt < 0)
break;
ellisys_server = optarg;
ellisys_port = 24352;
break;
+#ifdef __TIZEN_PATCH__
+ case 'C':
+ rotate_count = atoi(optarg);
+ break;
+ case 'W':
+ file_size = atoll(optarg);
+ break;
+#endif
case '#':
packet_todo();
lmp_todo();
return EXIT_SUCCESS;
}
+#ifdef __TIZEN_PATCH__
+ if (writer_path && !control_writer(writer_path,
+ rotate_count, file_size)) {
+ printf("Failed to open '%s'\n", writer_path);
+ return EXIT_FAILURE;
+ }
+#else
if (writer_path && !control_writer(writer_path)) {
printf("Failed to open '%s'\n", writer_path);
return EXIT_FAILURE;
}
+#endif
if (ellisys_server)
ellisys_enable(ellisys_server, ellisys_port);
str = "Scannable undirected - ADV_SCAN_IND";
break;
case 0x03:
- str = "Non connectable undirect - ADV_NONCONN_IND";
+ str = "Non connectable undirected - ADV_NONCONN_IND";
break;
case 0x04:
str = "Connectable directed - ADV_DIRECT_IND (low duty cycle)";
#include <string.h>
#include "log.h"
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
#include "transfer.h"
#include "session.h"
{
struct mns_data *mns = user_data;
struct obc_transfer *transfer;
- struct sendevent_apparam apparam;
+ guint8 masinstanceid;
+ GObexApparam *apparam;
gchar *event_type;
gchar *folder;
gchar *old_folder;
if (transfer == NULL)
goto fail;
- apparam.masinstanceid_tag = MAP_AP_MASINSTANCEID;
- apparam.masinstanceid_len = 1;
/* Obexd currently supports single SDP for MAS */
- apparam.masinstanceid = 0;
+ masinstanceid = 0;
+ apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_MASINSTANCEID,
+ masinstanceid);
- obc_transfer_set_apparam(transfer, &apparam);
+ obc_transfer_set_apparam(transfer, apparam);
if (obc_session_queue(mns->session, transfer, NULL, NULL, &err))
return dbus_message_new_method_return(message);
internal = TRUE;
} else if (!g_ascii_strncasecmp(location, "sim", 3)) {
if (strlen(location) == 3)
+#ifdef __TIZEN_PATCH__
+ tmp = g_strdup("SIM1");
+#else
tmp = g_strdup("sim1");
+#endif
else
tmp = g_ascii_strup(location, 4);
DBG("");
manager_unregister_session(os);
- messages_disconnect(mas->backend_data);
+#ifdef __TIZEN_PATCH__
+ if (mas)
+#endif
+ {
+ messages_disconnect(mas->backend_data);
- mas_clean(mas);
+ mas_clean(mas);
+ }
}
static int mas_get(struct obex_session *os, void *user_data)
#include "filesystem.h"
#define VCARD_TYPE "text/x-vcard"
+#define VCARD_FILE CONFIGDIR "/vcard.vcf"
static void *opp_connect(struct obex_session *os, int *err)
{
static int opp_get(struct obex_session *os, void *user_data)
{
const char *type;
- char *folder, *path;
- int err = 0;
if (obex_get_name(os))
return -EPERM;
if (type == NULL)
return -EPERM;
- folder = g_strdup(obex_option_root_folder());
- path = g_build_filename(folder, "/vcard.vcf", NULL);
-
if (g_ascii_strcasecmp(type, VCARD_TYPE) == 0) {
- if (obex_get_stream_start(os, path) < 0)
- err = -ENOENT;
+ if (obex_get_stream_start(os, VCARD_FILE) < 0)
+ return -ENOENT;
} else
- err = -EPERM;
+ return -EPERM;
- g_free(folder);
- g_free(path);
- return err;
+ return 0;
}
static void opp_disconnect(struct obex_session *os, void *user_data)
int ret;
void *request;
+ if (name == NULL) {
+ ret = -EBADR;
+ goto fail;
+ }
+
DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
if (oflag != O_RDONLY) {
ret = -EPERM;
goto fail;
}
-
- if (name == NULL) {
- ret = -EBADR;
- goto fail;
- }
-
#ifdef __TIZEN_PATCH__
if (strcmp(name, "/telecom/mch") == 0)
pbap->params->required_missedcall_call_header = TRUE;
%{_bindir}/l2ping
%{_bindir}/obexctl
%{_bindir}/rfcomm
+%{_bindir}/btsnoop
%{_bindir}/mpris-proxy
%{_bindir}/sdptool
%{_bindir}/ciptool
%files devel
%manifest %{name}.manifest
%defattr(-, root, root)
-/usr/include/bluetooth
+%{_includedir}/bluetooth/*
%{_libdir}/libbluetooth.so
%{_libdir}/pkgconfig/bluez.pc
#endif
#define SOURCE_RETRY_TIMEOUT 2
#define SINK_RETRY_TIMEOUT SOURCE_RETRY_TIMEOUT
+#define CT_RETRY_TIMEOUT 1
+#define TG_RETRY_TIMEOUT CT_RETRY_TIMEOUT
#define SOURCE_RETRIES 1
#define SINK_RETRIES SOURCE_RETRIES
+#define CT_RETRIES 1
+#define TG_RETRIES CT_RETRIES
/* Tracking of remote services to be auto-reconnected upon link loss */
guint sink_timer;
uint8_t sink_retries;
guint ct_timer;
+ uint8_t ct_retries;
guint tg_timer;
+ uint8_t tg_retries;
};
static void policy_connect(struct policy_data *data,
struct btd_service *service;
data->ct_timer = 0;
+ data->ct_retries++;
service = btd_device_get_service(data->dev, AVRCP_REMOTE_UUID);
if (service != NULL)
return FALSE;
}
-static void policy_set_ct_timer(struct policy_data *data)
+static void policy_set_ct_timer(struct policy_data *data, int timeout)
{
if (data->ct_timer > 0)
g_source_remove(data->ct_timer);
- data->ct_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT,
- policy_connect_ct, data);
+ data->ct_timer = g_timeout_add_seconds(timeout, policy_connect_ct,
+ data);
}
static struct policy_data *find_data(struct btd_device *dev)
* avrcp connection immediately; irrespective of local
* or remote initiated a2dp connection
*/
- policy_set_ct_timer(data);
+ policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT);
#else
policy_connect(data, controller);
#endif
else if (btd_service_get_state(controller) !=
BTD_SERVICE_STATE_CONNECTED)
- policy_set_ct_timer(data);
+ policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT);
break;
case BTD_SERVICE_STATE_DISCONNECTING:
break;
struct btd_service *service;
data->tg_timer = 0;
+ data->tg_retries++;
service = btd_device_get_service(data->dev, AVRCP_TARGET_UUID);
if (service != NULL)
return FALSE;
}
-static void policy_set_tg_timer(struct policy_data *data)
+static void policy_set_tg_timer(struct policy_data *data, int timeout)
{
if (data->tg_timer > 0)
g_source_remove(data->tg_timer);
+
#ifdef __TIZEN_PATCH__
data->tg_timer = g_timeout_add_seconds(TARGET_CONNECT_TIMEOUT,
policy_connect_tg,
data);
#else
- data->tg_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT,
- policy_connect_tg,
+ data->tg_timer = g_timeout_add_seconds(timeout, policy_connect_tg,
data);
#endif
}
policy_connect(data, target);
else if (btd_service_get_state(target) !=
BTD_SERVICE_STATE_CONNECTED)
- policy_set_tg_timer(data);
+ policy_set_tg_timer(data, CONTROL_CONNECT_TIMEOUT);
break;
case BTD_SERVICE_STATE_DISCONNECTING:
break;
}
break;
case BTD_SERVICE_STATE_DISCONNECTED:
+ if (old_state == BTD_SERVICE_STATE_CONNECTING) {
+ int err = btd_service_get_error(service);
+
+ if (err == -EAGAIN) {
+ if (data->ct_retries < CT_RETRIES)
+ policy_set_ct_timer(data,
+ CT_RETRY_TIMEOUT);
+ else
+ data->ct_retries = 0;
+ break;
+ } else if (data->ct_timer > 0) {
+ g_source_remove(data->ct_timer);
+ data->ct_timer = 0;
+ }
+ } else if (old_state == BTD_SERVICE_STATE_CONNECTED) {
+ data->ct_retries = 0;
+ }
break;
case BTD_SERVICE_STATE_CONNECTING:
break;
}
break;
case BTD_SERVICE_STATE_DISCONNECTED:
+ if (old_state == BTD_SERVICE_STATE_CONNECTING) {
+ int err = btd_service_get_error(service);
+
+ if (err == -EAGAIN) {
+ if (data->tg_retries < TG_RETRIES)
+ policy_set_tg_timer(data,
+ TG_RETRY_TIMEOUT);
+ else
+ data->tg_retries = 0;
+ break;
+ } else if (data->tg_timer > 0) {
+ g_source_remove(data->tg_timer);
+ data->tg_timer = 0;
+ }
+ } else if (old_state == BTD_SERVICE_STATE_CONNECTED) {
+ data->tg_retries = 0;
+ }
break;
case BTD_SERVICE_STATE_CONNECTING:
break;
btd_device_device_set_name(device, devices[index].name);
btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
devices[index].pid, devices[index].version);
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
return true;
}
struct a2dp_setup_cb {
struct a2dp_setup *setup;
- a2dp_discover_cb_t discover_cb;
a2dp_select_cb_t select_cb;
a2dp_config_cb_t config_cb;
a2dp_stream_cb_t resume_cb;
struct avdtp_stream *stream;
struct avdtp_error *err;
avdtp_set_configuration_cb setconf_cb;
- GSList *seps;
GSList *caps;
gboolean reconfigure;
gboolean start;
}
}
-static void finalize_discover(struct a2dp_setup *s)
-{
- GSList *l;
-
- for (l = s->cb; l != NULL; ) {
- struct a2dp_setup_cb *cb = l->data;
-
- l = l->next;
-
- if (!cb->discover_cb)
- continue;
-
- cb->discover_cb(s->session, s->seps, s->err, cb->user_data);
- setup_cb_free(cb);
- }
-}
-
static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
{
GSList *l;
return;
}
-#ifdef __TIZEN_PATCH__
- if (new_state == AVDTP_STATE_STREAMING && sep->suspend_timer) {
- g_source_remove(sep->suspend_timer);
- sep->suspend_timer = 0;
- }
-#endif
-
if (new_state != AVDTP_STATE_IDLE)
return;
{
struct a2dp_setup *setup = data;
struct btd_device *dev = NULL;
-
struct btd_service *service;
/* Check if configuration was aborted */
return NULL;
}
+#ifdef __TIZEN_PATCH__
+ if (chan->auth_id) {
+ DBG("auth is already going...");
+ return NULL;
+ }
+#endif
+
if (chan->session)
return avdtp_ref(chan->session);
finalize_select(setup);
}
+static gboolean check_vendor_codec(struct a2dp_sep *sep, uint8_t *cap,
+ size_t len)
+{
+ uint8_t *capabilities;
+ size_t length;
+ a2dp_vendor_codec_t *local_codec;
+ a2dp_vendor_codec_t *remote_codec;
+
+ if (len < sizeof(a2dp_vendor_codec_t))
+ return FALSE;
+
+ remote_codec = (a2dp_vendor_codec_t *) cap;
+
+ if (sep->endpoint == NULL)
+ return FALSE;
+
+ length = sep->endpoint->get_capabilities(sep,
+ &capabilities, sep->user_data);
+
+ if (length < sizeof(a2dp_vendor_codec_t))
+ return FALSE;
+
+ local_codec = (a2dp_vendor_codec_t *) capabilities;
+
+ if (btohl(remote_codec->vendor_id) != btohl(local_codec->vendor_id))
+ return FALSE;
+
+ if (btohs(remote_codec->codec_id) != btohs(local_codec->codec_id))
+ return FALSE;
+
+ DBG("vendor 0x%08x codec 0x%04x", btohl(remote_codec->vendor_id),
+ btohs(remote_codec->codec_id));
+
+ return TRUE;
+}
+
static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
const char *sender)
{
continue;
}
#else
- if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
- continue;
+ if (check_vendor_codec(sep, cap->data,
+ service->length - sizeof(*cap)))
+ return sep;
#endif
return sep;
return a2dp_find_sep(session, l, NULL);
}
-static void discover_cb(struct avdtp *session, GSList *seps,
- struct avdtp_error *err, void *user_data)
-{
- struct a2dp_setup *setup = user_data;
-
- DBG("err %p", err);
-
- setup->seps = seps;
- setup->err = err;
-
- finalize_discover(setup);
-}
-
-unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb,
- void *user_data)
-{
- struct a2dp_setup *setup;
- struct a2dp_setup_cb *cb_data;
-
- setup = a2dp_setup_get(session);
- if (!setup)
- return 0;
-
- cb_data = setup_cb_new(setup);
- cb_data->discover_cb = cb;
- cb_data->user_data = user_data;
-
- if (avdtp_discover(session, discover_cb, setup) == 0)
- return cb_data->id;
-
- setup_cb_free(cb_data);
- return 0;
-}
-
unsigned int a2dp_select_capabilities(struct avdtp *session,
uint8_t type, const char *sender,
a2dp_select_cb_t cb,
if (!setup->cb) {
DBG("aborting setup %p", setup);
- if (!avdtp_abort(setup->session, setup->stream))
- return TRUE;
+ avdtp_abort(setup->session, setup->stream);
+ return TRUE;
}
setup_unref(setup);
void *user_data);
};
-typedef void (*a2dp_discover_cb_t) (struct avdtp *session, GSList *seps,
- struct avdtp_error *err,
- void *user_data);
typedef void (*a2dp_select_cb_t) (struct avdtp *session,
struct a2dp_sep *sep, GSList *caps,
void *user_data);
int *err);
void a2dp_remove_sep(struct a2dp_sep *sep);
-unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb,
- void *user_data);
unsigned int a2dp_select_capabilities(struct avdtp *session,
uint8_t type, const char *sender,
a2dp_select_cb_t cb,
g_free(session);
}
-static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
+static void avctp_set_state(struct avctp *session, avctp_state_t new_state,
+ int err)
{
GSList *l;
avctp_state_t old_state = session->state;
if (cb->dev && cb->dev != session->device)
continue;
- cb->cb(session->device, old_state, new_state, cb->user_data);
+ cb->cb(session->device, old_state, new_state, err,
+ cb->user_data);
}
switch (new_state) {
failed:
DBG("AVCTP Browsing: disconnected");
- avctp_set_state(session, AVCTP_STATE_CONNECTED);
+ avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
if (session->browsing) {
avctp_channel_destroy(session->browsing);
failed:
DBG("AVCTP session %p got disconnected", session);
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
return FALSE;
}
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_browsing_cb, session);
- avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED);
+ avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED, 0);
/* Process any request that was pending the connection to complete */
if (browsing->process_id == 0 && !g_queue_is_empty(browsing->queue))
return;
fail:
- avctp_set_state(session, AVCTP_STATE_CONNECTED);
+ avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
if (session->browsing) {
avctp_channel_destroy(session->browsing);
GError *gerr = NULL;
if (err) {
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
error("%s", err->message);
return;
}
BT_IO_OPT_IMTU, &omtu,
BT_IO_OPT_INVALID);
if (gerr) {
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
error("%s", gerr->message);
g_error_free(gerr);
return;
init_uinput(session);
- avctp_set_state(session, AVCTP_STATE_CONNECTED);
+ avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
}
static void auth_cb(DBusError *derr, void *user_data)
if (derr && dbus_error_is_set(derr)) {
error("Access denied: %s", derr->message);
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
return;
}
NULL, &err)) {
error("bt_io_accept: %s", err->message);
g_error_free(err);
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
}
}
if (session->control != NULL) {
error("Control: Refusing unexpected connect");
g_io_channel_shutdown(chan, TRUE, NULL);
+
+ /*
+ * Close AVCTP channel if remote tried connect
+ * at the same time
+ * AVRCP SPEC V1.5 4.1.1 Connection Establishment
+ */
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EAGAIN);
return;
}
- avctp_set_state(session, AVCTP_STATE_CONNECTING);
+ avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
session->control = avctp_channel_create(session, chan, NULL);
src = btd_adapter_get_address(device_get_adapter(dev));
return;
drop:
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
}
static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan,
if (bt_io_accept(chan, avctp_connect_browsing_cb, session, NULL,
&err)) {
- avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING);
+ avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING, 0);
return;
}
if (session->state > AVCTP_STATE_DISCONNECTED)
return session;
- avctp_set_state(session, AVCTP_STATE_CONNECTING);
+ avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
src = btd_adapter_get_address(session->server->adapter);
BT_IO_OPT_PSM, AVCTP_CONTROL_PSM,
BT_IO_OPT_INVALID);
if (err) {
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
error("%s", err->message);
g_error_free(err);
return NULL;
if (session->browsing != NULL)
return 0;
- avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING);
+ avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING, 0);
src = btd_adapter_get_address(session->server->adapter);
if (session->state == AVCTP_STATE_DISCONNECTED)
return;
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
}
struct avctp *avctp_get(struct btd_device *device)
typedef void (*avctp_state_cb) (struct btd_device *dev,
avctp_state_t old_state,
avctp_state_t new_state,
- void *user_data);
+ int err, void *user_data);
typedef bool (*avctp_passthrough_cb) (struct avctp *session,
uint8_t op, bool pressed,
#include <unistd.h>
#include <assert.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
#ifdef __TIZEN_PATCH__
#endif /* __BROADCOM_QOS_PATCH__ */
#endif /* __TIZEN_PATCH__ */
-#include <glib.h>
-
-
#include "btio/btio.h"
#include "src/log.h"
#include "src/shared/util.h"
dd = hci_open_dev(0);
+ if (dd < 0)
+ return FALSE;
+
cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
cr->type = ACL_LINK;
if (discover->id > 0)
g_source_remove(discover->id);
- discover->cb(session, session->seps, err ? &avdtp_err : NULL,
+ if (discover->cb)
+ discover->cb(session, session->seps, err ? &avdtp_err : NULL,
discover->user_data);
g_free(discover);
session->discover = NULL;
}
#ifdef __TIZEN_PATCH__
- adapter = device_get_adapter(session->device);
- bdaddr = device_get_address(session->device);
- if (adapter && bdaddr)
- device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
- if (!device)
- error("device is NOT found");
+ if (session->device) {
+ adapter = device_get_adapter(session->device);
+ bdaddr = device_get_address(session->device);
+ if (adapter && bdaddr)
+ device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
+ if (!device)
+ error("device is NOT found");
+ }
#endif
connection_lost(session, ETIMEDOUT);
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session, seid->seid);
+ sep = find_local_sep_by_seid(session,
+ req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session, seid->seid);
+ sep = find_local_sep_by_seid(session,
+ req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
struct seid_req req;
int ret;
- if (!stream && session->discover) {
- /* Don't call cb since it being aborted */
- session->discover->cb = NULL;
- finalize_discovery(session, -ECANCELED);
- return -EALREADY;
- }
-
if (!g_slist_find(session->streams, stream))
return -EINVAL;
#define AVRCP_REQUEST_CONTINUING 0x40
#define AVRCP_ABORT_CONTINUING 0x41
#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
-#define AVRCP_SET_ADDRESSED_PLAYER 0x60
#define AVRCP_SET_BROWSED_PLAYER 0x70
#define AVRCP_GET_FOLDER_ITEMS 0x71
#define AVRCP_CHANGE_PATH 0x72
#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
#define AVRCP_PLAY_ITEM 0x74
-#define AVRCP_GET_TOTAL_NUMBER_OF_ITEMS 0x75
#define AVRCP_SEARCH 0x80
#define AVRCP_ADD_TO_NOW_PLAYING 0x90
#define AVRCP_GENERAL_REJECT 0xA0
#define AVRCP_CHARSET_UTF8 106
#define AVRCP_BROWSING_TIMEOUT 1
-#define AVRCP_CT_VERSION 0x0106
-#define AVRCP_TG_VERSION 0x0105
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint64_t uid;
uint16_t uid_counter;
bool browsed;
- bool addressed;
uint8_t *features;
char *path;
- guint changed_id;
struct pending_list_items *p;
char *change_path;
unsigned int pos_timer_id = 0;
#endif
+#ifdef SUPPORT_AVRCP_CONTROL
static sdp_record_t *avrcp_ct_record(void)
{
sdp_list_t *svclass_id, *pfseq, *apseq, *apseq1, *root;
feat = feat | AVRCP_FEATURE_CATEGORY_2;
#endif
#else
- uint16_t avctp_ver = 0x0103;
+ uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = AVRCP_CT_VERSION;
+ profile[0].version = avrcp_ver;
#ifdef __TIZEN_PATCH__
adapter_avrcp_ct_ver = avrcp_ver;
#endif
return record;
}
+#endif
+#ifdef SUPPORT_AVRCP_TARGET
static sdp_record_t *avrcp_tg_record(void)
{
sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing;
feat = feat | AVRCP_FEATURE_CATEGORY_2;
#endif
#else
- uint16_t avctp_ver = 0x0103;
+ uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = AVRCP_TG_VERSION;
+ profile[0].version = avrcp_ver;
#ifdef __TIZEN_PATCH__
adapter_avrcp_tg_ver = avrcp_ver;
#endif
return record;
}
+#endif
static unsigned int attr_get_max_val(uint8_t attr)
{
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
struct avrcp_header *pdu = (void *) buf;
- uint8_t code;
uint16_t size;
GSList *l;
int attr;
set_company_id(pdu->company_id, IEEEID_BTSIG);
pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+ pdu->params[0] = id;
DBG("id=%u", id);
- if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) {
- code = AVC_CTYPE_REJECTED;
- size = 1;
- pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED;
- goto done;
- }
-
- code = AVC_CTYPE_CHANGED;
- pdu->params[0] = id;
-
switch (id) {
case AVRCP_EVENT_STATUS_CHANGED:
size = 2;
memcpy(&pdu->params[1], position_val, sizeof(uint32_t));
break;
#endif
- case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- size = 5;
- memcpy(&pdu->params[1], &player->id, sizeof(uint16_t));
- memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t));
- break;
- case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
- size = 1;
- break;
default:
error("Unknown event %u", id);
return;
}
-done:
pdu->params_len = htons(size);
#ifdef __TIZEN_PATCH__
if (id == AVRCP_EVENT_PLAYBACK_POS_CHANGED &&
pos_timer_id > 0) {
- /* Remove the timer function which was added for register
- * notification. As we are sending changed event eariler
- * then time interval.
+ /* Remove the timer function which was added for register notification.
+ * As we are sending changed event eariler then time interval.
*/
DBG("Removing the timer function added by register notification");
g_source_remove(pos_timer_id);
err = avctp_send_vendordep(session->conn,
session->transaction_events[id],
- code, AVC_SUBUNIT_PANEL,
+ AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
if (err < 0)
continue;
return play_status_to_val(value);
}
-static uint16_t player_get_id(struct avrcp_player *player)
-{
- if (player == NULL)
- return 0x0000;
-
- return player->id;
-}
-
-static uint16_t player_get_uid_counter(struct avrcp_player *player)
-{
- if (player == NULL)
- return 0x0000;
-
- return player->uid_counter;
-}
-
static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
pdu->params[len++] = val;
}
- g_list_free(settings);
-
break;
case AVRCP_EVENT_VOLUME_CHANGED:
pdu->params[1] = media_transport_get_device_volume(dev);
case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
len = 5;
- /* time interval in seconds at which the change in
- * playback position shall be notified.
- */
+ /* time interval in seconds at which the change in playback position
+ shall be notified */
memcpy(&playback_interval, &pdu->params[1], sizeof(uint32_t));
playback_interval = ((playback_interval>>24)&0xff) |
((playback_interval<<8)&0xff0000) |
play_status = player_get_status(player);
if (play_status != AVRCP_PLAY_STATUS_PLAYING) {
- DBG("Play Pos Changed Event is skipped(%d)",
- play_status);
+ DBG("Play Pos Changed Event is skipped(%d)", play_status);
} else {
DBG("Playback interval : %d secs", playback_interval);
pos_timer_id = g_timeout_add_seconds(
/* retrieve current playback position for interim response */
playback_position = player_get_playback_position(player);
playback_position = (playback_position & 0x000000ff) << 24 |
- (playback_position & 0x0000ff00) << 8 |
- (playback_position & 0x00ff0000) >> 8 |
- (playback_position & 0xff000000) >> 24;
+ (playback_position & 0x0000ff00) << 8 |
+ (playback_position & 0x00ff0000) >> 8 |
+ (playback_position & 0xff000000) >> 24;
memcpy(&pdu->params[1], &playback_position, sizeof(uint32_t));
break;
#endif
- case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- len = 5;
- memcpy(&pdu->params[1], player_get_id(player),
- sizeof(uint16_t));
- memcpy(&pdu->params[3], player_get_uid_counter(player),
- sizeof(uint16_t));
- break;
- case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
- len = 1;
- break;
+
default:
/* All other events are not supported yet */
goto err;
struct avrcp_header *pdu,
uint8_t transaction)
{
+#ifndef __TIZEN_PATCH__
+ struct avrcp_player *player = session->controller->player;
+#else
+ struct avrcp_player *player = target_get_player(session);
+#endif
uint16_t len = ntohs(pdu->params_len);
uint8_t volume;
if (len != 1)
goto err;
+ if (!player)
+ goto err;
+
volume = pdu->params[0] & 0x7F;
media_transport_update_device_volume(session->dev, volume);
return AVC_CTYPE_REJECTED;
}
-static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id)
-{
- struct avrcp_server *server = session->server;
- GSList *l;
-
- for (l = server->players; l; l = l->next) {
- struct avrcp_player *player = l->data;
-
- if (player->id == id)
- return player;
- }
-
- return NULL;
-}
-
-static gboolean notify_addressed_player_changed(gpointer user_data)
-{
- struct avrcp_player *player = user_data;
- uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED,
- AVRCP_EVENT_TRACK_CHANGED,
- AVRCP_EVENT_TRACK_REACHED_START,
- AVRCP_EVENT_TRACK_REACHED_END,
- AVRCP_EVENT_SETTINGS_CHANGED,
- AVRCP_EVENT_PLAYBACK_POS_CHANGED
- };
- uint8_t i;
-
- avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL);
-
- /*
- * TG shall complete all player specific
- * notifications with AV/C C-Type REJECTED
- * with error code as Addressed Player Changed.
- */
- for (i = 0; i < sizeof(events); i++)
- avrcp_player_event(player, events[i], NULL);
-
- player->changed_id = 0;
-
- return FALSE;
-}
-
-static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
-{
- struct avrcp_player *player;
- uint16_t len = ntohs(pdu->params_len);
- uint16_t player_id = 0;
- uint8_t status;
-
- if (len < 1) {
- status = AVRCP_STATUS_INVALID_PARAM;
- goto err;
- }
-
- player_id = bt_get_be16(&pdu->params[0]);
- player = find_tg_player(session, player_id);
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
-
- if (player) {
- player->addressed = true;
- status = AVRCP_STATUS_SUCCESS;
- pdu->params_len = htons(len);
- pdu->params[0] = status;
- } else {
- status = AVRCP_STATUS_INVALID_PLAYER_ID;
- goto err;
- }
-
- /* Don't emit player changed immediately since PTS expect the
- * response of SetAddressedPlayer before the event.
- */
- player->changed_id = g_idle_add(notify_addressed_player_changed,
- player);
-
- return AVC_CTYPE_ACCEPTED;
-
-err:
- pdu->params_len = htons(sizeof(status));
- pdu->params[0] = status;
- return AVC_CTYPE_REJECTED;
-}
-
static const struct control_pdu_handler control_handlers[] = {
{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
avrcp_handle_get_capabilities },
avrcp_handle_request_continuing },
{ AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL,
avrcp_handle_abort_continuing },
- { AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL,
- avrcp_handle_set_addressed_player },
{ },
};
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
AVC_SUBUNIT_PANEL, buf, length,
NULL, session);
}
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
AVC_SUBUNIT_PANEL, buf, length,
NULL, session);
}
return 0;
}
-static gboolean avrcp_get_total_numberofitems_rsp(struct avctp *conn,
- uint8_t *operands, size_t operand_count,
- void *user_data)
-{
- struct avrcp_browsing_header *pdu = (void *) operands;
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- uint32_t num_of_items;
-
- if (pdu == NULL)
- return -ETIMEDOUT;
-
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7)
- return -EINVAL;
-
- if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS)
- goto done;
-
- player->uid_counter = get_be16(&pdu->params[1]);
- num_of_items = get_be32(&pdu->params[3]);
-
- if (!num_of_items)
- return -EINVAL;
-
-done:
- media_player_total_items_complete(mp, num_of_items);
- return FALSE;
-}
-
-static void avrcp_get_total_numberofitems(struct avrcp *session)
-{
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 7];
- struct avrcp_player *player = session->controller->player;
- struct avrcp_browsing_header *pdu = (void *) buf;
-
- memset(buf, 0, sizeof(buf));
-
- pdu->pdu_id = AVRCP_GET_TOTAL_NUMBER_OF_ITEMS;
- pdu->param_len = htons(7 + sizeof(uint32_t));
-
- pdu->params[0] = player->scope;
-
- avctp_send_browsing_req(session->conn, buf, sizeof(buf),
- avrcp_get_total_numberofitems_rsp, session);
-}
-
-static int ct_get_total_numberofitems(struct media_player *mp, const char *name,
- void *user_data)
-{
- struct avrcp_player *player = user_data;
- struct avrcp *session;
-
- session = player->sessions->data;
-
- if (session->controller->version != 0x0106) {
- error("version not supported");
- return -1;
- }
-
- if (g_str_has_prefix(name, "/NowPlaying"))
- player->scope = 0x03;
- else if (g_str_has_suffix(name, "/search"))
- player->scope = 0x02;
- else
- player->scope = 0x01;
-
- avrcp_get_total_numberofitems(session);
-
- return 0;
-}
-
static const struct media_player_callback ct_cbs = {
.set_setting = ct_set_setting,
.play = ct_play,
.search = ct_search,
.play_item = ct_play_item,
.add_to_nowplaying = ct_add_to_nowplaying,
- .total_items = ct_get_total_numberofitems,
};
static struct avrcp_player *create_ct_player(struct avrcp *session,
avrcp_stop_position_timer();
#endif
- if (player->changed_id > 0)
- g_source_remove(player->changed_id);
-
g_slist_free(player->sessions);
g_free(player->path);
g_free(player->change_path);
for (l = player->sessions; l; l = l->next) {
struct avrcp *session = l->data;
- struct avrcp_data *controller = session->controller;
-
- controller->players = g_slist_remove(controller->players,
- player);
- /* Check if current player is being removed */
- if (controller->player == player)
- controller->player = g_slist_nth_data(
- controller->players, 0);
+ session->controller->players = g_slist_remove(
+ session->controller->players,
+ player);
}
player_destroy(player);
i += len;
}
+ if (g_slist_find(removed, session->controller->player))
+ session->controller->player = NULL;
+
g_slist_free_full(removed, player_remove);
return FALSE;
case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
case AVRCP_EVENT_UIDS_CHANGED:
case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+ /* These events above are controller specific */
+ if (!session->controller)
+ break;
case AVRCP_EVENT_VOLUME_CHANGED:
#endif
avrcp_register_notification(session, event);
}
}
+ if (!session->controller)
+ return FALSE;
+
if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
avrcp_list_player_attributes(session);
session);
}
+#ifdef SUPPORT_AVRCP_TARGET
static void target_init(struct avrcp *session)
{
struct avrcp_server *server = session->server;
DBG("%p version 0x%04x", target, target->version);
service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
- if (service != NULL)
- btd_service_connecting_complete(service, 0);
+ btd_service_connecting_complete(service, 0);
player = g_slist_nth_data(server->players, 0);
if (player != NULL) {
if (target->version < 0x0104)
return;
- session->supported_events |=
- (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
- (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
- (1 << AVRCP_EVENT_VOLUME_CHANGED);
-
#ifdef __TIZEN_PATCH__
if (adapter_avrcp_tg_ver < 0x0104)
return;
avrcp_connect_browsing(session);
}
+#endif
+#ifdef SUPPORT_AVRCP_CONTROL
static void controller_init(struct avrcp *session)
{
struct avrcp_player *player;
#endif
service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
- if (service != NULL)
- btd_service_connecting_complete(service, 0);
+ btd_service_connecting_complete(service, 0);
/* Only create player if category 1 is supported */
if (!(controller->features & AVRCP_FEATURE_CATEGORY_1))
avrcp_connect_browsing(session);
}
+#endif
static void session_init_control(struct avrcp *session)
{
handle_vendordep_pdu,
session);
session->control_handlers = control_handlers;
-
+#ifdef SUPPORT_AVRCP_CONTROL
if (btd_device_get_service(session->dev, AVRCP_TARGET_UUID) != NULL)
controller_init(session);
+#endif
+#ifdef SUPPORT_AVRCP_TARGET
if (btd_device_get_service(session->dev, AVRCP_REMOTE_UUID) != NULL)
target_init(session);
+#endif
}
static void controller_destroy(struct avrcp *session)
{
struct avrcp_data *controller = session->controller;
- struct btd_service *service;
DBG("%p", controller);
g_slist_free_full(controller->players, player_destroy);
- service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
-
- if (session->control_id == 0)
- btd_service_connecting_complete(service, -EIO);
- else
- btd_service_disconnecting_complete(service, 0);
-
g_free(controller);
}
{
struct avrcp_data *target = session->target;
struct avrcp_player *player = target->player;
- struct btd_service *service;
DBG("%p", target);
if (player != NULL)
player->sessions = g_slist_remove(player->sessions, session);
- service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
-
- if (session->control_id == 0)
- btd_service_connecting_complete(service, -EIO);
- else
- btd_service_disconnecting_complete(service, 0);
-
g_free(target);
}
-static void session_destroy(struct avrcp *session)
+static void session_destroy(struct avrcp *session, int err)
{
struct avrcp_server *server = session->server;
+ struct btd_service *service;
server->sessions = g_slist_remove(server->sessions, session);
session_abort_pending_pdu(session);
+ service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+ if (service != NULL) {
+ if (session->control_id == 0)
+ btd_service_connecting_complete(service, err);
+ else
+ btd_service_disconnecting_complete(service, 0);
+ }
+
+ service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
+ if (service != NULL) {
+ if (session->control_id == 0)
+ btd_service_connecting_complete(service, err);
+ else
+ btd_service_disconnecting_complete(service, 0);
+ }
+
if (session->browsing_timer > 0)
g_source_remove(session->browsing_timer);
}
static void state_changed(struct btd_device *device, avctp_state_t old_state,
- avctp_state_t new_state, void *user_data)
+ avctp_state_t new_state, int err,
+ void *user_data)
{
struct avrcp_server *server;
struct avrcp *session;
if (session == NULL)
break;
- session_destroy(session);
+ session_destroy(session, err);
break;
case AVCTP_STATE_CONNECTING:
struct avrcp_server *server;
struct avrcp_player *player;
GSList *l;
- static uint16_t id = 0;
server = find_server(servers, adapter);
if (!server)
return NULL;
player = g_new0(struct avrcp_player, 1);
- player->id = ++id;
player->server = server;
player->cb = cb;
player->user_data = user_data;
}
}
- avrcp_player_event(player,
- AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
-
return player;
}
target->player = g_slist_nth_data(server->players, 0);
}
- avrcp_player_event(player,
- AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
-
player_destroy(player);
}
return FALSE;
}
-static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
-{
- uint8_t buf[AVRCP_HEADER_LENGTH + 2];
- struct avrcp_header *pdu = (void *) buf;
- uint8_t code;
- uint16_t size;
- int err;
-
- /* Verify that the event is registered */
- if (!(session->registered_events & (1 << id)))
- return -ENOENT;
-
- memset(buf, 0, sizeof(buf));
-
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
- code = AVC_CTYPE_CHANGED;
- pdu->params[0] = id;
-
- DBG("id=%u", id);
-
- switch (id) {
- case AVRCP_EVENT_VOLUME_CHANGED:
- size = 2;
- memcpy(&pdu->params[1], data, sizeof(uint8_t));
- break;
- default:
- error("Unknown event %u", id);
- return -EINVAL;
- }
-
- pdu->params_len = htons(size);
-
- err = avctp_send_vendordep(session->conn,
- session->transaction_events[id],
- code, AVC_SUBUNIT_PANEL,
- buf, size + AVRCP_HEADER_LENGTH);
- if (err < 0)
- return err;
-
- /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- session->registered_events ^= 1 << id;
-
- return err;
-}
-
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify)
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume)
{
struct avrcp_server *server;
struct avrcp *session;
return -EINVAL;
session = find_session(server->sessions, dev);
- if (session == NULL)
+ if (session == NULL || session->target == NULL)
return -ENOTCONN;
- if (notify) {
- if (!session->target)
- return -ENOTSUP;
- return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
- &volume);
- }
-
- if (!session->controller || session->controller->version < 0x0104)
+ if (session->target->version < 0x0104)
return -ENOTSUP;
memset(buf, 0, sizeof(buf));
set_company_id(pdu->company_id, IEEEID_BTSIG);
+ DBG("volume=%u", volume);
+
pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
pdu->params[0] = volume;
pdu->params_len = htons(1);
control_unregister(service);
}
+#ifdef SUPPORT_AVRCP_TARGET
static void avrcp_target_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
if (server->ct_record_id == 0)
avrcp_server_unregister(server);
}
+#endif
+#ifdef SUPPORT_AVRCP_TARGET
static int avrcp_target_server_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
return 0;
}
+#endif
static struct btd_profile avrcp_target_profile = {
.name = "audio-avrcp-target",
.connect = avrcp_connect,
.disconnect = avrcp_disconnect,
-
+#ifdef SUPPORT_AVRCP_TARGET
.adapter_probe = avrcp_target_server_probe,
.adapter_remove = avrcp_target_server_remove,
+#endif
};
static int avrcp_controller_probe(struct btd_service *service)
control_unregister(service);
}
+#ifdef SUPPORT_AVRCP_CONTROL
static void avrcp_controller_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
if (server->tg_record_id == 0)
avrcp_server_unregister(server);
}
+#endif
+#ifdef SUPPORT_AVRCP_CONTROL
static int avrcp_controller_server_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
return 0;
}
+#endif
static struct btd_profile avrcp_controller_profile = {
.name = "avrcp-controller",
.connect = avrcp_connect,
.disconnect = avrcp_disconnect,
+#ifdef SUPPORT_AVRCP_CONTROL
.adapter_probe = avrcp_controller_server_probe,
.adapter_remove = avrcp_controller_server_remove,
+#endif
};
static int avrcp_init(void)
{
-#ifdef __TIZEN_PATCH__
-#ifdef SUPPORT_AVRCP_CONTROL
- btd_profile_register(&avrcp_controller_profile);
-#else
- btd_profile_register(&avrcp_target_profile);
-#endif
-#else
btd_profile_register(&avrcp_controller_profile);
btd_profile_register(&avrcp_target_profile);
-#endif
+
return 0;
}
static void avrcp_exit(void)
{
-#ifdef __TIZEN_PATCH__
-#ifdef SUPPORT_AVRCP_CONTROL
- btd_profile_unregister(&avrcp_controller_profile);
-#else
- btd_profile_unregister(&avrcp_target_profile);
-#endif
-#else
btd_profile_unregister(&avrcp_controller_profile);
btd_profile_unregister(&avrcp_target_profile);
-#endif
}
BLUETOOTH_PLUGIN_DEFINE(avrcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
bool (*previous) (void *user_data);
};
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify);
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume);
struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
struct avrcp_player_cb *cb,
};
static void state_changed(struct btd_device *dev, avctp_state_t old_state,
- avctp_state_t new_state, void *user_data)
+ avctp_state_t new_state, int err,
+ void *user_data)
{
struct control *control = user_data;
DBusConnection *conn = btd_get_dbus_connection();
return 0;
}
-
-gboolean control_is_active(struct btd_service *service)
-{
- struct control *control = btd_service_get_user_data(service);
-
- if (control && control->session)
- return TRUE;
-
- return FALSE;
-}
int control_init_target(struct btd_service *service);
int control_init_remote(struct btd_service *service);
void control_unregister(struct btd_service *service);
-gboolean control_is_active(struct btd_service *service);
int control_connect(struct btd_service *service);
int control_disconnect(struct btd_service *service);
#include "sink.h"
#endif
-
#define MEDIA_INTERFACE "org.bluez.Media1"
#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
#define MEDIA_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
#define SINK_SUSPEND_TIMEOUT 4 /* 4 seconds */
unsigned int suspend_timer_id = 0;
+static gboolean a2dp_sink_support = false;
+static gboolean a2dp_source_support = true;
#endif
struct media_adapter {
void *user_data)
{
struct a2dp_config_data *data = user_data;
- gboolean *ret_value = ret;
- data->cb(data->setup, *ret_value ? TRUE : FALSE);
+ data->cb(data->setup, ret ? TRUE : FALSE);
}
static int set_config(struct a2dp_sep *sep, uint8_t *configuration,
if (parse_properties(&props, &uuid, &delay_reporting, &codec,
&capabilities, &size) < 0)
return btd_error_invalid_args(msg);
+#ifdef __TIZEN_PATCH__
+ if(!a2dp_sink_support && !g_strcmp0(uuid, A2DP_SINK_UUID)) {
+ error("A2DP sink role is not supported.");
+ return btd_error_not_supported(msg);
+ }
+ if(!a2dp_source_support && !g_strcmp0(uuid, A2DP_SOURCE_UUID)) {
+ error("A2DP source role is not supported.");
+ return btd_error_not_supported(msg);
+ }
+#endif
if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
codec, capabilities, size, &err) == NULL) {
if (err == -EPROTONOSUPPORT)
folder->msg = NULL;
}
-void media_player_total_items_complete(struct media_player *mp,
- uint32_t num_of_items)
-{
- struct media_folder *folder = mp->scope;
-
- if (folder == NULL || folder->msg == NULL)
- return;
-
- if (folder->number_of_items != num_of_items) {
- folder->number_of_items = num_of_items;
-
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_FOLDER_INTERFACE,
- "NumberOfItems");
- }
-}
-
static const GDBusMethodTable media_player_methods[] = {
{ GDBUS_METHOD("Play", NULL, NULL, media_player_play) },
{ GDBUS_METHOD("Pause", NULL, NULL, media_player_pause) },
static void media_player_change_scope(struct media_player *mp,
struct media_folder *folder)
{
- struct player_callback *cb = mp->cb;
- int err;
-
if (mp->scope == folder)
return;
done:
mp->scope = folder;
- if (cb->cbs->total_items) {
- err = cb->cbs->total_items(mp, folder->item->name,
- cb->user_data);
- if (err < 0)
- DBG("Failed to get total num of items");
- } else {
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_FOLDER_INTERFACE,
- "NumberOfItems");
- }
-
g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
MEDIA_FOLDER_INTERFACE, "Name");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+ MEDIA_FOLDER_INTERFACE, "NumberOfItems");
}
static struct media_folder *find_folder(GSList *folders, const char *pattern)
uint64_t uid, void *user_data);
int (*add_to_nowplaying) (struct media_player *mp, const char *name,
uint64_t uid, void *user_data);
- int (*total_items) (struct media_player *mp, const char *name,
- void *user_data);
};
struct media_player *media_player_controller_create(const char *path,
void media_player_change_folder_complete(struct media_player *player,
const char *path, int ret);
void media_player_search_complete(struct media_player *mp, int ret);
-void media_player_total_items_complete(struct media_player *mp,
- uint32_t num_of_items);
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
struct sink *sink = user_data;
int id, perr;
- sink->connect_id = 0;
-
if (err) {
avdtp_unref(sink->session);
sink->session = NULL;
if (!sink->session)
return FALSE;
- sink->connect_id = a2dp_discover(sink->session, discovery_complete,
- sink);
- if (sink->connect_id == 0)
+ if (avdtp_discover(sink->session, discovery_complete, sink) < 0)
return FALSE;
return TRUE;
if (sink->connect_id > 0) {
a2dp_cancel(sink->connect_id);
sink->connect_id = 0;
- btd_service_connecting_complete(sink->service, 0);
+ btd_service_connecting_complete(sink->service, -ECANCELED);
avdtp_unref(sink->session);
sink->session = NULL;
struct source *source = user_data;
int id, perr;
- source->connect_id = 0;
-
if (err) {
avdtp_unref(source->session);
source->session = NULL;
if (!source->session)
return FALSE;
- source->connect_id = a2dp_discover(source->session, discovery_complete,
- source);
- if (source->connect_id == 0)
+ if (avdtp_discover(source->session, discovery_complete, source) < 0)
return FALSE;
return TRUE;
if (source->connect_id > 0) {
a2dp_cancel(source->connect_id);
source->connect_id = 0;
- btd_service_connecting_complete(source->service, 0);
+ btd_service_connecting_complete(source->service, -ECANCELED);
avdtp_unref(source->session);
source->session = NULL;
struct media_transport *transport = data;
struct a2dp_transport *a2dp = transport->data;
uint16_t volume;
- bool notify;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
g_dbus_pending_property_error(id,
return;
}
- g_dbus_pending_property_success(id);
-
- if (a2dp->volume == volume)
- return;
+ if (a2dp->volume != volume)
+ avrcp_set_volume(transport->device, volume);
a2dp->volume = volume;
- notify = transport->source_watch ? true : false;
- if (notify)
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- transport->path,
- MEDIA_TRANSPORT_INTERFACE,
- "Volume");
-
- avrcp_set_volume(transport->device, volume, notify);
+ g_dbus_pending_property_success(id);
}
static const GDBusMethodTable transport_methods[] = {
transport->destroy = destroy_a2dp;
a2dp->volume = 127;
+ avrcp_set_volume(transport->device, a2dp->volume);
transport->source_watch = source_add_state_cb(service,
source_state_changed,
transport);
DBG("Failed to send request to read appearance");
}
+#ifdef __TIZEN_PATCH__
+static void read_rpa_res_characteristic_value_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct gas *gas = user_data;
+ uint8_t rpa_res_support;
+
+ if (!success) {
+ DBG("Reading RPA Resolution Char Value failed with ATT error: %u", att_ecode);
+ return;
+ }
+
+ /* The RPA Resolution Char Value value is a 8-bit unsigned integer */
+ if (length != 1) {
+ DBG("Malformed RPA resolution char value");
+ return;
+ }
+
+ rpa_res_support = *value;
+
+ DBG("GAP RPA Resolution Char Value: %d", rpa_res_support);
+
+ device_set_rpa_res_char_value(gas->device, rpa_res_support);
+}
+
+static void handle_rpa_res_characteristic_value(struct gas *gas, uint16_t value_handle)
+{
+ if (!bt_gatt_client_read_value(gas->client, value_handle,
+ read_rpa_res_characteristic_value_cb, gas, NULL))
+ DBG("Failed to send request to read RPA resolution Char Value");
+}
+#endif
+
static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid)
{
bt_uuid_t lhs;
handle_device_name(gas, value_handle);
else if (uuid_cmp(GATT_CHARAC_APPEARANCE, &uuid))
handle_appearance(gas, value_handle);
+#ifdef __TIZEN_PATCH__
+ else if (uuid_cmp(GATT_CHARAC_CENTRAL_RPA_RESOLUTION, &uuid))
+ handle_rpa_res_characteristic_value(gas, value_handle);
+#endif
else {
char uuid_str[MAX_LEN_UUID_STR];
static unsigned long bnepgetconninfo;
#endif
-static struct {
- const char *name; /* Friendly name */
- const char *uuid128; /* UUID 128 */
- uint16_t id; /* Service class identifier */
-} __svc[] = {
- { "panu", PANU_UUID, BNEP_SVC_PANU },
- { "gn", GN_UUID, BNEP_SVC_GN },
- { "nap", NAP_UUID, BNEP_SVC_NAP },
- { NULL }
-};
-
struct __service_16 {
uint16_t dst;
uint16_t src;
void *disconn_data;
};
-const char *bnep_uuid(uint16_t id)
-{
- int i;
-
- for (i = 0; __svc[i].uuid128; i++)
- if (__svc[i].id == id)
- return __svc[i].uuid128;
- return NULL;
-}
-
-const char *bnep_name(uint16_t id)
-{
- int i;
-
- for (i = 0; __svc[i].name; i++)
- if (__svc[i].id == id)
- return __svc[i].name;
- return NULL;
-}
-
int bnep_init(void)
{
ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
req.sock = sk;
req.role = role;
+#ifdef __TIZEN_PATCH__
+ req.flags = (1 << BNEP_SETUP_RESPONSE);
+#endif
if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
int err = -errno;
error("bnep: Failed to add device %s: %s(%d)",
return 0;
}
+#ifdef __TIZEN_PATCH__
+static uint32_t bnep_getsuppfeat(void)
+{
+ uint32_t feat;
+
+ if (ioctl(ctl, BNEPGETSUPPFEAT, &feat) < 0)
+ feat = 0;
+
+ DBG("supported features: 0x%x", feat);
+
+ return feat;
+}
+#endif
+
static int bnep_if_up(const char *devname)
{
struct ifreq ifr;
g_free(session);
}
-int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb, void *data)
+int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb,
+ bnep_disconnect_cb disconn_cb,
+ void *conn_data, void *disconn_data)
{
GError *gerr = NULL;
int err;
- if (!session || !conn_cb)
+ if (!session || !conn_cb || !disconn_cb)
return -EINVAL;
session->attempts = 0;
session->conn_cb = conn_cb;
- session->conn_data = data;
+ session->disconn_cb = disconn_cb;
+ session->conn_data = conn_data;
+ session->disconn_data = disconn_data;
bt_io_get(session->io, &gerr, BT_IO_OPT_DEST_BDADDR, &session->dst_addr,
BT_IO_OPT_INVALID);
bnep_conndel(&session->dst_addr);
}
-void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb,
- void *data)
-{
- if (!session || !disconn_cb)
- return;
-
- if (!session->disconn_cb && !session->disconn_data) {
- session->disconn_cb = disconn_cb;
- session->disconn_data = data;
- }
-}
-
#ifndef __TIZEN_PATCH__
static int bnep_add_to_bridge(const char *devname, const char *bridge)
{
return err;
}
-int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface,
- const bdaddr_t *addr)
+static ssize_t bnep_send_ctrl_rsp(int sk, uint8_t ctrl, uint16_t resp)
{
- int err;
+ ssize_t sent;
- if (!bridge || !iface || !addr)
- return -EINVAL;
+ switch (ctrl) {
+ case BNEP_CMD_NOT_UNDERSTOOD: {
+ struct bnep_ctrl_cmd_not_understood_cmd rsp;
- err = bnep_connadd(sk, dst, iface);
- if (err < 0)
- return err;
+ rsp.type = BNEP_CONTROL;
+ rsp.ctrl = ctrl;
+ rsp.unkn_ctrl = (uint8_t) resp;
-#ifndef __TIZEN_PATCH__
- err = bnep_add_to_bridge(iface, bridge);
- if (err < 0) {
- bnep_conndel(addr);
- return err;
+ sent = send(sk, &rsp, sizeof(rsp), 0);
+ break;
}
-#endif
-
- return bnep_if_up(iface);
-}
-
-void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr)
-{
- if (!bridge || !iface || !addr)
- return;
+ case BNEP_FILTER_MULT_ADDR_RSP:
+ case BNEP_FILTER_NET_TYPE_RSP:
+ case BNEP_SETUP_CONN_RSP: {
+ struct bnep_control_rsp rsp;
- bnep_del_from_bridge(iface, bridge);
- bnep_if_down(iface);
- bnep_conndel(addr);
-}
+ rsp.type = BNEP_CONTROL;
+ rsp.ctrl = ctrl;
+ rsp.resp = htons(resp);
-ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp)
-{
- struct bnep_control_rsp rsp;
-
- rsp.type = type;
- rsp.ctrl = ctrl;
- rsp.resp = htons(resp);
+ sent = send(sk, &rsp, sizeof(rsp), 0);
+ break;
+ }
+ default:
+ error("bnep: wrong response type");
+ sent = -1;
+ break;
+ }
- return send(sk, &rsp, sizeof(rsp), 0);
+ return sent;
}
-uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
- uint16_t *src)
+static uint16_t bnep_setup_decode(int sk, struct bnep_setup_conn_req *req,
+ uint16_t *dst)
{
const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+ uint16_t src;
uint8_t *dest, *source;
uint32_t val;
+#ifdef __TIZEN_PATCH__
+ if (((req->type != BNEP_CONTROL) &&
+ (req->type != (BNEP_CONTROL | BNEP_EXT_HEADER))) ||
+ req->ctrl != BNEP_SETUP_CONN_REQ)
+#else
+ if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+#endif
+ return BNEP_CONN_NOT_ALLOWED;
+
dest = req->service;
source = req->service + req->uuid_size;
switch (req->uuid_size) {
case 2: /* UUID16 */
*dst = get_be16(dest);
- *src = get_be16(source);
+ src = get_be16(source);
break;
case 16: /* UUID128 */
/* Check that the bytes in the UUID, except the service ID
if (val > 0xffff)
return BNEP_CONN_INVALID_SRC;
- *src = val;
+ src = val;
break;
default:
return BNEP_CONN_INVALID_SVC;
switch (*dst) {
case BNEP_SVC_NAP:
case BNEP_SVC_GN:
- if (*src == BNEP_SVC_PANU)
+ if (src == BNEP_SVC_PANU)
return BNEP_SUCCESS;
return BNEP_CONN_INVALID_SRC;
case BNEP_SVC_PANU:
- if (*src == BNEP_SVC_PANU || *src == BNEP_SVC_GN ||
- *src == BNEP_SVC_NAP)
+ if (src == BNEP_SVC_PANU || src == BNEP_SVC_GN ||
+ src == BNEP_SVC_NAP)
return BNEP_SUCCESS;
return BNEP_CONN_INVALID_SRC;
}
#endif
+#ifdef __TIZEN_PATCH__
+static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge,
+ char *iface, const bdaddr_t *addr,
+ uint8_t *setup_data, int len)
+{
+ int err, n;
+ uint16_t rsp;
+
+ n = read(sk, setup_data, len);
+ if (n != len) {
+ err = -EIO;
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ err = bnep_connadd(sk, dst, iface);
+ if (err < 0) {
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ err = bnep_if_up(iface);
+ if (err < 0) {
+ bnep_del_from_bridge(iface, bridge);
+ bnep_conndel(addr);
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ rsp = BNEP_SUCCESS;
+
+reply:
+ if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) {
+ err = -errno;
+ error("bnep: send ctrl rsp error: %s (%d)", strerror(errno),
+ errno);
+ }
+
+ return err;
+}
+#endif
+
+int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
+ uint8_t *setup_data, int len)
+{
+ int err;
+#ifdef __TIZEN_PATCH__
+ uint32_t feat;
+#endif
+ uint16_t rsp, dst;
+ struct bnep_setup_conn_req *req = (void *) setup_data;
+
+ /* Highest known Control command ID
+ * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+ if (req->type == BNEP_CONTROL &&
+ req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+ error("bnep: cmd not understood");
+ err = bnep_send_ctrl_rsp(sk, BNEP_CMD_NOT_UNDERSTOOD,
+ req->ctrl);
+ if (err < 0)
+ error("send not understood ctrl rsp error: %s (%d)",
+ strerror(errno), errno);
+
+ return err;
+ }
+
+ /* Processing BNEP_SETUP_CONNECTION_REQUEST_MSG */
+ rsp = bnep_setup_decode(sk, req, &dst);
+ if (rsp != BNEP_SUCCESS) {
+ err = -rsp;
+ error("bnep: error while decoding setup connection request: %d",
+ rsp);
+#ifdef __TIZEN_PATCH__
+ goto failed;
+#else
+ goto reply;
+#endif
+ }
+
+#ifdef __TIZEN_PATCH__
+ feat = bnep_getsuppfeat();
+
+ /*
+ * Take out setup data if kernel doesn't support handling it, especially
+ * setup request. If kernel would have set session flags, they should
+ * be checked and handled respectively.
+ */
+ if (!feat || !(feat & (1 << BNEP_SETUP_RESPONSE)))
+ return bnep_server_add_legacy(sk, dst, bridge, iface, addr,
+ setup_data, len);
+#endif
+
+ err = bnep_connadd(sk, dst, iface);
+ if (err < 0) {
+ rsp = BNEP_CONN_NOT_ALLOWED;
+#ifdef __TIZEN_PATCH__
+ goto failed;
+#else
+ goto reply;
+#endif
+ }
+
+#ifndef __TIZEN_PATCH__
+ err = bnep_add_to_bridge(iface, bridge);
+ if (err < 0)
+ goto failed_conn;
+#endif
+
+ err = bnep_if_up(iface);
+ if (err < 0)
+#ifdef __TIZEN_PATCH__
+ goto failed_bridge;
+#else
+ rsp = BNEP_CONN_NOT_ALLOWED;
+#endif
+
+#ifdef __TIZEN_PATCH__
+ return 0;
+
+failed_bridge:
+ bnep_del_from_bridge(iface, bridge);
+
+failed_conn:
+ bnep_conndel(addr);
+
+ return err;
+
+failed:
+ if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) {
+ err = -errno;
+ error("bnep: send ctrl rsp error: %s (%d)", strerror(-err),
+ -err);
+ }
+#else
+reply:
+ if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) {
+ err = -errno;
+ error("bnep: send ctrl rsp error: %s (%d)", strerror(errno),
+ errno);
+ }
+#endif
+
+ return err;
+}
+
+void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr)
+{
+ if (!bridge || !iface || !addr)
+ return;
+
+ bnep_del_from_bridge(iface, bridge);
+ bnep_if_down(iface);
+ bnep_conndel(addr);
+}
int bnep_init(void);
int bnep_cleanup(void);
-const char *bnep_uuid(uint16_t id);
-const char *bnep_name(uint16_t id);
-
struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role,
char *iface);
void bnep_free(struct bnep *session);
typedef void (*bnep_connect_cb) (char *iface, int err, void *data);
-int bnep_connect(struct bnep *b, bnep_connect_cb conn_cb, void *data);
typedef void (*bnep_disconnect_cb) (void *data);
-void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb,
- void *data);
+int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb,
+ bnep_disconnect_cb disconn_cb,
+ void *conn_data, void *disconn_data);
void bnep_disconnect(struct bnep *session);
-int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface,
- const bdaddr_t *addr);
+int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
+ uint8_t *setup_data, int len);
void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr);
-
-ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp);
-uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
- uint16_t *src);
#ifdef __TIZEN_PATCH__
int bnep_if_down_wrapper(const char *devname);
int bnep_conndel_wrapper(const bdaddr_t *dst);
if (!nc->session)
goto failed;
- perr = bnep_connect(nc->session, bnep_conn_cb, nc);
+ perr = bnep_connect(nc->session, bnep_conn_cb, bnep_disconn_cb, nc, nc);
if (perr < 0) {
error("bnep connect(): %s (%d)", strerror(-perr), -perr);
goto failed;
}
- bnep_set_disconnect(nc->session, bnep_disconn_cb, nc);
-
if (nc->io) {
g_io_channel_unref(nc->io);
nc->io = NULL;
struct btd_service *service;
struct network_conn *nc;
const char *svc;
- const char *uuid;
uint16_t id;
int err;
+ char uuid_str[MAX_LEN_UUID_STR];
+ bt_uuid_t uuid16, uuid128;
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc,
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);
id = get_pan_srv_id(svc);
- uuid = bnep_uuid(id);
-
- if (uuid == NULL)
+ bt_uuid16_create(&uuid16, id);
+#ifdef __TIZEN_PATCH__
+ bt_uuid_to_uuid128(&uuid16, &uuid128);
+#else
+ bt_uuid_to_uuid128(&uuid128, &uuid16);
+#endif
+ if (bt_uuid_to_string(&uuid128, uuid_str, MAX_LEN_UUID_STR) < 0)
return btd_error_invalid_args(msg);
- service = btd_device_get_service(peer->device, uuid);
+ service = btd_device_get_service(peer->device, uuid_str);
if (service == NULL)
return btd_error_not_supported(msg);
{
struct network_peer *peer = data;
struct network_conn *nc;
- const char *uuid;
+ char uuid_str[MAX_LEN_UUID_STR];
+#ifdef __TIZEN_PATCH__
+ const char *uuid = uuid_str;
+#endif
+ bt_uuid_t uuid16, uuid128;
nc = find_connection_by_state(peer->connections, CONNECTED);
if (nc == NULL)
return FALSE;
- uuid = bnep_uuid(nc->id);
+ bt_uuid16_create(&uuid16, nc->id);
+#ifdef __TIZEN_PATCH__
+ bt_uuid_to_uuid128(&uuid16, &uuid128);
+#else
+ bt_uuid_to_uuid128(&uuid128, &uuid16);
+#endif
+ bt_uuid_to_string(&uuid128, uuid_str, MAX_LEN_UUID_STR);
+#ifdef __TIZEN_PATCH__
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+#else
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid_str);
+#endif
return TRUE;
}
#include "src/log.h"
#include "src/error.h"
#include "src/sdpd.h"
+#include "src/shared/util.h"
#include "bnep.h"
#include "server.h"
static struct network_server *find_server_by_uuid(GSList *list,
const char *uuid)
{
- for (; list; list = list->next) {
- struct network_server *ns = list->data;
+ bt_uuid_t srv_uuid, bnep_uuid;
- if (strcasecmp(uuid, bnep_uuid(ns->id)) == 0)
- return ns;
+ if (!bt_string_to_uuid(&srv_uuid, uuid)) {
+ for (; list; list = list->next) {
+ struct network_server *ns = list->data;
- if (strcasecmp(uuid, bnep_name(ns->id)) == 0)
- return ns;
+ bt_uuid16_create(&bnep_uuid, ns->id);
+
+ /* UUID value compare */
+ if (!bt_uuid_cmp(&srv_uuid, &bnep_uuid))
+ return ns;
+ }
+ } else {
+ for (; list; list = list->next) {
+ struct network_server *ns = list->data;
+
+ /* String value compare */
+ switch (ns->id) {
+ case BNEP_SVC_PANU:
+ if (!strcasecmp(uuid, "panu"))
+ return ns;
+ break;
+ case BNEP_SVC_NAP:
+ if (!strcasecmp(uuid, "nap"))
+ return ns;
+ break;
+ case BNEP_SVC_GN:
+ if (!strcasecmp(uuid, "gn"))
+ return ns;
+ break;
+ }
+ }
}
return NULL;
static gboolean bnep_setup(GIOChannel *chan,
GIOCondition cond, gpointer user_data)
{
+ const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
struct network_adapter *na = user_data;
struct network_server *ns;
uint8_t packet[BNEP_MTU];
struct bnep_setup_conn_req *req = (void *) packet;
- uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
+ uint16_t dst_role = 0;
+ uint32_t val;
int n, sk;
+ char *bridge = NULL;
if (cond & G_IO_NVAL)
return FALSE;
sk = g_io_channel_unix_get_fd(chan);
+#ifdef __TIZEN_PATCH__
+ /*
+ * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case
+ * of kernel setup connection msg handling.
+ */
+ n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+#else
/* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
n = read(sk, packet, sizeof(packet));
+#endif
if (n < 0) {
error("read(): %s(%d)", strerror(errno), errno);
return FALSE;
}
- /* Highest known Control command ID
- * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
- if (req->type == BNEP_CONTROL &&
- req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
- uint8_t pkt[3];
-
- pkt[0] = BNEP_CONTROL;
- pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
- pkt[2] = req->ctrl;
-
- send(sk, pkt, sizeof(pkt), 0);
-
+ /*
+ * Initial received data packet is BNEP_SETUP_CONNECTION_REQUEST_MSG
+ * minimal size of this frame is 3 octets: 1 byte of BNEP Type +
+ * 1 byte of BNEP Control Type + 1 byte of BNEP services UUID size.
+ */
+ if (n < 3) {
+ error("To few setup connection request data received");
return FALSE;
}
- if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
- return FALSE;
-
- rsp = bnep_setup_decode(req, &dst_role, &src_role);
- if (rsp != BNEP_SUCCESS)
- goto reply;
+ switch (req->uuid_size) {
+ case 2:
+ dst_role = get_be16(req->service);
+ break;
+ case 16:
+ if (memcmp(&req->service[4], bt_base, sizeof(bt_base)) != 0)
+ break;
- rsp = BNEP_CONN_NOT_ALLOWED;
+ /* Intentional no-brake */
- ns = find_server(na->servers, dst_role);
- if (!ns) {
- error("Server unavailable: (0x%x)", dst_role);
- goto reply;
- }
+ case 4:
+ val = get_be32(req->service);
+ if (val > 0xffff)
+ break;
- if (!ns->record_id) {
- error("Service record not available");
- goto reply;
+ dst_role = val;
+ break;
+ default:
+ break;
}
- if (!ns->bridge) {
- error("Bridge interface not configured");
- goto reply;
- }
+ ns = find_server(na->servers, dst_role);
+ if (!ns || !ns->record_id || !ns->bridge)
+ error("Server error, bridge not initialized: (0x%x)", dst_role);
+ else
+ bridge = ns->bridge;
strncpy(na->setup->dev, BNEP_INTERFACE, 16);
na->setup->dev[15] = '\0';
- if (bnep_server_add(sk, dst_role, ns->bridge, na->setup->dev,
- &na->setup->dst) < 0)
- goto reply;
+ if (bnep_server_add(sk, bridge, na->setup->dev, &na->setup->dst,
+ packet, n) < 0)
+ error("BNEP server cannot be added");
#ifdef __TIZEN_PATCH__
{
na->setup = NULL;
- rsp = BNEP_SUCCESS;
-
-reply:
- bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
-
return FALSE;
}
goto done;
#ifndef __TIZEN_PATCH__
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- path, NETWORK_SERVER_INTERFACE,
- server_methods, NULL, NULL,
- na, path_unregister)) {
+ if (!g_dbus_register_interface(btd_get_dbus_connection(), path,
+ NETWORK_SERVER_INTERFACE,
+ server_methods, NULL, NULL, na,
+ path_unregister)) {
error("D-Bus failed to register %s interface",
NETWORK_SERVER_INTERFACE);
server_free(ns);
#include "attrib/gatt.h"
#include "attrib-server.h"
#include "gatt-database.h"
+#include "advertising.h"
#include "eir.h"
#ifdef __TIZEN_PATCH__
struct btd_adapter *adapter;
DBusMessage *msg;
};
-
-static GSList *read_host_requests = NULL;
-
#endif
struct btd_adapter {
bool le_privacy_enabled; /* whether LE Privacy feature enabled */
char local_irk[MGMT_IRK_SIZE]; /* adapter local IRK */
uint8_t disc_type;
-#ifdef IPSP_SUPPORT
bool ipsp_intialized; /* Ipsp Initialization state */
-#endif
struct le_data_length_read_handler *read_handler;
struct le_data_length_read_default_data_length_handler *def_read_handler;
#endif
sdp_list_t *services; /* Services associated to adapter */
struct btd_gatt_database *database;
+ struct btd_advertising *adv_manager;
gboolean initialized;
#ifdef __TIZEN_PATCH__
unsigned int pair_device_id;
guint pair_device_timeout;
+
unsigned int db_id; /* Service event handler for GATT db */
#ifdef __TIZEN_PATCH__
guint private_addr_timeout;
+ uint8_t central_rpa_res_support;
#endif
bool is_default; /* true if adapter is default one */
};
DBG("%s", adapter->path);
+#ifdef __TIZEN_PATCH__
+ if (rec == NULL) {
+ DBG("record is NULL return");
+ return;
+ }
+#endif
+
/* skip record without a browse group */
if (sdp_get_browse_groups(rec, &browse_list) < 0) {
DBG("skipping record without browse group");
g_free(auth);
}
+
#ifdef __TIZEN_PATCH__
void btd_adapter_unpair_device(struct btd_adapter *adapter,
struct btd_device *dev)
const struct mgmt_cp_start_discovery *rp = param;
DBG("status 0x%02x", status);
+#ifndef __TIZEN_PATCH__
+ DBG("Discovery Type 0x%02x", rp->type);
+#endif
if (length < sizeof(*rp)) {
error("Wrong size of start discovery return parameters");
}
if (status == MGMT_STATUS_SUCCESS) {
-#ifndef __TIZEN_PATCH__
- DBG("Discovery Type 0x%02x", rp->type);
-#endif
-
#ifdef __TIZEN_PATCH__
DBG("Return param discovery type 0x%02x", rp->type);
adapter->discovery_type |= rp->type;
const struct mgmt_cp_start_discovery *rp = param;
DBG("status 0x%02x", status);
+ DBG("Discovery Type 0x%02x", rp->type);
if (length < sizeof(*rp)) {
- error("Wrong size of start LE discovery return parameters");
+ error("Wrong size of start discovery return parameters");
return;
}
if (status == MGMT_STATUS_SUCCESS) {
- DBG("Discovery Type 0x%02x", rp->type);
adapter->discovery_type |= rp->type;
adapter->discovery_enable = 0x01;
for (j = i; j < adv_len - 2; j++)
adv_data[j] = data[j + 2];
- adv_data[j] = name_len + 1;
if (name_len > ADV_DATA_MAX_LENGTH - adv_len) {
adv_data[j + 1] = EIR_NAME_SHORT;
- memcpy(adv_data + j + 2, name, ADV_DATA_MAX_LENGTH - adv_len);
+ name_len = ADV_DATA_MAX_LENGTH - adv_len;
} else {
adv_data[j + 1] = EIR_NAME_COMPLETE;
- memcpy(adv_data + j + 2, name, name_len);
}
+ adv_data[j] = name_len + 1;
+ memcpy(adv_data + j + 2, name, name_len);
g_free(data);
return adv_len + name_len;
const struct mgmt_rp_le_read_maximum_data_length *rp = param;
uint16_t max_tx_octects, max_tx_time;
uint16_t max_rx_octects, max_rx_time;
- int32_t err;
if (status != MGMT_STATUS_SUCCESS) {
error("le read maximum data length failed: %s (0x%02x)",
max_tx_time =0;
max_rx_octects = 0;
max_rx_time = 0;
- err = -EIO;
+ return;
}
if (length < sizeof(*rp)) {
error("Too small le read maximum data length response");
- max_tx_octects = 0;
- max_tx_time =0;
- max_rx_octects = 0;
- max_rx_time = 0;
- err = -EIO;
+ g_free(adapter->read_handler);
+ return;
} else {
max_tx_octects = rp->max_tx_octets;
max_tx_time =rp->max_tx_time;
max_rx_octects = rp->max_rx_octets;
max_rx_time = rp->max_rx_time;
- err = 0;
}
if (!adapter->read_handler ||
adapter->read_handler->read_callback(adapter,
max_tx_octects, max_tx_time,
max_rx_octects, max_rx_time,
- err, adapter->read_handler->user_data);
+ adapter->read_handler->user_data);
g_free(adapter->read_handler);
adapter->read_handler = NULL;
struct btd_adapter *adapter,
uint16_t max_tx_octects, uint16_t max_tx_time,
uint16_t max_rx_octects, uint16_t max_rx_time,
- int32_t err, void *user_data)
+ void *user_data)
{
DBusMessage *reply;
struct le_data_length_read_request *read_request;
- DBG("inside le_read_data_length_complete");
-
read_request = find_read_le_data_length_request(adapter);
if (!read_request)
return;
DBUS_TYPE_UINT16, &max_tx_time,
DBUS_TYPE_UINT16, &max_rx_octects,
DBUS_TYPE_UINT16, &max_rx_time,
- DBUS_TYPE_INT32, &err,
DBUS_TYPE_INVALID);
if (!reply) {
struct btd_adapter *adapter = user_data;
const struct mgmt_rp_le_read_host_suggested_data_length *rp = param;
uint16_t def_tx_octects, def_tx_time;
- int32_t err;
if (status != MGMT_STATUS_SUCCESS) {
error("Read host suggested def le data length values failed: %s (0x%02x)",
mgmt_errstr(status), status);
def_tx_octects = 0;
def_tx_time =0;
- err = -EIO;
+ return;
}
if (length < sizeof(*rp)) {
- DBG("Too small le read host data length response");
- err = -EIO;
+ goto done;
} else {
def_tx_octects = rp->def_tx_octets;
def_tx_time =rp->def_tx_time;
- err = 0;
DBG("retrieving host suggested data length values %d %d", def_tx_octects, def_tx_time);
}
adapter->def_read_handler->read_callback(adapter,
def_tx_octects, def_tx_time,
- err, adapter->def_read_handler->user_data);
+ adapter->def_read_handler->user_data);
done:
if (adapter->def_read_handler)
g_free(adapter->def_read_handler->user_data);
return -EIO;
}
-static struct le_data_length_read_request *find_read_le_host_data_length_request(
- struct btd_adapter *adapter)
-{
- GSList *match;
-
- match = g_slist_find_custom(read_host_requests, adapter, read_request_cmp);
-
- if (match)
- return match->data;
-
- return NULL;
-}
-
static void le_read_host_suggested_default_length_complete(
struct btd_adapter *adapter,
uint16_t def_tx_octects, uint16_t def_tx_time,
- int32_t err, void *user_data)
+ void *user_data)
{
DBusMessage *reply;
struct le_data_length_read_request *read_request;
- read_request = find_read_le_host_data_length_request(adapter);
+ read_request = find_read_le_data_length_request(adapter);
if (!read_request)
return;
reply = g_dbus_create_reply(read_request->msg,
DBUS_TYPE_UINT16, &def_tx_octects,
DBUS_TYPE_UINT16, &def_tx_time,
- DBUS_TYPE_INT32, &err,
DBUS_TYPE_INVALID);
if (!reply) {
return;
}
- read_host_requests = g_slist_remove(read_host_requests, read_request);
+ read_requests = g_slist_remove(read_requests, read_request);
dbus_message_unref(read_request->msg);
g_free(read_request);
struct le_data_length_read_request *read_request;
struct le_data_length_read_default_data_length_handler *handler;
- if (find_read_le_host_data_length_request(adapter))
+ if (find_read_le_data_length_request(adapter))
return btd_error_in_progress(msg);
if (btd_adapter_le_read_suggested_default_data_length(adapter))
read_request->msg = dbus_message_ref(msg);
read_request->adapter = adapter;
- read_host_requests = g_slist_append(read_host_requests, read_request);
+ read_requests = g_slist_append(read_requests, read_request);
handler = g_new0(struct le_data_length_read_default_data_length_handler, 1);
data->adapter = adapter;
data->id = id;
-#ifdef __TIZEN_PATCH__
+/* Because of timing issue, sometimes pscan / iscan value was wrong. */
+#if 0
+/* #ifdef __TIZEN_PATCH__ */
/*
- * Use mgmt_reply to avoid dbus timeout in a state of bonding.
+ * Use mgmt_send_nowait to avoid dbus timeout in a state of bonding.
*/
- if (mgmt_reply(adapter->mgmt, opcode, adapter->dev_id, len, param,
+ if (mgmt_send_nowait(adapter->mgmt, opcode, adapter->dev_id, len, param,
property_set_mode_complete, data, g_free) > 0)
#else
if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param,
return;
}
-#ifdef __TIZEN_PATCH__
- if (adapter_le_read_ble_feature_info())
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
- ADAPTER_INTERFACE, "SupportedLEFeatures");
-
- adapter_get_adv_tx_power(adapter);
-#endif
-
property_set_mode(adapter, MGMT_SETTING_POWERED, iter, id);
}
return TRUE;
}
-#ifdef IPSP_SUPPORT
+
static gboolean property_get_ipsp_init_state(
const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
return TRUE;
}
#endif
-#endif
static gboolean property_get_uuids(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
return reply;
}
-#ifdef IPSP_SUPPORT
+static gboolean adapter_ipsp_connected(struct btd_adapter *adapter)
+{
+ GSList *l, *next;
+
+ DBG("%s", adapter->path);
+
+ for (l = adapter->connections; l != NULL; l = next) {
+ struct btd_device *dev = l->data;
+
+ next = g_slist_next(l);
+
+ if (device_is_ipsp_connected(dev))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void adapter_set_ipsp_init_state(struct btd_adapter *adapter, gboolean initialized)
{
if (adapter->ipsp_intialized == initialized)
if (adapter->ipsp_intialized)
return btd_error_already_exists(msg);
- /* Register IPSP service as GATT primary service */
- err = gatt_register_internet_protocol_service(adapter);
-
- if (!err)
- return btd_error_failed(msg, "Failed to register IPSP service");
-
/* Enable BT 6lowpan in kernel */
err = initialize_6lowpan(adapter);
if (!adapter->ipsp_intialized)
return btd_error_not_permitted(msg, "IPSP not initialized");
- /* Un-register IPSP service as GATT primary service */
- err = gatt_unregister_internet_protocol_service(adapter);
-
- if (!err)
- return btd_error_failed(msg, "Failed to un-register IPSP service");
+ if (adapter_ipsp_connected(adapter))
+ return btd_error_not_permitted(msg, "IPSP Client device found connected");
/* Disable BT 6lowpan in kernel */
err = deinitialize_6lowpan(adapter);
return dbus_message_new_method_return(msg);
}
#endif
-#endif
static DBusMessage *remove_device(DBusConnection *conn,
DBusMessage *msg, void *user_data)
device = list->data;
- btd_device_set_temporary(device, TRUE);
+ btd_device_set_temporary(device, true);
if (!btd_device_is_connected(device)) {
btd_adapter_remove_device(adapter, device);
{ GDBUS_ASYNC_METHOD("scan_filter_enable",
GDBUS_ARGS({ "client_if", "i" }, { "enable", "b" }), NULL,
adapter_le_scan_filter_enable) },
-#ifdef IPSP_SUPPORT
- { GDBUS_ASYNC_METHOD("InitializeIpsp",
+ { GDBUS_METHOD("InitializeIpsp",
NULL, NULL,
adapter_initialize_ipsp) },
- { GDBUS_ASYNC_METHOD("DeinitializeIpsp",
+ { GDBUS_METHOD("DeinitializeIpsp",
NULL, NULL,
adapter_deinitialize_ipsp) },
-#endif
{ GDBUS_METHOD("SetScanRespData",
GDBUS_ARGS({ "value", "ay" },
{ "slot_id", "i" }), NULL,
GDBUS_ARGS({ "address", "s" }),
GDBUS_ARGS({ "device", "o" }),
find_device) },
+#ifdef __TIZEN_PATCH__
#ifdef __BROADCOM_PATCH__
{ GDBUS_METHOD("SetWbsParameters",
GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
NULL,
set_nb_parameters) },
+#endif
{ GDBUS_METHOD("SetManufacturerData",
GDBUS_ARGS({ "value", "ay" }), NULL,
adapter_set_manufacturer_data) },
#ifdef __TIZEN_PATCH__
{ GDBUS_ASYNC_METHOD("LEReadMaximumDataLength", NULL,
GDBUS_ARGS({"maxTxOctets", "q" }, { "maxTxTime", "q" },
- {"maxRxOctets", "q" }, { "maxRxTime", "q" },
- { "read_error", "i" }),
+ {"maxRxOctets", "q" }, { "maxRxTime", "q" }),
le_read_maximum_data_length)},
{ GDBUS_ASYNC_METHOD("LEWriteHostSuggestedDataLength",
GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }), NULL,
le_write_host_suggested_default_data_length)},
{ GDBUS_ASYNC_METHOD("LEReadHostSuggestedDataLength", NULL,
- GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" },
- { "read_error", "i" }),
+ GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }),
le_read_host_suggested_default_data_length)},
#endif
{ }
property_set_connectable },
{ "Version", "s", property_get_version },
{ "SupportedLEFeatures", "as", property_get_supported_le_features},
-#ifdef IPSP_SUPPORT
{ "IpspInitStateChanged", "b", property_get_ipsp_init_state},
#endif
-#endif
{ }
};
DBG("IRKs loaded for hci%u", adapter->dev_id);
}
+#ifdef __TIZEN_PATCH__
+static void load_devices_rpa_res_support(struct btd_adapter *adapter)
+{
+ GSList *l;
+
+ DBG("%s", adapter->path);
+
+ for (l = adapter->devices; l; l = l->next) {
+ struct btd_device *device = l->data;
+
+ btd_adapter_set_dev_rpa_res_support(adapter, device);
+ }
+}
+#endif
+
static void load_irks(struct btd_adapter *adapter, GSList *irks)
{
struct mgmt_cp_load_irks *cp;
if (!device)
goto free;
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
adapter->devices = g_slist_append(adapter->devices, device);
/* TODO: register services from pre-loaded list of primaries */
g_slist_free_full(irks, g_free);
load_conn_params(adapter, params);
g_slist_free_full(params, g_free);
+
+#ifdef __TIZEN_PATCH__
+ load_devices_rpa_res_support(adapter);
+#endif
}
int btd_adapter_block_address(struct btd_adapter *adapter,
return NULL;
}
+#ifdef __TIZEN_PATCH__
+uint8_t btd_adapter_get_rpa_res_support_value(struct btd_adapter *adapter)
+{
+ return adapter->central_rpa_res_support;
+}
+#endif
+
int adapter_connect_list_add(struct btd_adapter *adapter,
struct btd_device *device)
{
static void adapter_start(struct btd_adapter *adapter)
{
+#ifdef __TIZEN_PATCH__
+ if (adapter_le_read_ble_feature_info())
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "SupportedLEFeatures");
+
+ adapter_get_adv_tx_power(adapter);
+#endif
+
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Powered");
DBG("LE Privacy is enabled.");
else
DBG("LE Privacy is disabled.");
+
+ adapter->central_rpa_res_support = 0x00;
#endif
adapter->auths = g_queue_new();
btd_adapter_gatt_server_stop(adapter);
#endif
+ btd_advertising_manager_destroy(adapter->adv_manager);
+ adapter->adv_manager = NULL;
+
g_slist_free(adapter->pin_callbacks);
adapter->pin_callbacks = NULL;
}
device_set_last_addr_type(dev, bdaddr_type);
-#ifdef IPSP_SUPPORT
device_set_ipsp_connected(dev, FALSE);
-#endif
#else
if (device_is_temporary(dev) && !adapter->discovery_list) {
eir_data_free(&eir_data);
/* Report an unknown name to the kernel even if there is a short name
* known, but still update the name with the known short name. */
+ name_known = device_name_known(dev);
+
+ if (eir_data.name && (eir_data.name_complete || !name_known))
+ btd_device_device_set_name(dev, eir_data.name);
+
#ifdef __TIZEN_PATCH__
+ /* As this logic, (eir_data.name_complete || !name_known) is always true.
+ So some abnormal UI bug occurs. Such like complete name display and
+ next time short name display.
+ */
if (eir_data.name_complete)
name_known = device_name_known(dev);
else
name_known = false;
-#else
- name_known = device_name_known(dev);
#endif
- if (eir_data.name && (eir_data.name_complete || !name_known))
- btd_device_device_set_name(dev, eir_data.name);
-
if (eir_data.class != 0)
device_set_class(dev, eir_data.class);
if (auth->svc_id > 0)
return FALSE;
-/* The below patch should be removed after we provide the API
- * to control autorization for the specific UUID.
- */
if (device_is_trusted(device) == TRUE) {
auth->cb(NULL, auth->user_data);
goto next;
connected = TRUE;
else
connected = FALSE;
-#ifdef IPSP_SUPPORT
+
device_set_ipsp_connected(device, connected);
-#endif
}
static void bt_le_data_length_changed_callback(uint16_t index, uint16_t length,
device_le_data_length_changed(device, ev->max_tx_octets, ev->max_tx_time,
ev->max_rx_octets, ev->max_rx_time);
}
-
#endif
struct btd_adapter_pin_cb_iter *btd_adapter_pin_cb_iter_new(
key->pin_len);
device_set_bonded(device, BDADDR_BREDR);
-
- if (device_is_temporary(device))
- btd_device_set_temporary(device, FALSE);
}
bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
key->type, key->enc_size, ediv, rand);
device_set_bonded(device, addr->type);
-
- if (device_is_temporary(device))
- btd_device_set_temporary(device, FALSE);
}
bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, 0,
key->type);
- if (device_is_temporary(device))
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
}
static void store_irk(struct btd_adapter *adapter, const bdaddr_t *peer,
store_irk(adapter, &addr->bdaddr, addr->type, irk->val);
- if (device_is_temporary(device))
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
+
+#ifdef __TIZEN_PATCH__
+ btd_adapter_set_dev_rpa_res_support(adapter, device);
+#endif
}
static void store_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer,
return;
#endif
} else {
- hash = rp->hash;
- randomizer = rp->randomizer;
+ hash = rp->hash192;
+ randomizer = rp->rand192;
}
if (!adapter->oob_handler || !adapter->oob_handler->read_local_cb)
return -EIO;
}
-#ifndef __TIZEN_PATCH__
static void services_modified(struct gatt_db_attribute *attrib, void *user_data)
{
struct btd_adapter *adapter = user_data;
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "UUIDs");
}
-#endif
static int adapter_register(struct btd_adapter *adapter)
{
struct agent *agent;
-#ifndef __TIZEN_PATCH__
struct gatt_db *db;
-#endif
if (powering_down)
return -EBUSY;
agent_unref(agent);
}
-#ifndef __TIZEN_PATCH__
adapter->database = btd_gatt_database_new(adapter);
if (!adapter->database) {
error("Failed to create GATT database for adapter");
adapter->db_id = gatt_db_register(db, services_modified,
services_modified,
adapter, NULL);
-#else
+
+/* Disable Old GATT Server */
+#if 0
btd_adapter_gatt_server_start(adapter);
#endif
+ /* Don't start advertising managers on non-LE controllers. */
+ if (adapter->supported_settings & MGMT_SETTING_LE) {
+ adapter->adv_manager = btd_advertising_manager_new(adapter);
+
+ /* LEAdvertisingManager1 is experimental so optional */
+ if (!adapter->adv_manager)
+ error("Failed to register LEAdvertisingManager1 "
+ "interface for adapter");
+ } else {
+ info("Not starting LEAdvertisingManager, LE not supported");
+ }
load_config(adapter);
fix_storage(adapter);
load_drivers(adapter);
return false;
}
-#ifdef IPSP_SUPPORT
int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type)
return -EIO;
}
-#endif
+
+static void set_dev_rpa_res_support_complete(uint8_t status,
+ uint16_t length, const void *param,
+ void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Failed to set RPA resolution support of device : %s (0x%02x)",
+ mgmt_errstr(status), status);
+ else
+ DBG("Set RPA resolution support successful");
+}
+
+int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter,
+ struct btd_device *device)
+
+{
+ struct mgmt_cp_set_dev_rpa_res_support cp;
+
+ DBG("btd_adapter_set_dev_rpa_res_support called");
+
+ memset(&cp, 0, sizeof(cp));
+
+ bacpy(&cp.addr.bdaddr, device_get_address(device));
+ cp.addr.type = btd_device_get_bdaddr_type(device);
+ cp.res_support = device_get_rpa_res_char_value(device);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEV_RPA_RES_SUPPORT,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_dev_rpa_res_support_complete, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
#endif
static void clear_devices_complete(uint8_t status, uint16_t length,
break;
}
-#if 0
if (missing_settings & MGMT_SETTING_SECURE_CONN)
set_mode(adapter, MGMT_OP_SET_SECURE_CONN, 0x01);
+ if (main_opts.fast_conn &&
+ (missing_settings & MGMT_SETTING_FAST_CONNECTABLE))
+ set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01);
+
+#ifdef __TIZEN_PATCH__
+ /* Set the RPA resolution value to '1' if privacy is supported */
+ if (adapter->le_privacy_enabled &&
+ adapter->supported_settings & MGMT_SETTING_PRIVACY)
+ adapter->central_rpa_res_support = 0x01;
#endif
err = adapter_register(adapter);
gboolean btd_adapter_disable_le_auto_connect(struct btd_adapter *adapter);
void adapter_check_version(struct btd_adapter *adapter, uint8_t hci_ver);
GSList *btd_adapter_get_connections(struct btd_adapter *adapter);
-#ifdef IPSP_SUPPORT
int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type);
int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type);
-#endif
+
+int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter,
+ struct btd_device *device);
typedef void (*read_max_data_length_cb_t) (struct btd_adapter *adapter,
- uint16_t max_txOctects,
- uint16_t max_txTime,
- uint16_t max_rxOctects,
- uint16_t max_rxTime,
- int32_t read_error,
+ const uint16_t max_txOctects,
+ const uint16_t max_txTime,
+ const uint16_t max_rxOctects,
+ const uint16_t max_rxTime,
void *user_data);
struct le_data_length_read_handler {
};
typedef void (*read_host_suggested_default_data_length_cb_t) (struct btd_adapter *adapter,
- uint16_t def_txOctects,
- uint16_t def_txTime,
- int32_t read_error,
+ const uint16_t def_txOctects,
+ const uint16_t def_txTime,
void *user_data);
struct le_data_length_read_default_data_length_handler {
read_host_suggested_default_data_length_cb_t read_callback;
void *user_data;
};
-
-int btd_adapter_le_set_data_length(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- uint16_t max_tx_octets, uint16_t max_tx_time);
-void device_le_data_length_changed(struct btd_device *device, uint16_t max_tx_octets,
- uint16_t max_tx_time, uint16_t max_rx_octets, uint16_t max_rx_time);
-
#endif
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "advertising.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "adapter.h"
+#include "dbus-common.h"
+#include "error.h"
+#include "log.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+
+#define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
+#define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1"
+
+struct btd_advertising {
+ struct btd_adapter *adapter;
+ struct queue *ads;
+};
+
+#define AD_TYPE_BROADCAST 0
+#define AD_TYPE_PERIPHERAL 1
+
+struct advertisement {
+ struct btd_advertising *manager;
+ char *owner;
+ char *path;
+ GDBusClient *client;
+ GDBusProxy *proxy;
+ DBusMessage *reg;
+ uint8_t type; /* Advertising type */
+};
+
+static bool match_advertisement_path(const void *a, const void *b)
+{
+ const struct advertisement *ad = a;
+ const char *path = b;
+
+ return g_strcmp0(ad->path, path);
+}
+
+static void advertisement_free(void *data)
+{
+ struct advertisement *ad = data;
+
+ if (ad->client) {
+ g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+ g_dbus_client_unref(ad->client);
+ }
+
+ if (ad->proxy)
+ g_dbus_proxy_unref(ad->proxy);
+
+ if (ad->owner)
+ g_free(ad->owner);
+
+ if (ad->path)
+ g_free(ad->path);
+
+ free(ad);
+}
+
+static gboolean advertisement_free_idle_cb(void *data)
+{
+ advertisement_free(data);
+
+ return FALSE;
+}
+
+static void advertisement_release(void *data)
+{
+ struct advertisement *ad = data;
+ DBusMessage *message;
+
+ DBG("Releasing advertisement %s, %s", ad->owner, ad->path);
+
+ message = dbus_message_new_method_call(ad->owner, ad->path,
+ LE_ADVERTISEMENT_IFACE,
+ "Release");
+
+ if (!message) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ g_dbus_send_message(btd_get_dbus_connection(), message);
+}
+
+static void advertisement_destroy(void *data)
+{
+ advertisement_release(data);
+ advertisement_free(data);
+}
+
+static void advertisement_remove(void *data)
+{
+ struct advertisement *ad = data;
+
+ g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+
+ /* TODO: mgmt API call to remove advert */
+
+ queue_remove(ad->manager->ads, ad);
+
+ g_idle_add(advertisement_free_idle_cb, ad);
+}
+
+static void client_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+ DBG("Client disconnected");
+
+ advertisement_remove(user_data);
+}
+
+static bool parse_advertising_type(GDBusProxy *proxy, uint8_t *type)
+{
+ DBusMessageIter iter;
+ const char *msg_type;
+
+ if (!g_dbus_proxy_get_property(proxy, "Type", &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, &msg_type);
+
+ if (!g_strcmp0(msg_type, "broadcast")) {
+ *type = AD_TYPE_BROADCAST;
+ return true;
+ }
+
+ if (!g_strcmp0(msg_type, "peripheral")) {
+ *type = AD_TYPE_PERIPHERAL;
+ return true;
+ }
+
+ return false;
+}
+
+static void refresh_advertisement(struct advertisement *ad)
+{
+ DBG("Refreshing advertisement: %s", ad->path);
+}
+
+static bool parse_advertisement(struct advertisement *ad)
+{
+ if (!parse_advertising_type(ad->proxy, &ad->type)) {
+ error("Failed to read \"Type\" property of advertisement");
+ return false;
+ }
+
+ /* TODO: parse the remaining properties into a shared structure */
+
+ refresh_advertisement(ad);
+
+ return true;
+}
+
+static void advertisement_proxy_added(GDBusProxy *proxy, void *data)
+{
+ struct advertisement *ad = data;
+ DBusMessage *reply;
+
+ if (!parse_advertisement(ad)) {
+ error("Failed to parse advertisement");
+
+ reply = btd_error_failed(ad->reg,
+ "Failed to register advertisement");
+ goto done;
+ }
+
+ g_dbus_client_set_disconnect_watch(ad->client, client_disconnect_cb,
+ ad);
+
+ reply = dbus_message_new_method_return(ad->reg);
+
+ DBG("Advertisement registered: %s", ad->path);
+
+done:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(ad->reg);
+ ad->reg = NULL;
+}
+
+static struct advertisement *advertisement_create(DBusConnection *conn,
+ DBusMessage *msg, const char *path)
+{
+ struct advertisement *ad;
+ const char *sender = dbus_message_get_sender(msg);
+
+ if (!path || !g_str_has_prefix(path, "/"))
+ return NULL;
+
+ ad = new0(struct advertisement, 1);
+ if (!ad)
+ return NULL;
+
+ ad->client = g_dbus_client_new_full(conn, sender, path, path);
+ if (!ad->client)
+ goto fail;
+
+ ad->owner = g_strdup(sender);
+ if (!ad->owner)
+ goto fail;
+
+ ad->path = g_strdup(path);
+ if (!ad->path)
+ goto fail;
+
+ DBG("Adding proxy for %s", path);
+ ad->proxy = g_dbus_proxy_new(ad->client, path, LE_ADVERTISEMENT_IFACE);
+ if (!ad->proxy)
+ goto fail;
+
+ g_dbus_client_set_proxy_handlers(ad->client, advertisement_proxy_added,
+ NULL, NULL, ad);
+
+ ad->reg = dbus_message_ref(msg);
+
+ return ad;
+
+fail:
+ advertisement_free(ad);
+ return NULL;
+}
+
+static DBusMessage *register_advertisement(DBusConnection *conn,
+ DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_advertising *manager = user_data;
+ DBusMessageIter args;
+ const char *path;
+ struct advertisement *ad;
+
+ DBG("RegisterAdvertisement");
+
+ 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);
+
+ if (queue_find(manager->ads, match_advertisement_path, path))
+ return btd_error_already_exists(msg);
+
+ /* TODO: support more than one advertisement */
+ if (!queue_isempty(manager->ads))
+ return btd_error_failed(msg, "Already advertising");
+
+ dbus_message_iter_next(&args);
+
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+ return btd_error_invalid_args(msg);
+
+ ad = advertisement_create(conn, msg, path);
+ if (!ad)
+ return btd_error_failed(msg,
+ "Failed to register advertisement");
+
+ DBG("Registered advertisement at path %s", path);
+
+ ad->manager = manager;
+ queue_push_tail(manager->ads, ad);
+
+ return NULL;
+}
+
+static DBusMessage *unregister_advertisement(DBusConnection *conn,
+ DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_advertising *manager = user_data;
+ DBusMessageIter args;
+ const char *path;
+ struct advertisement *ad;
+
+ DBG("UnregisterAdvertisement");
+
+ 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);
+
+ ad = queue_find(manager->ads, match_advertisement_path, path);
+ if (!ad)
+ return btd_error_does_not_exist(msg);
+
+ advertisement_remove(ad);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterAdvertisement",
+ GDBUS_ARGS({ "advertisement", "o" },
+ { "options", "a{sv}" }),
+ NULL, register_advertisement) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterAdvertisement",
+ GDBUS_ARGS({ "service", "o" }),
+ NULL,
+ unregister_advertisement) },
+ { }
+};
+
+static void advertising_manager_destroy(void *user_data)
+{
+ struct btd_advertising *manager = user_data;
+
+ queue_destroy(manager->ads, advertisement_destroy);
+
+ free(manager);
+}
+
+static struct btd_advertising *
+advertising_manager_create(struct btd_adapter *adapter)
+{
+ struct btd_advertising *manager;
+
+ manager = new0(struct btd_advertising, 1);
+ if (!manager)
+ return NULL;
+
+ manager->adapter = adapter;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ LE_ADVERTISING_MGR_IFACE,
+ methods, NULL, NULL, manager,
+ advertising_manager_destroy)) {
+ error("Failed to register " LE_ADVERTISING_MGR_IFACE);
+ free(manager);
+ return NULL;
+ }
+
+ manager->ads = queue_new();
+
+ return manager;
+}
+
+struct btd_advertising *
+btd_advertising_manager_new(struct btd_adapter *adapter)
+{
+ struct btd_advertising *manager;
+
+ if (!adapter)
+ return NULL;
+
+ manager = advertising_manager_create(adapter);
+ if (!manager)
+ return NULL;
+
+ DBG("LE Advertising Manager created for adapter: %s",
+ adapter_get_path(adapter));
+
+ return manager;
+}
+
+void btd_advertising_manager_destroy(struct btd_advertising *manager)
+{
+ if (!manager)
+ return;
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(manager->adapter),
+ LE_ADVERTISING_MGR_IFACE);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+struct btd_adapter;
+struct btd_advertising;
+
+struct btd_advertising *btd_advertising_manager_new(
+ struct btd_adapter *adapter);
+void btd_advertising_manager_destroy(struct btd_advertising *manager);
}
#ifdef __TIZEN_PATCH__
- static int attribute_uuid_cmp(gconstpointer a, gconstpointer b)
+static int attribute_uuid_cmp(gconstpointer a, gconstpointer b)
{
const struct attribute *attrib1 = a;
const bt_uuid_t *uuid = b;
put_le16(a->handle, value);
/* Attribute Value */
- put_uuid_le(&a->uuid, &value[2]);
+ bt_uuid_to_le(&a->uuid, &value[2]);
}
length = enc_find_info_resp(format, adl, pdu, len);
if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
range = NULL;
-
else
range->end = a->handle;
}
put_le16(appearance, &atval[0]);
attrib_db_add_new(server, server->appearance_handle, &uuid, ATT_NONE,
ATT_NOT_PERMITTED, atval, 2);
-
server->gap_sdp_handle = attrib_create_sdp_new(server, 0x0001,
"Generic Access Profile");
if (server->gap_sdp_handle == 0) {
uint8_t last_bdaddr_type;
gboolean le_auto_connect;
guint auto_id;
-#ifdef IPSP_SUPPORT
gboolean ipsp_connected; /* IPSP Connection state */
-#endif
+ uint8_t rpa_res_support; /* RPA Resolution capability of device */
uint16_t max_tx_octets;
uint16_t max_tx_time;
uint16_t max_rx_octets;
g_key_file_remove_key(key_file, "General", "Appearance", NULL);
}
+#ifdef __TIZEN_PATCH__
+ if (device->rpa_res_support) {
+ g_key_file_set_integer(key_file, "General", "RPAResSupport",
+ device->rpa_res_support);
+ } else {
+ g_key_file_remove_key(key_file, "General", "RPAResSupport", NULL);
+ }
+#endif
+
update_technologies(key_file, device);
g_key_file_set_boolean(key_file, "General", "Trusted",
if (!device->le_state.bonded)
gatt_db_clear(device->db);
}
-#ifndef __TIZEN_PATCH__
+
static void gatt_server_cleanup(struct btd_device *device)
{
if (!device->server)
bt_gatt_server_unref(device->server);
device->server = NULL;
}
-#endif
+
static void attio_cleanup(struct btd_device *device)
{
#ifdef __TIZEN_PATCH__
}
gatt_client_cleanup(device);
-#ifndef __TIZEN_PATCH__
gatt_server_cleanup(device);
-#endif
if (device->att) {
bt_att_unref(device->att);
return TRUE;
}
-#ifdef IPSP_SUPPORT
+
static gboolean dev_property_get_ipsp_conn_state(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
return TRUE;
}
#endif
-#endif
static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
store_device_info(device);
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "Blocked");
DBG("%s %s (%d)", profile->name, strerror(-err), -err);
if (!err)
- btd_device_set_temporary(dev, FALSE);
+ btd_device_set_temporary(dev, false);
if (dev->pending == NULL)
return;
if (!btd_adapter_get_powered(dev->adapter))
return btd_error_not_ready(msg);
- btd_device_set_temporary(dev, FALSE);
+ btd_device_set_temporary(dev, false);
if (!state->svc_resolved)
goto resolve_services;
if (dev->le_state.connected)
return dbus_message_new_method_return(msg);
- btd_device_set_temporary(dev, FALSE);
+ btd_device_set_temporary(dev, false);
if (dev->disable_auto_connect) {
dev->disable_auto_connect = FALSE;
#endif
int err;
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
#ifdef __TIZEN_PATCH__
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &conn_type,
err = device_connect_le(device);
else if (connect_le) /* Send bonding request if LE is already connected*/
err = adapter_create_bonding(adapter, &device->bdaddr,
- bdaddr_type,
- IO_CAPABILITY_NOINPUTNOOUTPUT);
+ bdaddr_type, io_cap);
else
err = adapter_create_bonding(adapter, &device->bdaddr,
BDADDR_BREDR, io_cap);
return dbus_message_new_error(msg,
ERROR_INTERFACE ".AuthenticationCanceled",
"Authentication Canceled");
+ case MGMT_STATUS_ALREADY_PAIRED:
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".AlreadyExists",
+ "Already Paired");
default:
return dbus_message_new_error(msg,
ERROR_INTERFACE ".AuthenticationFailed",
return;
}
- if (device->gatt_connected == connected)
- return;
-
- device->gatt_connected = connected;
+ if (device->gatt_connected != connected)
+ device->gatt_connected = connected;
DBG("GattConnected %d", connected);
if (device->bdaddr_type == BDADDR_BREDR) {
if(device->le)
device->bdaddr_type = BDADDR_LE_PUBLIC;
- else
+ else {
device = btd_adapter_get_device(device->adapter,
&device->bdaddr, BDADDR_LE_PUBLIC);
+ if (device == NULL)
+ return btd_error_no_such_adapter(msg);
+ }
}
if (device->gatt_connected)
if (device->auto_id == 0)
device->auto_id = g_timeout_add(200, att_connect, device);
- return dbus_message_new_method_return(msg);
+ device->connect = dbus_message_ref(msg);
+ return NULL;
}
static DBusMessage *disconnect_le(DBusConnection *conn, DBusMessage *msg,
if (!device->le_state.connected)
return btd_error_not_connected(msg);
+ if (device->connect) {
+ DBusMessage *reply = btd_error_failed(device->connect,
+ "Cancelled");
+ g_dbus_send_message(dbus_conn, reply);
+ dbus_message_unref(device->connect);
+ device->connect = NULL;
+ }
+
+ if (device->le_state.connected)
+ device->disconnects = g_slist_append(device->disconnects,
+ dbus_message_ref(msg));
+
disconnect_all(device);
/*
if(device->bredr)
device->bdaddr_type = BDADDR_BREDR;
- return dbus_message_new_method_return(msg);
+ return NULL;
}
-#ifdef IPSP_SUPPORT
static DBusMessage *connect_ipsp(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
if (device->bdaddr_type == BDADDR_BREDR) {
if(device->le)
device->bdaddr_type = BDADDR_LE_PUBLIC;
- else
+ else {
device = btd_adapter_get_device(device->adapter,
&device->bdaddr, BDADDR_LE_PUBLIC);
+ if (device == NULL)
+ return btd_error_no_such_adapter(msg);
+ }
}
if (device->ipsp_connected)
return dbus_message_new_method_return(msg);;
}
-#endif
-#ifdef __TIZEN_PATCH__
static DBusMessage *le_set_data_length(
DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
dbus_uint16_t max_tx_octets;
dbus_uint16_t max_tx_time;
+ const gchar *address;
+ bdaddr_t bdaddr;
struct btd_device *device = user_data;
int status;
char addr[BT_ADDRESS_STRING_SIZE];
else
return dbus_message_new_method_return(msg);
}
-#endif
static DBusMessage *is_connected_profile(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{ GDBUS_METHOD("ReadPayloadTimeout", NULL,
NULL, read_auth_payload_timeout)},
#endif
- { GDBUS_METHOD("ConnectLE",
+ { GDBUS_ASYNC_METHOD("ConnectLE",
GDBUS_ARGS({ "auto_connect", "b"}),
NULL, connect_le) },
- { GDBUS_METHOD("DisconnectLE", NULL, NULL, disconnect_le) },
+ { GDBUS_ASYNC_METHOD("DisconnectLE", NULL, NULL, disconnect_le) },
{ GDBUS_METHOD("IsConnectedProfile", GDBUS_ARGS({ "UUID", "s" }),
GDBUS_ARGS({ "IsConnected", "b" }), is_connected_profile)},
{ GDBUS_METHOD("LeConnUpdate",
GDBUS_ARGS({ "pattern", "s" }), NULL,
discover_services) },
{ GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
-#ifdef IPSP_SUPPORT
{ GDBUS_ASYNC_METHOD("ConnectIpsp", NULL, NULL, connect_ipsp) },
{ GDBUS_ASYNC_METHOD("DisconnectIpsp", NULL, NULL, disconnect_ipsp) },
-#endif
{ GDBUS_ASYNC_METHOD("LESetDataLength",
GDBUS_ARGS({"max_tx_octets", "q" },
{ "max_tx_time", "q" }), NULL,
{ "GattConnected", "b", dev_property_get_gatt_connected },
{ "PayloadTimeout", "q", dev_property_get_payload},
{ "LastAddrType", "y", dev_property_get_last_addr_type},
-#ifdef IPSP_SUPPORT
{ "IpspConnected", "b", dev_property_get_ipsp_conn_state },
#endif
-#endif
{ }
};
}
#ifdef __TIZEN_PATCH__
+ /* Load RPA Resolution Support value */
+ device->rpa_res_support = g_key_file_get_integer(key_file,
+ "General", "RPAResSupport", NULL);
+
str = g_key_file_get_string(key_file, "General", "ManufacturerDataLen", NULL);
if (str) {
device->manufacturer_data_len = strtol(str, NULL, 10);
}
device->adapter = adapter;
- device->temporary = TRUE;
+ device->temporary = true;
gatt_db_register(device->db, gatt_service_added, gatt_service_removed,
device, NULL);
if (device->browse)
browse_request_cancel(device->browse);
-
while (device->services != NULL) {
struct btd_service *service = device->services->data;
adapter_connect_list_add(device->adapter, device);
done:
+#ifdef __TIZEN_PATCH__
+ device_set_gatt_connected(device, FALSE);
+#endif
attio_cleanup(device);
}
*/
gatt_db_foreach_service(device->db, NULL, add_primary, &services);
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
if (req)
update_gatt_uuids(req, device->primaries, services);
return;
}
- device->att_mtu = bt_att_get_mtu(device->att);
- g_attrib_set_mtu(device->attrib, device->att_mtu);
-
- DBG("MTU: %u", device->att_mtu);
-
register_gatt_services(device);
device_accept_gatt_profiles(device);
- g_slist_foreach(device->attios, attio_connected, device->attrib);
-
btd_gatt_client_ready(device->client_dbus);
}
DBG("start 0x%04x, end: 0x%04x", start_handle, end_handle);
}
+static void gatt_debug(const char *str, void *user_data)
+{
+ DBG("%s", str);
+}
+
static void gatt_client_init(struct btd_device *device)
{
gatt_client_cleanup(device);
return;
}
+
#ifdef __TIZEN_PATCH__
if (!bt_gatt_client_set_debug(device->client, gatt_client_debug_func,
NULL, NULL)) {
error("Failed to set debug function");
}
+#else
+ bt_gatt_client_set_debug(device->client, gatt_debug, NULL, NULL);
#endif
+ /* Notify attio so it can react to notifications */
+ g_slist_foreach(device->attios, attio_connected, device->attrib);
if (!bt_gatt_client_set_ready_handler(device->client,
gatt_client_ready_cb,
}
}
-#ifndef __TIZEN_PATCH__
static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
{
if (!db) {
device->server = bt_gatt_server_new(db, device->att, device->att_mtu);
if (!device->server)
error("Failed to initialize bt_gatt_server");
+
+ bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
}
-#endif
static bool local_counter(uint32_t *sign_cnt, void *user_data)
{
BtIOSecLevel sec_level;
uint16_t mtu;
uint16_t cid;
-#ifndef __TIZEN_PATCH__
struct btd_gatt_database *database;
-#endif
bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
BT_IO_OPT_IMTU, &mtu,
return false;
}
-#ifdef __TIZEN_PATCH__
+#if 0
dev->attachid = attrib_channel_attach(attrib);
if (dev->attachid == 0) {
g_attrib_unref(attrib);
bt_att_set_remote_key(dev->att, dev->remote_csrk->key,
remote_counter, dev);
-#ifndef __TIZEN_PATCH__
database = btd_adapter_get_database(dev->adapter);
-#endif
gatt_client_init(dev);
-#ifndef __TIZEN_PATCH__
gatt_server_init(dev, btd_gatt_database_get_db(database));
-#endif
/*
* Remove the device from the connect_list and give the passive
device->last_bdaddr_type = type;
}
-#ifdef IPSP_SUPPORT
+gboolean device_is_ipsp_connected(struct btd_device * device)
+{
+ return device->ipsp_connected;
+}
+
void device_set_ipsp_connected(struct btd_device *device, gboolean connected)
{
if (device == NULL) {
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "IpspConnected");
}
-#endif
-
void device_le_data_length_changed(struct btd_device *device, uint16_t max_tx_octets,
uint16_t max_tx_time, uint16_t max_rx_octets, uint16_t max_rx_time)
{
return device->temporary;
}
-void btd_device_set_temporary(struct btd_device *device, gboolean temporary)
+void btd_device_set_temporary(struct btd_device *device, bool temporary)
{
if (!device)
return;
DBG("temporary %d", temporary);
+ device->temporary = temporary;
+
if (temporary) {
if (device->bredr)
adapter_whitelist_remove(device->adapter, device);
adapter_connect_list_remove(device->adapter, device);
- } else {
- if (device->bredr)
- adapter_whitelist_add(device->adapter, device);
- store_device_info(device);
+ return;
}
- device->temporary = temporary;
+ if (device->bredr)
+ adapter_whitelist_add(device->adapter, device);
+
+ store_device_info(device);
}
void btd_device_set_trusted(struct btd_device *device, gboolean trusted)
device->bredr_state.bonded = true;
else
device->le_state.bonded = true;
+
+ btd_device_set_temporary(device, false);
}
void device_set_legacy(struct btd_device *device, bool legacy)
g_dbus_emit_property_changed(dbus_conn, dev->path,
DEVICE_INTERFACE, "Paired");
- btd_device_set_temporary(dev, TRUE);
+ btd_device_set_temporary(dev, true);
if (btd_device_is_connected(dev))
device_request_disconnect(dev, NULL);
device_auth_req_free(device);
/* If we're already paired nothing more is needed */
- if (state->paired)
+ if (state->paired) {
+#ifdef __TIZEN_PATCH__
+#ifdef TIZEN_WEARABLE
+ DBG("Already paired. Send Paired Signal for Wearble syspopup termn");
+ DBG("state->svc_resolved [%d]", state->svc_resolved);
+ if (state->svc_resolved)
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "Paired");
+#endif /* TIZEN_WEARABLE */
+#endif /* __TIZEN_PATCH__ */
return;
+ }
device_set_paired(device, bdaddr_type);
err ? FALSE : TRUE);
else
#endif
- btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
- device->bdaddr_type,
- err ? FALSE : TRUE);
+ btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
+ device->bdaddr_type,
+ err ? FALSE : TRUE);
agent_unref(device->authr->agent);
device->authr->agent = NULL;
}
#ifdef __TIZEN_PATCH__
+int device_get_rpa_res_char_value(struct btd_device *device)
+{
+ return device->rpa_res_support;
+}
+
+/* Store the RPA Resolution Characteristic Value of remote device.
+ * This value would be checked before start directed advertising using RPA.
+ */
+void device_set_rpa_res_char_value(struct btd_device *device, uint8_t value)
+{
+ if (device->rpa_res_support == value)
+ return;
+
+ device->rpa_res_support = value;
+ store_device_info(device);
+}
+
void device_set_manufacturer_info(struct btd_device *device, struct eir_data *eir)
{
if (!device)
gboolean device_is_trusted(struct btd_device *device);
void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type);
void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type);
-void btd_device_set_temporary(struct btd_device *device, gboolean temporary);
+void btd_device_set_temporary(struct btd_device *device, bool temporary);
void btd_device_set_trusted(struct btd_device *device, gboolean trusted);
void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type);
void device_set_legacy(struct btd_device *device, bool legacy);
void device_set_payload_timeout(struct btd_device *device,
uint16_t payload_timeout);
void device_set_last_addr_type(struct btd_device *device, uint8_t type);
-#ifdef IPSP_SUPPORT
+gboolean device_is_ipsp_connected(struct btd_device * device);
void device_set_ipsp_connected(struct btd_device *device, gboolean connected);
-#endif
+int device_get_rpa_res_char_value(struct btd_device *device);
+void device_set_rpa_res_char_value(struct btd_device *device, uint8_t value);
#endif
struct btd_device *btd_device_ref(struct btd_device *device);
eir->manufacturer_data_len = data_len;
#endif
eir_parse_msd(eir, data, data_len);
-
break;
}
struct queue *services;
struct queue *all_notify_clients;
+#ifdef __TIZEN_PATCH__
+ guint wait_charcs_id;
+#endif
};
struct service {
struct async_dbus_op *op = user_data;
struct descriptor *desc = op->data;
struct service *service = desc->chrc->service;
+ DBusMessage *reply;
- if (!success) {
- DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode);
-
- desc->read_id = 0;
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- return;
- }
+ if (!success)
+ goto fail;
if (!op->offset)
gatt_db_attribute_reset(desc->attr);
- gatt_db_attribute_write(desc->attr, op->offset, value, length, 0, NULL,
- write_descriptor_cb, desc);
+ if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0,
+ NULL, write_descriptor_cb, desc)) {
+ error("Failed to store attribute");
+ goto fail;
+ }
/*
* If the value length is exactly MTU-1, then we may not have read the
return;
}
+ /* Read the stored data from db */
+ if (!gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op)) {
+ error("Failed to read database");
+ goto fail;
+ }
+
desc->read_id = 0;
- /* Read the stored data from db */
- gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op);
+ return;
+
+fail:
+ reply = create_gatt_dbus_error(op->msg, att_ecode);
+ desc->read_id = 0;
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ return;
}
static DBusMessage *descriptor_read_value(DBusConnection *conn,
struct async_dbus_op *op = user_data;
struct characteristic *chrc = op->data;
struct service *service = chrc->service;
+ DBusMessage *reply;
- if (!success) {
- DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode);
-
- chrc->read_id = 0;
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- return ;
- }
+ if (!success)
+ goto fail;
if (!op->offset)
gatt_db_attribute_reset(chrc->attr);
- gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0, NULL,
- write_characteristic_cb, chrc);
+ if (!gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0,
+ NULL, write_characteristic_cb, chrc)) {
+ error("Failed to store attribute");
+ goto fail;
+ }
/*
* If the value length is exactly MTU-1, then we may not have read the
chrc->read_id = 0;
/* Read the stored data from db */
- gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op);
+ if (!gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op)) {
+ error("Failed to read database");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ reply = create_gatt_dbus_error(op->msg, att_ecode);
+ chrc->read_id = 0;
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
}
static DBusMessage *characteristic_read_value(DBusConnection *conn,
supported = true;
chrc->write_id = bt_gatt_client_write_without_response(gatt,
chrc->value_handle,
- chrc->props & BT_GATT_CHRC_PROP_AUTH,
+ false,
+ value, value_len);
+ if (chrc->write_id) {
+ chrc->write_id = 0;
+ return dbus_message_new_method_return(msg);
+ }
+ } else if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_AUTH) {
+ DBG("BT_GATT_CHRC_PROP_AUTH");
+ supported = true;
+ chrc->write_id = bt_gatt_client_write_without_response(gatt,
+ chrc->value_handle,
+ true,
value, value_len);
if (chrc->write_id) {
chrc->write_id = 0;
return strcmp(client->owner, sender) == 0;
}
+#ifdef GATT_NO_RELAY
+struct char_value {
+ uint8_t *data;
+ uint8_t len;
+ char *chrc_path;
+};
+
+static void emit_value_changed_signal_to_dest(gpointer data, gpointer user_data)
+{
+ dbus_int32_t result = 0;
+ struct notify_client *notify_client = data;
+ struct char_value *value = user_data;
+
+ g_dbus_emit_signal_to_dest(btd_get_dbus_connection(),
+ notify_client->owner, value->chrc_path,
+ GATT_CHARACTERISTIC_IFACE, "GattValueChanged",
+ DBUS_TYPE_INT32, &result,
+ DBUS_TYPE_STRING, &value->chrc_path,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value->data, value->len,
+ DBUS_TYPE_INVALID);
+}
+#endif
+
+#ifdef __TIZEN_PATCH__
+void gatt_characteristic_value_changed(void *data, uint8_t data_len, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ char *chrc_path = strdup(chrc->path);
+#ifdef GATT_NO_RELAY
+ struct char_value *value;
+
+ value = new0(struct char_value, 1);
+ value->len = data_len;
+ value->data = data;
+ value->chrc_path = chrc_path;
+
+ queue_foreach(chrc->notify_clients,
+ emit_value_changed_signal_to_dest, value);
+
+
+ if (value)
+ g_free(value);
+#else
+ dbus_int32_t result = 0;
+
+ g_dbus_emit_signal(btd_get_dbus_connection(), chrc->path,
+ GATT_CHARACTERISTIC_IFACE, "GattValueChanged",
+ DBUS_TYPE_INT32, &result,
+ DBUS_TYPE_STRING, &chrc_path,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_len,
+ DBUS_TYPE_INVALID);
+#endif
+
+ if (chrc_path)
+ free(chrc_path);
+
+}
+#endif
+
static void notify_cb(uint16_t value_handle, const uint8_t *value,
uint16_t length, void *user_data)
{
#ifdef __TIZEN_PATCH__
gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
notify_characteristic_cb, chrc);
+
+ gatt_characteristic_value_changed(value, length, chrc);
#else
gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
write_characteristic_cb, chrc);
#endif
};
+#ifdef __TIZEN_PATCH__
+static const GDBusSignalTable characteristic_signals[] = {
+ { GDBUS_SIGNAL("GattValueChanged",
+ GDBUS_ARGS({ "Result", "i"},
+ { "Characteristic Path","s"},
+ { "GattData", "ay"})) },
+};
+#endif
+
static void characteristic_free(void *data)
{
struct characteristic *chrc = data;
chrc->service = service;
+#ifndef __TIZEN_PATCH__
gatt_db_attribute_get_char_data(attr, &chrc->handle,
&chrc->value_handle,
&chrc->props, &uuid);
+#else
+ if (!gatt_db_attribute_get_char_data(attr, &chrc->handle,
+ &chrc->value_handle,
+ &chrc->props, &uuid)) {
+ queue_destroy(chrc->descs, NULL);
+ queue_destroy(chrc->notify_clients, NULL);
+ free(chrc);
+ return NULL;
+ }
+#endif
+
chrc->attr = gatt_db_get_attribute(service->client->db,
chrc->value_handle);
+ if (!chrc->attr) {
+ error("Attribute 0x%04x not found", chrc->value_handle);
+ characteristic_free(chrc);
+ return NULL;
+ }
+
bt_uuid_to_uuid128(&uuid, &chrc->uuid);
chrc->path = g_strdup_printf("%s/char%04x", service->path,
if (!g_dbus_register_interface(btd_get_dbus_connection(), chrc->path,
GATT_CHARACTERISTIC_IFACE,
+#ifdef __TIZEN_PATCH__
+ characteristic_methods, characteristic_signals,
+#else
characteristic_methods, NULL,
+#endif
characteristic_properties,
chrc, characteristic_free)) {
error("Unable to register GATT characteristic with handle "
if (!client)
return;
+#ifdef __TIZEN_PATCH__
+ if (client->wait_charcs_id)
+ g_source_remove(client->wait_charcs_id);
+#endif
queue_destroy(client->services, unregister_service);
queue_destroy(client->all_notify_clients, NULL);
bt_gatt_client_unref(client->gatt);
notify_client_free(notify_client);
}
+#ifdef __TIZEN_PATCH__
+static void check_chrcs_ready(void *data, void *user_data)
+{
+ gboolean *chrcs_ready = user_data;
+ struct service *service = data;
+
+ /*
+ * Return FALSE if charcteristics are not ready or if there is
+ * any pending request to read char. extended properties exist.
+ */
+ if (!service->chrcs_ready ||
+ !queue_isempty(service->pending_ext_props))
+ *chrcs_ready = FALSE;
+}
+
+static gboolean check_all_chrcs_ready(gpointer user_data)
+{
+ struct btd_gatt_client *client = user_data;
+ gboolean all_chrcs_ready = TRUE;
+ static int count = 0;
+
+ queue_foreach(client->services, check_chrcs_ready, &all_chrcs_ready);
+
+ /*
+ * By adding below condition, forcing to call check_chrcs_ready()
+ * function to check whether all char./extended properties are ready.
+ * Above function would be called max. for 20 times (Assuming more
+ * no of services). Emit signal only when all characteristics are read.
+ */
+ if (all_chrcs_ready == FALSE && count < 20) {
+ count++;
+ return TRUE;
+ }
+
+ device_set_gatt_connected(client->device, TRUE);
+
+ client->wait_charcs_id = 0;
+
+ count = 0;
+
+ return FALSE;
+}
+#endif
+
void btd_gatt_client_ready(struct btd_gatt_client *client)
{
struct bt_gatt_client *gatt;
if (queue_isempty(client->services)) {
DBG("Exporting services");
create_services(client);
+#ifdef __TIZEN_PATCH__
+ /*
+ * In case of more number of services and services having
+ * characteristics extended properties; GattConnected signal
+ * should be emitted only after all the characteristics are ready.
+ * This piece of code checks all the characteristics periodically and
+ * emit the signal if characteristics are ready.
+ */
+ if (client->wait_charcs_id > 0)
+ g_source_remove(client->wait_charcs_id);
+
+ client->wait_charcs_id = g_timeout_add(100,
+ check_all_chrcs_ready, client);
+#endif
return;
}
#include <stdint.h>
#include <stdlib.h>
+#include <errno.h>
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "device.h"
#include "gatt-database.h"
#include "dbus-common.h"
+#include "profile.h"
#ifndef ATT_CID
#define ATT_CID 4
struct gatt_db_attribute *svc_chngd;
struct gatt_db_attribute *svc_chngd_ccc;
struct queue *services;
+ struct queue *profiles;
};
struct external_service {
struct queue *descs;
};
+struct external_profile {
+ struct btd_gatt_database *database;
+ char *owner;
+ char *path; /* Path to GattProfile1 */
+ unsigned int id;
+ struct queue *profiles; /* btd_profile list */
+};
+
struct external_chrc {
struct external_service *service;
char *path;
unsigned int id;
struct gatt_db_attribute *attrib;
struct queue *owner_queue;
- void *setup_data;
+ struct iovec data;
+#ifdef __TIZEN_PATCH__
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+#endif
};
struct device_state {
return queue_find(database->device_states, dev_state_match, &dev_info);
}
+#ifdef __TIZEN_PATCH__
+static bool dev_addr_match(const void *a, const void *b)
+{
+ const struct device_state *dev_state = a;
+ const struct device_info *dev_info = b;
+
+ if (bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static struct device_state *
+find_device_state_from_address(struct btd_gatt_database *database, const bdaddr_t *bdaddr)
+{
+ struct device_info dev_info;
+
+ memset(&dev_info, 0, sizeof(dev_info));
+
+ bacpy(&dev_info.bdaddr, bdaddr);
+
+ return queue_find(database->device_states, dev_addr_match, &dev_info);
+}
+#endif
+
static bool ccc_state_match(const void *a, const void *b)
{
const struct ccc_state *ccc = a;
free(service);
}
+static void profile_remove(void *data)
+{
+ struct btd_profile *p = data;
+
+ DBG("Removed \"%s\"", p->name);
+
+ adapter_foreach(adapter_remove_profile, 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;
queue_destroy(database->device_states, device_state_free);
queue_destroy(database->services, service_free);
+ queue_destroy(database->profiles, profile_free);
queue_destroy(database->ccc_callbacks, ccc_cb_free);
database->device_states = NULL;
database->ccc_callbacks = NULL;
gatt_db_attribute_read_result(attrib, id, error, value, len);
}
+#ifdef __TIZEN_PATCH__
+static void gap_rpa_res_support_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ uint8_t error = 0;
+ size_t len = 1;
+ const uint8_t *value = NULL;
+ uint8_t rpa_res_support = 0x00;
+
+ rpa_res_support = btd_adapter_get_rpa_res_support_value(database->adapter);
+
+ if (offset > 1) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ len -= offset;
+ value = len ? &rpa_res_support : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+#endif
+
static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end)
{
sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
/* Add the GAP service */
bt_uuid16_create(&uuid, UUID_GAP);
+
+#ifndef __TIZEN_PATCH__
service = gatt_db_add_service(database->db, &uuid, true, 5);
+#else
+ service = gatt_db_add_service(database->db, &uuid, true, 7);
+#endif
+
database->gap_handle = database_add_record(database, UUID_GAP, service,
"Generic Access Profile");
gap_appearance_read_cb,
NULL, database);
+#ifdef __TIZEN_PATCH__
+ /* Central address resolution characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_CENTRAL_RPA_RESOLUTION);
+ gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ gap_rpa_res_support_read_cb,
+ NULL, database);
+#endif
+
gatt_db_service_set_active(service, true);
}
bool indicate;
};
+#ifdef __TIZEN_PATCH__
+struct notify_indicate {
+ struct btd_gatt_database *database;
+ GDBusProxy *proxy;
+ uint16_t handle, ccc_handle;
+ const uint8_t *value;
+ uint16_t len;
+ bool indicate;
+};
+
+struct notify_indicate_cb {
+ GDBusProxy *proxy;
+ struct btd_device *device;
+};
+
+static void indicate_confirm_free(void *data)
+{
+ struct notify_indicate_cb *indicate = data;
+
+ if (indicate)
+ free(indicate);
+}
+
+static void indicate_confirm_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+ struct btd_device *device = user_data;
+ char dstaddr[18] = { 0 };
+ char *addr_value = NULL;
+ gboolean complete = FALSE;
+
+ ba2str(device_get_address(device), dstaddr);
+ addr_value = g_strdup(dstaddr);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &addr_value);
+
+ complete = TRUE;
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+ &complete);
+}
+
+static void indicate_confirm_reply_cb(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ DBG("Failed to send indication/notification");
+ dbus_error_free(&error);
+ return;
+ }
+}
+#endif
+
static void conf_cb(void *user_data)
{
DBG("GATT server received confirmation");
+#ifdef __TIZEN_PATCH__
+ struct notify_indicate_cb *confirm = user_data;
+
+ if (confirm) {
+ /* Send confirmation to application */
+ if (g_dbus_proxy_method_call(confirm->proxy, "IndicateConfirm",
+ indicate_confirm_setup_cb,
+ indicate_confirm_reply_cb, confirm->device,
+ NULL) == TRUE)
+ return;
+ }
+#endif
+}
+
+#ifdef __TIZEN_PATCH__
+static void send_notification_indication_to_device(void *data, void *user_data)
+{
+ struct device_state *device_state = data;
+ struct notify_indicate *notify_indicate = user_data;
+ struct ccc_state *ccc;
+ struct btd_device *device;
+ struct notify_indicate_cb *confirm;
+
+ ccc = find_ccc_state(device_state, notify_indicate->ccc_handle);
+ if (!ccc)
+ return;
+
+ if (!ccc->value[0] || (notify_indicate->indicate && !(ccc->value[0] & 0x02)))
+ return;
+
+ device = btd_adapter_get_device(notify_indicate->database->adapter,
+ &device_state->bdaddr,
+ device_state->bdaddr_type);
+ if (!device)
+ return;
+
+ confirm = new0(struct notify_indicate_cb, 1);
+ confirm->proxy = notify_indicate->proxy;
+ confirm->device = device;
+ /*
+ * TODO: If the device is not connected but bonded, send the
+ * notification/indication when it becomes connected.
+ */
+ if (!notify_indicate->indicate) {
+ DBG("GATT server sending notification");
+ bt_gatt_server_send_notification(
+ btd_device_get_gatt_server(device),
+ notify_indicate->handle, notify_indicate->value,
+ notify_indicate->len);
+ /* In case of Notification, send response to application
+ * as remote device do not respond for notification */
+ conf_cb(confirm);
+ return;
+ }
+
+ DBG("GATT server sending indication");
+
+ bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
+ notify_indicate->handle,
+ notify_indicate->value,
+ notify_indicate->len, conf_cb,
+ confirm, indicate_confirm_free);
+}
+
+static void send_notification_indication_to_devices(GDBusProxy *proxy,
+ struct btd_gatt_database *database,
+ uint16_t handle, const uint8_t *value,
+ uint16_t len, uint16_t ccc_handle,
+ bool indicate)
+{
+ struct notify_indicate notify_indicate;
+ DBG("");
+ memset(¬ify_indicate, 0, sizeof(notify_indicate));
+
+ notify_indicate.database = database;
+ notify_indicate.proxy = proxy;
+ notify_indicate.handle = handle;
+ notify_indicate.ccc_handle = ccc_handle;
+ notify_indicate.value = value;
+ notify_indicate.len = len;
+ notify_indicate.indicate = indicate;
+
+ queue_foreach(database->device_states, send_notification_indication_to_device,
+ ¬ify_indicate);
}
+static void send_unicast_notification_indication_to_device(GDBusProxy *proxy,
+ struct btd_gatt_database *database,
+ uint16_t handle, const uint8_t *value,
+ uint16_t len, uint16_t ccc_handle,
+ bool indicate, const bdaddr_t *unicast_addr)
+{
+ struct device_state *dev_state;
+ struct notify_indicate notify_indicate;
+ DBG("");
+
+ memset(¬ify_indicate, 0, sizeof(notify_indicate));
+
+ notify_indicate.database = database;
+ notify_indicate.proxy = proxy;
+ notify_indicate.handle = handle;
+ notify_indicate.ccc_handle = ccc_handle;
+ notify_indicate.value = value;
+ notify_indicate.len = len;
+ notify_indicate.indicate = indicate;
+
+ /* Find and return a device state. */
+ dev_state = find_device_state_from_address(database, unicast_addr);
+
+ if (dev_state)
+ send_notification_indication_to_device(dev_state, ¬ify_indicate);
+}
+#endif
+
static void send_notification_to_device(void *data, void *user_data)
{
struct device_state *device_state = data;
static bool parse_primary(GDBusProxy *proxy, bool *primary)
{
DBusMessageIter iter;
+ dbus_bool_t val;
if (!g_dbus_proxy_get_property(proxy, "Primary", &iter))
return false;
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
return false;
- dbus_message_iter_get_basic(&iter, primary);
+ dbus_message_iter_get_basic(&iter, &val);
+
+ *primary = val;
+
return true;
}
static uint8_t dbus_error_to_att_ecode(const char *error_name)
{
+ /* TODO: Parse error ATT ecode from error_message */
if (strcmp(error_name, "org.bluez.Error.Failed") == 0)
return 0x80; /* For now return this "application error" */
if (strcmp(error_name, "org.bluez.Error.InvalidValueLength") == 0)
return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
- if (strcmp(error_name, "org.bluez.Error.InProgress") == 0)
- return BT_ERROR_ALREADY_IN_PROGRESS;
-
return 0;
}
free(op);
}
+#ifdef __TIZEN_PATCH__
+static struct pending_op *pending_read_new(struct queue *owner_queue,
+ struct gatt_db_attribute *attrib,
+ struct bt_att *att,
+ unsigned int id)
+#else
static struct pending_op *pending_read_new(struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id)
+#endif
{
struct pending_op *op;
+#ifdef __TIZEN_PATCH__
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ char address[18];
+#endif
op = new0(struct pending_op, 1);
if (!op)
return NULL;
+#ifdef __TIZEN_PATCH__
+ if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+ free(op);
+ return NULL;
+ }
+#endif
+
op->owner_queue = owner_queue;
+#ifdef __TIZEN_PATCH__
+ memcpy(&op->bdaddr, &bdaddr, sizeof(bdaddr_t));
+ op->bdaddr_type = bdaddr_type;
+#endif
+
op->attrib = attrib;
op->id = id;
queue_push_tail(owner_queue, op);
return op;
}
+#ifdef __TIZEN_PATCH__
+static void read_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = user_data;
+ char dstaddr[18] = { 0 };
+ char *addr_value = NULL;
+ uint16_t offset = 0;
+
+ ba2str(&op->bdaddr, dstaddr);
+ addr_value = g_strdup(dstaddr);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &addr_value);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &op->id);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &offset);
+}
+#endif
+
+#ifdef __TIZEN_PATCH__
+static void send_read(struct gatt_db_attribute *attrib, struct bt_att *att,
+ GDBusProxy *proxy, struct queue *owner_queue,
+ unsigned int id)
+#else
static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
struct queue *owner_queue,
unsigned int id)
+#endif
{
struct pending_op *op;
uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+#ifdef __TIZEN_PATCH__
+ op = pending_read_new(owner_queue, attrib, att, id);
+#else
op = pending_read_new(owner_queue, attrib, id);
+#endif
if (!op) {
error("Failed to allocate memory for pending read call");
ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
goto error;
}
+#ifdef __TIZEN_PATCH__
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb, read_reply_cb,
+ op, pending_op_free) == TRUE)
+#else
if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb,
op, pending_op_free) == TRUE)
+#endif
return;
pending_op_free(op);
static void write_setup_cb(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
- struct iovec *iov = op->setup_data;
DBusMessageIter array;
+#ifdef __TIZEN_PATCH__
+ char dstaddr[18] = { 0 };
+ char *addr_value = NULL;
+ uint16_t offset = 0;
+#endif
+
+#ifdef __TIZEN_PATCH__
+ ba2str(&op->bdaddr, dstaddr);
+ addr_value = g_strdup(dstaddr);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &addr_value);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &op->id);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &offset);
+#endif
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);
+ &op->data.iov_base, op->data.iov_len);
dbus_message_iter_close_container(iter, &array);
}
gatt_db_attribute_write_result(op->attrib, op->id, ecode);
}
+#ifdef __TIZEN_PATCH__
+static struct pending_op *pending_write_new(struct queue *owner_queue,
+ struct gatt_db_attribute *attrib, struct bt_att *att,
+ unsigned int id,
+ const uint8_t *value,
+ size_t len)
+#else
static struct pending_op *pending_write_new(struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id,
const uint8_t *value,
size_t len)
+#endif
{
struct pending_op *op;
- struct iovec iov;
+#ifdef __TIZEN_PATCH__
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ char address[18];
+#endif
op = new0(struct pending_op, 1);
if (!op)
return NULL;
+#ifdef __TIZEN_PATCH__
+ if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+ free(op);
+ return NULL;
+ }
+#endif
- iov.iov_base = (uint8_t *) value;
- iov.iov_len = len;
+ op->data.iov_base = (uint8_t *) value;
+ op->data.iov_len = len;
+#ifdef __TIZEN_PATCH__
+ memcpy(&op->bdaddr, &bdaddr, sizeof(bdaddr_t));
+ op->bdaddr_type = bdaddr_type;
+#endif
op->owner_queue = owner_queue;
op->attrib = attrib;
op->id = id;
- op->setup_data = &iov;
queue_push_tail(owner_queue, op);
return op;
}
+#ifdef __TIZEN_PATCH__
+static void send_write(struct gatt_db_attribute *attrib, struct bt_att *att,
+ GDBusProxy *proxy, struct queue *owner_queue,
+ unsigned int id, const uint8_t *value, size_t len)
+#else
static void send_write(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
struct queue *owner_queue,
unsigned int id,
const uint8_t *value, size_t len)
+#endif
{
struct pending_op *op;
uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+#ifdef __TIZEN_PATCH__
+ op = pending_write_new(owner_queue, attrib, att, id, value, len);
+#else
op = pending_write_new(owner_queue, attrib, id, value, len);
+#endif
if (!op) {
error("Failed to allocate memory for pending read call");
ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
return 0;
}
+ /*
+ * TODO: All of the errors below should fall into the so called
+ * "Application Error" range. Since there is no well defined error for
+ * these, we return a generic ATT protocol error for now.
+ */
+
if (chrc->ntfy_cnt == UINT_MAX) {
/* Maximum number of per-device CCC descriptors configured */
- return BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
}
/* Don't support undefined CCC values yet */
if (value > 2 ||
(value == 1 && !(chrc->props & BT_GATT_CHRC_PROP_NOTIFY)) ||
(value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
- return BT_ERROR_CCC_IMPROPERLY_CONFIGURED;
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
/*
* Always call StartNotify for an incoming enable and ignore the return
if (g_dbus_proxy_method_call(chrc->proxy,
"StartNotify", NULL, NULL,
NULL, NULL) == FALSE)
- return BT_ATT_ERROR_UNLIKELY;
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
__sync_fetch_and_add(&chrc->ntfy_cnt, 1);
DBusMessageIter array;
uint8_t *value = NULL;
int len = 0;
+#ifdef __TIZEN_PATCH__
+ bool enable = FALSE;
+ const bdaddr_t *unicast_addr = NULL;
+#endif
+
+#ifdef __TIZEN_PATCH__
+ if (strcmp(name, "Value") == 0) {
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ DBG("Malformed \"Value\" property received");
+ return;
+ }
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ if (len < 0) {
+ DBG("Malformed \"Value\" property received");
+ return;
+ }
+
+ /* Truncate the value if it's too large */
+ len = MIN(BT_ATT_MAX_VALUE_LEN, len);
+ value = len ? value : NULL;
+ } else if (strcmp(name, "Notifying") == 0) {
+ gboolean notify_indicate = FALSE;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+ DBG("Malformed \"Notifying\" property received");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, ¬ify_indicate);
+
+ DBG("Set Notification %d", notify_indicate);
+ /* Set notification/indication */
+ set_ccc_notify_indicate(chrc->ccc, notify_indicate);
+ return;
+ } else if (strcmp(name, "Unicast") == 0) {
+ const char *address = NULL;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+ DBG("Malformed \"Value\" property received");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &address);
+
+ if (address) {
+ /* Set the address for unicast notification/indication */
+ set_ccc_unicast_address(chrc->ccc, address);
+ }
+ return;
+ } else
+ return;
+
+ enable = get_ccc_notify_indicate(chrc->ccc);
+
+ if (enable) {
+
+ unicast_addr = get_ccc_unicast_address(chrc->ccc);
+
+ if (unicast_addr && bacmp(unicast_addr, BDADDR_ANY)) {
+ send_unicast_notification_indication_to_device(proxy,
+ chrc->service->database,
+ gatt_db_attribute_get_handle(chrc->attrib),
+ value, len,
+ gatt_db_attribute_get_handle(chrc->ccc),
+ chrc->props & BT_GATT_CHRC_PROP_INDICATE,
+ unicast_addr);
+ /* reset the unicast address */
+ set_ccc_unicast_address(chrc->ccc, NULL);
+ } else
+ send_notification_indication_to_devices(proxy,
+ chrc->service->database,
+ gatt_db_attribute_get_handle(chrc->attrib),
+ value, len,
+ gatt_db_attribute_get_handle(chrc->ccc),
+ chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+
+ set_ccc_notify_indicate(chrc->ccc, FALSE);
+ }
+#else
if (strcmp(name, "Value"))
return;
value, len,
gatt_db_attribute_get_handle(chrc->ccc),
chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+#endif
}
static bool database_add_ccc(struct external_service *service,
error("Read callback called with incorrect attribute");
return;
}
-
+#ifdef __TIZEN_PATCH__
+ send_read(attrib, att, desc->proxy, desc->pending_reads, id);
+#else
send_read(attrib, desc->proxy, desc->pending_reads, id);
+#endif
}
static void desc_write_cb(struct gatt_db_attribute *attrib,
return;
}
+#ifdef __TIZEN_PATCH__
+ send_write(attrib, att, desc->proxy, desc->pending_writes, id, value, len);
+#else
send_write(attrib, desc->proxy, desc->pending_writes, id, value, len);
+#endif
}
static bool database_add_desc(struct external_service *service,
return;
}
+#ifdef __TIZEN_PATCH__
+ send_read(attrib, att, chrc->proxy, chrc->pending_reads, id);
+#else
send_read(attrib, chrc->proxy, chrc->pending_reads, id);
+#endif
}
static void chrc_write_cb(struct gatt_db_attribute *attrib,
return;
}
+#ifdef __TIZEN_PATCH__
+ send_write(attrib, att, chrc->proxy, chrc->pending_writes, id, value, len);
+#else
send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len);
+#endif
}
+#ifdef __TIZEN_PATCH__
+static bool database_check_ccc_desc(struct external_desc *desc)
+{
+ bt_uuid_t uuid, uuid_ccc;
+ char uuidstr[MAX_LEN_UUID_STR];
+
+ if (!parse_uuid(desc->proxy, &uuid)) {
+ error("Failed to read \"UUID\" property of descriptor");
+ return false;
+ }
+
+ bt_uuid16_create(&uuid_ccc, GATT_CLIENT_CHARAC_CFG_UUID);
+ if (bt_uuid_cmp(&uuid, &uuid_ccc) == 0)
+ return true;
+ else
+ return false;
+}
+#endif
+
static bool database_add_chrc(struct external_service *service,
struct external_chrc *chrc)
{
return false;
}
+#ifndef __TIZEN_PATCH__
+ /* Existing implementation adds CCC descriptor by default
+ * if notification and indication properties are set. But as per the requirment
+ * CCCD shall be added by the application */
if (!database_add_ccc(service, chrc))
return false;
+#endif
if (!database_add_cep(service, chrc))
return false;
while (entry) {
struct external_desc *desc = entry->data;
- if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path))
+ if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path)) {
+#ifdef __TIZEN_PATCH__
+ entry = entry->next;
+#endif
continue;
-
+ }
+#ifdef __TIZEN_PATCH__
+ /* Check if Application wants to add CCC and use existing
+ * implemenation to add CCC descriptors */
+ if (database_check_ccc_desc(desc)) {
+ if (!database_add_ccc(service, chrc)) {
+ chrc->attrib = NULL;
+ return false;
+ }
+ desc->attrib = chrc->ccc;
+ desc->handled = true;
+ } else if (!database_add_desc(service, desc)) {
+ chrc->attrib = NULL;
+ error("Failed to create descriptor entry");
+ return false;
+ }
+#else
if (!database_add_desc(service, desc)) {
chrc->attrib = NULL;
error("Failed to create descriptor entry");
return false;
}
-
+#endif
entry = entry->next;
}
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_add(struct external_profile *profile, const char *uuid)
+{
+ struct btd_profile *p;
+
+ p = new0(struct btd_profile, 1);
+ if (!p)
+ goto fail;
+ /* 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)
+ goto fail;
+
+ p->remote_uuid = (const void *) g_strdup(uuid);
+ if (!p->remote_uuid)
+ goto fail;
+
+ p->auto_connect = true;
+
+ queue_push_tail(profile->profiles, p);
+
+ DBG("Added \"%s\"", p->name);
+
+ return 0;
+fail:
+ error("Fail to add profile");
+
+ if (p) {
+ g_free(p->name);
+ g_free(p->remote_uuid);
+ free(p);
+ }
+
+ return -ENOMEM;
+}
+
+static void add_profile(void *data, void *user_data)
+{
+ struct btd_adapter *adapter = user_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);
+ if (!profile)
+ return -ENOMEM;
+
+ profile->owner = g_strdup(sender);
+ if (!profile->owner)
+ goto fail;
+
+ profile->path = g_strdup(path);
+ if (!profile->path)
+ goto fail;
+
+ profile->profiles = queue_new();
+ if (!profile->profiles)
+ goto fail;
+
+ 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[] = {
+#ifdef __TIZEN_PATCH__
+ { GDBUS_ASYNC_METHOD("RegisterService",
+ GDBUS_ARGS({ "service", "o" }, { "options", "a{sv}" }),
+ NULL, manager_register_service) },
+ { GDBUS_ASYNC_METHOD("UnregisterService",
+ GDBUS_ARGS({ "service", "o" }),
+ NULL, manager_unregister_service) },
+ { GDBUS_ASYNC_METHOD("RegisterProfile",
+ GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
+ { "options", "a{sv}" }), NULL,
+ manager_register_profile) },
+ { GDBUS_ASYNC_METHOD("UnregisterProfile",
+ GDBUS_ARGS({ "profile", "o" }),
+ NULL, manager_unregister_profile) },
+ { }
+#else
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService",
GDBUS_ARGS({ "service", "o" }, { "options", "a{sv}" }),
NULL, manager_register_service) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterService",
GDBUS_ARGS({ "service", "o" }),
NULL, manager_unregister_service) },
+ { 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) },
{ }
+#endif
};
struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
if (!database->services)
goto fail;
+ database->profiles = queue_new();
+ if (!database->profiles)
+ goto fail;
+
database->ccc_callbacks = queue_new();
if (!database->ccc_callbacks)
goto fail;
} else {
new_service_add = TRUE;
}
+ } else if (local_attr->type.value.u16 == GATT_CHARAC_UUID) {
+ continue;
}
/* Fix : RESOURCE_LEAK */
if (temp_att) {
attribute_cmp);
}
+#if 0
gboolean gatt_register_internet_protocol_service(struct btd_adapter *adapter)
{
bt_uuid_t uuid;
return TRUE;
}
#endif
+#endif
#ifdef __TIZEN_PATCH__
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid,
gboolean reverse_sdp;
gboolean name_resolv;
gboolean debug_keys;
+ gboolean fast_conn;
#ifdef __TIZEN_PATCH__
gboolean le_privacy;
#endif
g_free(str);
}
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "FastConnectable", &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ main_opts.fast_conn = boolean;
#ifdef __TIZEN_PATCH__
boolean = g_key_file_get_boolean(config, "General",
"EnableLEPrivacy", &err);
g_dbus_set_flags(gdbus_flags);
-#ifdef __TIZEN_PATCH__
+#if 0
gatt_init();
#endif
adapter_cleanup();
-#ifdef __TIZEN_PATCH__
+#if 0
gatt_cleanup();
#endif
# Possible values: "off", "single", "multiple"
#MultiProfile = off
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
#ifdef __TIZEN_PATCH__
# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
# otherwise the feature is disabled by default for the local device.
# timeout). The policy plugin should contain a sane set of values by
# default, but this list can be overridden here. By setting the list to
# empty the reconnection feature gets disabled.
-#ReconnectUUIDs=
\ No newline at end of file
+#ReconnectUUIDs=
+
# Possible values: "dual", "bredr", "le"
#ControllerMode = dual
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
#ifdef __TIZEN_PATCH__
# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
# otherwise the feature is disabled by default for the local device.
<attribute id=\"0x0317\"> \
<uint32 value=\"0x00000003\"/> \
</attribute> \
- <attribute id=\"0x0200\"> \
- <uint16 value=\"%u\" name=\"psm\"/> \
- </attribute> \
</record>"
#define MAS_RECORD \
<attribute id=\"0x0317\"> \
<uint32 value=\"0x0000007f\"/> \
</attribute> \
- <attribute id=\"0x0200\"> \
- <uint16 value=\"%u\" name=\"psm\"/> \
- </attribute> \
</record>"
#define MNS_RECORD \
<attribute id=\"0x0100\"> \
<text value=\"%s\"/> \
</attribute> \
- <attribute id=\"0x0317\"> \
- <uint32 value=\"0x0000007f\"/> \
- </attribute> \
<attribute id=\"0x0200\"> \
<uint16 value=\"%u\" name=\"psm\"/> \
</attribute> \
+ <attribute id=\"0x0317\"> \
+ <uint32 value=\"0x0000007f\"/> \
+ </attribute> \
</record>"
#define SYNC_RECORD \
char *uuid;
char *service;
char *role;
+
char *record;
char *(*get_record)(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm);
static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
- uint16_t psm = 0;
- uint8_t chan = 0;
-
- if (l2cap)
- psm = l2cap->psm;
- if (rfcomm)
- chan = rfcomm->chan;
-
- return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm);
+ return g_strdup_printf(PSE_RECORD, rfcomm->chan, ext->version,
+ ext->name);
}
static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
- uint16_t psm = 0;
- uint8_t chan = 0;
-
- if (l2cap)
- psm = l2cap->psm;
- if (rfcomm)
- chan = rfcomm->chan;
-
- return g_strdup_printf(MAS_RECORD, chan, ext->version, ext->name, psm);
+ return g_strdup_printf(MAS_RECORD, rfcomm->chan, ext->version,
+ ext->name);
}
static char *get_mns_record(struct ext_profile *ext, struct ext_io *l2cap,
.uuid = OBEX_PSE_UUID,
.name = "Phone Book Access",
.channel = PBAP_DEFAULT_CHANNEL,
- .psm = BTD_PROFILE_PSM_AUTO,
- .mode = BT_IO_MODE_ERTM,
.authorize = true,
.get_record = get_pse_record,
.version = 0x0101,
.uuid = OBEX_MAS_UUID,
.name = "Message Access",
.channel = MAS_DEFAULT_CHANNEL,
- .psm = BTD_PROFILE_PSM_AUTO,
- .mode = BT_IO_MODE_ERTM,
.authorize = true,
.get_record = get_mas_record,
.version = 0x0100
#define BT_ATT_ERROR_INSUFFICIENT_RESOURCES 0x11
/*
- * Common Profile and Service Error Code descriptions (see Supplement to the
- * Bluetooth Core Specification, sections 1.2 and 2). The error codes within
- * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
- * following:
- */
-#define BT_ERROR_CCC_IMPROPERLY_CONFIGURED 0xfd
-#define BT_ERROR_ALREADY_IN_PROGRESS 0xfe
-#define BT_ERROR_OUT_OF_RANGE 0xff
-
-/*
* ATT attribute permission bitfield values. Permissions are grouped as
* "Access", "Encryption", "Authentication", and "Authorization". A bitmask of
* permissions is a byte that encodes a combination of these.
#define ATT_OP_SIGNED_MASK 0x80
#define ATT_TIMEOUT_INTERVAL 30000 /* 30000 ms */
+/*
+ * Common Profile and Service Error Code descriptions (see Supplement to the
+ * Bluetooth Core Specification, sections 1.2 and 2). The error codes within
+ * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
+ * following:
+ */
+#define BT_ERROR_CCC_IMPROPERLY_CONFIGURED 0xfd
+#define BT_ERROR_ALREADY_IN_PROGRESS 0xfe
+#define BT_ERROR_OUT_OF_RANGE 0xff
+
/* Length of signature in write signed packet */
#define BT_ATT_SIGNATURE_LEN 12
static void respond_not_supported(struct bt_att *att, uint8_t opcode)
{
- struct bt_att_pdu_error_rsp pdu;
+ uint8_t pdu[4];
- pdu.opcode = opcode;
- pdu.handle = 0x0000;
- pdu.ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ pdu[0] = opcode;
+ pdu[1] = 0;
+ pdu[2] = 0;
+ pdu[3] = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
- bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu), NULL, NULL,
+ bt_att_send(att, BT_ATT_OP_ERROR_RSP, pdu, sizeof(pdu), NULL, NULL,
NULL);
}
return sign_set_key(&att->remote_sign, sign_key, func, user_data);
}
+
+bool bt_att_has_crypto(struct bt_att *att)
+{
+ if (!att)
+ return false;
+
+ return att->crypto ? true : false;
+}
bt_att_counter_func_t func, void *user_data);
bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16],
bt_att_counter_func_t func, void *user_data);
+bool bt_att_has_crypto(struct bt_att *att);
#include <config.h>
#endif
+#ifdef __TIZEN_PATCH__
+#include <stdio.h>
+#endif
#include <endian.h>
#include <fcntl.h>
#include <unistd.h>
uint16_t index;
bool aborted;
bool pklg_format;
+#ifdef __TIZEN_PATCH__
+ char *path;
+ int16_t rotate_count;
+ ssize_t file_size;
+#endif
};
struct btsnoop *btsnoop_open(const char *path, unsigned long flags)
return NULL;
}
+#ifdef __TIZEN_PATCH__
+struct btsnoop *btsnoop_create(const char *path, uint32_t type,
+ int16_t rotate_count, ssize_t file_size)
+#else
struct btsnoop *btsnoop_create(const char *path, uint32_t type)
+#endif
{
struct btsnoop *btsnoop;
struct btsnoop_hdr hdr;
return NULL;
}
+#ifdef __TIZEN_PATCH__
+ if (rotate_count > 0 && file_size > 0) {
+ btsnoop->path = strdup(path);
+ btsnoop->rotate_count = rotate_count;
+ btsnoop->file_size = file_size;
+ }
+#endif
+
return btsnoop_ref(btsnoop);
}
+#ifdef __TIZEN_PATCH__
+static int btsnoop_create_2(struct btsnoop *btsnoop)
+{
+ struct btsnoop_hdr hdr;
+ ssize_t written;
+
+ if (btsnoop->fd >= 0)
+ close(btsnoop->fd);
+
+ btsnoop->fd = open(btsnoop->path,
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (btsnoop->fd < 0) {
+ btsnoop_unref(btsnoop);
+ return -1;
+ }
+
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htobe32(btsnoop_version);
+ hdr.type = htobe32(btsnoop->type);
+
+ written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
+ if (written < 0) {
+ btsnoop_unref(btsnoop);
+ return -1;
+ }
+
+ return btsnoop->fd;
+}
+
+static void btsnoop_rotate_files(struct btsnoop *btsnoop)
+{
+ char *filename = NULL;
+ char *new_filename = NULL;
+ int i;
+ int postfix_width = 0;
+ int err;
+
+ if (btsnoop->rotate_count <= 1)
+ return;
+
+ for (i = btsnoop->rotate_count / 10; i; i /= 10)
+ postfix_width++;
+
+ for (i = btsnoop->rotate_count - 2; i >= 0; i--) {
+ if (i == 0) {
+ filename = strdup(btsnoop->path);
+ err = (filename == NULL) ? -1 : 0;
+ } else {
+ err = asprintf(&filename, "%s.%0*d",
+ btsnoop->path, postfix_width, i);
+ }
+
+ if (err < 0 || access(filename, F_OK) < 0)
+ goto done;
+
+ err = asprintf(&new_filename, "%s.%0*d",
+ btsnoop->path, postfix_width, i + 1);
+ if (err < 0)
+ goto done;
+
+ err = rename(filename, new_filename);
+
+done:
+ if (new_filename) {
+ free(new_filename);
+ new_filename = NULL;
+ }
+
+ if (filename) {
+ free(filename);
+ filename = NULL;
+ }
+
+ if (err < 0)
+ break;
+ }
+
+ return;
+}
+#endif
+
struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop)
{
if (!btsnoop)
if (__sync_sub_and_fetch(&btsnoop->ref_count, 1))
return;
+#ifdef __TIZEN_PATCH__
+ if (btsnoop->path)
+ free(btsnoop->path);
+#endif
+
if (btsnoop->fd >= 0)
close(btsnoop->fd);
pkt.drops = htobe32(0);
pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);
+#ifdef __TIZEN_PATCH__
+ if ((btsnoop->rotate_count > 0 && btsnoop->file_size > 0) &&
+ lseek(btsnoop->fd, 0x00, SEEK_CUR) +
+ BTSNOOP_PKT_SIZE + size > btsnoop->file_size) {
+ btsnoop_rotate_files(btsnoop);
+ if (btsnoop_create_2(btsnoop) < 0)
+ return false;
+ }
+#endif
+
written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
if (written < 0)
return false;
struct btsnoop;
struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
+#ifdef __TIZEN_PATCH__
+struct btsnoop *btsnoop_create(const char *path, uint32_t type,
+ int16_t rotate_count, ssize_t file_size);
+#else
struct btsnoop *btsnoop_create(const char *path, uint32_t type);
+#endif
struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
void btsnoop_unref(struct btsnoop *btsnoop);
struct queue *long_write_queue;
bool in_long_write;
+ unsigned int reliable_write_session_id;
+
/* List of registered disconnect/notification/indication callbacks */
struct queue *notify_list;
struct queue *notify_chrcs;
struct request {
struct bt_gatt_client *client;
bool long_write;
+ bool prep_write;
bool removed;
int ref_count;
unsigned int id;
static void cancel_long_write_cb(uint8_t opcode, const void *pdu, uint16_t len,
void *user_data)
{
- /* Do nothing */
+ struct bt_gatt_client *client = user_data;
+
+ if (queue_isempty(client->long_write_queue))
+ client->in_long_write = false;
}
-bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id)
+static bool cancel_long_write_req(struct bt_gatt_client *client,
+ struct request *req)
{
- struct request *req;
uint8_t pdu = 0x00;
- if (!client || !id || !client->att)
- return false;
-
- req = queue_remove_if(client->pending_requests, match_req_id,
- UINT_TO_PTR(id));
- if (!req)
- return false;
-
- req->removed = true;
-
- if (!bt_att_cancel(client->att, req->att_id) && !req->long_write)
- return false;
-
- /* If this was a long-write, we need to abort all prepared writes */
- if (!req->long_write)
- return true;
-
+ /*
+ * att_id == 0 means that request has been queued and no prepare write
+ * has been sent so far.Let's just remove if from the queue.
+ * Otherwise execute write needs to be send.
+ */
if (!req->att_id)
- queue_remove(client->long_write_queue, req);
- else
- bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ,
- &pdu, sizeof(pdu),
+ return queue_remove(client->long_write_queue, req);
+
+ return !!bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+ sizeof(pdu),
cancel_long_write_cb,
- NULL, NULL);
+ client, NULL);
- if (queue_isempty(client->long_write_queue))
- client->in_long_write = false;
+}
- return true;
+static void cancel_prep_write_cb(uint8_t opcode, const void *pdu, uint16_t len,
+ void *user_data)
+{
+ struct request *req = user_data;
+ struct bt_gatt_client *client = req->client;
+
+ client->reliable_write_session_id = 0;
}
-static void cancel_request(void *data)
+static bool cancel_prep_write_session(struct bt_gatt_client *client,
+ struct request *req)
{
- struct request *req = data;
uint8_t pdu = 0x00;
+ return !!bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+ sizeof(pdu),
+ cancel_prep_write_cb,
+ req, request_unref);
+}
+
+static bool cancel_request(struct request *req)
+{
req->removed = true;
- if (!req->long_write) {
- bt_att_cancel(req->client->att, req->att_id);
- return;
- }
+ if (req->long_write)
+ return cancel_long_write_req(req->client, req);
- if (!req->att_id)
- queue_remove(req->client->long_write_queue, req);
+ if (req->prep_write)
+ return cancel_prep_write_session(req->client, req);
- if (queue_isempty(req->client->long_write_queue))
- req->client->in_long_write = false;
+ return bt_att_cancel(req->client->att, req->att_id);
+}
- bt_att_send(req->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
- &pdu, sizeof(pdu),
- cancel_long_write_cb,
- NULL, NULL);
+bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id)
+{
+ struct request *req;
+
+ if (!client || !id || !client->att)
+ return false;
- bt_att_cancel(req->client->att, req->att_id);
+ req = queue_remove_if(client->pending_requests, match_req_id,
+ UINT_TO_PTR(id));
+ if (!req)
+ return false;
+
+ return cancel_request(req);
}
bool bt_gatt_client_cancel_all(struct bt_gatt_client *client)
if (!client || !client->att)
return false;
- queue_remove_all(client->pending_requests, NULL, NULL, cancel_request);
+ queue_remove_all(client->pending_requests, NULL, NULL,
+ (queue_destroy_func_t) cancel_request);
if (client->discovery_req) {
bt_gatt_request_cancel(client->discovery_req);
}
struct write_op {
+ struct bt_gatt_client *client;
bt_gatt_client_callback_t callback;
void *user_data;
bt_gatt_destroy_func_t destroy;
req->destroy = long_write_op_free;
req->long_write = true;
- if (client->in_long_write) {
+ if (client->in_long_write || client->reliable_write_session_id > 0) {
queue_push_tail(client->long_write_queue, req);
return req->id;
}
return req->id;
}
+struct prep_write_op {
+ bt_gatt_client_write_long_callback_t callback;
+ void *user_data;
+ bt_gatt_destroy_func_t destroy;
+ uint8_t *pdu;
+ uint16_t pdu_len;
+};
+
+static void destroy_prep_write_op(void *data)
+{
+ struct prep_write_op *op = data;
+
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ free(op->pdu);
+ free(op);
+}
+
+static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct request *req = user_data;
+ struct prep_write_op *op = req->data;
+ bool success;
+ uint8_t att_ecode;
+ bool reliable_error;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ reliable_error = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_PREP_WRITE_RSP) {
+ success = false;
+ reliable_error = false;
+ att_ecode = 0;
+ goto done;
+ }
+
+ if (!pdu || length != op->pdu_len ||
+ memcmp(pdu, op->pdu, op->pdu_len)) {
+ success = false;
+ reliable_error = true;
+ att_ecode = 0;
+ goto done;
+ }
+
+ success = true;
+ reliable_error = false;
+ att_ecode = 0;
+
+done:
+ if (op->callback)
+ op->callback(success, reliable_error, att_ecode, op->user_data);
+}
+
+static struct request *get_reliable_request(struct bt_gatt_client *client,
+ unsigned int id)
+{
+ struct request *req;
+ struct prep_write_op *op;
+
+ op = new0(struct prep_write_op, 1);
+ if (!op)
+ return NULL;
+
+ /* Following prepare writes */
+ if (id != 0)
+ req = queue_find(client->pending_requests, match_req_id,
+ UINT_TO_PTR(id));
+ else
+ req = request_create(client);
+
+ if (!req) {
+ free(op);
+ return NULL;
+ }
+
+ req->data = op;
+
+ return req;
+}
+
+unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
+ unsigned int id, uint16_t value_handle,
+ uint16_t offset, const uint8_t *value,
+ uint16_t length,
+ bt_gatt_client_write_long_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ struct request *req;
+ struct prep_write_op *op;
+ uint8_t pdu[4 + length];
+
+ if (!client)
+ return 0;
+
+ if (client->in_long_write)
+ return 0;
+
+ /*
+ * Make sure that client who owns reliable session continues with
+ * prepare writes or this is brand new reliable session (id == 0)
+ */
+ if (id != client->reliable_write_session_id) {
+ util_debug(client->debug_callback, client->debug_data,
+ "There is other reliable write session ongoing %u",
+ client->reliable_write_session_id);
+
+ return 0;
+ }
+
+ req = get_reliable_request(client, id);
+ if (!req)
+ return 0;
+
+ op = (struct prep_write_op *)req->data;
+
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ req->destroy = destroy_prep_write_op;
+ req->prep_write = true;
+
+ put_le16(value_handle, pdu);
+ put_le16(offset, pdu + 2);
+ memcpy(pdu + 4, value, length);
+
+ /*
+ * Before sending command we need to remember pdu as we need to validate
+ * it in the response. Store handle, offset and value. Therefore
+ * increase length by 4 (handle + offset) as we need it in couple places
+ * below
+ */
+ length += 4;
+
+ op->pdu = malloc(length);
+ if (!op->pdu) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
+ }
+
+ memcpy(op->pdu, pdu, length);
+ op->pdu_len = length;
+
+ /*
+ * Now we are ready to send command
+ * Note that request_unref will be done on write execute
+ */
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ, pdu,
+ sizeof(pdu), prep_write_cb, req,
+ NULL);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
+ }
+
+ /*
+ * Store first request id for prepare write and treat it as a session id
+ * valid until write execute is done
+ */
+ if (client->reliable_write_session_id == 0)
+ client->reliable_write_session_id = req->id;
+
+ return client->reliable_write_session_id;
+}
+
+static void exec_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct request *req = user_data;
+ struct write_op *op = req->data;
+ bool success;
+ uint8_t att_ecode;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_EXEC_WRITE_RSP || pdu || length) {
+ success = false;
+ att_ecode = 0;
+ goto done;
+ }
+
+ success = true;
+ att_ecode = 0;
+
+done:
+ if (op->callback)
+ op->callback(success, att_ecode, op->user_data);
+
+ op->client->reliable_write_session_id = 0;
+
+ start_next_long_write(op->client);
+}
+
+unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client,
+ unsigned int id,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ struct request *req;
+ struct write_op *op;
+ uint8_t pdu;
+
+ if (!client)
+ return 0;
+
+ if (client->in_long_write)
+ return 0;
+
+ if (client->reliable_write_session_id != id)
+ return 0;
+
+ op = new0(struct write_op, 1);
+ if (!op)
+ return 0;
+
+ req = queue_find(client->pending_requests, match_req_id,
+ UINT_TO_PTR(id));
+ if (!req) {
+ free(op);
+ return 0;
+ }
+
+ op->client = client;
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ pdu = 0x01;
+
+ req->data = op;
+ req->destroy = destroy_write_op;
+
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+ sizeof(pdu), exec_write_cb,
+ req, request_unref);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
+ }
+
+ return id;
+}
+
static bool match_notify_chrc_value_handle(const void *a, const void *b)
{
const struct notify_chrc *chrc = a;
bt_gatt_client_write_long_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
+ unsigned int id,
+ uint16_t value_handle, uint16_t offset,
+ const uint8_t *value, uint16_t length,
+ bt_gatt_client_write_long_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client,
+ unsigned int id,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
uint16_t chrc_value_handle,
uint32_t permissions;
uint16_t value_len;
uint8_t *value;
+#ifdef __TIZEN_PATCH__
+ bool notify_indicate;
+ bdaddr_t unicast_addr;
+#endif
gatt_db_read_t read_func;
gatt_db_write_t write_func;
return true;
}
+
+#ifdef __TIZEN_PATCH__
+void set_ccc_notify_indicate(struct gatt_db_attribute *ccc,
+ bool enable)
+{
+ if (ccc)
+ ccc->notify_indicate = enable;
+}
+
+bool get_ccc_notify_indicate(const struct gatt_db_attribute *ccc)
+{
+ if (ccc)
+ return ccc->notify_indicate;
+
+ return false;
+}
+
+void set_ccc_unicast_address(const struct gatt_db_attribute *ccc,
+ const char *address)
+{
+ if (ccc)
+ str2ba(address, &ccc->unicast_addr);
+}
+
+bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc)
+{
+ if (ccc)
+ return &ccc->unicast_addr;
+ return NULL;
+}
+#endif
unsigned int id, int err);
bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib);
+
+#ifdef __TIZEN_PATCH__
+void set_ccc_notify_indicate(struct gatt_db_attribute *ccc,
+ bool enable);
+
+bool get_ccc_notify_indicate(const struct gatt_db_attribute *ccc);
+
+void set_ccc_unicast_address(const struct gatt_db_attribute *ccc,
+ const char *address);
+
+bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc);
+#endif
struct bt_gatt_request {
struct bt_att *att;
unsigned int id;
+ uint16_t start_handle;
uint16_t end_handle;
int ref_count;
bt_uuid_t uuid;
static void discovery_op_complete(struct bt_gatt_request *op, bool success,
uint8_t ecode)
{
+ /* Reset success if there is some result to report */
+ if (ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND && op->result_head)
+ success = true;
+
if (op->callback)
op->callback(success, ecode, success ? op->result_head : NULL,
op->user_data);
if (opcode == BT_ATT_OP_ERROR_RSP) {
success = false;
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto success;
-
goto done;
}
}
last_end = get_le16(pdu + length - data_length + 2);
+
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last_end < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_end + 1;
+
if (last_end < op->end_handle) {
uint8_t pdu[6];
- put_le16(last_end + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(op->service_type, pdu + 4);
put_le16(op->end_handle,
cur_result->pdu + length - data_length + 1);
-success:
success = true;
done:
if (opcode == BT_ATT_OP_ERROR_RSP) {
success = false;
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto success;
-
goto done;
}
* last_end is end handle of last data set
*/
last_end = get_le16(pdu + length - 2);
+
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last_end < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_end + 1;
+
if (last_end < op->end_handle) {
uint8_t pdu[6 + get_uuid_len(&op->uuid)];
- put_le16(last_end + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(op->service_type, pdu + 4);
bt_uuid_to_le(&op->uuid, pdu + 6);
goto done;
}
-success:
- success = true;
+ success = false;
done:
discovery_op_complete(op, success, att_ecode);
return NULL;
op->att = att;
+ op->start_handle = start;
op->end_handle = end;
op->callback = callback;
op->user_data = user_data;
if (opcode == BT_ATT_OP_ERROR_RSP) {
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto done;
-
success = false;
goto failed;
}
}
last_handle = get_le16(pdu + length - data_length);
+
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last_handle < op->start_handle) {
+ success = false;
+ goto failed;
+ }
+
+ op->start_handle = last_handle + 1;
if (last_handle != op->end_handle) {
uint8_t pdu[6];
- put_le16(last_handle + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(GATT_INCLUDE_UUID, pdu + 4);
goto failed;
}
-done:
success = true;
failed:
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ op->start_handle = start;
op->end_handle = end;
put_le16(start, pdu);
if (opcode == BT_ATT_OP_ERROR_RSP) {
success = false;
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto success;
-
goto done;
}
goto done;
}
last_handle = get_le16(pdu + length - data_length);
+
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last_handle < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_handle + 1;
+
if (last_handle != op->end_handle) {
uint8_t pdu[6];
- put_le16(last_handle + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(GATT_CHARAC_UUID, pdu + 4);
goto done;
}
-success:
- success = true;
-
done:
discovery_op_complete(op, success, att_ecode);
}
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ op->start_handle = start;
op->end_handle = end;
put_le16(start, pdu);
if (opcode == BT_ATT_OP_ERROR_RSP) {
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- success = true;
- else
- success = false;
-
+ success = false;
goto done;
}
}
last_handle = get_le16(pdu + length - data_length);
+
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last_handle < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_handle + 1;
+
if (last_handle != op->end_handle) {
uint8_t pdu[4 + get_uuid_len(&op->uuid)];
- put_le16(last_handle + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
bt_uuid_to_le(&op->uuid, pdu + 4);
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ op->start_handle = start;
op->end_handle = end;
op->uuid = *uuid;
if (opcode == BT_ATT_OP_ERROR_RSP) {
success = false;
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto success;
-
goto done;
}
}
last_handle = get_le16(pdu + length - data_length);
+
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last_handle < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_handle + 1;
+
if (last_handle != op->end_handle) {
uint8_t pdu[4];
- put_le16(last_handle + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
op->id = bt_att_send(op->att, BT_ATT_OP_FIND_INFO_REQ,
return;
success = false;
- goto done;
}
-success:
success = true;
done:
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ op->start_handle = start;
op->end_handle = end;
put_le16(start, pdu);
gdouble end_time;
unsigned int timeout;
unsigned int timeout_id;
+ unsigned int teardown_id;
tester_destroy_func_t destroy;
void *user_data;
};
if (test->timeout_id > 0)
g_source_remove(test->timeout_id);
+ if (test->teardown_id > 0)
+ g_source_remove(test->teardown_id);
+
if (test->destroy)
test->destroy(test->user_data);
{
struct test_case *test = user_data;
+ test->teardown_id = 0;
test->stage = TEST_STAGE_TEARDOWN;
print_progress(test->name, COLOR_MAGENTA, "teardown");
test->post_teardown_func(test->test_data);
}
-void tester_test_passed(void)
+static void test_result(enum test_result result)
{
struct test_case *test;
test->timeout_id = 0;
}
- test->result = TEST_RESULT_PASSED;
- print_progress(test->name, COLOR_GREEN, "test passed");
-
- g_idle_add(teardown_callback, test);
-}
-
-void tester_test_failed(void)
-{
- struct test_case *test;
+ test->result = result;
+ switch (result) {
+ case TEST_RESULT_PASSED:
+ print_progress(test->name, COLOR_GREEN, "test passed");
+ break;
+ case TEST_RESULT_FAILED:
+ print_progress(test->name, COLOR_RED, "test failed");
+ break;
+ case TEST_RESULT_NOT_RUN:
+ print_progress(test->name, COLOR_YELLOW, "test not run");
+ break;
+ case TEST_RESULT_TIMED_OUT:
+ print_progress(test->name, COLOR_RED, "test timed out");
+ break;
+ }
- if (!test_current)
+ if (test->teardown_id > 0)
return;
- test = test_current->data;
-
- if (test->stage != TEST_STAGE_RUN)
- return;
+ test->teardown_id = g_idle_add(teardown_callback, test);
+}
- if (test->timeout_id > 0) {
- g_source_remove(test->timeout_id);
- test->timeout_id = 0;
- }
+void tester_test_passed(void)
+{
+ test_result(TEST_RESULT_PASSED);
+}
- test->result = TEST_RESULT_FAILED;
- print_progress(test->name, COLOR_RED, "test failed");
+void tester_test_failed(void)
+{
+ test_result(TEST_RESULT_FAILED);
+}
- g_idle_add(teardown_callback, test);
+void tester_test_abort(void)
+{
+ test_result(TEST_RESULT_NOT_RUN);
}
void tester_teardown_complete(void)
void tester_test_passed(void);
void tester_test_failed(void);
+void tester_test_abort(void);
void tester_teardown_complete(void);
void tester_teardown_failed(void);
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+import dbus.exceptions
+import dbus.mainloop.glib
+import dbus.service
+
+import array
+import gobject
+
+from random import randint
+
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'
+
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+
+class NotSupportedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotSupported'
+
+
+class NotPermittedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotPermitted'
+
+
+class InvalidValueLengthException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
+
+
+class FailedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.Failed'
+
+
+class Advertisement(dbus.service.Object):
+ PATH_BASE = '/org/bluez/example/advertisement'
+
+ def __init__(self, bus, index, advertising_type):
+ self.path = self.PATH_BASE + str(index)
+ self.bus = bus
+ self.ad_type = advertising_type
+ self.service_uuids = None
+ self.manufacturer_data = None
+ self.solicit_uuids = None
+ self.service_data = None
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ properties = dict()
+ properties['Type'] = self.ad_type
+ if self.service_uuids is not None:
+ properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
+ signature='s')
+ if self.solicit_uuids is not None:
+ properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
+ signature='s')
+ if self.manufacturer_data is not None:
+ properties['ManufacturerData'] = dbus.Dictionary(
+ self.manufacturer_data, signature='qay')
+ if self.service_data is not None:
+ properties['ServiceData'] = dbus.Dictionary(self.service_data,
+ signature='say')
+ return {LE_ADVERTISEMENT_IFACE: properties}
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_service_uuid(self, uuid):
+ if not self.service_uuids:
+ self.service_uuids = []
+ self.service_uuids.append(uuid)
+
+ def add_solicit_uuid(self, uuid):
+ if not self.solicit_uuids:
+ self.solicit_uuids = []
+ self.solicit_uuids.append(uuid)
+
+ def add_manufacturer_data(self, manuf_code, data):
+ if not self.manufacturer_data:
+ self.manufacturer_data = dict()
+ self.manufacturer_data[manuf_code] = data
+
+ def add_service_data(self, uuid, data):
+ if not self.service_data:
+ self.service_data = dict()
+ self.service_data[uuid] = data
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ print 'GetAll'
+ if interface != LE_ADVERTISEMENT_IFACE:
+ raise InvalidArgsException()
+ print 'returning props'
+ return self.get_properties()[LE_ADVERTISEMENT_IFACE]
+
+ @dbus.service.method(LE_ADVERTISEMENT_IFACE,
+ in_signature='',
+ out_signature='')
+ def Release(self):
+ print '%s: Released!' % self.path
+
+class TestAdvertisement(Advertisement):
+
+ def __init__(self, bus, index):
+ Advertisement.__init__(self, bus, index, 'broadcast')
+ self.add_service_uuid('0000180D-0000-1000-8000-00805F9B34FB')
+ self.add_service_uuid('0000180F-0000-1000-8000-00805F9B34FB')
+ self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
+ self.add_service_data('00009999-0000-1000-8000-00805F9B34FB',
+ [0x00, 0x01, 0x02, 0x03, 0x04])
+
+
+def register_ad_cb():
+ print 'Advertisement registered'
+
+
+def register_ad_error_cb(error):
+ print 'Failed to register advertisement: ' + str(error)
+ mainloop.quit()
+
+
+def find_adapter(bus):
+ remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
+ DBUS_OM_IFACE)
+ objects = remote_om.GetManagedObjects()
+
+ for o, props in objects.iteritems():
+ if LE_ADVERTISING_MANAGER_IFACE in props:
+ return o
+
+ return None
+
+
+def main():
+ global mainloop
+
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ adapter = find_adapter(bus)
+ if not adapter:
+ print 'LEAdvertisingManager1 interface not found'
+ return
+
+ ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+ LE_ADVERTISING_MANAGER_IFACE)
+
+ test_advertisement = TestAdvertisement(bus, 0)
+
+ mainloop = gobject.MainLoop()
+
+ ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
+ reply_handler=register_ad_cb,
+ error_handler=register_ad_error_cb)
+
+ mainloop.run()
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "lib/bnep.h"
+#include "profiles/network/bnep.h"
+
+enum {
+ MODE_LISTEN,
+ MODE_CONNECT,
+};
+
+static GMainLoop *mloop;
+static GIOChannel *bnep_io;
+static struct bnep *session;
+
+static int mode;
+static bool no_close_after_disconn;
+static int send_frame_timeout;
+
+static bdaddr_t src_addr, dst_addr;
+static char iface[16];
+static char bridge[16];
+static bool send_ctrl_msg_type_set = false;
+static uint8_t ctrl_msg_type = 0x00;
+static bool send_bnep_msg_type_set = false;
+static uint8_t bnep_msg_type = 0x00;
+static int ctrl_msg_retransmition_nb = 0;
+static int bnep_msg_retransmission_nb = 0;
+static uint16_t local_role = BNEP_SVC_PANU;
+static uint16_t remote_role = BNEP_SVC_NAP;
+static uint16_t ntw_proto_down_range = 0x0000;
+static uint16_t ntw_proto_up_range = 0xdc05;
+static uint16_t ntw_proto_type = 0x0000;
+static uint8_t mcast_addr_down_range[6];
+static uint8_t mcast_addr_up_range[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+static uint8_t src_hw_addr[6];
+static uint8_t dst_hw_addr[6];
+static uint8_t general_frame_payload[] = "abcdef0123456789_bnep_test_data";
+
+static int set_forward_delay(int sk)
+{
+ unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0, 0, 0 };
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
+ ifr.ifr_data = (char *) args;
+
+ if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) {
+ error("setting forward delay failed: %d (%s)",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nap_create_bridge(void)
+{
+ int sk, err;
+
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -EOPNOTSUPP;
+
+ if (ioctl(sk, SIOCBRADDBR, bridge) < 0) {
+ if (errno != EEXIST) {
+ close(sk);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ err = set_forward_delay(sk);
+ if (err < 0) {
+ printf("failed to set forward delay\n");
+ ioctl(sk, SIOCBRDELBR, bridge);
+ }
+
+ close(sk);
+
+ return err;
+}
+
+static int cleanup(void)
+{
+ bnep_cleanup();
+
+ if (mode == MODE_LISTEN)
+ bnep_server_delete(bridge, iface, &dst_addr);
+
+ if (bnep_io) {
+ g_io_channel_shutdown(bnep_io, TRUE, NULL);
+ g_io_channel_unref(bnep_io);
+ bnep_io = NULL;
+ }
+
+ return 0;
+}
+
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ printf("%s\n", __func__);
+
+ if (no_close_after_disconn)
+ return FALSE;
+
+ /* Cleanup since it's called when disconnected l2cap */
+ if (cleanup() < 0) {
+ printf("cleanup went wrong...\n");
+ return FALSE;
+ }
+
+ g_main_loop_quit(mloop);
+ return FALSE;
+}
+
+static ssize_t send_compressed_frame(int sk, uint8_t type)
+{
+ uint8_t frame[100];
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ frame[0] = type;
+ memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr));
+ memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr));
+ frame[13] = ntw_proto_type & 0xff;
+ frame[14] = (ntw_proto_type >> 8);
+ memcpy(&frame[15], general_frame_payload,
+ sizeof(general_frame_payload));
+
+ /* TODO - set frame payload by user */
+ return send(sk, frame, 15 + sizeof(general_frame_payload), 0);
+}
+
+static ssize_t send_general_frame(int sk)
+{
+ uint8_t frame[100];
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ frame[0] = BNEP_GENERAL;
+ memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr));
+ memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr));
+ frame[13] = ntw_proto_type & 0xff;
+ frame[14] = (ntw_proto_type >> 8);
+ memcpy(&frame[15], general_frame_payload,
+ sizeof(general_frame_payload));
+
+ /* TODO - set frame payload by user */
+ return send(sk, frame, 15 + sizeof(general_frame_payload), 0);
+}
+
+static ssize_t send_ctrl_frame(int sk)
+{
+ /*
+ * Max buff size = type(1byte) + ctrl(1byte) + len(2byte) +
+ * mcast_addr_down(6byte) + mcast_addr_up(6byte)
+ */
+ uint8_t buff[16];
+ struct bnep_set_filter_req *frame = (void *) buff;
+ int err;
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ switch (ctrl_msg_type) {
+ case BNEP_FILTER_NET_TYPE_SET:
+ frame->type = BNEP_CONTROL;
+ frame->ctrl = ctrl_msg_type;
+ frame->len = htons(sizeof(ntw_proto_down_range) +
+ sizeof(ntw_proto_up_range));
+ memcpy(frame->list, &ntw_proto_down_range,
+ sizeof(ntw_proto_down_range));
+ memcpy(frame->list + sizeof(ntw_proto_down_range),
+ &ntw_proto_up_range, sizeof(ntw_proto_up_range));
+
+ err = send(sk, frame, sizeof(*frame) +
+ sizeof(ntw_proto_down_range) +
+ sizeof(ntw_proto_up_range), 0);
+ break;
+ case BNEP_FILTER_MULT_ADDR_SET:
+ frame->type = BNEP_CONTROL;
+ frame->ctrl = ctrl_msg_type;
+ frame->len = htons(sizeof(mcast_addr_down_range) +
+ sizeof(mcast_addr_up_range));
+ memcpy(frame->list, mcast_addr_down_range,
+ sizeof(mcast_addr_down_range));
+ memcpy(frame->list + sizeof(mcast_addr_down_range),
+ mcast_addr_up_range, sizeof(mcast_addr_up_range));
+
+ err = send(sk, frame, sizeof(*frame) +
+ sizeof(mcast_addr_down_range) +
+ sizeof(mcast_addr_up_range), 0);
+ break;
+ default:
+ err = -1;
+ break;
+ }
+
+ return err;
+}
+
+static int send_bnep_frame(int sk)
+{
+ int err;
+
+ switch (bnep_msg_type) {
+ case BNEP_GENERAL:
+ err = send_general_frame(sk);
+ break;
+ case BNEP_COMPRESSED:
+ err = send_compressed_frame(sk, BNEP_COMPRESSED);
+ break;
+ case BNEP_COMPRESSED_SRC_ONLY:
+ err = send_compressed_frame(sk,
+ BNEP_COMPRESSED_SRC_ONLY);
+ break;
+ case BNEP_COMPRESSED_DST_ONLY:
+ err = send_compressed_frame(sk,
+ BNEP_COMPRESSED_DST_ONLY);
+ break;
+ default:
+ printf("wrong bnep_msg_type 0x%02x\n", bnep_msg_type);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static void handle_bnep_msg_send(int sk)
+{
+ if (send_ctrl_msg_type_set) {
+ do {
+ if (send_ctrl_frame(sk) < 0)
+ printf("sending ctrl frame error: %s (%d)\n",
+ strerror(errno), errno);
+ } while (ctrl_msg_retransmition_nb--);
+ }
+
+ if (send_bnep_msg_type_set) {
+ do {
+ if (send_bnep_frame(sk) < 0)
+ printf("sending bnep frame error: %s (%d)\n",
+ strerror(errno), errno);
+ } while (bnep_msg_retransmission_nb--);
+ }
+}
+
+static gboolean setup_bnep_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ uint8_t packet[BNEP_MTU];
+ int sk, n, err;
+
+ printf("%s\n", __func__);
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ error("hangup or error or inval on BNEP socket");
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+#ifdef __TIZEN_PATCH__
+ n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+#else
+ n = read(sk, packet, sizeof(packet));
+#endif
+ if (n < 0) {
+ error("read(): %s(%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ err = nap_create_bridge();
+ if (err < 0) {
+ error("failed to create bridge: %s (%d)", strerror(-err), err);
+ return FALSE;
+ }
+
+ if (bnep_server_add(sk, (err < 0) ? NULL : bridge, iface, &dst_addr,
+ packet, n) < 0) {
+ printf("server_connadd failed\n");
+ cleanup();
+ return FALSE;
+ }
+
+ g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, bnep_watchdog_cb,
+ NULL);
+
+ handle_bnep_msg_send(sk);
+
+ g_io_channel_unref(bnep_io);
+ bnep_io = NULL;
+
+ return FALSE;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ printf("%s\n", __func__);
+
+ if (err) {
+ error("%s", err->message);
+ return;
+ }
+
+ g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ setup_bnep_cb, NULL);
+}
+
+static void connected_client_cb(char *iface, int err, void *data)
+{
+ int sk = PTR_TO_INT(data);
+
+ printf("%s\n", __func__);
+
+ handle_bnep_msg_send(sk);
+}
+
+static void disconnected_client_cb(void *data)
+{
+ printf("%s\n", __func__);
+
+ if (no_close_after_disconn)
+ return;
+
+ /* Cleanup since it's called when disconnected l2cap */
+ if (cleanup() < 0) {
+ printf("cleanup went wrong...\n");
+ return;
+ }
+
+ g_main_loop_quit(mloop);
+}
+
+static void connect_client_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ int perr;
+ int sk;
+
+ sk = g_io_channel_unix_get_fd(bnep_io);
+
+ session = bnep_new(sk, local_role, remote_role, bridge);
+ if (!session) {
+ printf("cannot create bnep session\n");
+ return;
+ }
+
+ perr = bnep_connect(session, connected_client_cb,
+ disconnected_client_cb, INT_TO_PTR(sk), NULL);
+ if (perr < 0)
+ printf("cannot initiate bnep connection\n");
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+ GError *err = NULL;
+ char address[18];
+
+ printf("%s\n", __func__);
+
+ bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst_addr, BT_IO_OPT_DEST,
+ address, BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ printf("incoming connection from: %s\n", address);
+
+ bnep_io = g_io_channel_ref(chan);
+ g_io_channel_set_close_on_unref(bnep_io, TRUE);
+
+ if (!bt_io_accept(bnep_io, connect_cb, NULL, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ g_io_channel_unref(bnep_io);
+ }
+}
+
+static int bnep_server_listen(void)
+{
+ GError *gerr = NULL;
+
+ printf("%s\n", __func__);
+
+ bnep_io = bt_io_listen(NULL, confirm_cb, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src_addr,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+ if (!bnep_io) {
+ printf("can't start server listening: err %s\n", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bnep_client_connect(void)
+{
+ GError *gerr = NULL;
+ char bdastr[18];
+
+ printf("%s\n", __func__);
+
+ ba2str(&dst_addr, bdastr);
+ printf("connecting %s\n", bdastr);
+
+ bnep_io = bt_io_connect(connect_client_cb, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src_addr,
+ BT_IO_OPT_DEST_BDADDR, &dst_addr,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+ if (!bnep_io) {
+ printf("cannot connect: err %s\n", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void exit_handler(int sig)
+{
+ printf("got sig = %d, cleaning up...\n", sig);
+
+ if (cleanup() < 0)
+ printf("cleanup failure...\n");
+ else
+ printf("cleanup successful - exit\n");
+
+ exit(0);
+}
+
+static void usage(void)
+{
+ printf("bneptest - BNEP testing ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\tbneptest [-i] -b <bridge name> -n <iface name>"
+ " <connection mode> [send_ctrl_cmd] [options]\n"
+ "\t-i hci dev number <hci number>, def. 0\n"
+ "\t-b bridge name <string>\n"
+ "\t-n interface name <string>\n");
+ printf("Connect Mode:\n"
+ "\t-c connect <dst_addr>\n"
+ "\t-r remote role <16 bit svc value>\n"
+ "\t-l local role <16 bit svc valu>\n");
+ printf("Listen Mode:\n"
+ "\t-s start server listening\n");
+ printf("Send control command:\n"
+ "\t-t send message type <control msg type>, def. 0\n"
+ "\t-e start network protocol type range <16 bit val>, def. 0\n"
+ "\t-d end network protocol type range <16 bit val>, def. 1500\n"
+ "\t-g start multicast addr range <xx:xx:xx:xx:xx:xx>, def. 0\n"
+ "\t-j end multicast addr range <xx:xx:xx:xx:xx:xx>, def. f\n"
+ "\t-y number of ctrl frame retransmission <integer>, def. 0\n"
+ "\t-u number of bnep frame retransmission <integer>, def. 0\n");
+ printf("Send bnep generic frame:\n"
+ "\t-w send bnep generic frame <bnep generic type>, def. 0\n"
+ "\t-k set src mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n"
+ "\t-f set dst mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n");
+ printf("Options:\n"
+ "\t-T send message timeout after setup <seconds>\n"
+ "\t-N don't close bneptest after disconnect\n");
+}
+
+static struct option main_options[] = {
+ { "device", 1, 0, 'i' },
+ { "listen", 0, 0, 's' },
+ { "connect", 1, 0, 'c' },
+ { "snd_ctrl_msg_type", 1, 0, 't' },
+ { "snd_bnep_msg_type", 1, 0, 'w' },
+ { "src_hw_addr", 1, 0, 'k' },
+ { "dst_hw_addr", 1, 0, 'f' },
+ { "send_timeout", 1, 0, 'T' },
+ { "ntw_proto_down_range", 1, 0, 'd' },
+ { "ntw_proto_up_range", 1, 0, 'e' },
+ { "mcast_addr_down_range", 1, 0, 'g' },
+ { "mcast_addr_up_range", 1, 0, 'j' },
+ { "local_role", 1, 0, 'l' },
+ { "remote_role", 1, 0, 'r' },
+ { "bridge name", 1, 0, 'b' },
+ { "iface name", 1, 0, 'n' },
+ { "no_close", 0, 0, 'N' },
+ { "retrans_ctrl_nb", 0, 0, 'y' },
+ { "retrans_bnep_nb", 0, 0, 'u' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int opt, i;
+ int err;
+ bool is_set_b_name = false, is_set_i_name = false;
+
+ DBG("");
+
+ signal(SIGINT, exit_handler);
+
+ hci_devba(0, &src_addr);
+ bacpy(&src_addr, BDADDR_ANY);
+
+ mloop = g_main_loop_new(NULL, FALSE);
+ if (!mloop) {
+ printf("cannot create main loop\n");
+
+ exit(1);
+ }
+
+ while ((opt = getopt_long(argc, argv,
+ "+i:c:b:n:t:T:d:e:g:j:k:f:w:l:r:y:u:Nsh",
+ main_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &src_addr);
+ else
+ str2ba(optarg, &src_addr);
+ break;
+ case 's':
+ mode = MODE_LISTEN;
+ break;
+ case 'c':
+ str2ba(optarg, &dst_addr);
+ mode = MODE_CONNECT;
+ break;
+ case 't':
+ send_ctrl_msg_type_set = true;
+ ctrl_msg_type = atoi(optarg);
+ break;
+ case 'w':
+ send_bnep_msg_type_set = true;
+ bnep_msg_type = atoi(optarg);
+ break;
+ case 'k':
+ for (i = 0; i <= 5; i++, optarg += 3)
+ src_hw_addr[i] = strtol(optarg, NULL, 16);
+ break;
+ case 'f':
+ for (i = 0; i <= 5; i++, optarg += 3)
+ dst_hw_addr[i] = strtol(optarg, NULL, 16);
+ break;
+ case 'T':
+ send_frame_timeout = atoi(optarg);
+ break;
+ case 'd':
+ ntw_proto_down_range = htons(atoi(optarg));
+ break;
+ case 'e':
+ ntw_proto_up_range = htons(atoi(optarg));
+ break;
+ case 'g':
+ for (i = 5; i >= 0; i--, optarg += 3)
+ mcast_addr_down_range[i] =
+ strtol(optarg, NULL, 16);
+ break;
+ case 'j':
+ for (i = 5; i >= 0; i--, optarg += 3)
+ mcast_addr_up_range[i] =
+ strtol(optarg, NULL, 16);
+ break;
+ case 'l':
+ local_role = atoi(optarg);
+ break;
+ case 'r':
+ remote_role = atoi(optarg);
+ break;
+ case 'b':
+ strncpy(bridge, optarg, 16);
+ bridge[15] = '\0';
+ is_set_b_name = true;
+ break;
+ case 'n':
+ strncpy(iface, optarg, 14);
+ strcat(iface, "\%d");
+ iface[15] = '\0';
+ is_set_i_name = true;
+ break;
+ case 'N':
+ no_close_after_disconn = true;
+ break;
+ case 'y':
+ ctrl_msg_retransmition_nb = atoi(optarg);
+ break;
+ case 'u':
+ bnep_msg_retransmission_nb = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ if (!is_set_b_name || !is_set_i_name) {
+ printf("bridge, interface name must be set!\n");
+ exit(1);
+ }
+
+ switch (mode) {
+ case MODE_CONNECT:
+ err = bnep_init();
+ if (err < 0) {
+ printf("cannot initialize bnep\n");
+ exit(1);
+ }
+ err = bnep_client_connect();
+ if (err < 0)
+ exit(1);
+
+ break;
+ case MODE_LISTEN:
+ err = bnep_init();
+ if (err < 0) {
+ printf("cannot initialize bnep\n");
+ exit(1);
+ }
+ err = bnep_server_listen();
+ if (err < 0)
+ exit(1);
+
+ break;
+ default:
+ printf("connect/listen mode not set, exit...\n");
+ exit(1);
+ }
+
+ g_main_loop_run(mloop);
+
+ printf("Done\n");
+
+ g_main_loop_unref(mloop);
+
+ return 0;
+}
struct bt_att *att;
struct gatt_db *db;
struct bt_gatt_client *gatt;
+
+ unsigned int reliable_session_id;
};
static void print_prompt(void)
return "Group type Not Supported";
case BT_ATT_ERROR_INSUFFICIENT_RESOURCES:
return "Insufficient Resources";
- case BT_ERROR_CCC_IMPROPERLY_CONFIGURED:
- return "CCC Improperly Configured";
- case BT_ERROR_ALREADY_IN_PROGRESS:
- return "Procedure Already in Progress";
- case BT_ERROR_OUT_OF_RANGE:
- return "Out of Range";
default:
return "Unknown error type";
}
free(value);
}
+static void write_prepare_usage(void)
+{
+ printf("Usage: write-prepare [options] <value_handle> <offset> "
+ "<value>\n"
+ "Options:\n"
+ "\t-s, --session-id\tSession id\n"
+ "e.g.:\n"
+ "\twrite-prepare -s 1 0x0001 00 01 00\n");
+}
+
+static struct option write_prepare_options[] = {
+ { "session-id", 1, 0, 's' },
+ { }
+};
+
+static void cmd_write_prepare(struct client *cli, char *cmd_str)
+{
+ int opt, i;
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 0;
+ unsigned int id = 0;
+ uint16_t handle;
+ uint16_t offset;
+ char *endptr = NULL;
+ unsigned int length;
+ uint8_t *value = NULL;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+ printf("Too many arguments\n");
+ write_value_usage();
+ return;
+ }
+
+ /* Add command name for getopt_long */
+ argc++;
+ argv[0] = "write-prepare";
+
+ optind = 0;
+ while ((opt = getopt_long(argc, argv , "s:", write_prepare_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ if (!optarg) {
+ write_prepare_usage();
+ return;
+ }
+
+ id = atoi(optarg);
+
+ break;
+ default:
+ write_prepare_usage();
+ return;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ write_prepare_usage();
+ return;
+ }
+
+ if (cli->reliable_session_id != id) {
+ printf("Session id != Ongoing session id (%u!=%u)\n", id,
+ cli->reliable_session_id);
+ return;
+ }
+
+ handle = strtol(argv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid handle: %s\n", argv[0]);
+ return;
+ }
+
+ endptr = NULL;
+ offset = strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0' || errno == ERANGE) {
+ printf("Invalid offset: %s\n", argv[1]);
+ return;
+ }
+
+ /*
+ * First two arguments are handle and offset. What remains is the value
+ * length
+ */
+ length = argc - 2;
+
+ if (length == 0)
+ goto done;
+
+ if (length > UINT16_MAX) {
+ printf("Write value too long\n");
+ return;
+ }
+
+ value = malloc(length);
+ if (!value) {
+ printf("Failed to allocate memory for value\n");
+ return;
+ }
+
+ for (i = 2; i < argc; i++) {
+ if (strlen(argv[i]) != 2) {
+ printf("Invalid value byte: %s\n", argv[i]);
+ free(value);
+ return;
+ }
+
+ value[i-2] = strtol(argv[i], &endptr, 0);
+ if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE) {
+ printf("Invalid value byte: %s\n", argv[i]);
+ free(value);
+ return;
+ }
+ }
+
+done:
+ cli->reliable_session_id =
+ bt_gatt_client_prepare_write(cli->gatt, id,
+ handle, offset,
+ value, length,
+ write_long_cb, NULL,
+ NULL);
+ if (!cli->reliable_session_id)
+ printf("Failed to proceed prepare write\n");
+ else
+ printf("Prepare write success.\n"
+ "Session id: %d to be used on next write\n",
+ cli->reliable_session_id);
+
+ free(value);
+}
+
+static void write_execute_usage(void)
+{
+ printf("Usage: write-execute <session_id> <execute>\n"
+ "e.g.:\n"
+ "\twrite-execute 1 0\n");
+}
+
+static void cmd_write_execute(struct client *cli, char *cmd_str)
+{
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 0;
+ char *endptr = NULL;
+ unsigned int session_id;
+ bool execute;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 514, argv, &argc)) {
+ printf("Too many arguments\n");
+ write_value_usage();
+ return;
+ }
+
+ if (argc < 2) {
+ write_execute_usage();
+ return;
+ }
+
+ session_id = strtol(argv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ printf("Invalid session id: %s\n", argv[0]);
+ return;
+ }
+
+ if (session_id != cli->reliable_session_id) {
+ printf("Invalid session id: %u != %u\n", session_id,
+ cli->reliable_session_id);
+ return;
+ }
+
+ execute = !!strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ printf("Invalid execute: %s\n", argv[1]);
+ return;
+ }
+
+ if (execute) {
+ if (!bt_gatt_client_write_execute(cli->gatt, session_id,
+ write_cb, NULL, NULL))
+ printf("Failed to proceed write execute\n");
+ } else {
+ bt_gatt_client_cancel(cli->gatt, session_id);
+ }
+
+ cli->reliable_session_id = 0;
+}
+
static void register_notify_usage(void)
{
printf("Usage: register-notify <chrc value handle>\n");
"\tWrite a characteristic or descriptor value" },
{ "write-long-value", cmd_write_long_value,
"Write long characteristic or descriptor value" },
+ { "write-prepare", cmd_write_prepare,
+ "\tWrite prepare characteristic or descriptor value" },
+ { "write-execute", cmd_write_execute,
+ "\tExecute already prepared write" },
{ "register-notify", cmd_register_notify,
"\tSubscribe to not/ind from a characteristic" },
{ "unregister-notify", cmd_unregister_notify,
"Unregister a not/ind session"},
{ "set-sec-level", cmd_set_sec_level,
- "Set security level on le connection"},
+ "\tSet security level on le connection"},
{ "get-sec-level", cmd_get_sec_level,
- "Get security level on le connection"},
+ "\tGet security level on le connection"},
{ "set-sign-key", cmd_set_sign_key,
"\tSet signing key for signed write command"},
{ }
"\t-m, --mtu <mtu>\t\t\tThe ATT MTU to use\n"
"\t-s, --security-level <sec>\tSet security level (low|"
"medium|high)\n"
+ "\t-t, --type [random|public] \t The source address type\n"
"\t-v, --verbose\t\t\tEnable extra logging\n"
"\t-r, --heart-rate\t\tEnable Heart Rate service\n"
"\t-h, --help\t\t\tDisplay help\n");
{ "index", 1, 0, 'i' },
{ "mtu", 1, 0, 'm' },
{ "security-level", 1, 0, 's' },
+ { "type", 1, 0, 't' },
{ "verbose", 0, 0, 'v' },
{ "heart-rate", 0, 0, 'r' },
{ "help", 0, 0, 'h' },
{ }
};
-static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec)
+static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec,
+ uint8_t src_type)
{
int sk, nsk;
struct sockaddr_l2 srcaddr, addr;
memset(&srcaddr, 0, sizeof(srcaddr));
srcaddr.l2_family = AF_BLUETOOTH;
srcaddr.l2_cid = htobs(ATT_CID);
- srcaddr.l2_bdaddr_type = 0;
+ srcaddr.l2_bdaddr_type = src_type;
bacpy(&srcaddr.l2_bdaddr, src);
if (bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr)) < 0) {
int dev_id = -1;
int fd;
int sec = BT_SECURITY_LOW;
+ uint8_t src_type = BDADDR_LE_PUBLIC;
uint16_t mtu = 0;
sigset_t mask;
bool hr_visible = false;
struct server *server;
- while ((opt = getopt_long(argc, argv, "+hvrs:m:i:",
+ while ((opt = getopt_long(argc, argv, "+hvrs:t:m:i:",
main_options, NULL)) != -1) {
switch (opt) {
case 'h':
return EXIT_FAILURE;
}
break;
+ case 't':
+ if (strcmp(optarg, "random") == 0)
+ src_type = BDADDR_LE_RANDOM;
+ else if (strcmp(optarg, "public") == 0)
+ src_type = BDADDR_LE_PUBLIC;
+ else {
+ fprintf(stderr,
+ "Allowed types: random, public\n");
+ return EXIT_FAILURE;
+ }
+ break;
case 'm': {
int arg;
return EXIT_FAILURE;
}
- fd = l2cap_le_att_listen_and_accept(&src_addr, sec);
+ fd = l2cap_le_att_listen_and_accept(&src_addr, sec, src_type);
if (fd < 0) {
fprintf(stderr, "Failed to accept L2CAP ATT connection\n");
return EXIT_FAILURE;
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
static struct mgmt *mgmt = NULL;
static uint16_t mgmt_index = MGMT_INDEX_NONE;
return i;
}
+static void print_eir(const uint8_t *eir, uint16_t eir_len)
+{
+ uint16_t parsed = 0;
+ char str[33];
+
+ while (parsed < eir_len - 1) {
+ uint8_t field_len = eir[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > eir_len)
+ break;
+
+ switch (eir[1]) {
+ case 0x01:
+ print("Flags: 0x%02x", eir[2]);
+ break;
+ case 0x0d:
+ print("Class of Device: 0x%02x%02x%02x",
+ eir[4], eir[3], eir[2]);
+ break;
+ case 0x1b:
+ ba2str((bdaddr_t *) (eir + 2), str);
+ print("LE Device Address: %s (%s)", str,
+ eir[8] ? "random" : "public");
+ break;
+ case 0x1c:
+ print("LE Role: 0x%02x", eir[2]);
+ break;
+ case 0x22:
+ bin2hex(eir + 2, 16, str, sizeof(str));
+ print("LE SC Confirmation Value: %s", str);
+ break;
+ case 0x23:
+ bin2hex(eir + 2, 16, str, sizeof(str));
+ print("LE SC Random Value: %s", str);
+ break;
+ default:
+ print("Type %u: %u byte%s", eir[1], field_len - 1,
+ (field_len - 1) == 1 ? "" : "s");
+ break;
+ }
+
+ eir += field_len + 1;
+ }
+}
+
static bool load_identity(uint16_t index, struct mgmt_irk_info *irk)
{
char identity_path[PATH_MAX];
print("hci%u removed (unconfigured)", index);
}
+static void ext_index_added(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_ext_index_added *ev = param;
+
+ print("hci%u added (type %u bus %u)", index, ev->type, ev->bus);
+}
+
+static void ext_index_removed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_ext_index_removed *ev = param;
+
+ print("hci%u removed (type %u bus %u)", index, ev->type, ev->bus);
+}
+
static const char *options_str[] = {
"external",
"public-address",
index, addr, ev->status, mgmt_errstr(ev->status));
}
+static void class_of_dev_changed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_class_of_dev_changed *ev = param;
+
+ if (len != sizeof(*ev)) {
+ error("Invalid class_of_dev_changed length (%u bytes)", len);
+ return;
+ }
+
+ print("hci%u class of device changed: 0x%02x%02x%02x", index,
+ ev->dev_class[2], ev->dev_class[1], ev->dev_class[0]);
+}
+
static void local_name_changed(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
ev->entered);
}
+static void local_oob_data_updated(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_local_oob_data_updated *ev = param;
+ uint16_t eir_len;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) local_oob_updated event", len);
+ return;
+ }
+
+ eir_len = le16_to_cpu(ev->eir_len);
+ if (len != sizeof(*ev) + eir_len) {
+ error("local_oob_updated: expected %zu bytes, got %u bytes",
+ sizeof(*ev) + eir_len, len);
+ return;
+ }
+
+ print("hci%u oob data updated: type %u len %u", index,
+ ev->type, eir_len);
+}
+
+static void advertising_added(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_advertising_added *ev = param;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) advertising_added event", len);
+ return;
+ }
+
+ print("hci%u advertising_added: instance %u", index, ev->instance);
+}
+
+static void advertising_removed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_advertising_removed *ev = param;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) advertising_removed event", len);
+ return;
+ }
+
+ print("hci%u advertising_removed: instance %u", index, ev->instance);
+}
+
static void version_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
}
}
+static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_config_info *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_options, missing_options;
+
+ if (status != 0) {
+ error("Reading hci%u config failed with status 0x%02x (%s)",
+ index, status, mgmt_errstr(status));
+ goto done;
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small info reply (%u bytes)", len);
+ goto done;
+ }
+
+ print("hci%u:\tUnconfigured controller", index);
+
+ print("\tmanufacturer %u", le16_to_cpu(rp->manufacturer));
+
+ supported_options = le32_to_cpu(rp->supported_options);
+ print("\tsupported options: %s", options2str(supported_options));
+
+ missing_options = le32_to_cpu(rp->missing_options);
+ print("\tmissing options: %s", options2str(missing_options));
+
+done:
+ pending_index--;
+
+ if (pending_index > 0)
+ return;
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
if (status != 0) {
error("Reading index list failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len < sizeof(*rp)) {
error("Too small index list reply (%u bytes)", len);
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
- count = get_le16(&rp->num_controllers);
+ count = le16_to_cpu(rp->num_controllers);
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
error("Index count (%u) doesn't match reply length (%u)",
count, len);
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
print("Unconfigured index list with %u item%s",
- count, count != 1 ? "s" : "");
+ count, count != 1 ? "s" : "");
for (i = 0; i < count; i++) {
- uint16_t index;
+ uint16_t index = le16_to_cpu(rp->index[i]);
- index = get_le16(&rp->index[i]);
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
+ config_info_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ pending_index++;
+ }
+
+ if (!count)
+ noninteractive_quit(EXIT_SUCCESS);
+}
- print("\thci%u", index);
+static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ if (index == MGMT_INDEX_NONE) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST,
+ MGMT_INDEX_NONE, 0, NULL,
+ unconf_index_rsp, mgmt, NULL)) {
+ error("Unable to send unconf_index_list cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ return;
}
-done:
- noninteractive_quit(EXIT_SUCCESS);
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
+ config_info_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
}
-static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
+static void config_options_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_rp_read_config_info *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_options, missing_options;
if (status != 0) {
error("Reading hci%u config failed with status 0x%02x (%s)",
goto done;
}
- print("hci%u:\tmanufacturer %u", index, get_le16(&rp->manufacturer));
-
- print("\tsupported options: %s",
- options2str(get_le32(&rp->supported_options)));
- print("\tmissing options: %s",
- options2str(get_le32(&rp->missing_options)));
+ print("hci%u:\tConfiguration options", index);
-done:
- noninteractive_quit(EXIT_SUCCESS);
-}
+ supported_options = le32_to_cpu(rp->supported_options);
+ print("\tsupported options: %s", options2str(supported_options));
-static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
-{
- void *data;
+ missing_options = le32_to_cpu(rp->missing_options);
+ print("\tmissing options: %s", options2str(missing_options));
- if (index == MGMT_INDEX_NONE) {
- if (mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST,
- MGMT_INDEX_NONE, 0, NULL,
- unconf_index_rsp, mgmt, NULL) == 0) {
- error("Unable to send unconf_index_list cmd");
- return noninteractive_quit(EXIT_FAILURE);
- }
+done:
+ pending_index--;
+ if (pending_index > 0)
return;
- }
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
- config_info_rsp, data, NULL) == 0) {
- error("Unable to send read_config_info cmd");
- return noninteractive_quit(EXIT_FAILURE);
- }
+ noninteractive_quit(EXIT_SUCCESS);
}
static void info_rsp(uint8_t status, uint16_t len, const void *param,
{
const struct mgmt_rp_read_info *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_settings, current_settings;
char addr[18];
- pending_index--;
-
if (status != 0) {
error("Reading hci%u info failed with status 0x%02x (%s)",
index, status, mgmt_errstr(status));
goto done;
}
+ print("hci%u:\tPrimary controller", index);
+
ba2str(&rp->bdaddr, addr);
- print("hci%u:\taddr %s version %u manufacturer %u"
- " class 0x%02x%02x%02x", index,
- addr, rp->version, get_le16(&rp->manufacturer),
+ print("\taddr %s version %u manufacturer %u class 0x%02x%02x%02x",
+ addr, rp->version, le16_to_cpu(rp->manufacturer),
rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
- print("\tsupported settings: %s",
- settings2str(get_le32(&rp->supported_settings)));
+ supported_settings = le32_to_cpu(rp->supported_settings);
+ print("\tsupported settings: %s", settings2str(supported_settings));
- print("\tcurrent settings: %s",
- settings2str(get_le32(&rp->current_settings)));
+ current_settings = le32_to_cpu(rp->current_settings);
+ print("\tcurrent settings: %s", settings2str(current_settings));
print("\tname %s", rp->name);
print("\tshort name %s", rp->short_name);
- if (pending_index > 0)
+ if (supported_settings & MGMT_SETTING_CONFIGURATION) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+ index, 0, NULL, config_options_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config cmd");
+ goto done;
+ }
return;
+ }
done:
+ pending_index--;
+
+ if (pending_index > 0)
+ return;
+
noninteractive_quit(EXIT_SUCCESS);
}
return noninteractive_quit(EXIT_FAILURE);
}
- count = get_le16(&rp->num_controllers);
+ count = le16_to_cpu(rp->num_controllers);
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
error("Index count (%u) doesn't match reply length (%u)",
print("Index list with %u item%s", count, count != 1 ? "s" : "");
for (i = 0; i < count; i++) {
- uint16_t index;
- void *data;
-
- index = get_le16(&rp->index[i]);
+ uint16_t index = le16_to_cpu(rp->index[i]);
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
- info_rsp, data, NULL) == 0) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+ info_rsp, UINT_TO_PTR(index), NULL)) {
error("Unable to send read_info cmd");
return noninteractive_quit(EXIT_FAILURE);
}
static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
{
- void *data;
-
if (index == MGMT_INDEX_NONE) {
- if (mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
MGMT_INDEX_NONE, 0, NULL,
- index_rsp, mgmt, NULL) == 0) {
+ index_rsp, mgmt, NULL)) {
error("Unable to send index_list cmd");
return noninteractive_quit(EXIT_FAILURE);
}
return;
}
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
- data, NULL) == 0) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
+ UINT_TO_PTR(index), NULL)) {
error("Unable to send read_info cmd");
return noninteractive_quit(EXIT_FAILURE);
}
}
+static void ext_index_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_ext_index_list *rp = param;
+ uint16_t count, index_filter = PTR_TO_UINT(user_data);
+ unsigned int i;
+
+ if (status != 0) {
+ error("Reading ext index list failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small ext index list reply (%u bytes)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ count = get_le16(&rp->num_controllers);
+
+ if (len < sizeof(*rp) + count * (sizeof(uint16_t) + sizeof(uint8_t))) {
+ error("Index count (%u) doesn't match reply length (%u)",
+ count, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Extended index list with %u item%s",
+ count, count != 1 ? "s" : "");
+
+ for (i = 0; i < count; i++) {
+ uint16_t index = le16_to_cpu(rp->entry[i].index);
+
+ if (index_filter != MGMT_INDEX_NONE && index_filter != index)
+ continue;
+
+ switch (rp->entry[i].type) {
+ case 0x00:
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO,
+ index, 0, NULL, info_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ pending_index++;
+ break;
+ case 0x01:
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+ index, 0, NULL, config_info_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ pending_index++;
+ break;
+ case 0x02:
+ print("hci%u:\tAMP controller (%u)", index,
+ rp->entry[i].bus);
+ break;
+ default:
+ print("hci%u:\tType %u controller (%u)", index,
+ rp->entry[i].type, rp->entry[i].bus);
+ break;
+ }
+ }
+
+ if (!count)
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_extinfo(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST,
+ MGMT_INDEX_NONE, 0, NULL,
+ ext_index_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send ext_index_list cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
/* Wrapper to get the index and opcode to the response callback */
struct command_data {
uint16_t id;
}
print("%s succeeded. Class 0x%02x%02x%02x", mgmt_opstr(op),
- rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]);
+ rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
noninteractive_quit(EXIT_SUCCESS);
}
break;
case 'h':
disconnect_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
disconnect_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
uuid_t uuid;
uint128_t uint128;
uuid_t uuid128;
- uint8_t type;
+ uint8_t type = SCAN_TYPE_DUAL;
int8_t rssi;
uint16_t count;
int opt;
if (index == MGMT_INDEX_NONE)
index = 0;
- type = 0;
- type |= (1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
rssi = 127;
count = 0;
find_service_options, NULL)) != -1) {
switch (opt) {
case 'l':
- type &= ~(1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
break;
case 'b':
- type |= (1 << BDADDR_BREDR);
- type &= ~(1 << BDADDR_LE_PUBLIC);
- type &= ~(1 << BDADDR_LE_RANDOM);
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
break;
case 'u':
if (count == MAX_UUIDS) {
print("Max %u UUIDs supported", MAX_UUIDS);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
if (bt_string2uuid(&uuid, optarg) < 0) {
print("Invalid UUID: %s", optarg);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
cp = (void *) buf;
break;
case 'h':
find_service_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
find_service_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
{
struct mgmt_cp_start_discovery cp;
- uint8_t type;
+ uint8_t type = SCAN_TYPE_DUAL;
int opt;
if (index == MGMT_INDEX_NONE)
index = 0;
- type = 0;
- type |= (1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
-
while ((opt = getopt_long(argc, argv, "+lbh", find_options,
NULL)) != -1) {
switch (opt) {
case 'l':
- type &= ~(1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
break;
case 'b':
- type |= (1 << BDADDR_BREDR);
- type &= ~(1 << BDADDR_LE_PUBLIC);
- type &= ~(1 << BDADDR_LE_RANDOM);
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
break;
case 'h':
find_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
find_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
}
}
+static void stop_find_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "Stop Discovery failed: status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ printf("Discovery stopped\n");
+ discovery = false;
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void stop_find_usage(void)
+{
+ printf("Usage: btmgmt stop-find [-l|-b]>\n");
+}
+
+static struct option stop_find_options[] = {
+ { "help", 0, 0, 'h' },
+ { "le-only", 1, 0, 'l' },
+ { "bredr-only", 1, 0, 'b' },
+ { 0, 0, 0, 0 }
+};
+
+static void cmd_stop_find(struct mgmt *mgmt, uint16_t index, int argc,
+ char **argv)
+{
+ struct mgmt_cp_stop_discovery cp;
+ uint8_t type = SCAN_TYPE_DUAL;
+ int opt;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ while ((opt = getopt_long(argc, argv, "+lbh", stop_find_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'l':
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
+ break;
+ case 'b':
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
+ break;
+ case 'h':
+ default:
+ stop_find_usage();
+ optind = 0;
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.type = type;
+
+ if (mgmt_send(mgmt, MGMT_OP_STOP_DISCOVERY, index, sizeof(cp), &cp,
+ stop_find_rsp, NULL, NULL) == 0) {
+ fprintf(stderr, "Unable to send stop_discovery cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
static void name_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
break;
case 'h':
pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
break;
case 'h':
cancel_pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
cancel_pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
break;
case 'h':
unpair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
unpair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
case 'l':
if (count >= MAX_IRKS) {
error("Number of IRKs exceeded");
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
if (strlen(optarg) > 3 &&
local_index = atoi(optarg);
if (!load_identity(local_index, &cp->irks[count])) {
error("Unable to load identity");
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
count++;
break;
case 'h':
irks_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
irks_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
break;
case 'h':
block_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
block_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
break;
case 'h':
unblock_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
unblock_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
const struct mgmt_rp_read_local_oob_data *rp = param;
- const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param;
char str[33];
if (status != 0) {
return noninteractive_quit(EXIT_FAILURE);
}
- bin2hex(rp->hash, 16, str, sizeof(str));
+ bin2hex(rp->hash192, 16, str, sizeof(str));
print("Hash C from P-192: %s", str);
- bin2hex(rp->randomizer, 16, str, sizeof(str));
+ bin2hex(rp->rand192, 16, str, sizeof(str));
print("Randomizer R with P-192: %s", str);
- if (len < sizeof(*rp_ext))
+ if (len < sizeof(*rp))
return noninteractive_quit(EXIT_SUCCESS);
- bin2hex(rp_ext->hash256, 16, str, sizeof(str));
+ bin2hex(rp->hash256, 16, str, sizeof(str));
print("Hash C from P-256: %s", str);
- bin2hex(rp_ext->randomizer256, 16, str, sizeof(str));
+ bin2hex(rp->rand256, 16, str, sizeof(str));
print("Randomizer R with P-256: %s", str);
noninteractive_quit(EXIT_SUCCESS);
cmd_del_device(mgmt, index, 2, rm_argv);
}
+static void local_oob_ext_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+ uint16_t eir_len;
+
+ if (status != 0) {
+ error("Read Local OOB Ext Data failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small (%u bytes) read_local_oob_ext rsp", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ eir_len = le16_to_cpu(rp->eir_len);
+ if (len != sizeof(*rp) + eir_len) {
+ error("local_oob_ext: expected %zu bytes, got %u bytes",
+ sizeof(*rp) + eir_len, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print_eir(rp->eir, eir_len);
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_bredr_oob(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_read_local_oob_ext_data cp;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.type = SCAN_TYPE_BREDR;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+ index, sizeof(cp), &cp,
+ local_oob_ext_rsp, NULL, NULL)) {
+ error("Unable to send read_local_oob_ext cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_le_oob(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_read_local_oob_ext_data cp;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.type = SCAN_TYPE_LE;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+ index, sizeof(cp), &cp,
+ local_oob_ext_rsp, NULL, NULL)) {
+ error("Unable to send read_local_oob_ext cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static const char *adv_flags_str[] = {
+ "connectable",
+ "general-discoverable",
+ "limited-discoverable",
+ "managed-flags",
+ "tx-power",
+ "scan-rsp-appearance",
+ "scan-rsp-local-name",
+};
+
+static const char *adv_flags2str(uint32_t flags)
+{
+ static char str[256];
+ unsigned i;
+ int off;
+
+ off = 0;
+ str[0] = '\0';
+
+ for (i = 0; i < NELEM(adv_flags_str); i++) {
+ if ((flags & (1 << i)) != 0)
+ off += snprintf(str + off, sizeof(str) - off, "%s ",
+ adv_flags_str[i]);
+ }
+
+ return str;
+}
+
+static void adv_features_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_adv_features *rp = param;
+ uint32_t supported_flags;
+
+ if (status != 0) {
+ error("Reading adv features failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small adv features reply (%u bytes)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp) + rp->num_instances * sizeof(uint8_t)) {
+ error("Instances count (%u) doesn't match reply length (%u)",
+ rp->num_instances, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ supported_flags = le32_to_cpu(rp->supported_flags);
+ print("Supported flags: %s", adv_flags2str(supported_flags));
+ print("Max advertising data len: %u", rp->max_adv_data_len);
+ print("Max scan response data len: %u", rp->max_scan_rsp_len);
+ print("Max instances: %u", rp->max_instances);
+
+ print("Instances list with %u item%s", rp->num_instances,
+ rp->num_instances != 1 ? "s" : "");
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_advinfo(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_ADV_FEATURES, index, 0, NULL,
+ adv_features_rsp, NULL, NULL)) {
+ error("Unable to send advertising features command");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void add_adv_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_add_advertising *rp = param;
+
+ if (status != 0) {
+ error("Add Advertising failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ error("Invalid Add Advertising response length (%u)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Instance added: %u", rp->instance);
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void add_adv_usage(void)
+{
+ print("Usage: add-adv [options] <instance_id>\nOptions:\n"
+ "\t -u, --uuid <uuid> Service UUID\n"
+ "\t -d, --adv-data <data> Advertising Data bytes\n"
+ "\t -s, --scan-rsp <data> Scan Response Data bytes\n"
+ "\t -t, --timeout <timeout> Timeout in seconds\n"
+ "\t -c, --connectable \"connectable\" flag\n"
+ "\t -g, --general-discov \"general-discoverable\" flag\n"
+ "\t -l, --limited-discov \"limited-discoverable\" flag\n"
+ "\t -m, --managed-flags \"managed-flags\" flag\n"
+ "\t -p, --tx-power \"tx-power\" flag\n"
+ "e.g.:\n"
+ "\tadd-adv -u 180d -u 180f -d 080954657374204C45 1");
+}
+
+static struct option add_adv_options[] = {
+ { "help", 0, 0, 'h' },
+ { "uuid", 1, 0, 'u' },
+ { "adv-data", 1, 0, 'd' },
+ { "scan-rsp", 1, 0, 's' },
+ { "timeout", 1, 0, 't' },
+ { "connectable", 0, 0, 'c' },
+ { "general-discov", 0, 0, 'g' },
+ { "limited-discov", 0, 0, 'l' },
+ { "managed-flags", 0, 0, 'm' },
+ { "tx-power", 0, 0, 'p' },
+ { 0, 0, 0, 0}
+};
+
+static bool parse_bytes(char *optarg, uint8_t **bytes, size_t *len)
+{
+ unsigned i;
+
+ if (!optarg) {
+ add_adv_usage();
+ return false;
+ }
+
+ *len = strlen(optarg);
+
+ if (*len % 2) {
+ error("Malformed data");
+ return false;
+ }
+
+ *len /= 2;
+ if (*len > UINT8_MAX) {
+ error("Data too long");
+ return false;
+ }
+
+ *bytes = malloc(*len);
+ if (!*bytes) {
+ error("Failed to allocate memory");
+ return false;
+ }
+
+ for (i = 0; i < *len; i++) {
+ if (sscanf(optarg + (i * 2), "%2hhx", *bytes + i) != 1) {
+ error("Invalid data");
+ free(*bytes);
+ *bytes = NULL;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#define MAX_AD_UUID_BYTES 32
+
+static void cmd_add_adv(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_add_advertising *cp = NULL;
+ int opt;
+ uint8_t *adv_data = NULL, *scan_rsp = NULL;
+ size_t adv_len = 0, scan_rsp_len = 0;
+ size_t cp_len;
+ uint8_t uuids[MAX_AD_UUID_BYTES];
+ size_t uuid_bytes = 0;
+ uint8_t uuid_type = 0;
+ uint16_t timeout = 0;
+ uint8_t instance;
+ uuid_t uuid;
+ bool success = false;
+ bool quit = true;
+ uint32_t flags = 0;
+
+ while ((opt = getopt_long(argc, argv, "+u:d:s:t:cglmph",
+ add_adv_options, NULL)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (bt_string2uuid(&uuid, optarg) < 0) {
+ print("Invalid UUID: %s", optarg);
+ goto done;
+ }
+
+ if (uuid_type && uuid_type != uuid.type) {
+ print("UUID types must be consistent");
+ goto done;
+ }
+
+ if (uuid.type == SDP_UUID16) {
+ if (uuid_bytes + 2 >= MAX_AD_UUID_BYTES) {
+ print("Too many UUIDs");
+ goto done;
+ }
+
+ put_le16(uuid.value.uuid16, uuids + uuid_bytes);
+ uuid_bytes += 2;
+ } else if (uuid.type == SDP_UUID128) {
+ if (uuid_bytes + 16 >= MAX_AD_UUID_BYTES) {
+ print("Too many UUIDs");
+ goto done;
+ }
+
+ bswap_128(uuid.value.uuid128.data,
+ uuids + uuid_bytes);
+ uuid_bytes += 16;
+ } else {
+ printf("Unsupported UUID type");
+ goto done;
+ }
+
+ if (!uuid_type)
+ uuid_type = uuid.type;
+
+ break;
+ case 'd':
+ if (adv_len) {
+ print("Only one adv-data option allowed");
+ goto done;
+ }
+
+ if (!parse_bytes(optarg, &adv_data, &adv_len))
+ goto done;
+ break;
+ case 's':
+ if (scan_rsp_len) {
+ print("Only one scan-rsp option allowed");
+ goto done;
+ }
+
+ if (!parse_bytes(optarg, &scan_rsp, &scan_rsp_len))
+ goto done;
+ break;
+ case 't':
+ timeout = strtol(optarg, NULL, 0);
+ break;
+ case 'c':
+ flags |= MGMT_ADV_FLAG_CONNECTABLE;
+ break;
+ case 'g':
+ flags |= MGMT_ADV_FLAG_DISCOV;
+ break;
+ case 'l':
+ flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
+ break;
+ case 'm':
+ flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
+ break;
+ case 'p':
+ flags |= MGMT_ADV_FLAG_TX_POWER;
+ break;
+ case 'h':
+ success = true;
+ default:
+ add_adv_usage();
+ goto done;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc != 1) {
+ add_adv_usage();
+ goto done;
+ }
+
+ if (uuid_bytes)
+ uuid_bytes += 2;
+
+ instance = strtol(argv[0], NULL, 0);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp_len = sizeof(*cp) + uuid_bytes + adv_len + scan_rsp_len;
+ cp = malloc0(cp_len);
+ if (!cp)
+ goto done;
+
+ cp->instance = instance;
+ put_le32(flags, &cp->flags);
+ put_le16(timeout, &cp->timeout);
+ cp->adv_data_len = adv_len + uuid_bytes;
+ cp->scan_rsp_len = scan_rsp_len;
+
+ if (uuid_bytes) {
+ cp->data[0] = uuid_bytes - 1;
+ cp->data[1] = uuid_type == SDP_UUID16 ? 0x03 : 0x07;
+ memcpy(cp->data + 2, uuids, uuid_bytes - 2);
+ }
+
+ memcpy(cp->data + uuid_bytes, adv_data, adv_len);
+ memcpy(cp->data + uuid_bytes + adv_len, scan_rsp, scan_rsp_len);
+
+ if (!mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index, cp_len, cp,
+ add_adv_rsp, NULL, NULL)) {
+ error("Unable to send \"Add Advertising\" command");
+ goto done;
+ }
+
+ quit = false;
+
+done:
+ free(adv_data);
+ free(scan_rsp);
+ free(cp);
+
+ if (quit)
+ noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void rm_adv_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_remove_advertising *rp = param;
+
+ if (status != 0) {
+ error("Remove Advertising failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ error("Invalid Remove Advertising response length (%u)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Instance removed: %u", rp->instance);
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void rm_adv_usage(void)
+{
+ print("Usage: rm-adv <instance_id>");
+}
+
+static void cmd_rm_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_remove_advertising cp;
+ uint8_t instance;
+
+ if (argc != 2) {
+ rm_adv_usage();
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ instance = strtol(argv[1], NULL, 0);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.instance = instance;
+
+ if (!mgmt_send(mgmt, MGMT_OP_REMOVE_ADVERTISING, index, sizeof(cp), &cp,
+ rm_adv_rsp, NULL, NULL)) {
+ error("Unable to send \"Remove Advertising\" command");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_clr_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ char *all_instances = "0";
+ char *rm_argv[] = { "rm-adv", all_instances, NULL };
+
+ cmd_rm_adv(mgmt, index, 2, rm_argv);
+}
+
struct cmd_info {
char *cmd;
void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
{ "commands", cmd_commands, "List supported commands" },
{ "config", cmd_config, "Show configuration info" },
{ "info", cmd_info, "Show controller info" },
+ { "extinfo", cmd_extinfo, "Show extended controller info" },
{ "power", cmd_power, "Toggle powered state" },
{ "discov", cmd_discov, "Toggle discoverable state" },
{ "connectable",cmd_connectable,"Toggle connectable state" },
{ "con", cmd_con, "List connections" },
{ "find", cmd_find, "Discover nearby devices" },
{ "find-service", cmd_find_service, "Discover nearby service" },
+ { "stop-find", cmd_stop_find, "Stop discovery" },
{ "name", cmd_name, "Set local name" },
{ "pair", cmd_pair, "Pair with a remote device" },
{ "cancelpair", cmd_cancel_pair,"Cancel pairing" },
{ "add-device", cmd_add_device, "Add Device" },
{ "del-device", cmd_del_device, "Remove Device" },
{ "clr-devices",cmd_clr_devices,"Clear Devices" },
+ { "bredr-oob", cmd_bredr_oob, "Local OOB data (BR/EDR)" },
+ { "le-oob", cmd_le_oob, "Local OOB data (LE)" },
+ { "advinfo", cmd_advinfo, "Show advertising features" },
+ { "add-adv", cmd_add_adv, "Add advertising instance" },
+ { "rm-adv", cmd_rm_adv, "Remove advertising instance" },
+ { "clr-adv", cmd_clr_adv, "Clear advertising instances" },
};
static void cmd_quit(struct mgmt *mgmt, uint16_t index,
NULL, NULL);
mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed,
NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED, index,
+ class_of_dev_changed, NULL, NULL);
mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index,
local_name_changed, NULL, NULL);
mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found,
unconf_index_removed, NULL, NULL);
mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index,
new_config_options, NULL, NULL);
-
+ mgmt_register(mgmt, MGMT_EV_EXT_INDEX_ADDED, index,
+ ext_index_added, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_EXT_INDEX_REMOVED, index,
+ ext_index_removed, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_LOCAL_OOB_DATA_UPDATED, index,
+ local_oob_data_updated, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_ADVERTISING_ADDED, index,
+ advertising_added, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_ADVERTISING_REMOVED, index,
+ advertising_removed, NULL, NULL);
}
static void cmd_select(struct mgmt *mgmt, uint16_t index,
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
+#include <time.h>
+#include <sys/time.h>
#include <getopt.h>
#include <endian.h>
#include <arpa/inet.h>
close(input_fd[i]);
}
+#define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci"
+
+static void command_split(const char *input)
+{
+ unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+ uint16_t pktlen,opcode;
+ uint32_t type;
+ struct timeval tv;
+ uint16_t index, max_index = 0;
+ char write_file_name[255];
+ struct btsnoop *btsnoop_read_file = NULL;
+ struct btsnoop *btsnoop_write_file[16];
+ time_t t;
+ struct tm tm;
+ unsigned long num_packets = 0;
+
+ btsnoop_read_file = btsnoop_open(input, BTSNOOP_FLAG_PKLG_SUPPORT);
+ if (!btsnoop_read_file)
+ return;
+
+ type = btsnoop_get_type(btsnoop_read_file);
+ if (type != BTSNOOP_TYPE_MONITOR) {
+ fprintf(stderr, "unsupported link data type %u\n", type);
+ btsnoop_unref(btsnoop_read_file);
+ return;
+ }
+
+next_packet:
+ if (!btsnoop_read_hci(btsnoop_read_file, &tv, &index, &opcode, buf,
+ &pktlen))
+ goto close_files;
+
+ if (opcode == 0xffff)
+ goto next_packet;
+
+ switch (opcode) {
+ case BTSNOOP_OPCODE_NEW_INDEX:
+ t = tv.tv_sec;
+ localtime_r(&t, &tm);
+
+ if (max_index < index)
+ max_index = index;
+
+ sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log",
+ BT_SNOOP_TYPE_HCI_PREFIX, index, tm.tm_hour, tm.tm_min,
+ tm.tm_sec, tv.tv_usec);
+
+ printf("New Index %d would be saved in %s\n", index,
+ write_file_name);
+
+ btsnoop_write_file[index] = btsnoop_create(write_file_name,
+ BTSNOOP_TYPE_HCI, -1, -1);
+ if (!btsnoop_write_file[index])
+ goto close_files;
+
+ break;
+ case BTSNOOP_OPCODE_DEL_INDEX:
+ printf("Del Index %d\n", index);
+
+ btsnoop_unref(btsnoop_write_file[index]);
+ btsnoop_write_file[index] = NULL;
+ break;
+ default:
+ btsnoop_write_hci(btsnoop_write_file[index], &tv, index,
+ opcode, buf, pktlen);
+ }
+ num_packets++;
+
+ goto next_packet;
+
+close_files:
+ for (index = 0; index < max_index; index++)
+ btsnoop_unref(btsnoop_write_file[index]);
+
+ btsnoop_unref(btsnoop_read_file);
+
+ printf("BT Snoop data link transfer is completed for %lu packets\n",
+ num_packets);
+}
+
static void command_extract_eir(const char *input)
{
struct btsnoop_pkt pkt;
printf("commands:\n"
"\t-m, --merge <output> Merge multiple btsnoop files\n"
"\t-e, --extract <input> Extract data from btsnoop file\n"
+ "\t-s, --split <input> Split btmon file into legacy btsnoop file(s)\n"
"\t-h, --help Show help options\n");
}
{ "merge", required_argument, NULL, 'm' },
{ "extract", required_argument, NULL, 'e' },
{ "type", required_argument, NULL, 't' },
+ { "split", required_argument, NULL, 's' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
};
-enum { INVALID, MERGE, EXTRACT };
+enum { INVALID, MERGE, EXTRACT, SPLIT };
int main(int argc, char *argv[])
{
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
+ opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL);
if (opt < 0)
break;
command = EXTRACT;
input_path = optarg;
break;
+ case 's':
+ command = SPLIT;
+ input_path = optarg;
case 't':
type = optarg;
break;
fprintf(stderr, "extract type not supported\n");
break;
+ case SPLIT:
+ if (argc - optind > 0) {
+ fprintf(stderr, "extra arguments not allowed\n");
+ return EXIT_FAILURE;
+ }
+
+ command_split(input_path);
+ break;
+
default:
usage();
return EXIT_FAILURE;
test_post_teardown, 2, user, free); \
} while (0)
+#define test_bredr20(name, data, setup, func) \
+ do { \
+ struct test_data *user; \
+ user = malloc(sizeof(struct test_data)); \
+ if (!user) \
+ break; \
+ user->hciemu_type = HCIEMU_TYPE_LEGACY; \
+ user->test_setup = setup; \
+ user->test_data = data; \
+ user->expected_version = 0x04; \
+ user->expected_manufacturer = 0x003f; \
+ user->expected_supported_settings = 0x000010bf; \
+ user->initial_settings = 0x00000080; \
+ user->unmet_conditions = 0; \
+ tester_add_full(name, data, \
+ test_pre_setup, test_setup, func, NULL, \
+ test_post_teardown, 2, user, free); \
+ } while (0)
+
#define test_bredr(name, data, setup, func) \
do { \
struct test_data *user; \
uint16_t send_len;
const void * (*send_func)(uint16_t *len);
uint8_t expect_status;
+ bool expect_ignore_param;
const void *expect_param;
uint16_t expect_len;
const void * (*expect_func)(uint16_t *len);
.expect_status = MGMT_STATUS_INVALID_INDEX,
};
+static const struct generic_data read_unconf_index_list_invalid_param_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_unconf_index_list_invalid_index_test = {
+ .send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_config_info_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_CONFIG_INFO,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_config_info_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_CONFIG_INFO,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_ext_index_list_invalid_param_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_EXT_INDEX_LIST,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_ext_index_list_invalid_index_test = {
+ .send_opcode = MGMT_OP_READ_EXT_INDEX_LIST,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
static const char set_powered_on_param[] = { 0x01 };
static const char set_powered_invalid_param[] = { 0x02 };
static const char set_powered_garbage_param[] = { 0x01, 0x00 };
.expect_hci_len = sizeof(set_connectable_off_adv_param),
};
+static uint16_t settings_powered_le_discoverable[] = {
+ MGMT_OP_SET_LE,
+ MGMT_OP_SET_CONNECTABLE,
+ MGMT_OP_SET_POWERED,
+ MGMT_OP_SET_DISCOVERABLE, 0 };
+
static uint16_t settings_powered_le_discoverable_advertising[] = {
MGMT_OP_SET_LE,
MGMT_OP_SET_CONNECTABLE,
static const char set_fast_conn_on_param[] = { 0x01 };
static const char set_fast_conn_on_settings_1[] = { 0x87, 0x00, 0x00, 0x00 };
+static const char set_fast_conn_on_settings_2[] = { 0x85, 0x00, 0x00, 0x00 };
+static const char set_fast_conn_on_settings_3[] = { 0x84, 0x00, 0x00, 0x00 };
static const struct generic_data set_fast_conn_on_success_test_1 = {
.setup_settings = settings_powered_connectable,
.expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
};
+static const struct generic_data set_fast_conn_on_success_test_2 = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_on_param,
+ .send_len = sizeof(set_fast_conn_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_fast_conn_on_settings_2,
+ .expect_len = sizeof(set_fast_conn_on_settings_2),
+ .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
+static const struct generic_data set_fast_conn_on_success_test_3 = {
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_on_param,
+ .send_len = sizeof(set_fast_conn_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_fast_conn_on_settings_3,
+ .expect_len = sizeof(set_fast_conn_on_settings_3),
+ .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
static const struct generic_data set_fast_conn_on_not_supported_test_1 = {
.setup_settings = settings_powered_connectable,
.send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
.expect_status = MGMT_STATUS_NOT_SUPPORTED,
};
+static const char set_fast_conn_nval_param[] = { 0xff };
+
+static const struct generic_data set_fast_conn_nval_param_test_1 = {
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_nval_param,
+ .send_len = sizeof(set_fast_conn_nval_param),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
static const char set_bondable_on_param[] = { 0x01 };
static const char set_bondable_invalid_param[] = { 0x02 };
static const char set_bondable_garbage_param[] = { 0x01, 0x00 };
static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP,
MGMT_OP_SET_POWERED, 0 };
+static uint16_t settings_powered_sc[] = { MGMT_OP_SET_SSP,
+ MGMT_OP_SET_SECURE_CONN,
+ MGMT_OP_SET_POWERED, 0 };
+
static const char set_sc_on_param[] = { 0x01 };
static const char set_sc_only_on_param[] = { 0x02 };
static const char set_sc_invalid_param[] = { 0x03 };
.expect_hci_len = sizeof(set_le_scan_off),
};
+static const struct generic_data read_adv_features_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_ADV_FEATURES,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_adv_features_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_ADV_FEATURES,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const uint8_t add_advertising_param_1[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_2[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0a,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+ 0x03, 0x19, 0x40, 0x03,
+ 0x05, 0x03, 0x0d, 0x18, 0x0f, 0x18,
+};
+
+static const uint8_t add_advertising_param_3[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_4[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_5[] = {
+ 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_6[] = {
+ 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_7[] = {
+ 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_8[] = {
+ 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t advertising_instance_param[] = {
+ 0x01,
+};
+
+static const uint8_t set_adv_data_1[] = {
+ 0x09, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_2[] = {
+ 0x0c, 0x02, 0x01, 0x04, 0x03, 0x03, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_3[] = {
+ 0x06, 0x05, 0x08, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_4[] = {
+ 0x03, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_5[] = {
+ 0x0c, 0x02, 0x01, 0x02, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_6[] = {
+ 0x0c, 0x02, 0x01, 0x01, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_7[] = {
+ 0x0c, 0x02, 0x01, 0x02, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_8[] = {
+ 0x0c, 0x02, 0x0a, 0x00, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_scan_rsp_1[] = {
+ 0x0a, 0x03, 0x19, 0x40, 0x03, 0x05, 0x03, 0x0d, 0x18, 0x0f,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t add_advertising_invalid_param_1[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+};
+
+static const uint8_t add_advertising_invalid_param_2[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x04, 0x03, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_3[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x02, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_4[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x05, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_5[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18,
+};
+
+static const uint8_t add_advertising_invalid_param_6[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+};
+
+static const uint8_t add_advertising_invalid_param_7[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x04, 0x03, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_8[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x02, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_9[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x05, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_10[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18,
+};
+
+static const struct generic_data add_advertising_fail_1 = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data add_advertising_fail_2 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_1,
+ .send_len = sizeof(add_advertising_invalid_param_1),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_3 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_2,
+ .send_len = sizeof(add_advertising_invalid_param_2),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_4 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_3,
+ .send_len = sizeof(add_advertising_invalid_param_3),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_5 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_4,
+ .send_len = sizeof(add_advertising_invalid_param_4),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_6 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_5,
+ .send_len = sizeof(add_advertising_invalid_param_5),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_7 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_6,
+ .send_len = sizeof(add_advertising_invalid_param_6),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_8 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_7,
+ .send_len = sizeof(add_advertising_invalid_param_7),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_9 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_8,
+ .send_len = sizeof(add_advertising_invalid_param_8),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_10 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_9,
+ .send_len = sizeof(add_advertising_invalid_param_9),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_11 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_10,
+ .send_len = sizeof(add_advertising_invalid_param_10),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_12 = {
+ .setup_settings = settings_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_3,
+ .send_len = sizeof(add_advertising_param_3),
+ .expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data add_advertising_success_1 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_1,
+ .expect_hci_len = sizeof(set_adv_data_1),
+};
+
+static const char set_powered_adv_instance_settings_param[] = {
+ 0x81, 0x02, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_success_2 = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_on_param,
+ .send_len = sizeof(set_powered_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_3,
+ .expect_hci_len = sizeof(set_adv_data_3),
+};
+
+static const struct generic_data add_advertising_success_3 = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_on_param,
+ .send_len = sizeof(set_powered_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_on_set_adv_enable_param,
+ .expect_hci_len = sizeof(set_adv_on_set_adv_enable_param),
+};
+
+static const struct generic_data add_advertising_success_4 = {
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_on_param,
+ .send_len = sizeof(set_adv_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_adv_settings_param_2,
+ .expect_len = sizeof(set_adv_settings_param_2),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_4,
+ .expect_hci_len = sizeof(set_adv_data_4),
+};
+
+static const char set_adv_off_param[] = { 0x00 };
+
+static const struct generic_data add_advertising_success_5 = {
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_off_param,
+ .send_len = sizeof(set_adv_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_3,
+ .expect_hci_len = sizeof(set_adv_data_3),
+};
+
+static const struct generic_data add_advertising_success_6 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_1,
+ .expect_hci_len = sizeof(set_adv_data_1),
+};
+
+static const struct generic_data add_advertising_success_7 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_scan_rsp_1,
+ .expect_hci_len = sizeof(set_scan_rsp_1),
+};
+
+static const struct generic_data add_advertising_success_8 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_4,
+ .send_len = sizeof(add_advertising_param_4),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const struct generic_data add_advertising_success_9 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_5,
+ .send_len = sizeof(add_advertising_param_5),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_5,
+ .expect_hci_len = sizeof(set_adv_data_5),
+};
+
+static const struct generic_data add_advertising_success_10 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_6,
+ .send_len = sizeof(add_advertising_param_6),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_6,
+ .expect_hci_len = sizeof(set_adv_data_6),
+};
+
+static const struct generic_data add_advertising_success_11 = {
+ .setup_settings = settings_powered_le_discoverable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_7,
+ .send_len = sizeof(add_advertising_param_7),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_7,
+ .expect_hci_len = sizeof(set_adv_data_7),
+};
+
+static const struct generic_data add_advertising_success_12 = {
+ .setup_settings = settings_powered_le_discoverable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_8,
+ .send_len = sizeof(add_advertising_param_8),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_8,
+ .expect_hci_len = sizeof(set_adv_data_8),
+};
+
+static uint16_t settings_powered_le_connectable[] = {
+ MGMT_OP_SET_POWERED,
+ MGMT_OP_SET_LE,
+ MGMT_OP_SET_CONNECTABLE, 0 };
+
+static uint8_t set_connectable_off_scan_adv_param[] = {
+ 0x00, 0x08, /* min_interval */
+ 0x00, 0x08, /* max_interval */
+ 0x02, /* type */
+ 0x01, /* own_addr_type */
+ 0x00, /* direct_addr_type */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* direct_addr */
+ 0x07, /* channel_map */
+ 0x00, /* filter_policy */
+};
+
+static const struct generic_data add_advertising_success_13 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_scan_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_scan_adv_param),
+};
+
+static const struct generic_data add_advertising_success_14 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const struct generic_data add_advertising_success_15 = {
+ .setup_settings = settings_powered_le_connectable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const char set_connectable_settings_param_3[] = {
+ 0x83, 0x02, 0x00, 0x00 };
+
+static const struct generic_data add_advertising_success_16 = {
+ .send_opcode = MGMT_OP_SET_CONNECTABLE,
+ .send_param = set_connectable_on_param,
+ .send_len = sizeof(set_connectable_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_connectable_settings_param_3,
+ .expect_len = sizeof(set_connectable_settings_param_3),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const struct generic_data add_advertising_success_17 = {
+ .send_opcode = MGMT_OP_SET_CONNECTABLE,
+ .send_param = set_connectable_off_param,
+ .send_len = sizeof(set_connectable_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_le_settings_param_2,
+ .expect_len = sizeof(set_le_settings_param_2),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const char set_powered_off_le_settings_param[] = {
+ 0x80, 0x02, 0x00, 0x00
+};
+
+static const struct generic_data add_advertising_timeout_power_off = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_off_param,
+ .send_len = sizeof(set_powered_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_off_le_settings_param,
+ .expect_len = sizeof(set_powered_off_le_settings_param),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+};
+
+static const uint8_t remove_advertising_param_1[] = {
+ 0x01,
+};
+
+static const uint8_t remove_advertising_param_2[] = {
+ 0x00,
+};
+
+static const struct generic_data remove_advertising_fail_1 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_1,
+ .send_len = sizeof(remove_advertising_param_1),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data remove_advertising_success_1 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_1,
+ .send_len = sizeof(remove_advertising_param_1),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = remove_advertising_param_1,
+ .expect_len = sizeof(remove_advertising_param_1),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_off_param,
+ .expect_hci_len = sizeof(set_adv_off_param),
+};
+
+static const struct generic_data remove_advertising_success_2 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_2,
+ .send_len = sizeof(remove_advertising_param_2),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = remove_advertising_param_1,
+ .expect_len = sizeof(remove_advertising_param_1),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_off_param,
+ .expect_hci_len = sizeof(set_adv_off_param),
+};
+
+static const struct generic_data read_local_oob_not_powered_test = {
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_NOT_POWERED,
+};
+
+static const struct generic_data read_local_oob_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_local_oob_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_local_oob_legacy_pairing_test = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data read_local_oob_success_ssp_test = {
+ .setup_settings = settings_powered_ssp,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_ignore_param = true,
+ .expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_DATA,
+};
+
+static const struct generic_data read_local_oob_success_sc_test = {
+ .setup_settings = settings_powered_sc,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_ignore_param = true,
+ .expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA,
+};
+
static void client_cmd_complete(uint16_t opcode, uint8_t status,
const void *param, uint8_t len,
void *user_data)
setup_powered_callback, NULL, NULL);
}
+static void setup_add_advertising_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ tester_setup_failed();
+ return;
+ }
+
+ tester_print("Add Advertising setup complete");
+
+ setup_bthost();
+}
+
+static void setup_add_advertising_not_powered(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising_connectable(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising_timeout(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->timeout = 5;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_set_and_add_advertising(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index,
+ sizeof(param), ¶m,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
static void setup_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
return;
}
- if (test->expect_func)
- expect_param = test->expect_func(&expect_len);
+ if (!test->expect_ignore_param) {
+ if (test->expect_func)
+ expect_param = test->expect_func(&expect_len);
- if (length != expect_len) {
- tester_test_failed();
- return;
- }
+ if (length != expect_len) {
+ tester_test_failed();
+ return;
+ }
- if (expect_param && expect_len > 0 &&
- memcmp(param, expect_param, length)) {
- tester_test_failed();
- return;
+ if (expect_param && expect_len > 0 &&
+ memcmp(param, expect_param, length)) {
+ tester_test_failed();
+ return;
+ }
}
test_condition_complete(data);
test_bredrle("Read info - Invalid index",
&read_info_invalid_index_test,
NULL, test_command_generic);
+ test_bredrle("Read unconfigured index list - Invalid parameters",
+ &read_unconf_index_list_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read unconfigured index list - Invalid index",
+ &read_unconf_index_list_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredrle("Read configuration info - Invalid parameters",
+ &read_config_info_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read configuration info - Invalid index",
+ &read_config_info_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredrle("Read extended index list - Invalid parameters",
+ &read_ext_index_list_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read extended index list - Invalid index",
+ &read_ext_index_list_invalid_index_test,
+ NULL, test_command_generic);
test_bredrle("Set powered on - Success",
&set_powered_on_success_test,
test_bredrle("Set fast connectable on - Success 1",
&set_fast_conn_on_success_test_1,
NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Success 2",
+ &set_fast_conn_on_success_test_2,
+ NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Success 3",
+ &set_fast_conn_on_success_test_3,
+ NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Invalid Params 1",
+ &set_fast_conn_nval_param_test_1,
+ NULL, test_command_generic);
test_le("Set fast connectable on - Not Supported 1",
&set_fast_conn_on_not_supported_test_1,
NULL, test_command_generic);
&remove_device_success_5,
setup_add_device, test_command_generic);
+ test_bredrle("Read Advertising Features - Invalid parameters",
+ &read_adv_features_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Advertising Features - Invalid index",
+ &read_adv_features_invalid_index_test,
+ NULL, test_command_generic);
+
+ test_bredrle("Add Advertising - Failure: LE off",
+ &add_advertising_fail_1,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 1 (AD too long)",
+ &add_advertising_fail_2,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 2 (Malformed len)",
+ &add_advertising_fail_3,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 3 (Malformed len)",
+ &add_advertising_fail_4,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 4 (Malformed len)",
+ &add_advertising_fail_5,
+ NULL, test_command_generic);
+ test_le("Add Advertising - Invalid Params 5 (AD too long)",
+ &add_advertising_fail_6,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 6 (ScRsp too long)",
+ &add_advertising_fail_7,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 7 (Malformed len)",
+ &add_advertising_fail_8,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 8 (Malformed len)",
+ &add_advertising_fail_9,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 9 (Malformed len)",
+ &add_advertising_fail_10,
+ NULL, test_command_generic);
+ test_le("Add Advertising - Invalid Params 10 (ScRsp too long)",
+ &add_advertising_fail_11,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Timeout Not Powered",
+ &add_advertising_fail_12,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Timeout Power off",
+ &add_advertising_timeout_power_off,
+ setup_add_advertising_timeout,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 1",
+ &add_advertising_success_1,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 2",
+ &add_advertising_success_2,
+ setup_add_advertising_not_powered,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 3",
+ &add_advertising_success_3,
+ setup_add_advertising_not_powered,
+ test_command_generic);
+ test_bredrle("Add Advertising - Set Advertising on override 1",
+ &add_advertising_success_4,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Set Advertising off override 2",
+ &add_advertising_success_5,
+ setup_set_and_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 4",
+ &add_advertising_success_6,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 5",
+ &add_advertising_success_7,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 6 - Flag 0",
+ &add_advertising_success_8,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 7 - Flag 1",
+ &add_advertising_success_9,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 8 - Flag 2",
+ &add_advertising_success_10,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 8 - Flag 3",
+ &add_advertising_success_11,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 9 - Flag 4",
+ &add_advertising_success_12,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 10 - ADV_SCAN_IND",
+ &add_advertising_success_13,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 11 - ADV_NONCONN_IND",
+ &add_advertising_success_14,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 12 - ADV_IND",
+ &add_advertising_success_15,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 13 - connectable -> on",
+ &add_advertising_success_16,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 14 - connectable -> off",
+ &add_advertising_success_17,
+ setup_add_advertising_connectable,
+ test_command_generic);
+
+ test_bredrle("Remove Advertising - Invalid Params 1",
+ &remove_advertising_fail_1,
+ NULL, test_command_generic);
+ test_bredrle("Remove Advertising - Success 1",
+ &remove_advertising_success_1,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Remove Advertising - Success 2",
+ &remove_advertising_success_2,
+ setup_add_advertising,
+ test_command_generic);
+
+ test_bredrle("Read Local OOB Data - Not powered",
+ &read_local_oob_not_powered_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Invalid parameters",
+ &read_local_oob_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Invalid index",
+ &read_local_oob_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredr20("Read Local OOB Data - Legacy pairing",
+ &read_local_oob_legacy_pairing_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Success SSP",
+ &read_local_oob_success_ssp_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Success SC",
+ &read_local_oob_success_sc_test,
+ NULL, test_command_generic);
+
return tester_run();
}
done:
dbus_message_unref(copy);
- return DBUS_HANDLER_RESULT_HANDLED;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static struct player *find_player_by_bus_name(const char *name)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to send/pull: %s\n", error.name);
+ rl_printf("Failed to send: %s\n", error.name);
dbus_error_free(&error);
return;
}
g_dbus_proxy_get_path(proxy));
}
-static void opp_pull(GDBusProxy *proxy, int argc, char *argv[])
-{
- if (argc < 2) {
- rl_printf("Missing file argument\n");
- return;
- }
-
- if (g_dbus_proxy_method_call(proxy, "PullBusinessCard", send_setup,
- send_reply, g_strdup(argv[1]), g_free) == FALSE) {
- rl_printf("Failed to pull\n");
- return;
- }
-
- rl_printf("Attempting to pull %s from %s\n", argv[1],
- g_dbus_proxy_get_path(proxy));
-}
-
static void push_reply(DBusMessage *message, void *user_data)
{
DBusMessageIter iter;
rl_printf("Command not supported\n");
}
-static void cmd_pull(int argc, char *argv[])
-{
- GDBusProxy *proxy;
-
- if (!check_default_session())
- return;
-
- proxy = find_opp(g_dbus_proxy_get_path(default_session));
- if (proxy) {
- opp_pull(proxy, argc, argv);
- return;
- }
-
- rl_printf("Command not supported\n");
-}
-
static void change_folder_reply(DBusMessage *message, void *user_data)
{
DBusError error;
{ "suspend", "<transfer>", cmd_suspend, "Suspend transfer" },
{ "resume", "<transfer>", cmd_resume, "Resume transfer" },
{ "send", "<file>", cmd_send, "Send file" },
- { "pull", "<file>", cmd_pull,
- "Pull Vobject & stores in file" },
{ "cd", "<path>", cmd_cd, "Change current folder" },
{ "ls", "<options>", cmd_ls, "List current folder" },
{ "cp", "<source file> <destination file>", cmd_cp,
#include "src/shared/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
+#include "src/shared/crypto.h"
+
+#define REMOTE_IRK "\x69\x30\xde\xc3\x8f\x84\x74\x14" \
+ "\xe1\x23\x99\xc1\xca\x9a\xc3\x31"
static bool use_bredr = false;
static bool use_le = false;
static bool use_sconly = false;
static bool use_legacy = false;
static bool use_random = false;
+static bool use_privacy = false;
static bool use_debug = false;
static bool use_cross = false;
+static bool provide_tk = false;
static bool provide_p192 = false;
static bool provide_p256 = false;
+static bool provide_initiator = false;
+static bool provide_acceptor = false;
static struct mgmt *mgmt;
static uint16_t index1 = MGMT_INDEX_NONE;
static uint16_t index2 = MGMT_INDEX_NONE;
static bdaddr_t bdaddr1;
static bdaddr_t bdaddr2;
+static uint8_t oob_tk[16];
static void pin_code_request_event(uint16_t index, uint16_t len,
const void *param, void *user_data)
cp.addr.type = BDADDR_LE_RANDOM;
else
cp.addr.type = BDADDR_LE_PUBLIC;
- if (hash192 && rand192) {
+ if (hash192) {
memcpy(cp.hash192, hash192, 16);
- memcpy(cp.rand192, rand192, 16);
+ if (rand192)
+ memcpy(cp.rand192, rand192, 16);
+ else
+ memset(cp.rand192, 0, 16);
} else {
memset(cp.hash192, 0, 16);
memset(cp.rand192, 0, 16);
static void read_oob_data_complete(uint8_t status, uint16_t len,
const void *param, void *user_data)
{
- const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+ const struct mgmt_rp_read_local_oob_data *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
const uint8_t *hash192, *rand192, *hash256, *rand256;
int i;
printf("[Index %u]\n", index);
+ hash192 = NULL;
+ rand192 = NULL;
+ hash256 = NULL;
+ rand256 = NULL;
+
+ if (index == index1 && !provide_initiator) {
+ printf(" Skipping initiator OOB data\n");
+ goto done;
+ } else if (index == index2 && !provide_acceptor) {
+ printf(" Skipping acceptor OOB data\n");
+ goto done;
+ }
+
if (provide_p192) {
hash192 = rp->hash192;
- rand192 = rp->randomizer192;
- } else {
- hash192 = NULL;
- rand192 = NULL;
+ rand192 = rp->rand192;
}
printf(" Hash C from P-192: ");
printf(" Randomizer R with P-192: ");
for (i = 0; i < 16; i++)
- printf("%02x", rp->randomizer192[i]);
+ printf("%02x", rp->rand192[i]);
printf("\n");
- if (len < sizeof(*rp)) {
- hash256 = NULL;
- rand256 = NULL;
+ if (len < sizeof(*rp))
goto done;
- }
if (provide_p256) {
hash256 = rp->hash256;
- rand256 = rp->randomizer256;
- } else {
- hash256 = NULL;
- rand256 = NULL;
+ rand256 = rp->rand256;
}
printf(" Hash C from P-256: ");
printf(" Randomizer R with P-256: ");
for (i = 0; i < 16; i++)
- printf("%02x", rp->randomizer256[i]);
+ printf("%02x", rp->rand256[i]);
printf("\n");
done:
hash192, rand192, hash256, rand256);
}
+static void read_oob_ext_data_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ uint16_t eir_len, parsed;
+ const uint8_t *eir, *tk, *hash256, *rand256;
+ int i;
+
+ if (status) {
+ fprintf(stderr, "Reading OOB data for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ printf("[Index %u]\n", index);
+
+ eir_len = le16_to_cpu(rp->eir_len);
+ printf(" OOB data len: %u\n", eir_len);
+
+ if (provide_tk)
+ tk = oob_tk;
+ else
+ tk = NULL;
+
+ hash256 = NULL;
+ rand256 = NULL;
+
+ if (index == index1 && !provide_initiator) {
+ printf(" Skipping initiator OOB data\n");
+ goto done;
+ } else if (index == index2 && !provide_acceptor) {
+ printf(" Skipping acceptor OOB data\n");
+ goto done;
+ }
+
+ if (eir_len < 2)
+ goto done;
+
+ eir = rp->eir;
+ parsed = 0;
+
+ while (parsed < eir_len - 1) {
+ uint8_t field_len = eir[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > eir_len)
+ break;
+
+ /* LE Bluetooth Device Address */
+ if (eir[1] == 0x1b) {
+ char str[18];
+
+ ba2str((bdaddr_t *) (eir + 2), str);
+ printf(" Device address: %s (%s)\n", str,
+ eir[8] ? "random" : "public");
+ }
+
+ /* LE Role */
+ if (eir[1] == 0x1c)
+ printf(" Role: 0x%02x\n", eir[2]);
+
+ /* LE Secure Connections Confirmation Value */
+ if (eir[1] == 0x22) {
+ hash256 = eir + 2;
+
+ printf(" Hash C from P-256: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", hash256[i]);
+ printf("\n");
+ }
+
+ /* LE Secure Connections Random Value */
+ if (eir[1] == 0x23) {
+ rand256 = eir + 2;
+
+ printf(" Randomizer R with P-256: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", rand256[i]);
+ printf("\n");
+ }
+
+ eir += field_len + 1;
+ }
+
+done:
+ if (index == index1)
+ add_remote_oob_data(index2, &bdaddr1,
+ tk, NULL, hash256, rand256);
+ else if (index == index2)
+ add_remote_oob_data(index1, &bdaddr2,
+ tk, NULL, hash256, rand256);
+}
+
static void set_powered_complete(uint8_t status, uint16_t len,
const void *param, void *user_data)
{
mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL,
read_oob_data_complete,
UINT_TO_PTR(index), NULL);
+ } else if (use_le && provide_p256) {
+ uint8_t type = (1 << BDADDR_LE_PUBLIC) |
+ (1 << BDADDR_LE_RANDOM);
+
+ mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, index,
+ sizeof(type), &type,
+ read_oob_ext_data_complete,
+ UINT_TO_PTR(index), NULL);
+ } else if (use_le && provide_tk) {
+ const uint8_t *tk = oob_tk;
+
+ if (index == index1)
+ add_remote_oob_data(index2, &bdaddr1,
+ tk, NULL, NULL, NULL);
+ else if (index == index2)
+ add_remote_oob_data(index1, &bdaddr2,
+ tk, NULL, NULL, NULL);
} else {
if (index == index1)
add_remote_oob_data(index2, &bdaddr1,
sizeof(cp), &cp, NULL, NULL, NULL);
}
+static void clear_identity_resolving_keys(uint16_t index)
+{
+ struct mgmt_cp_load_irks cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.irk_count = cpu_to_le16(0);
+
+ mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index,
+ sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
static void clear_remote_oob_data(uint16_t index)
{
struct mgmt_cp_remove_remote_oob_data cp;
sizeof(cp), &cp, NULL, NULL, NULL);
}
+static void set_powered_down_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Power down for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_bredr_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting BR/EDR for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_le_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting LE for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_ssp_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Simple Pairing for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_static_address_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Static address for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_secure_conn_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Secure connections for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_privacy_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting privacy for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_debug_keys_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting debug keys for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_bondable_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Setting bondable for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
static void read_info(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
return;
}
+ if (use_privacy && !(supported_settings & MGMT_SETTING_PRIVACY)) {
+ fprintf(stderr, "Privacy support missing\n");
+ mainloop_quit();
+ return;
+ }
+
if (use_debug && !(supported_settings & MGMT_SETTING_DEBUG_KEYS)) {
fprintf(stderr, "Debug keys support missing\n");
mainloop_quit();
return;
}
+ if (provide_tk) {
+ const uint8_t *tk = oob_tk;
+ int i;
+
+ printf(" TK Value: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", tk[i]);
+ printf("\n");
+ }
+
mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index,
pin_code_request_event,
UINT_TO_PTR(index), NULL);
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
- NULL, NULL, NULL);
+ set_powered_down_complete,
+ UINT_TO_PTR(index), NULL);
clear_link_keys(index);
clear_long_term_keys(index);
+ clear_identity_resolving_keys(index);
clear_remote_oob_data(index);
if (use_bredr) {
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
- NULL, NULL, NULL);
+ set_bredr_complete,
+ UINT_TO_PTR(index), NULL);
val = use_cross ? 0x01 : 0x00;
mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
- NULL, NULL, NULL);
+ set_le_complete,
+ UINT_TO_PTR(index), NULL);
val = use_legacy ? 0x00 : 0x01;
mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
- NULL, NULL, NULL);
+ set_ssp_complete,
+ UINT_TO_PTR(index), NULL);
} else if (use_le) {
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
- NULL, NULL, NULL);
+ set_le_complete,
+ UINT_TO_PTR(index), NULL);
val = use_cross ? 0x01 : 0x00;
mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
- NULL, NULL, NULL);
+ set_bredr_complete,
+ UINT_TO_PTR(index), NULL);
+
+ if (use_cross) {
+ val = use_legacy ? 0x00 : 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
+ set_ssp_complete,
+ UINT_TO_PTR(index), NULL);
+ }
} else {
fprintf(stderr, "Invalid transport for pairing\n");
mainloop_quit();
str2ba("c0:00:aa:bb:00:00", &bdaddr);
bdaddr.b[0] = index;
- mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index,
- 6, &bdaddr, NULL, NULL, NULL);
+ mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr,
+ set_static_address_complete,
+ UINT_TO_PTR(index), NULL);
if (index == index1)
bacpy(&bdaddr1, &bdaddr);
bacpy(&bdaddr, BDADDR_ANY);
- mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index,
- 6, &bdaddr, NULL, NULL, NULL);
+ mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr,
+ set_static_address_complete,
+ UINT_TO_PTR(index), NULL);
}
if (use_sc) {
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
} else if (use_sconly) {
val = 0x02;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
} else {
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
+ }
+
+ if (use_privacy) {
+ struct mgmt_cp_set_privacy cp;
+
+ if (index == index2) {
+ cp.privacy = 0x01;
+ memcpy(cp.irk, REMOTE_IRK, sizeof(cp.irk));
+ } else {
+ cp.privacy = 0x00;
+ memset(cp.irk, 0, sizeof(cp.irk));
+ }
+
+ mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+ set_privacy_complete,
+ UINT_TO_PTR(index), NULL);
+ } else {
+ struct mgmt_cp_set_privacy cp;
+
+ cp.privacy = 0x00;
+ memset(cp.irk, 0, sizeof(cp.irk));
+
+ mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+ set_privacy_complete,
+ UINT_TO_PTR(index), NULL);
}
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
- NULL, NULL, NULL);
+ set_debug_keys_complete,
+ UINT_TO_PTR(index), NULL);
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val,
- NULL, NULL, NULL);
+ set_bondable_complete,
+ UINT_TO_PTR(index), NULL);
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
printf("Selecting index %u for initiator\n", index1);
printf("Selecting index %u for acceptor\n", index2);
+ if (provide_tk) {
+ struct bt_crypto *crypto;
+
+ printf("Generating Security Manager TK Value\n");
+
+ crypto = bt_crypto_new();
+ bt_crypto_random_bytes(crypto, oob_tk, 16);
+ bt_crypto_unref(crypto);
+ }
+
mgmt_send(mgmt, MGMT_OP_READ_INFO, index1, 0, NULL,
read_info, UINT_TO_PTR(index1), NULL);
mgmt_send(mgmt, MGMT_OP_READ_INFO, index2, 0, NULL,
"\t-O, --sconly Use Secure Connections Only\n"
"\t-P, --legacy Use Legacy Pairing\n"
"\t-R, --random Use Static random address\n"
+ "\t-Y, --privacy Use LE privacy feature\n"
"\t-D, --debug Use Pairing debug keys\n"
"\t-C, --cross Use cross-transport pairing\n"
+ "\t-0, --tk Provide LE legacy OOB data\n"
"\t-1, --p192 Provide P-192 OOB data\n"
"\t-2, --p256 Provide P-256 OOB data\n"
+ "\t-I, --initiator Initiator provides OOB data\n"
+ "\t-A, --acceptor Acceptor provides OOB data\n"
"\t-h, --help Show help options\n");
}
static const struct option main_options[] = {
- { "bredr", no_argument, NULL, 'B' },
- { "le", no_argument, NULL, 'L' },
- { "sc", no_argument, NULL, 'S' },
- { "sconly", no_argument, NULL, 'O' },
- { "legacy", no_argument, NULL, 'P' },
- { "random", no_argument, NULL, 'R' },
- { "static", no_argument, NULL, 'R' },
- { "debug", no_argument, NULL, 'D' },
- { "cross", no_argument, NULL, 'C' },
- { "dual", no_argument, NULL, 'C' },
- { "p192", no_argument, NULL, '1' },
- { "p256", no_argument, NULL, '2' },
- { "version", no_argument, NULL, 'v' },
- { "help", no_argument, NULL, 'h' },
+ { "bredr", no_argument, NULL, 'B' },
+ { "le", no_argument, NULL, 'L' },
+ { "sc", no_argument, NULL, 'S' },
+ { "sconly", no_argument, NULL, 'O' },
+ { "legacy", no_argument, NULL, 'P' },
+ { "random", no_argument, NULL, 'R' },
+ { "static", no_argument, NULL, 'R' },
+ { "privacy", no_argument, NULL, 'Y' },
+ { "debug", no_argument, NULL, 'D' },
+ { "cross", no_argument, NULL, 'C' },
+ { "dual", no_argument, NULL, 'C' },
+ { "tk", no_argument, NULL, '0' },
+ { "p192", no_argument, NULL, '1' },
+ { "p256", no_argument, NULL, '2' },
+ { "initiator", no_argument, NULL, 'I' },
+ { "acceptor", no_argument, NULL, 'A' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
{ }
};
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "BLSOPRDC12vh",
+ opt = getopt_long(argc, argv, "BLSOPRYDC012IAvh",
main_options, NULL);
if (opt < 0)
break;
case 'R':
use_random = true;
break;
+ case 'Y':
+ use_privacy = true;
+ break;
case 'D':
use_debug = true;
break;
case 'C':
use_cross = true;
break;
+ case '0':
+ provide_tk = true;
+ break;
case '1':
provide_p192 = true;
break;
case '2':
provide_p256 = true;
break;
+ case 'I':
+ provide_initiator = true;
+ break;
+ case 'A':
+ provide_acceptor = true;
+ break;
case 'v':
printf("%s\n", VERSION);
return EXIT_SUCCESS;
return EXIT_FAILURE;
}
+ if (use_privacy && !use_le && !use_cross ) {
+ fprintf(stderr, "Specify --privacy with --le or --cross\n");
+ return EXIT_FAILURE;
+ }
+
if (use_random && !use_le) {
fprintf(stderr, "Specify --random with --le\n");
return EXIT_FAILURE;
#include <glib.h>
#include "src/shared/util.h"
+#include "src/shared/tester.h"
#include "src/log.h"
#include "android/avctp.h"
};
struct context {
- GMainLoop *main_loop;
struct avctp *session;
guint source;
guint process;
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, function); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
+ tester_add(name, &data, NULL, function, NULL); \
} while (0)
static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s", prefix, str);
}
static void test_free(gconstpointer user_data)
g_free(data->pdu_list);
}
+static void destroy_context(struct context *context)
+{
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ avctp_shutdown(context->session);
+
+ test_free(context->data);
+ g_free(context);
+}
+
static gboolean context_quit(gpointer user_data)
{
struct context *context = user_data;
if (context->process > 0)
g_source_remove(context->process);
- g_main_loop_quit(context->main_loop);
+ destroy_context(context);
+
+ tester_test_passed();
return FALSE;
}
len = write(context->fd, pdu->data, pdu->size);
- if (g_test_verbose())
- util_hexdump('<', pdu->data, len, test_debug, "AVCTP: ");
+ util_hexdump('<', pdu->data, len, test_debug, "AVCTP: ");
g_assert_cmpint(len, ==, pdu->size);
g_assert(len > 0);
- if (g_test_verbose())
- util_hexdump('>', buf, len, test_debug, "AVCTP: ");
+ util_hexdump('>', buf, len, test_debug, "AVCTP: ");
g_assert_cmpint(len, ==, pdu->size);
GIOChannel *channel;
int err, sv[2];
- context->main_loop = g_main_loop_new(NULL, FALSE);
- g_assert(context->main_loop);
-
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
g_assert(err == 0);
return context;
}
-static void destroy_context(struct context *context)
-{
- if (context->source > 0)
- g_source_remove(context->source);
-
- avctp_shutdown(context->session);
-
- g_main_loop_unref(context->main_loop);
-
- test_free(context->data);
- g_free(context);
-}
-
-static void execute_context(struct context *context)
-{
- g_main_loop_run(context->main_loop);
-
- destroy_context(context);
-}
-
static ssize_t handler(struct avctp *session,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data)
{
- DBG("transaction %d code %d subunit %d operand_count %zu",
- transaction, *code, *subunit, operand_count);
-
g_assert_cmpint(transaction, ==, 0);
g_assert_cmpint(*code, ==, 0);
g_assert_cmpint(*subunit, ==, 0);
{
struct context *context = user_data;
- DBG("code 0x%02x subunit %d operand_count %zu", code, subunit,
- operand_count);
-
g_assert_cmpint(code, ==, 0x0a);
g_assert_cmpint(subunit, ==, 0);
g_assert_cmpint(operand_count, ==, 0);
avctp_send_vendor_req(context->session, AVC_CTYPE_CONTROL, 0, NULL,
0, handler_response, context);
-
- execute_context(context);
}
static void test_server(gconstpointer data)
ret = avctp_register_pdu_handler(context->session,
AVC_OP_VENDORDEP, handler, NULL);
- DBG("ret %d", ret);
g_assert_cmpint(ret, !=, 0);
}
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_dummy(gconstpointer data)
{
struct context *context = create_context(0x0100, data);
- destroy_context(context);
+ context_quit(context);
}
int main(int argc, char *argv[])
{
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
- if (g_test_verbose())
- __btd_log_init("*", 0);
+ __btd_log_init("*", 0);
/* Connection Channel Management tests */
raw_pdu(0x00, 0xff, 0xff, 0x00, 0x00, 0x00),
raw_pdu(0x03, 0xff, 0xff));
- return g_test_run();
+ return tester_run();
}
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/tester.h"
#include "src/log.h"
+
#include "android/avdtp.h"
#define MAX_SEID 0x3E
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, function); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
+ tester_add(name, &data, NULL, function, NULL); \
} while (0)
struct context {
- GMainLoop *main_loop;
struct avdtp *session;
struct avdtp_local_sep *sep;
struct avdtp_stream *stream;
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s", prefix, str);
}
static void test_free(gconstpointer user_data)
g_free(data->pdu_list);
}
+static void unregister_sep(void *data)
+{
+ struct avdtp_local_sep *sep = data;
+
+ /* Removed from the queue by caller */
+ avdtp_unregister_sep(NULL, sep);
+}
+
+static void destroy_context(struct context *context)
+{
+ if (context->source > 0)
+ g_source_remove(context->source);
+ avdtp_unref(context->session);
+
+ test_free(context->data);
+ queue_destroy(context->lseps, unregister_sep);
+
+ g_free(context);
+}
+
static gboolean context_quit(gpointer user_data)
{
struct context *context = user_data;
if (context->process > 0)
g_source_remove(context->process);
- g_main_loop_quit(context->main_loop);
+ destroy_context(context);
+
+ tester_test_passed();
return FALSE;
}
len = write(context->fd, pdu->data, pdu->size);
- if (g_test_verbose())
- util_hexdump('<', pdu->data, len, test_debug, "AVDTP: ");
+ util_hexdump('<', pdu->data, len, test_debug, "AVDTP: ");
g_assert_cmpint(len, ==, pdu->size);
g_assert(len > 0);
- if (g_test_verbose())
- util_hexdump('>', buf, len, test_debug, "AVDTP: ");
+ util_hexdump('>', buf, len, test_debug, "AVDTP: ");
g_assert_cmpint(len, ==, pdu->size);
GIOChannel *channel;
int err, sv[2];
- context->main_loop = g_main_loop_new(NULL, FALSE);
- g_assert(context->main_loop);
-
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
g_assert(err == 0);
return context_new(version, 672, 672, data);
}
-static void unregister_sep(void *data)
-{
- struct avdtp_local_sep *sep = data;
-
- /* Removed from the queue by caller */
- avdtp_unregister_sep(NULL, sep);
-}
-
-static void destroy_context(struct context *context)
-{
- if (context->source > 0)
- g_source_remove(context->source);
- avdtp_unref(context->session);
-
- g_main_loop_unref(context->main_loop);
-
- test_free(context->data);
- queue_destroy(context->lseps, unregister_sep);
-
- g_free(context);
-}
-
-static void execute_context(struct context *context)
-{
- g_main_loop_run(context->main_loop);
-
- destroy_context(context);
-}
-
static gboolean sep_getcap_ind(struct avdtp *session,
struct avdtp_local_sep *sep,
GSList **caps, uint8_t *err,
struct context *context = user_data;
int ret;
+ if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-09-C")) {
+ context_quit(context);
+ return;
+ }
+
if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-07-C")) {
g_assert(err != NULL);
g_assert_cmpint(avdtp_error_error_code(err), ==, 0x13);
g_assert(sep);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_server_1_3(gconstpointer data)
g_assert(sep);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_server_1_3_sink(gconstpointer data)
g_assert(sep);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_server_0_sep(gconstpointer data)
struct context *context = create_context(0x0100, data);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_server_seid(gconstpointer data)
context);
g_assert(!sep);
- destroy_context(context);
+ context_quit(context);
}
static void test_server_seid_duplicate(gconstpointer data)
/* Check SEID ids with DISCOVER */
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static gboolean sep_getcap_ind_frg(struct avdtp *session,
g_assert(sep);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void discover_cb(struct avdtp *session, GSList *seps,
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
-
- execute_context(context);
}
static void test_client_1_3(gconstpointer data)
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
-
- execute_context(context);
}
static void test_client_frg(gconstpointer data)
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
-
- execute_context(context);
}
int main(int argc, char *argv[])
{
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
- if (g_test_verbose())
- __btd_log_init("*", 0);
+ __btd_log_init("*", 0);
/*
* Stream Management Service
raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
0xff, 0xff, 0x02, 0x40),
raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
- 0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+ 0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+ raw_pdu(0x52, 0x03));
define_test("/TP/SIG/SMG/BV-10-C", test_server,
raw_pdu(0x00, 0x01),
raw_pdu(0x02, 0x01, 0x04, 0x00),
raw_pdu(0x50, 0x0d, 0x04, 0x00, 0x00),
raw_pdu(0x52, 0x0d));
- return g_test_run();
+ return tester_run();
}
#include <glib.h>
#include "src/shared/util.h"
+#include "src/shared/tester.h"
#include "src/log.h"
#include "lib/bluetooth.h"
};
struct context {
- GMainLoop *main_loop;
struct avrcp *session;
guint source;
guint browse_source;
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, function); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
+ tester_add(name, &data, NULL, function, NULL); \
} while (0)
static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s", prefix, str);
}
static void test_free(gconstpointer user_data)
g_free(data->pdu_list);
}
+static void destroy_context(struct context *context)
+{
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ if (context->browse_source > 0)
+ g_source_remove(context->browse_source);
+
+ avrcp_shutdown(context->session);
+
+ test_free(context->data);
+ g_free(context);
+}
+
static gboolean context_quit(gpointer user_data)
{
struct context *context = user_data;
if (context->process > 0)
g_source_remove(context->process);
- g_main_loop_quit(context->main_loop);
+ destroy_context(context);
+
+ tester_test_passed();
return FALSE;
}
else
len = write(context->fd, pdu->data, pdu->size);
- if (g_test_verbose())
- util_hexdump('<', pdu->data, len, test_debug, "AVRCP: ");
+ util_hexdump('<', pdu->data, len, test_debug, "AVRCP: ");
g_assert_cmpint(len, ==, pdu->size);
ssize_t len;
int fd;
- DBG("");
-
pdu = &context->data->pdu_list[context->pdu_offset++];
g_assert(!pdu->browse);
if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
context->source = 0;
- g_print("%s: cond %x\n", __func__, cond);
+ tester_debug("%s: cond %x\n", __func__, cond);
return FALSE;
}
ssize_t len;
int fd;
- DBG("");
-
pdu = &context->data->pdu_list[context->pdu_offset++];
g_assert(pdu->browse);
if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
context->browse_source = 0;
- g_print("%s: cond %x\n", __func__, cond);
+ tester_debug("%s: cond %x\n", __func__, cond);
return FALSE;
}
g_assert(len > 0);
- if (g_test_verbose())
- util_hexdump('>', buf, len, test_debug, "AVRCP: ");
+ util_hexdump('>', buf, len, test_debug, "AVRCP: ");
g_assert_cmpint(len, ==, pdu->size);
GIOChannel *channel;
int err, sv[2];
- DBG("");
-
- context->main_loop = g_main_loop_new(NULL, FALSE);
- g_assert(context->main_loop);
-
/* Control channel setup */
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
return context;
}
-static void destroy_context(struct context *context)
-{
- if (context->source > 0)
- g_source_remove(context->source);
-
- avrcp_shutdown(context->session);
-
- if (context->browse_source > 0)
- g_source_remove(context->browse_source);
-
- g_main_loop_unref(context->main_loop);
-
- test_free(context->data);
- g_free(context);
-}
-
static void test_dummy(gconstpointer data)
{
struct context *context = create_context(0x0100, data);
- destroy_context(context);
-}
-
-static void execute_context(struct context *context)
-{
- g_main_loop_run(context->main_loop);
-
- destroy_context(context);
+ context_quit(context);
}
static bool handle_play(struct avrcp *session, bool pressed, void *user_data)
if (g_str_equal(context->data->test_name, "/TP/PTH/BV-02-C"))
avrcp_send_passthrough(context->session, 0, AVC_FAST_FORWARD);
-
- execute_context(context);
}
int main(int argc, char *argv[])
{
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
- if (g_test_verbose())
- __btd_log_init("*", 0);
+ __btd_log_init("*", 0);
/* Media Player Selection Commands and Notifications */
0x00, 0x19, 0x58, AVRCP_ABORT_CONTINUING,
0x00, 0x00, 0x00));
- return g_test_run();
+ return tester_run();
}
data.uuid = bt_uuid; \
data.step = test_step; \
data.source_db = db; \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
tester_add(name, &data, NULL, function, NULL); \
} while (0)
raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00), \
raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
+#define SERVICE_DATA_2_PDUS \
+ MTU_EXCHANGE_CLIENT_PDUS, \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
+ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x18),\
+ raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x05, 0x00, 0x0a, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, \
+ 0x2a), \
+ raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
+ raw_pdu(0x08, 0x05, 0x00, 0x0a, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x07, 0x00, 0x0a, 0x08, 0x00, 0x29, \
+ 0x2a), \
+ raw_pdu(0x08, 0x08, 0x00, 0x0a, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x08, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x09, 0x00, 0x0a, 0x00), \
+ raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x01, 0x29)
+
+#define SERVICE_DATA_3_PDUS \
+ MTU_EXCHANGE_CLIENT_PDUS, \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x00, 0x01, 0x21, 0x01, 0x00, 0x18, \
+ 0x00, 0x02, 0x00, 0x02, 0x01, 0x18), \
+ raw_pdu(0x10, 0x01, 0x02, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x00, 0x03, 0x20, 0x03, 0x0d, 0x18),\
+ raw_pdu(0x10, 0x21, 0x03, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0x21, 0x03, 0x0a), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x00, 0x01, 0x21, 0x01, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x00, 0x01, 0x0a), \
+ raw_pdu(0x08, 0x00, 0x03, 0x20, 0x03, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x00, 0x03, 0x0a), \
+ raw_pdu(0x08, 0x00, 0x01, 0x21, 0x01, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x10, 0x01, 0x02, 0x11, 0x01, 0x00, \
+ 0x2a, 0x20, 0x01, 0x02, 0x21, 0x01, 0x01, 0x2a),\
+ raw_pdu(0x08, 0x21, 0x01, 0x21, 0x01, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x21, 0x01, 0x0a), \
+ raw_pdu(0x04, 0x12, 0x01, 0x1f, 0x01), \
+ raw_pdu(0x01, 0x04, 0x12, 0x01, 0x0a), \
+ raw_pdu(0x08, 0x00, 0x03, 0x20, 0x03, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x10, 0x03, 0x0a, 0x11, 0x03, 0x29, \
+ 0x2a), \
+ raw_pdu(0x08, 0x11, 0x03, 0x20, 0x03, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x11, 0x03, 0x0a), \
+ raw_pdu(0x04, 0x12, 0x03, 0x20, 0x03), \
+ raw_pdu(0x05, 0x01, 0x20, 0x03, 0x02, 0x29)
+
#define PRIMARY_DISC_SMALL_DB \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
raw_pdu(0x11, 0x06, 0x10, 0xF0, 0x17, 0xF0, 0x00, 0x18, \
if (step && step->post_func)
step->post_func(context);
- destroy_context(context);
+ if (context->data->pdu_list[context->pdu_offset].valid)
+ tester_test_abort();
+ else
+ tester_test_passed();
- tester_test_passed();
+ destroy_context(context);
return FALSE;
}
uint8_t key[16] = {0xD8, 0x51, 0x59, 0x48, 0x45, 0x1F, 0xEA, 0x32, 0x0D,
0xC0, 0x5A, 0x2E, 0x88, 0x30, 0x81, 0x88 };
+ if (!bt_att_has_crypto(context->att)) {
+ context_quit(context);
+ return;
+ }
+
g_assert(bt_att_set_local_key(context->att, key, local_counter,
context));
.length = 0x03
};
+static void test_long_write_cb(bool success, bool reliable_error,
+ uint8_t att_ecode, void *user_data)
+{
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
+
+ g_assert(att_ecode == step->expected_att_ecode);
+
+ context_quit(context);
+}
+
+static void test_long_write(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ g_assert(bt_gatt_client_write_long_value(context->client, false,
+ step->handle, 0, step->value,
+ step->length, test_long_write_cb,
+ context, NULL));
+}
+
+/* The maximum length of an attribute value shall be 512 octets. */
+static const uint8_t long_data_2[512] = { [0 ... 511] = 0xff };
+
+static const struct test_step test_long_write_1 = {
+ .handle = 0x0007,
+ .func = test_long_write,
+ .expected_att_ecode = 0,
+ .value = long_data_2,
+ .length = sizeof(long_data_2)
+};
+
+static const struct test_step test_long_write_2 = {
+ .handle = 0x0000,
+ .func = test_long_write,
+ .expected_att_ecode = 0x01,
+ .value = write_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_write_3 = {
+ .handle = 0x0003,
+ .func = test_long_write,
+ .expected_att_ecode = 0x03,
+ .value = write_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_write_4 = {
+ .handle = 0x0007,
+ .func = test_long_write,
+ .expected_att_ecode = 0x08,
+ .value = write_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_write_5 = {
+ .handle = 0x0007,
+ .func = test_long_write,
+ .expected_att_ecode = 0x05,
+ .value = write_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_write_6 = {
+ .handle = 0x0007,
+ .func = test_long_write,
+ .expected_att_ecode = 0x0c,
+ .value = write_data_1,
+ .length = 0x03
+};
+
static void att_write_cb(struct gatt_db_attribute *att, int err,
void *user_data)
{
g_assert(!err);
}
-static struct gatt_db_attribute *add_char_with_value(struct gatt_db *db,
- struct gatt_db_attribute *service_att,
- bt_uuid_t *uuid,
- uint32_t att_permissions,
- uint8_t char_properties,
- const void *value, size_t len)
+static struct gatt_db_attribute *
+add_char_with_value(struct gatt_db_attribute *service_att, uint16_t handle,
+ bt_uuid_t *uuid, uint32_t att_permissions,
+ uint8_t char_properties, const void *value,
+ size_t len)
{
struct gatt_db_attribute *attrib;
- attrib = gatt_db_service_add_characteristic(service_att, uuid,
+ if (handle)
+ attrib = gatt_db_service_insert_characteristic(service_att,
+ handle, uuid,
+ att_permissions,
+ char_properties,
+ NULL, NULL,
+ NULL);
+ else
+ attrib = gatt_db_service_add_characteristic(service_att, uuid,
att_permissions,
char_properties,
NULL, NULL,
}
static struct gatt_db_attribute *
-add_desc_with_value(struct gatt_db_attribute *att, bt_uuid_t *uuid,
- uint32_t att_perms, const uint8_t *value,
- size_t len)
+add_desc_with_value(struct gatt_db_attribute *att, uint16_t handle,
+ bt_uuid_t *uuid, uint32_t att_perms,
+ const uint8_t *value, size_t len)
{
struct gatt_db_attribute *desc_att;
- desc_att = gatt_db_service_add_descriptor(att, uuid, att_perms, NULL,
- NULL, NULL);
+ if (handle)
+ desc_att = gatt_db_service_insert_descriptor(att, handle, uuid,
+ att_perms, NULL, NULL,
+ NULL);
+ else
+ desc_att = gatt_db_service_add_descriptor(att, uuid, att_perms,
+ NULL, NULL, NULL);
gatt_db_attribute_write(desc_att, 0, value, len, 0x00, NULL,
att_write_cb, NULL);
case CHARACTERISTIC:
bt_string_to_uuid(&uuid, spec->uuid);
- add_char_with_value(db, att, &uuid,
+ add_char_with_value(att, spec->handle, &uuid,
spec->att_permissions,
spec->char_properties,
spec->value, spec->len);
case DESCRIPTOR:
bt_string_to_uuid(&uuid, spec->uuid);
- add_desc_with_value(att, &uuid, spec->att_permissions,
+ add_desc_with_value(att, spec->handle, &uuid,
+ spec->att_permissions,
spec->value, spec->len);
break;
return make_db(specs);
}
+#define CHARACTERISTIC_STR_AT(chr_handle, chr_uuid, permissions, properties, \
+ string) \
+ { \
+ .valid = true, \
+ .handle = chr_handle, \
+ .type = CHARACTERISTIC, \
+ .uuid = STR(chr_uuid), \
+ .att_permissions = permissions, \
+ .char_properties = properties, \
+ .value = (uint8_t *)string, \
+ .len = strlen(string), \
+ }
+
+#define DESCRIPTOR_STR_AT(desc_handle, desc_uuid, permissions, string) \
+ { \
+ .valid = true, \
+ .handle = desc_handle, \
+ .type = DESCRIPTOR, \
+ .uuid = STR(desc_uuid), \
+ .att_permissions = permissions, \
+ .value = (uint8_t *)string, \
+ .len = strlen(string), \
+ }
+
+static struct gatt_db *make_service_data_2_db(void)
+{
+ const struct att_handle_spec specs[] = {
+ PRIMARY_SERVICE(0x0001, GATT_UUID, 4),
+ CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, "BlueZ"),
+ DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ,
+ "Device Name"),
+ PRIMARY_SERVICE(0x0005, HEART_RATE_UUID, 6),
+ CHARACTERISTIC_STR_AT(0x0008,
+ GATT_CHARAC_MANUFACTURER_NAME_STRING,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE, ""),
+ DESCRIPTOR_STR_AT(0x000a, GATT_CHARAC_USER_DESC_UUID,
+ BT_ATT_PERM_READ, "Manufacturer Name"),
+ { }
+ };
+
+ return make_db(specs);
+}
+
+#define CHARACTERISTIC_AT(chr_handle, chr_uuid, permissions, properties, \
+ bytes...) \
+ { \
+ .valid = true, \
+ .handle = chr_handle, \
+ .type = CHARACTERISTIC, \
+ .uuid = STR(chr_uuid), \
+ .att_permissions = permissions, \
+ .char_properties = properties, \
+ .value = data(bytes), \
+ .len = sizeof(data(bytes)), \
+ }
+
+#define DESCRIPTOR_AT(desc_handle, desc_uuid, permissions, bytes...) \
+ { \
+ .valid = true, \
+ .handle = desc_handle, \
+ .type = DESCRIPTOR, \
+ .uuid = STR(desc_uuid), \
+ .att_permissions = permissions, \
+ .value = data(bytes), \
+ .len = sizeof(data(bytes)), \
+ }
+
+static struct gatt_db *make_service_data_3_db(void)
+{
+ const struct att_handle_spec specs[] = {
+ PRIMARY_SERVICE(0x0100, GAP_UUID, 0x0121 - 0x0100 + 1),
+ CHARACTERISTIC_STR_AT(0x0111, GATT_CHARAC_DEVICE_NAME,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, "BlueZ"),
+ CHARACTERISTIC_AT(0x0121, GATT_CHARAC_APPEARANCE,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, 0x00, 0x00),
+ PRIMARY_SERVICE(0x0200, GATT_UUID, 0x0200 - 0x0200 + 1),
+ PRIMARY_SERVICE(0x0300, HEART_RATE_UUID, 0x0320 - 0x0300 + 1),
+ CHARACTERISTIC_STR_AT(0x0311,
+ GATT_CHARAC_MANUFACTURER_NAME_STRING,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE, ""),
+ DESCRIPTOR_AT(0x0320, GATT_CLIENT_CHARAC_CFG_UUID,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x00, 0x00),
+ { }
+ };
+
+ return make_db(specs);
+}
+
/*
* Defined Test database 1:
* Tiny database fits into a single minimum sized-pdu.
BT_GATT_CHRC_PROP_READ,
"BlueZ Unit Tester"),
CHARACTERISTIC(0000B009-0000-0000-0123-456789abcdef,
- BT_ATT_PERM_READ,
- BT_GATT_CHRC_PROP_READ, 0x09),
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ, 0x09),
CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ,
BT_GATT_CHRC_PROP_READ, 0x00, 0x00),
PRIMARY_SERVICE(0xFFFF, DEVICE_INFORMATION_UUID, 1),
.length = 0x03
};
-/* The maximum length of an attribute value shall be 512 octets. */
-static const uint8_t long_data_2[512] = { [0 ... 511] = 0xff };
-
static const struct test_step test_long_read_2 = {
.handle = 0x0003,
.func = test_long_read,
int main(int argc, char *argv[])
{
- struct gatt_db *service_db_1, *ts_small_db, *ts_large_db_1;
+ struct gatt_db *service_db_1, *service_db_2, *service_db_3;
+ struct gatt_db *ts_small_db, *ts_large_db_1;
tester_init(&argc, &argv);
service_db_1 = make_service_data_1_db();
+ service_db_2 = make_service_data_2_db();
+ service_db_3 = make_service_data_3_db();
ts_small_db = make_test_spec_small_db();
ts_large_db_1 = make_test_spec_large_db_1();
raw_pdu(0x05, 0x01, 0x15, 0x00, 0x04, 0x29, 0x16, 0x00,
0x05, 0x29));
+ define_test_client("/TP/GAD/CL/BV-06-C/client-1", test_client,
+ service_db_1, NULL,
+ SERVICE_DATA_1_PDUS);
+
+ define_test_client("/TP/GAD/CL/BV-06-C/client-2", test_client,
+ service_db_2, NULL,
+ SERVICE_DATA_2_PDUS);
+
+ define_test_client("/TP/GAD/CL/BV-06-C/client-3", test_client,
+ service_db_3, NULL,
+ SERVICE_DATA_3_PDUS);
+
define_test_server("/TP/GAD/SR/BV-06-C/small", test_server,
ts_small_db, NULL,
raw_pdu(0x03, 0x00, 0x02),
raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
raw_pdu(0x01, 0x12, 0x07, 0x00, 0x0c));
+ define_test_server("/TP/GAW/SR/BV-07-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19),
+ raw_pdu(0x0a, 0x03, 0x00),
+ raw_pdu(0x0b, 0x42, 0x6c, 0x75, 0x65, 0x5a));
+
+ define_test_server("/TP/GAW/SR/BV-07-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0xc4, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0xc4, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19),
+ raw_pdu(0x0a, 0xc4, 0x00),
+ raw_pdu(0x0b, '1', '1', '1', '1', '1', '2', '2', '2',
+ '2', '2', '3', '3', '3', '3', '3', '4', '4',
+ '4', '4', '4', '5'));
+
define_test_server("/TP/GAW/SR/BV-03-C/small", test_server,
ts_small_db, NULL,
raw_pdu(0x03, 0x00, 0x02),
raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
raw_pdu(0x01, 0x12, 0x04, 0x00, 0x03));
+ define_test_client("/TP/GAW/CL/BV-05-C", test_client, service_db_1,
+ &test_long_write_1,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff),
+ raw_pdu(0x17, 0x07, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff),
+ raw_pdu(0x16, 0x07, 0x00, 0xfb, 0x01,
+ 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x07, 0x00, 0xfb, 0x01,
+ 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-07-C", test_client, service_db_1,
+ &test_long_write_2,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-08-C", test_client, service_db_1,
+ &test_long_write_3,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x03, 0x00, 0x03),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-11-C", test_client, service_db_1,
+ &test_long_write_4,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x07, 0x00, 0x08),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-12-C", test_client, service_db_1,
+ &test_long_write_5,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x07, 0x00, 0x05),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-13-C", test_client, service_db_1,
+ &test_long_write_6,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x07, 0x00, 0x0c),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-05-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x16, 0x03, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x17, 0x03, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-05-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x16, 0x82, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x17, 0x82, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BI-07-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-07-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-08-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x05, 0x00, 0x03));
+
+ define_test_server("/TP/GAW/SR/BI-08-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x73, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x73, 0x00, 0x03));
+
+ define_test_server("/TP/GAW/SR/BV-06-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-06-C/large-1", 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(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-10-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x16, 0x15, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x15, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x16, 0x03, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x17, 0x03, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x16, 0x15, 0xf0, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x17, 0x15, 0xf0, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-10-C/large-1", 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(0x19));
+
+ define_test_server("/TP/GAW/SR/BI-14-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-14-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-15-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x05, 0x00, 0x03));
+
+ define_test_server("/TP/GAW/SR/BI-15-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x73, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x73, 0x00, 0x03));
+
define_test_client("/TP/GAW/CL/BV-08-C", test_client, service_db_1,
&test_write_7,
SERVICE_DATA_1_PDUS,
raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
raw_pdu(0x01, 0x12, 0x08, 0x00, 0x0c));
+ define_test_server("/TP/GAW/SR/BV-08-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x13));
+
+ define_test_server("/TP/GAW/SR/BV-08-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x83, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x13));
+
+ define_test_server("/TP/GAW/SR/BI-20-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-20-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x0f, 0xf0, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x0f, 0xf0, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-21-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x13, 0xf0, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x13, 0xf0, 0x03));
+
+ define_test_server("/TP/GAW/SR/BI-21-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x04, 0x00, 0x03));
+
+ define_test_server("/TP/GAW/SR/BV-09-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x16, 0x04, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x17, 0x04, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-09-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x83, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x83, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x16, 0x83, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x17, 0x83, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BI-25-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-25-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-26-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x13, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x13, 0xf0, 0x03));
+
+ define_test_server("/TP/GAW/SR/BI-26-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x04, 0x00, 0x03));
+
return tester_run();
}
g_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
dbus_message_iter_get_basic(&iter, &string);
- g_assert(g_strcmp0(string, "value") == 0);
+ g_assert_cmpstr(string, ==, "value");
g_dbus_client_unref(context->dbus_client);
}
destroy_context(context);
}
+static void client_no_object_manager(void)
+{
+ struct context *context = create_context();
+ DBusConnection *conn;
+ DBusMessageIter iter;
+ static const GDBusPropertyTable string_properties[] = {
+ { "String", "s", get_string, set_string, string_exists },
+ { },
+ };
+
+ if (context == NULL)
+ return;
+
+ conn = g_dbus_setup_private(DBUS_BUS_SESSION, SERVICE_NAME1, NULL);
+ g_assert(conn != NULL);
+
+ context->data = g_strdup("value");
+
+ g_dbus_register_interface(conn,
+ SERVICE_PATH, SERVICE_NAME1,
+ methods, signals, string_properties,
+ context, NULL);
+
+ context->dbus_client = g_dbus_client_new_full(context->dbus_conn,
+ SERVICE_NAME1, SERVICE_PATH,
+ NULL);
+
+ g_dbus_client_set_disconnect_watch(context->dbus_client,
+ disconnect_handler, context);
+
+ context->proxy = g_dbus_proxy_new(context->dbus_client, SERVICE_PATH,
+ SERVICE_NAME1);
+
+ g_dbus_client_set_proxy_handlers(context->dbus_client, proxy_get_string,
+ NULL, NULL, context);
+
+ g_assert(!g_dbus_proxy_get_property(context->proxy, "String", &iter));
+
+ g_main_loop_run(context->main_loop);
+
+ g_dbus_proxy_unref(context->proxy);
+ g_dbus_unregister_interface(conn, SERVICE_PATH, SERVICE_NAME1);
+
+ dbus_connection_flush(conn);
+ dbus_connection_close(conn);
+ dbus_connection_unref(conn);
+
+ destroy_context(context);
+}
+
static void proxy_force_disconnect(GDBusProxy *proxy, void *user_data)
{
struct context *context = user_data;
g_test_add_func("/gdbus/client_proxy_removed", client_proxy_removed);
+ g_test_add_func("/gdbus/client_no_object_manager",
+ client_no_object_manager);
+
g_test_add_func("/gdbus/client_force_disconnect",
client_force_disconnect);
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
data.result_func = result_function; \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
g_test_add_data_func(name, &data, function); \
data.test_handler = test_handler; \
} while (0)
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
data.hf_result_func = result_func; \
data.response_func = response_function; \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
g_test_add_data_func(name, &data, function); \
data.test_handler = test_hf_handler; \
} while (0)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
+#include "attrib/gattrib.h"
+
+#include "android/hog.h"
+
+struct test_pdu {
+ bool valid;
+ const uint8_t *data;
+ size_t size;
+};
+
+struct test_data {
+ char *test_name;
+ struct test_pdu *pdu_list;
+};
+
+struct context {
+ GAttrib *attrib;
+ struct bt_hog *hog;
+ guint source;
+ guint process;
+ int fd;
+ unsigned int pdu_offset;
+ const struct test_data *data;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...) \
+{ \
+ .valid = true, \
+ .data = data(args), \
+ .size = sizeof(data(args)),\
+}
+
+#define false_pdu() \
+{ \
+ .valid = false, \
+}
+
+#define define_test(name, function, args...) \
+ do { \
+ const struct test_pdu pdus[] = { \
+ args, { } \
+ }; \
+ static struct test_data data; \
+ data.test_name = g_strdup(name); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
+ tester_add(name, &data, NULL, function, NULL); \
+ } while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ tester_debug("%s%s", prefix, str);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+ struct context *context = user_data;
+
+ if (context->process > 0)
+ g_source_remove(context->process);
+
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ bt_hog_unref(context->hog);
+
+ g_attrib_unref(context->attrib);
+
+ g_free(context);
+
+ tester_test_passed();
+
+ return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+ struct context *context = user_data;
+ const struct test_pdu *pdu;
+ ssize_t len;
+
+ pdu = &context->data->pdu_list[context->pdu_offset++];
+
+ len = write(context->fd, pdu->data, pdu->size);
+
+ util_hexdump('<', pdu->data, len, test_debug, "hog: ");
+
+ g_assert_cmpint(len, ==, pdu->size);
+
+ context->process = 0;
+
+ if (!context->data->pdu_list[context->pdu_offset].valid)
+ context_quit(context);
+
+ return FALSE;
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+ gpointer user_data)
+{
+ struct context *context = user_data;
+ unsigned char buf[512];
+ const struct test_pdu *pdu;
+ ssize_t len;
+ int fd;
+
+ pdu = &context->data->pdu_list[context->pdu_offset++];
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ context->source = 0;
+ g_print("%s: cond %x\n", __func__, cond);
+ return FALSE;
+ }
+
+ fd = g_io_channel_unix_get_fd(channel);
+
+ len = read(fd, buf, sizeof(buf));
+
+ g_assert(len > 0);
+
+ util_hexdump('>', buf, len, test_debug, "hog: ");
+
+ g_assert_cmpint(len, ==, pdu->size);
+
+ g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+ context->process = g_idle_add(send_pdu, context);
+
+ return TRUE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+ struct context *context;
+ GIOChannel *channel, *att_io;
+ int err, sv[2], fd;
+ char name[] = "bluez-hog";
+ uint16_t vendor = 0x0002;
+ uint16_t product = 0x0001;
+ uint16_t version = 0x0001;
+
+ context = g_new0(struct context, 1);
+ err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+ g_assert(err == 0);
+
+ att_io = g_io_channel_unix_new(sv[0]);
+
+ g_io_channel_set_close_on_unref(att_io, TRUE);
+
+ context->attrib = g_attrib_new(att_io, 23);
+ g_assert(context->attrib);
+
+ g_io_channel_unref(att_io);
+
+ fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
+ g_assert(fd > 0);
+
+ context->hog = bt_hog_new(fd, name, vendor, product, version, NULL);
+ g_assert(context->hog);
+
+ channel = g_io_channel_unix_new(sv[1]);
+
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ context->source = g_io_add_watch(channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ test_handler, context);
+ g_assert(context->source > 0);
+
+ g_io_channel_unref(channel);
+
+ context->fd = sv[1];
+ context->data = data;
+
+ return context;
+}
+
+static void test_hog(gconstpointer data)
+{
+ struct context *context = create_context(data);
+
+ g_assert(bt_hog_attach(context->hog, context->attrib));
+}
+
+int main(int argc, char *argv[])
+{
+ tester_init(&argc, &argv);
+
+ define_test("/TP/HGRF/RH/BV-01-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x12,
+ 0x18, 0x05, 0x00, 0x08, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+ 0x4b, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x07, 0x00, 0x02, 0x08, 0x00,
+ 0x4b, 0x2a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16),
+ raw_pdu(0x0a, 0x08, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16),
+ raw_pdu(0x0c, 0x04, 0x00, 0x16, 0x00),
+ raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16),
+ raw_pdu(0x0c, 0x08, 0x00, 0x16, 0x00),
+ raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16),
+ raw_pdu(0x0c, 0x04, 0x00, 0x2c, 0x00),
+ raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13),
+ raw_pdu(0x0c, 0x08, 0x00, 0x2c, 0x00),
+ raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13));
+
+ define_test("/TP/HGRF/RH/BV-08-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+ 0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x0a, 0x04, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x08, 0x00, 0x0a, 0x09, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+ raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0xee, 0xee, 0xff, 0xff),
+ raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+ raw_pdu(0x05, 0x01, 0x05, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x09, 0x00),
+ raw_pdu(0x0b, 0xff, 0xff, 0xee, 0xee),
+ raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+ raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x05, 0x00),
+ raw_pdu(0x0b, 0x01, 0x03),
+ raw_pdu(0x0a, 0x0a, 0x00),
+ raw_pdu(0x0b, 0x02, 0x03));
+
+ define_test("/TP/HGRF/RH/BV-09-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x12,
+ 0x18, 0x05, 0x00, 0x08, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+ 0x4a, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x07, 0x00, 0x02, 0x08, 0x00,
+ 0x4a, 0x2a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0x01, 0x11, 0x00, 0x01),
+ raw_pdu(0x0a, 0x08, 0x00),
+ raw_pdu(0x0b, 0x01, 0x11, 0x00, 0x01));
+
+ define_test("/TP/HGRF/RH/BV-06-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+ 0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x0a, 0x04, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x08, 0x00, 0x0a, 0x09, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
+ raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0xee, 0xee, 0xff, 0xff),
+ raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+ raw_pdu(0x05, 0x01, 0x05, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x09, 0x00),
+ raw_pdu(0x0b, 0xff, 0xff, 0xee, 0xee),
+ raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+ raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x05, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02),
+ raw_pdu(0x0a, 0x0a, 0x00),
+ raw_pdu(0x0b, 0x02, 0x02));
+
+ define_test("/TP/HGCF/RH/BV-01-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x06, 0x00, 0x12,
+ 0x18, 0x07, 0x00, 0x0c, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x0d, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x0d, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x06, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x1a, 0x04, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x06, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x07, 0x00, 0x0c, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a),
+ raw_pdu(0x08, 0x07, 0x00, 0x0c, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x09, 0x00, 0x1a, 0x0a, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x04, 0x00, 0x06, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+ raw_pdu(0x08, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x0a, 0x00, 0x0a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0xed, 0x00),
+ raw_pdu(0x04, 0x05, 0x00, 0x06, 0x00),
+ raw_pdu(0x05, 0x01, 0x05, 0x00, 0x02, 0x29,
+ 0x06, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x0a, 0x00),
+ raw_pdu(0x0b, 0xed, 0x00),
+ raw_pdu(0x04, 0x0b, 0x00, 0x0c, 0x00),
+ raw_pdu(0x05, 0x01, 0x0b, 0x00, 0x02, 0x29,
+ 0x0c, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x06, 0x00),
+ raw_pdu(0x0b, 0x01, 0x01),
+ raw_pdu(0x0a, 0x0c, 0x00),
+ raw_pdu(0x0b, 0x02, 0x01),
+ raw_pdu(0x0a, 0x05, 0x00),
+ raw_pdu(0x0b, 0x00, 0x00),
+ raw_pdu(0x0a, 0x0b, 0x00),
+ raw_pdu(0x0b, 0x00, 0x00),
+ raw_pdu(0x12, 0x05, 0x00, 0x01, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(0x12, 0x0b, 0x00, 0x01, 0x00),
+ raw_pdu(0x13));
+
+ define_test("/TP/HGRF/RH/BV-02-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+ 0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+ 0x4b, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x08, 0x00, 0x02, 0x09, 0x00,
+ 0x4b, 0x2a),
+ raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+ raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03),
+ raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+ raw_pdu(0x05, 0x01, 0x05, 0x00, 0x07, 0x29),
+ raw_pdu(0x0a, 0x09, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03),
+ raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+ raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x07, 0x29),
+ raw_pdu(0x0a, 0x05, 0x00),
+ raw_pdu(0x0b, 0x19, 0x2a),
+ raw_pdu(0x0a, 0x0a, 0x00),
+ raw_pdu(0x0b, 0x19, 0x2a));
+
+ return tester_run();
+}
}; \
static struct test_data data; \
data.mtu = _mtu; \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
g_test_add_data_func(name, &data, test_sdp); \
} while (0)
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
g_test_add_data_func(name, &data, function); \
} while (0)