+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 \
tools/hciattach_ath3k.c \
tools/hciattach_qualcomm.c \
tools/hciattach_intel.c \
+ tools/hciattach_sprd.c \
+ tools/pskey_get.c \
tools/hciattach_bcm43xx.c
# src/log.c
# tools/hciattach_bcm43xx.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>
struct attrib_cb *cb;
GSList *l = NULL;
+#ifdef __TIZEN_PATCH__
+ if (opt == GATT_OPT_INVALID)
+ return NULL;
+#endif
info = g_new0(struct gatt_info, 1);
l = g_slist_append(l, info);
/* 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;
}
#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;
}
<filesystem path="/usr/bin/l2ping" label="bluez" exec_label="bluez"/>
<filesystem path="/usr/bin/rfcomm" label="bluez" exec_label="bluez"/>
<filesystem path="/usr/bin/sdptool" label="bluez" exec_label="bluez"/>
- <filesystem path="/usr/bin/mpris-proxy" exec_label="none"/>
- <filesystem path="/usr/lib/udev/hid2hci" exec_label="none"/>
<filesystem path="/etc/bluetooth/audio.conf" label="bluez"/>
<filesystem path="/etc/bluetooth/main.conf" label="bluez"/>
<filesystem path="/etc/bluetooth/rfcomm.conf" label="bluez"/>
<filesystem path="/usr/lib/bluetooth/bluetoothd" label="bluez" exec_label="bluez"/>
+ <filesystem path="/usr/lib/udev/hid2hci" label="bluez" exec_label="bluez"/>
<filesystem path="/usr/bin/hciattach" label="bluez" exec_label="bluez"/>
<filesystem path="/usr/bin/hciconfig" label="bluez" exec_label="bluez"/>
+ <filesystem path="/usr/bin/mpris-proxy" label="bluez" exec_label="bluez"/>
+ <filesystem path="/usr/bin/btmon" label="bluez" exec_label="bluez"/>
+ <filesystem path="/usr/bin/btsnoop" label="bluez" exec_label="bluez"/>
+
+ <filesystem path="/etc/smack/accesses.d/bluez.rule" label="_" exec_label="none"/>
<dbus name="org.bluez" own="bluez" bus="system">
<node name="/org/bluez/device/connection">
--- /dev/null
+bluez syslogd -w----
+bluez device::sys_logging -w----
+bluez security-server::api-permissions -w----
+bluez dfms-kernel rwx---
+dbus bluez rwx---
+bluez _ rwx---
#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
====================================
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
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.
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,
#include "gdbus.h"
#ifdef __TIZEN_PATCH__
+#if 0
#include <syslog.h>
static void gdbus_dbg(const char *format, ...)
{
va_end(ap);
}
+#endif
#else
#define info(fmt...)
#endif
iface->user_data) == TRUE)
return DBUS_HANDLER_RESULT_HANDLED;
#ifdef __TIZEN_PATCH__
+#if 0
gdbus_dbg("%s: %s.%s()", dbus_message_get_path(message),
iface->name, method->name);
#endif
+#endif
return process_message(connection, message, method,
iface->user_data);
}
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 */
{ "LE Receiver Test", 228 },
{ "LE Transmitter Test", 229 },
{ "LE Test End", 230 },
- { "Reserved", 231 },
+ { "LE Read Maximum Data Length", 231 },
+ { "Reserved", 232 },
{ NULL }
};
return 0;
}
+
+#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)
+{
+ le_read_maximum_data_length_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ memset(&rp, 0, sizeof(rp));
+
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_READ_MAXIMUM_DATA_LENGTH;
+ rq.rparam = &rp;
+ rq.rlen = LE_READ_MAXIMUM_DATA_LENGTH_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *tx_octets = rp.max_tx_octets;
+ *tx_time = rp.max_tx_time;
+ *rx_octets = rp.max_rx_octets;
+ *rx_time = rp.max_rx_time;
+ *status = rp.status;
+ return 0;
+}
+
+int hci_le_write_host_suggested_data_length(
+ int dd, uint16_t *def_tx_octets,
+ uint16_t *def_tx_time, int to)
+{
+ le_write_host_suggested_data_length_cp cp;
+ struct hci_request rq;
+ uint8_t status;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.def_tx_octets = def_tx_octets;
+ cp.def_tx_time = def_tx_time;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH;
+ rq.cparam = &cp;
+ rq.clen = LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+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)
+{
+ le_read_host_suggested_data_length_rp rp;
+ struct hci_request rq;
+
+ memset(&rp, 0, sizeof(rp));
+ memset(&rq, 0, sizeof(rq));
+
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_READ_HOST_SUGGESTED_DATA_LENGTH;
+ rq.rparam = &rp;
+ rq.rlen = LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *def_tx_octets = rp.def_tx_octets;
+ *def_tx_time = rp.def_tx_time;
+ *status = rp.status;
+ return 0;
+}
+
+int hci_le_set_data_length(
+ int dd, const bdaddr_t *bdaddr, uint16_t *max_tx_octets,
+ uint16_t *max_tx_time, int to)
+{
+ le_set_data_length_cp cp;
+ le_set_data_length_rp rp;
+ struct hci_request rq;
+ uint8_t status;
+
+ memset(&cp, 0, sizeof(cp));
+ memset(&rp, 0, sizeof(rp));
+
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.max_tx_octets = max_tx_octets;
+ cp.max_tx_time = max_tx_time;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_SET_DATA_LENGTH;
+ rq.cparam = &cp;
+ rq.clen = LE_SET_DATA_LENGTH_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = LE_SET_DATA_LENGTH_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
} __attribute__ ((packed)) le_test_end_rp;
#define LE_TEST_END_RP_SIZE 3
+#ifdef __TIZEN_PATCH__
+#define OCF_LE_READ_MAXIMUM_DATA_LENGTH 0x002F
+typedef struct {
+ uint8_t status;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+ uint16_t max_rx_octets;
+ uint16_t max_rx_time;
+} __attribute__ ((packed))
+le_read_maximum_data_length_rp;
+#define LE_READ_MAXIMUM_DATA_LENGTH_SIZE 9
+
+#define OCF_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH 0x0030
+typedef struct {
+ uint16_t def_tx_octets;
+ uint16_t def_tx_time;
+} __attribute__ ((packed))
+le_write_host_suggested_data_length_cp;
+#define LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_CP_SIZE 4
+
+#define OCF_LE_READ_HOST_SUGGESTED_DATA_LENGTH 0x0024
+typedef struct {
+ uint8_t status;
+ uint16_t def_tx_octets;
+ uint16_t def_tx_time;
+} __attribute__ ((packed))
+le_read_host_suggested_data_length_rp;
+#define LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE 5
+
+#define OCF_LE_SET_DATA_LENGTH 0x0022
+typedef struct {
+ bdaddr_t bdaddr;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+} __attribute__ ((packed))
+le_set_data_length_cp;
+#define LE_SET_DATA_LENGTH_CP_SIZE 10
+
+typedef struct {
+ uint16_t handle;
+ uint8_t status;
+} __attribute__ ((packed))
+le_set_data_length_rp;
+#define LE_SET_DATA_LENGTH_RP_SIZE 3
+#endif
+
#define OCF_LE_ADD_DEVICE_TO_RESOLV_LIST 0x0027
typedef struct {
uint8_t bdaddr_type;
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__
int8_t adv_tx_power;
} __packed;
+#define MGMT_OP_ENABLE_6LOWPAN (TIZEN_OP_CODE_BASE + 0x12)
+struct mgmt_cp_enable_6lowpan {
+ uint8_t enable_6lowpan;
+} __packed;
+
+#define MGMT_OP_CONNECT_6LOWPAN (TIZEN_OP_CODE_BASE + 0x13)
+struct mgmt_cp_connect_6lowpan {
+ struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_DISCONNECT_6LOWPAN (TIZEN_OP_CODE_BASE + 0x14)
+struct mgmt_cp_disconnect_6lowpan {
+ struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x15)
+struct mgmt_rp_le_read_maximum_data_length {
+ uint8_t status;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+ uint16_t max_rx_octets;
+ uint16_t max_rx_time;
+} __packed;
+
+#define MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x16)
+struct mgmt_cp_le_write_host_suggested_data_length {
+ uint16_t def_tx_octets;
+ uint16_t def_tx_time;
+} __packed;
+
+#define MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x17)
+struct mgmt_rp_le_read_host_suggested_data_length {
+ uint8_t status;
+ uint16_t def_tx_octets;
+ uint16_t def_tx_time;
+} __packed;
+
+#define MGMT_OP_LE_SET_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x18)
+struct mgmt_cp_le_set_data_length {
+ bdaddr_t bdaddr;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+} __packed;
+#define MGMT_LE_SET_DATA_LENGTH_SIZE 10
+
/* 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)
int16_t connection_handle;
} __packed;
+#define MGMT_EV_6LOWPAN_CONN_STATE_CHANGED (TIZEN_EV_BASE + 0x0c)
+struct mgmt_ev_6lowpan_conn_state_changed {
+ struct mgmt_addr_info addr;
+ 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;
+ int16_t max_tx_octets;
+ int16_t max_tx_time;
+ int16_t max_rx_octets;
+ int16_t max_rx_time;
+} __packed;
+
/* Currently there is no support in kernel for below MGMT events. */
#if 0 // Not defined in kernel
#define MGMT_EV_NEW_LOCAL_IRK (TIZEN_EV_BASE + 0x0b)
"Set Manufacturer Data",
"LE Set Scan Parameters",
"Set Voice Setting",
- "Get Adv Tx Power"
+ "Get Adv Tx Power",
+ "Connect BT 6LOWPAN",
+ "Disconnect BT 6LOWPAN"
};
static const char *mgmt_tizen_ev[] = {
"LE Connection Update Failed",
"LE Device Found",
"Multi Adv State Change",
+ "BT 6LOWPAN state Change"
};
#endif /* End of __TIZEN_PATCH__ */
#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
#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907
#define GATT_REPORT_REFERENCE 0x2908
+#ifdef __TIZEN_PATCH__
+/* GATT Service UUIDs : Defined by SIG */
+#define GATT_IPSP_UUID 0x1820
+#endif
+
typedef struct {
enum {
BT_UUID_UNSPEC = 0,
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);
"\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)
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;
int err;
os->object = os->driver->open(filename, O_WRONLY | O_CREAT | O_TRUNC,
+#ifdef __TIZEN_PATCH__
+ 0644, os->service_data,
+#else
0600, os->service_data,
+#endif
os->size != OBJECT_SIZE_UNKNOWN ?
(size_t *) &os->size : NULL, &err);
if (os->object == NULL) {
Name: bluez
Summary: Bluetooth utilities
-Version: 5.28
-Release: 1
+Version: 5.30
+Release: 2
#VCS: framework/connectivity/bluez#bluez_4.101-slp2+22-132-g5d15421a5bd7101225aecd7c25b592b9a9ca218b
Group: Applications/System
License: GPL-2.0+ and LGPL-2.1+ and Apache-2.0
%if "%{?tizen_profile_name}" == "wearable"
export CFLAGS="${CFLAGS} -D__TIZEN_PATCH__ -D__BROADCOM_PATCH__ -D__BT_SCMST_FEATURE__ -DSUPPORT_SMS_ONLY -D__BROADCOM_QOS_PATCH__ -DTIZEN_WEARABLE"
%else
-%if "%{?tizen_profile_name}" == "mobile"
+%if "%{?tizen_profile_name}" == "mobile" || "%{?tizen_profile_name}" == "tv"
export CFLAGS="${CFLAGS} -D__TIZEN_PATCH__ -DSUPPORT_SMS_ONLY -DBLUEZ5_27_GATT_CLIENT"
+%if "%{?tizen_target_name}" == "Z300H" || "%{?tizen_target_name}" == "TM1"
+export CFLAGS="${CFLAGS} -D__SPRD_QOS_PATCH__"
+%else
export CFLAGS="${CFLAGS} -D__BROADCOM_PATCH__"
%endif
%endif
+%endif
export LDFLAGS=" -lncurses -Wl,--as-needed "
-export CFLAGS+=" -DPBAP_SIM_ENABLE"
+export CFLAGS+=" -DPBAP_SIM_ENABLE -DSUPPORT_AVRCP_TARGET"
%reconfigure --disable-static \
--sysconfdir=%{_sysconfdir} \
--localstatedir=%{_localstatedir} \
--with-systemdsystemunitdir=%{_libdir}/systemd/system \
--with-systemduserunitdir=%{_libdir}/systemd/user \
+ --libexecdir=%{_libdir} \
--enable-debug \
--enable-pie \
--enable-serial \
%endif
--enable-experimental \
--enable-autopair=no \
-%if "%{?tizen_profile_name}" == "mobile"
+%if "%{?tizen_profile_name}" == "mobile" || "%{?tizen_profile_name}" == "tv"
--enable-network \
--enable-hid=yes \
%else
%install
rm -rf %{buildroot}
%make_install
+# fixed to support new rpm and build environment
+install -d -m 0755 %{buildroot}/var/lib/bluetooth
+install -d -m 0755 %{buildroot}%{_libdir}/bluetooth/plugins
+install -d -m 0755 %{buildroot}%{_libdir}/bluetooth/obex/plugins
+install -d -m 0755 %{buildroot}%{_libdir}/obex/plugins
%if "%{?tizen_profile_name}" == "wearable"
install -D -m 0644 src/main_w.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf
%else
-%if "%{?tizen_profile_name}" == "mobile"
+%if "%{?tizen_profile_name}" == "mobile" || "%{?tizen_profile_name}" == "tv"
install -D -m 0644 src/main_m.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf
%endif
%endif
install -D -m 0755 %SOURCE101 %{buildroot}%{_bindir}/obex-root-setup
install -D -m 0755 %SOURCE102 %{buildroot}%{_sysconfdir}/obex/root-setup.d/000_create-symlinks
-#ln -sf bluetooth.service %{buildroot}%{_libdir}/systemd/system/dbus-org.bluez.service
+install -D -m 0644 bluez.rule %{buildroot}%{_sysconfdir}/smack/accesses.d/bluez.rule
+
+%if "%{?tizen_profile_name}" == "common"
+ln -sf bluetooth.service %{buildroot}%{_libdir}/systemd/system/dbus-org.bluez.service
+%endif
%post -n libbluetooth3 -p /sbin/ldconfig
%{_sysconfdir}/bluetooth/network.conf
#%{_sysconfdir}/bluetooth/rfcomm.conf
%{_sysconfdir}/dbus-1/system.d/bluetooth.conf
+%{_sysconfdir}/smack/accesses.d/bluez.rule
%{_datadir}/man/*/*
%{_libexecdir}/bluetooth/bluetoothd
%{_bindir}/hciconfig
%{_bindir}/mpris-proxy
%{_bindir}/rfcomm
%{_bindir}/hcitool
+%{_bindir}/btmon
+%{_bindir}/btsnoop
%dir %{_libdir}/bluetooth/plugins
-%exclude %{_libdir}/systemd/system/bluetooth.service
-#%exclude %{_libdir}/systemd/system/dbus-org.bluez.service
%dir %{_localstatedir}/lib/bluetooth
%dir %{_libexecdir}/bluetooth
-%exclude %{_datadir}/dbus-1/system-services/org.bluez.service
%{_datadir}/license/bluez
%{_libdir}/udev/hid2hci
%{_libdir}/udev/rules.d/97-hid2hci.rules
+%if "%{?tizen_profile_name}" == "common"
+%{_libdir}/systemd/system/bluetooth.service
+%{_libdir}/systemd/system/dbus-org.bluez.service
+%{_datadir}/dbus-1/system-services/org.bluez.service
+%else
+%exclude %{_libdir}/systemd/system/bluetooth.service
+%exclude %{_datadir}/dbus-1/system-services/org.bluez.service
+%endif
%files -n libbluetooth3
%defattr(-,root,root,-)
%{_bindir}/rctest
%{_bindir}/bccmd
%{_bindir}/bluetoothctl
-%{_bindir}/btmon
%{_bindir}/hcidump
%{_bindir}/bluemoon
%{_bindir}/hex2hcd
#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;
}
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 */
auto_config(setup);
}
+static gboolean endpoint_match_codec_ind(struct avdtp *session,
+ struct avdtp_media_codec_capability *codec,
+ void *user_data)
+{
+ struct a2dp_sep *sep = user_data;
+ a2dp_vendor_codec_t *remote_codec;
+ a2dp_vendor_codec_t *local_codec;
+ uint8_t *capabilities;
+ size_t length;
+
+ if (codec->media_codec_type != A2DP_CODEC_VENDOR)
+ return TRUE;
+
+ 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;
+ remote_codec = (a2dp_vendor_codec_t *) codec->data;
+
+ if (remote_codec->vendor_id != local_codec->vendor_id)
+ return FALSE;
+
+ if (remote_codec->codec_id != 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 gboolean endpoint_setconf_ind(struct avdtp *session,
struct avdtp_local_sep *sep,
struct avdtp_stream *stream,
};
static struct avdtp_sep_ind endpoint_ind = {
+ .match_codec = endpoint_match_codec_ind,
.get_capability = endpoint_getcap_ind,
.set_configuration = endpoint_setconf_ind,
.get_configuration = getconf_ind,
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);
for (; list; list = list->next) {
struct a2dp_sep *sep = list->data;
+#ifdef __TIZEN_PATCH__
struct avdtp_remote_sep *rsep;
struct avdtp_media_codec_capability *cap;
struct avdtp_service_capability *service;
+#endif
/* Use sender's endpoint if available */
if (sender) {
if (g_strcmp0(sender, name) != 0)
continue;
}
-
+#ifdef __TIZEN_PATCH__
rsep = avdtp_find_remote_sep(session, sep->lsep);
if (rsep == NULL)
continue;
service = avdtp_get_codec(rsep);
cap = (struct avdtp_media_codec_capability *) service->data;
-#ifdef __TIZEN_PATCH__
if (cap->media_codec_type != A2DP_CODEC_VENDOR) {
selected_sep = sep;
continue;
}
#else
- if (cap->media_codec_type != A2DP_CODEC_VENDOR)
- return sep;
-#endif
-
if (check_vendor_codec(sep, cap->data,
service->length - sizeof(*cap)))
return sep;
+
+#endif
+ return sep;
+
}
#ifdef __TIZEN_PATCH__
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);
if (codec_data->media_codec_type != lsep->codec)
continue;
+ if (lsep->ind && lsep->ind->match_codec)
+ if (!lsep->ind->match_codec(session, codec_data,
+ lsep->user_data))
+ continue;
+
if (sep->stream == NULL)
return sep;
}
/* Callbacks for indicating when we received a new command. The return value
* indicates whether the command should be rejected or accepted */
struct avdtp_sep_ind {
+ gboolean (*match_codec) (struct avdtp *session,
+ struct avdtp_media_codec_capability *codec,
+ void *user_data);
gboolean (*get_capability) (struct avdtp *session,
struct avdtp_local_sep *sep,
gboolean get_all,
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;
uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM;
#ifdef __TIZEN_PATCH__
uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0104;
- uint16_t feat = AVRCP_FEATURE_CATEGORY_1;
-
+ uint16_t feat = 0;
+#ifdef ENABLE_AVRCP_CATEGORY1
+ feat = AVRCP_FEATURE_CATEGORY_1;
+#endif
+#ifdef ENABLE_AVRCP_CATEGORY2
+ feat = feat | AVRCP_FEATURE_CATEGORY_2;
+#endif
#else
uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
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;
uint16_t lp_browsing = AVCTP_BROWSING_PSM;
#ifdef __TIZEN_PATCH__
uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0104;
- uint16_t feat = AVRCP_FEATURE_CATEGORY_1 |
- AVRCP_FEATURE_PLAYER_SETTINGS;
+ uint16_t feat = 0;
+#ifdef ENABLE_AVRCP_CATEGORY1
+ feat = AVRCP_FEATURE_CATEGORY_1 |
+ AVRCP_FEATURE_PLAYER_SETTINGS;
+#endif
+#ifdef ENABLE_AVRCP_CATEGORY2
+ feat = feat | AVRCP_FEATURE_CATEGORY_2;
+#endif
#else
uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
return record;
}
+#endif
static unsigned int attr_get_max_val(uint8_t attr)
{
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) {
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:
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)
};
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 {
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)
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__
int slot_id; /* Reservied slot id is 0 (Single adv) */
bool status; /* Advertising status */
};
+
+static GSList *read_requests = NULL;
+
+struct le_data_length_read_request {
+ struct btd_adapter *adapter;
+ DBusMessage *msg;
+};
#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;
+ bool ipsp_intialized; /* Ipsp Initialization state */
+ struct le_data_length_read_handler *read_handler;
+ struct le_data_length_read_default_data_length_handler *def_read_handler;
#endif
bool discovering; /* discovering property state */
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 */
};
ENABLE_PRIVACY,
GEN_IRK_THEN_ENABLE_PRIVACY
};
+
+enum {
+ DEINIT_6LOWPAN,
+ INIT_6LOWPAN
+};
#endif
static struct btd_adapter *btd_adapter_lookup(uint16_t index)
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)
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;
return btd_error_failed(msg, "set advertising data failed");
}
} else {
- if(adv_len > sizeof(cp)) {
- g_free(adv_data);
- DBG("Error: overrun advertising data length");
- return btd_error_failed(msg, "Overrun advertising data length");
- }
-
memcpy(&cp, adv_data, adv_len);
if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_DATA,
return btd_error_failed(msg, "set advertising data failed");
}
} else {
- if(adv_len > sizeof(cp)) {
- g_free(adv_data);
- DBG("Error: overrun scan response data length");
- return btd_error_failed(msg, "Overrun scan response data length");
- }
-
memcpy(&cp, adv_data, adv_len);
if (mgmt_send(adapter->mgmt, MGMT_OP_SET_SCAN_RSP_DATA,
return;
}
-#ifdef __BROADCOM_PATCH__
+//#ifdef __BROADCOM_PATCH__
static DBusMessage *set_wbs_parameters(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return dbus_message_new_method_return(msg);
}
+
+#ifdef __TIZEN_PATCH__
+void btd_adapter_set_read_le_data_length_handler(
+ struct btd_adapter *adapter,
+ struct le_data_length_read_handler *handler)
+{
+ adapter->read_handler = handler;
+}
+
+static void le_read_maximum_data_length_return_param_complete(
+ uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ 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;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("le read maximum data length failed: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ max_tx_octects = 0;
+ max_tx_time =0;
+ max_rx_octects = 0;
+ max_rx_time = 0;
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("Too small le read maximum data length response");
+ 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;
+ }
+
+ if (!adapter->read_handler ||
+ !adapter->read_handler->read_callback) {
+ g_free(adapter->read_handler);
+ return;
+ }
+
+ adapter->read_handler->read_callback(adapter,
+ max_tx_octects, max_tx_time,
+ max_rx_octects, max_rx_time,
+ adapter->read_handler->user_data);
+
+ g_free(adapter->read_handler);
+ adapter->read_handler = NULL;
+}
+
+int btd_adapter_le_read_maximum_data_length(
+ struct btd_adapter *adapter)
+{
+ if (mgmt_send(adapter->mgmt,
+ MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH,
+ adapter->dev_id, 0, NULL,
+ le_read_maximum_data_length_return_param_complete,
+ adapter, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+static gint read_request_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct le_data_length_read_request *data = a;
+ const struct btd_adapter *adapter = b;
+
+ return data->adapter != adapter;
+}
+
+static struct le_data_length_read_request *find_read_le_data_length_request(
+ struct btd_adapter *adapter)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(read_requests, adapter, read_request_cmp);
+
+ if (match)
+ return match->data;
+
+ return NULL;
+}
+
+static void le_read_data_length_complete(
+ struct btd_adapter *adapter,
+ uint16_t max_tx_octects, uint16_t max_tx_time,
+ uint16_t max_rx_octects, uint16_t max_rx_time,
+ void *user_data)
+{
+ DBusMessage *reply;
+ struct le_data_length_read_request *read_request;
+
+ read_request = find_read_le_data_length_request(adapter);
+ if (!read_request)
+ return;
+
+ reply = g_dbus_create_reply(read_request->msg,
+ DBUS_TYPE_UINT16, &max_tx_octects,
+ DBUS_TYPE_UINT16, &max_tx_time,
+ DBUS_TYPE_UINT16, &max_rx_octects,
+ DBUS_TYPE_UINT16, &max_rx_time,
+ DBUS_TYPE_INVALID);
+
+ if (!reply) {
+ btd_error_failed(read_request->msg,
+ "Failed to read max data length.");
+ return;
+ }
+
+ read_requests = g_slist_remove(read_requests, read_request);
+ dbus_message_unref(read_request->msg);
+ g_free(read_request);
+
+ if (!g_dbus_send_message(dbus_conn, reply))
+ error("D-Bus send failed");
+}
+
+static DBusMessage *le_read_maximum_data_length(
+ DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct le_data_length_read_request *read_request;
+ struct le_data_length_read_handler *handler;
+
+ if (find_read_le_data_length_request(adapter))
+ return btd_error_in_progress(msg);
+
+ if (btd_adapter_le_read_maximum_data_length(adapter))
+ return btd_error_failed(msg, "Unable to read maximum le data length");
+
+ read_request = g_new(struct le_data_length_read_request, 1);
+
+ read_request->msg = dbus_message_ref(msg);
+ read_request->adapter = adapter;
+
+ read_requests = g_slist_append(read_requests, read_request);
+
+ handler = g_new0(struct le_data_length_read_handler, 1);
+
+ handler->read_callback =
+ (read_max_data_length_cb_t)le_read_data_length_complete;
+
+ btd_adapter_set_read_le_data_length_handler(
+ read_request->adapter, handler);
+
+ return NULL;
+
+}
+
+void le_write_host_suggested_data_length_return_param_complete(
+ uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("le write host suggested data length failed: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ }
+
+ return;
+}
+
+static DBusMessage *le_write_host_suggested_default_data_length(
+ DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct mgmt_cp_le_write_host_suggested_data_length cp;
+ dbus_uint16_t def_tx_Octets;
+ dbus_uint16_t def_tx_time;
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT16, &def_tx_Octets,
+ DBUS_TYPE_UINT16, &def_tx_time,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.def_tx_octets = def_tx_Octets;
+ cp.def_tx_time = def_tx_time;
+
+ if (mgmt_send(adapter->mgmt,
+ MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+ adapter->dev_id, sizeof(cp), &cp,
+ le_write_host_suggested_data_length_return_param_complete,
+ adapter, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "Unable to write host suggested le data length values");
+}
+
+static void le_read_suggested_default_data_length_return_param_complete(
+ uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ 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;
+
+ 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;
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ goto done;;
+ } else {
+ def_tx_octects = rp->def_tx_octets;
+ def_tx_time =rp->def_tx_time;
+ DBG("retrieving host suggested data length values %d %d", def_tx_octects, def_tx_time);
+ }
+
+ if (!adapter->def_read_handler)
+ return;
+
+ if(!adapter->def_read_handler->read_callback) {
+ goto done;
+ }
+
+ adapter->def_read_handler->read_callback(adapter,
+ def_tx_octects, def_tx_time,
+ adapter->def_read_handler->user_data);
+done:
+ if (adapter->def_read_handler)
+ g_free(adapter->def_read_handler->user_data);
+
+ g_free(adapter->def_read_handler);
+ adapter->def_read_handler = NULL;
+}
+
+int btd_adapter_le_read_suggested_default_data_length(
+ struct btd_adapter *adapter)
+{
+ if (mgmt_send(adapter->mgmt,
+ MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH,
+ adapter->dev_id, 0, NULL,
+ le_read_suggested_default_data_length_return_param_complete,
+ adapter, NULL) > 0) {
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static void le_read_host_suggested_default_length_complete(
+ struct btd_adapter *adapter,
+ uint16_t def_tx_octects, uint16_t def_tx_time,
+ void *user_data)
+{
+ DBusMessage *reply;
+ struct le_data_length_read_request *read_request;
+
+ 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_INVALID);
+
+ if (!reply) {
+ btd_error_failed(read_request->msg,
+ "Failed to read host suggested def data length values");
+ return;
+ }
+
+ read_requests = g_slist_remove(read_requests, read_request);
+ dbus_message_unref(read_request->msg);
+ g_free(read_request);
+
+ if (!g_dbus_send_message(dbus_conn, reply))
+ error("D-Bus send failed");
+}
+
+static DBusMessage *le_read_host_suggested_default_data_length(
+ DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct le_data_length_read_request *read_request;
+ struct le_data_length_read_default_data_length_handler *handler;
+
+ if (find_read_le_data_length_request(adapter))
+ return btd_error_in_progress(msg);
+
+ if (btd_adapter_le_read_suggested_default_data_length(adapter))
+ return btd_error_failed(msg, "Unable to read host suggested def data length");
+
+ read_request = g_new(struct le_data_length_read_request, 1);
+
+ read_request->msg = dbus_message_ref(msg);
+ read_request->adapter = adapter;
+
+ read_requests = g_slist_append(read_requests, read_request);
+
+ handler = g_new0(struct le_data_length_read_default_data_length_handler, 1);
+
+ handler->read_callback =
+ (read_host_suggested_default_data_length_cb_t)le_read_host_suggested_default_length_complete;
+
+ read_request->adapter->def_read_handler = handler;
+
+ return NULL;
+}
+
+void le_set_data_length_return_param_complete(
+ uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("le_set_data_length failed: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ }
+
+ return;
+}
+
+int btd_adapter_le_set_data_length(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint16_t max_tx_octets, uint16_t max_tx_time)
+{
+ struct mgmt_cp_le_set_data_length cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ bacpy(&cp.bdaddr, bdaddr);
+
+ cp.max_tx_octets = max_tx_octets;
+ cp.max_tx_time = max_tx_time;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_LE_SET_DATA_LENGTH,
+ adapter->dev_id, sizeof(cp), &cp,
+ le_set_data_length_return_param_complete,
+ adapter, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+#endif
+
static DBusMessage *adapter_set_manufacturer_data(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return btd_error_failed(msg, "Set manufacturer data failed");
}
-#endif /* __BROADCOM_PATCH__ */
+//#endif /* __BROADCOM_PATCH__ */
#endif /* __TIZEN_PATCH__ */
static DBusMessage *stop_discovery(DBusConnection *conn,
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;
}
+static gboolean property_get_ipsp_init_state(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t ipsp_initialized;
+
+ DBG("property_get_ipsp_init_state called");
+ if (adapter->ipsp_intialized)
+ ipsp_initialized = TRUE;
+ else
+ ipsp_initialized = FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+ &ipsp_initialized);
+
+ return TRUE;
+}
#endif
static gboolean property_get_uuids(const GDBusPropertyTable *property,
return reply;
}
+
+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)
+ return;
+
+ adapter->ipsp_intialized = initialized;
+
+ DBG("Set Ipsp init state for adapter %s", adapter->path);
+
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "IpspInitStateChanged");
+}
+
+static void deinitialize_6lowpan_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ bool initialized = FALSE;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ error("De-Initialize BT 6lowpan failed for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ else {
+ adapter_set_ipsp_init_state(adapter, initialized);
+ DBG("De-Initialize BT 6lowpan successfully for hci%u",
+ adapter->dev_id);
+ }
+}
+
+static bool deinitialize_6lowpan(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_enable_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.enable_6lowpan = DEINIT_6LOWPAN;
+ if (mgmt_send(adapter->mgmt, MGMT_OP_ENABLE_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ deinitialize_6lowpan_complete, adapter, NULL) > 0)
+ return true;
+
+ error("Failed to de-initialize BT 6Lowpan for index %u",
+ adapter->dev_id);
+ return false;
+}
+
+static void initialize_6lowpan_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ bool initialized = TRUE;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Initialize BT 6lowpan failed for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ else {
+ adapter_set_ipsp_init_state(adapter, initialized);
+ DBG("Initialize BT 6lowpan successfully for hci%u",
+ adapter->dev_id);
+ }
+}
+
+static bool initialize_6lowpan(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_enable_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.enable_6lowpan = INIT_6LOWPAN;
+ if (mgmt_send(adapter->mgmt, MGMT_OP_ENABLE_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ initialize_6lowpan_complete, adapter, NULL) > 0)
+ return true;
+
+ error("Failed to initialize BT 6Lowpan for index %u",
+ adapter->dev_id);
+ return false;
+}
+
+static DBusMessage *adapter_initialize_ipsp(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+
+ DBG("Initialize IPSP");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (adapter->ipsp_intialized)
+ return btd_error_already_exists(msg);
+
+ /* Enable BT 6lowpan in kernel */
+ err = initialize_6lowpan(adapter);
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to initialize BT 6lowpan");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_deinitialize_ipsp(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+
+ DBG("De-initialize IPSP");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!adapter->ipsp_intialized)
+ return btd_error_not_permitted(msg, "IPSP not initialized");
+
+ 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);
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to deinitialize BT 6lowpan");
+
+ return dbus_message_new_method_return(msg);
+}
#endif
static DBusMessage *remove_device(DBusConnection *conn,
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);
{ "interval", "u" },
{ "window", "u" }), NULL,
adapter_le_set_scan_params) },
-#ifdef __TIZEN_PATCH__ /* scan filter method - refered bt_gatt_client.h */
{ GDBUS_ASYNC_METHOD("scan_filter_param_setup",
GDBUS_ARGS({ "client_if", "i" }, { "action", "i" },
{ "filt_index", "i" }, { "feat_seln", "i"},
{ GDBUS_ASYNC_METHOD("scan_filter_enable",
GDBUS_ARGS({ "client_if", "i" }, { "enable", "b" }), NULL,
adapter_le_scan_filter_enable) },
-#endif /* scan filter method */
+ { GDBUS_METHOD("InitializeIpsp",
+ NULL, NULL,
+ adapter_initialize_ipsp) },
+ { GDBUS_METHOD("DeinitializeIpsp",
+ NULL, NULL,
+ adapter_deinitialize_ipsp) },
{ GDBUS_METHOD("SetScanRespData",
GDBUS_ARGS({ "value", "ay" },
{ "slot_id", "i" }), NULL,
GDBUS_ARGS({ "address", "s" }),
GDBUS_ARGS({ "device", "o" }),
find_device) },
-#ifdef __BROADCOM_PATCH__
+#ifdef __TIZEN_PATCH__
{ GDBUS_METHOD("SetWbsParameters",
GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
NULL,
#endif
{ GDBUS_ASYNC_METHOD("RemoveDevice",
GDBUS_ARGS({ "device", "o" }), NULL, remove_device) },
+#ifdef __TIZEN_PATCH__
+ { GDBUS_ASYNC_METHOD("LEReadMaximumDataLength", NULL,
+ GDBUS_ARGS({"maxTxOctets", "q" }, { "maxTxTime", "q" },
+ {"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" }),
+ le_read_host_suggested_default_data_length)},
+#endif
{ }
};
property_set_connectable },
{ "Version", "s", property_get_version },
{ "SupportedLEFeatures", "as", property_get_supported_le_features},
+ { "IpspInitStateChanged", "b", property_get_ipsp_init_state},
#endif
{ }
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 */
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);
+ device_set_ipsp_connected(dev, FALSE);
#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;
ev->state_change_reason == 0)
adapter_le_enable_multi_adv(TRUE, ev->adv_instance);
}
+
+static void bt_6lowpan_conn_state_change_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_6lowpan_conn_state_changed *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+ gboolean connected = 0;
+
+ if (length < sizeof(*ev)) {
+ error("Too small device connected event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+
+ DBG("hci%u device %s", index, addr);
+
+ device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+ ev->addr.type);
+ if (!device) {
+ error("Unable to get device object for %s", addr);
+ return;
+ }
+
+ if (ev->connected)
+ connected = TRUE;
+ else
+ connected = FALSE;
+
+ device_set_ipsp_connected(device, connected);
+}
+
+static void bt_le_data_length_changed_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_le_data_length_changed *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too small data length changed event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+
+ DBG("hci%u device %s", index, addr);
+
+ device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+ ev->addr.type);
+ if (!device) {
+ error("Unable to get device object for %s", addr);
+ return;
+ }
+
+ 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);
}
static void store_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer,
mgmt_errstr(status), status);
hash = NULL;
randomizer = NULL;
+#ifndef __TIZEN_PATCH__
} else if (length < sizeof(*rp)) {
error("Too small read local OOB data response");
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;
}
+
+int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+
+{
+ struct mgmt_cp_connect_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_CONNECT_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+
+{
+ struct mgmt_cp_disconnect_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_DISCONNECT_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, 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);
adapter->dev_id,
multi_adv_state_change_callback,
adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_6LOWPAN_CONN_STATE_CHANGED,
+ adapter->dev_id,
+ bt_6lowpan_conn_state_change_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_LE_DATA_LENGTH_CHANGED,
+ adapter->dev_id,
+ bt_le_data_length_changed_callback,
+ adapter, NULL);
#endif
set_dev_class(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);
+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);
+
+typedef void (*read_max_data_length_cb_t) (struct btd_adapter *adapter,
+ 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 {
+ read_max_data_length_cb_t read_callback;
+ void *user_data;
+};
+
+typedef void (*read_host_suggested_default_data_length_cb_t) (struct btd_adapter *adapter,
+ 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;
+};
#endif
#ifdef __TIZEN_PATCH__
-#ifdef __BROADCOM_PATCH__
#include <errno.h>
return TRUE;
}
-#endif /* __BROADCOM_PATCH__ */
#endif /* __TIZEN_PATCH__ */
uint8_t service_data_len;
}adapter_le_service_data_params_t;
-#ifdef __BROADCOM_PATCH__
-
/*****************************************************************************
** Defentions for HCI Error Codes that are past in the events
*/
int data_len, uint8_t *p_data,
int mask_len, uint8_t *p_mask);
gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index);
-#else /* __BROADCOM_PATCH__ */
-
-gboolean adapter_le_read_ble_feature_info(void) { return FALSE; }
-gboolean adapter_le_is_supported_multi_advertising(void) { return FALSE; }
-gboolean adapter_le_is_supported_offloading(void) { return FALSE; }
-int adapter_le_get_max_adv_instance(void) { return 0; }
-int adapter_le_get_scan_filter_size(void) { return 0; }
-
-gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst,
- adapter_le_adv_param_t *p_params) { return FALSE; }
-gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp,
- uint8_t data_len, uint8_t *p_data) { return FALSE; }
-gboolean adapter_le_enable_multi_adv (gboolean enable, uint8_t inst_id)
- { return FALSE; }
-
-gboolean adapter_le_enable_scan_filtering (gboolean enable) { return FALSE; }
-gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params)
- { return FALSE; }
-gboolean adapter_le_set_scan_filter_data(int client_if, int action,
- int filt_type, int filter_index,
- int company_id,
- int company_id_mask,
- int uuid_len, uint8_t *p_uuid,
- int uuid_mask_len, uint8_t *p_uuid_mask,
- gchar *string, int addr_type,
- int data_len, uint8_t *p_data,
- int mask_len, uint8_t *p_mask)
- { return FALSE; }
-gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index)
- { return FALSE; }
-#endif /* __BROADCOM_PATCH__ */
#endif /* __TIZEN_PATCH__ */
--- /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);
const struct attribute *attrib1 = a;
const bt_uuid_t *uuid = b;
- return bt_uuid_cmp(&attrib1->uuid, uuid);
+ if (attrib1->uuid.value.u16 != GATT_PRIM_SVC_UUID) {
+ return bt_uuid_cmp(&attrib1->uuid, uuid);
+ } else {
+ bt_uuid_t prim_uuid;
+ prim_uuid = att_get_uuid(attrib1->data, attrib1->len);
+
+ return bt_uuid_cmp(&prim_uuid, uuid);
+ }
}
struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid)
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;
}
#ifdef __TIZEN_PATCH__
/* GATT service: service changed characteristic */
service_changed_handle = 0x0008;
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = GATT_CHR_PROP_READ;
+ atval[0] = GATT_CHR_PROP_INDICATE;
put_le16(service_changed_handle, &atval[1]);
put_le16(GATT_CHARAC_SERVICE_CHANGED, &atval[3]);
- put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, atval);
+
attrib_db_add_new(server, 0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
- atval, 4);
+ atval, 5);
/* GATT service: service changed attribute */
bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
- attrib_db_add_new(server, service_changed_handle, &uuid, ATT_NONE,
+ attrib_db_add_new(server, service_changed_handle, &uuid, ATT_NOT_PERMITTED,
ATT_NOT_PERMITTED, NULL, 0);
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE;
+ atval[1] = 0;
+ attrib_db_add_new(server, 0x0009, &uuid, ATT_NONE, ATT_NONE, atval, 2);
#endif
server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0006,
[Service]
Type=dbus
BusName=org.bluez
-ExecStart=@libexecdir@/bluetoothd
+ExecStart=@libexecdir@/bluetoothd -d -C
NotifyAccess=main
#WatchdogSec=10
-#Restart=on-failure
+Restart=always
+RestartSec=1
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
LimitNPROC=1
uint8_t last_bdaddr_type;
gboolean le_auto_connect;
guint auto_id;
+ gboolean ipsp_connected; /* IPSP Connection state */
+ 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;
+ uint16_t max_rx_time;
#endif
};
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;
}
+static gboolean dev_property_get_ipsp_conn_state(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *dev = data;
+ dbus_bool_t ipsp_connected;
+
+ if (dev->ipsp_connected)
+ ipsp_connected = TRUE;
+ else
+ ipsp_connected = FALSE;
+
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_BOOLEAN, &ipsp_connected);
+
+ return TRUE;
+}
#endif
static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
}
#ifdef __TIZEN_PATCH__
-static gboolean dev_property_get_prim_services(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct btd_device *device = data;
- gchar *attrib_data;
- DBusMessageIter array;
- GSList *l;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_STRING_AS_STRING,
- &array);
-
- for (l = device->primaries; l != NULL; l = l->next) {
- struct gatt_primary *primary = l->data;
-
- DBG("start handle = 0x%04x, end grp handle = 0x%04x "
- "uuid: %s\n", primary->range.start, primary->range.end,
- primary->uuid);
-
- /*
- * attrib_data contains the start_handle followed by '#', end
- * handle followed by '#' and 128 bit UUID as string. Attribute
- * data represents one service.
- */
-
- attrib_data = g_strdup_printf("%04x""#""%04x""#""%s",
- primary->range.start,primary->range.end,primary->uuid);
-
- DBG("attrib_data string: %s",attrib_data);
-
- dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
- &attrib_data);
-
- }
-
- dbus_message_iter_close_container(iter, &array);
-
- return TRUE;
-}
static gboolean property_get_flag(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_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",
}
#endif
-static DBusMessage *get_prim_services(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
-{
- struct btd_device *device = user_data;
- DBusMessage *reply;
- const char *device_path;
- DBusMessageIter iter, array_iter;
- GSList *l;
-
- reply = dbus_message_new_method_return(msg);
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
- device_path = device_get_path(device);
-
- for (l = device->primaries; l != NULL; l = l->next) {
- struct gatt_primary *primary = l->data;
- gchar* attrib_data;
-
- /*
- * attrib_data contains the start_handle followed by '#', end
- * handle followed by '#' and 128 bit UUID as string. Attribute
- * data represents one service.
- */
-
- attrib_data = g_strdup_printf("%s/service%04x", device_path,
- primary->range.start);
-
- DBG("attrib_data string: %s", attrib_data);
-
- dbus_message_iter_append_basic(&array_iter,
- DBUS_TYPE_OBJECT_PATH, &attrib_data);
-
- }
-
- dbus_message_iter_close_container(&iter, &array_iter);
-
- return reply;
-}
-
static DBusMessage *discover_services(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->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;
+}
+
+static DBusMessage *connect_ipsp(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+
+ DBG("bdaddr_type %d", device->bdaddr_type);
+
+ if (device->bdaddr_type == BDADDR_BREDR) {
+ if(device->le)
+ device->bdaddr_type = BDADDR_LE_PUBLIC;
+ 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 btd_error_already_connected(msg);
+
+ /* Initiate Connection for 6Lowan*/
+ if (btd_adapter_connect_ipsp(device->adapter, &device->bdaddr,
+ device->bdaddr_type) != 0)
+ return btd_error_failed(msg, "ConnectFailed");
+
+ return dbus_message_new_method_return(msg);;
+}
+
+static DBusMessage *disconnect_ipsp(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ DBG("bdaddr_type %d", device->bdaddr_type);
+
+ if (device->bdaddr_type == BDADDR_BREDR)
+ return btd_error_not_supported(msg);
+
+ if (!device->ipsp_connected)
+ return btd_error_not_connected(msg);
+
+ /* Disconnect the 6Lowpan connection */
+ if (btd_adapter_disconnect_ipsp(device->adapter, &device->bdaddr,
+ device->bdaddr_type) != 0)
+ return btd_error_failed(msg, "DisconnectFailed");
+
+ /* TODO: Handle disconnection of GATT connection, If the connection
+ * is established as part of IPSP connection. */
+
+ return dbus_message_new_method_return(msg);;
+}
+
+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];
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT16, &max_tx_octets,
+ DBUS_TYPE_UINT16, &max_tx_time,
+ DBUS_TYPE_INVALID)) {
+ DBG("error in retrieving values");
+ return btd_error_invalid_args(msg);
+ }
+
+ if (device->bdaddr_type == BDADDR_BREDR)
+ return btd_error_not_supported(msg);
+
+ ba2str(&device->bdaddr, addr);
+
+ DBG("Remote device address: %s", addr);
+ DBG("Max tx octets: %u, Max tx time: %u",
+ max_tx_octets, max_tx_time);
+
+ status = btd_adapter_le_set_data_length(device->adapter,
+ &device->bdaddr, max_tx_octets,
+ max_tx_time);
+
+ if (status != 0)
+ return btd_error_failed(msg, "Unable to set le data length values");
+ else
+ return dbus_message_new_method_return(msg);
}
static DBusMessage *is_connected_profile(DBusConnection *conn, DBusMessage *msg,
{ 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",
{ "interval_max", "u" }, { "latency", "u" },
{ "time_out", "u" }), NULL,
le_conn_update) },
- { GDBUS_METHOD("GetPrimServices",
- NULL, GDBUS_ARGS({ "Services", "ao" }),
- get_prim_services) },
{ GDBUS_ASYNC_METHOD("DiscoverServices",
GDBUS_ARGS({ "pattern", "s" }), NULL,
discover_services) },
{ GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
+ { GDBUS_ASYNC_METHOD("ConnectIpsp", NULL, NULL, connect_ipsp) },
+ { GDBUS_ASYNC_METHOD("DisconnectIpsp", NULL, NULL, disconnect_ipsp) },
+ { GDBUS_ASYNC_METHOD("LESetDataLength",
+ GDBUS_ARGS({"max_tx_octets", "q" },
+ { "max_tx_time", "q" }), NULL,
+ le_set_data_length)},
#endif
{ }
};
#ifdef __TIZEN_PATCH__
/* To handle Failed Legacy Pairing when initiated from Remote device*/
{ "LegacyPaired", "b", dev_property_get_paired },
- { "Services", "as", dev_property_get_prim_services },
{ "Flag", "q", property_get_flag },
{ "ManufacturerDataLen", "q", property_get_manufacturer_data_len },
{ "ManufacturerData", "ay", property_get_manufacturer_data },
{ "GattConnected", "b", dev_property_get_gatt_connected },
{ "PayloadTimeout", "q", dev_property_get_payload},
{ "LastAddrType", "y", dev_property_get_last_addr_type},
-
+ { "IpspConnected", "b", dev_property_get_ipsp_conn_state },
#endif
{ }
};
{ "RSSI", "i"},
{ "AdvDataLen", "i"},
{ "AdvData", "ay"})) },
+ { GDBUS_SIGNAL("LEDataLengthChanged",
+ GDBUS_ARGS({"max_tx_octets","q"},
+ { "max_tx_time", "q" },
+ { "max_rx_octets", "q"},
+ { "max_rx_time", "q"})) },
};
#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
if (gerr) {
DBG("%s", gerr->message);
-#ifdef __TIZEN_PATCH__
- /* Send disconnected property changed event */
- g_dbus_emit_property_changed(dbus_conn, device->path,
- DEVICE_INTERFACE, "GattConnected");
-#endif
if (attcb->err)
attcb->err(gerr, user_data);
device->last_bdaddr_type = type;
}
+
+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) {
+ error("device is NULL");
+ return;
+ }
+
+ if (device->ipsp_connected == connected)
+ return;
+
+ device->ipsp_connected = connected;
+
+ DBG("ipsp_connected %d", connected);
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "IpspConnected");
+}
+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)
+{
+ if (device == NULL) {
+ error("device is NULL");
+ return;
+ }
+
+ device->max_tx_octets = max_tx_octets;
+ device->max_tx_time = max_tx_time;
+ device->max_rx_octets = max_rx_octets;
+ device->max_rx_time = max_rx_time;
+
+ DBG("data length changed values :max_tx_octets: %d max_tx_time: %d max_rx_octets: %d max_rx_time: %d",
+ max_tx_octets, max_tx_time, max_rx_octets, max_rx_time);
+
+ g_dbus_emit_signal(dbus_conn, device->path,
+ DEVICE_INTERFACE, "LEDataLengthChanged",
+ DBUS_TYPE_UINT16, &max_tx_octets,
+ DBUS_TYPE_UINT16, &max_tx_time,
+ DBUS_TYPE_UINT16, &max_rx_octets,
+ DBUS_TYPE_UINT16, &max_rx_time,
+ DBUS_TYPE_INVALID);
+}
#endif
int device_discover_services(struct btd_device *device)
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__
+/* 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);
+gboolean device_is_ipsp_connected(struct btd_device * device);
+void device_set_ipsp_connected(struct btd_device *device, gboolean connected);
#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;
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,
notify_chrcs(service);
-#ifdef __TIZEN_PATCH__
- device_set_gatt_connected(service->client->device, TRUE);
-#endif
-
return FALSE;
}
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, 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);
}
¬ify);
}
+#ifdef __TIZEN_PATCH__
+static void send_unicast_notification_to_device(struct btd_gatt_database *database,
+ uint16_t handle, const uint8_t *value,
+ uint16_t len, uint16_t ccc_handle,
+ bool indicate, bdaddr_t *unicast_addr)
+{
+ struct notify notify;
+ struct device_state *dev_state;
+
+ memset(¬ify, 0, sizeof(notify));
+
+ notify.database = database;
+ notify.handle = handle;
+ notify.ccc_handle = ccc_handle;
+ notify.value = value;
+ notify.len = len;
+ notify.indicate = indicate;
+
+ /* Find and return a device state. */
+ dev_state = find_device_state_from_address(database, unicast_addr);
+
+ if (dev_state)
+ send_notification_to_device(dev_state, ¬ify);
+}
+#endif
+
static void send_service_changed(struct btd_gatt_database *database,
struct gatt_db_attribute *attrib)
{
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;
}
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;
DBusMessageIter array;
uint8_t *value = NULL;
int len = 0;
+#ifdef __TIZEN_PATCH__
+ 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, "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;
+
+ unicast_addr = get_ccc_unicast_address(chrc->ccc);
+
+ if (unicast_addr && bacmp(unicast_addr, BDADDR_ANY)) {
+ send_unicast_notification_to_device(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_to_devices(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);
+#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) {
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
--- /dev/null
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#Class = 0x000100
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# 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
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# 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=
# 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.
+EnableLEPrivacy = false
+#endif
+
#[Policy]
#
# The ReconnectUUIDs defines the set of remote services that should try
# empty the reconnection feature gets disabled.
#ReconnectUUIDs=
-#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.
-EnableLEPrivacy = false
-#endif
# Possible values: "dual", "bredr", "le"
#ControllerMode = dual
-#[Policy]
+# 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.
+EnableLEPrivacy = false
+#endif
+
+[Policy]
#
# The ReconnectUUIDs defines the set of remote services that should try
# to be reconnected to in case of a link loss (link supervision
# 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=
-
#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.
-EnableLEPrivacy = false
+ReconnectUUIDs=
+#else
+#ReconnectUUIDs=
#endif
+
#define BTD_PROFILE_PSM_AUTO -1
#define BTD_PROFILE_CHAN_AUTO -1
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_INTR_PSM 17
+#define HID_DEVICE_CTRL_PSM 19
+#endif
+
#define HFP_HF_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
<record> \
</attribute> \
</record>"
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_RECORD \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+ <record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x1124\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ <uint16 value=\"0x0011\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0006\"> \
+ <sequence> \
+ <uint16 value=\"0x656e\" /> \
+ <uint16 value=\"0x006a\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x000d\"> \
+ <sequence> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ <uint16 value=\"0x0013\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ </sequence> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0100\"> \
+ <text value=\"Bluez Mouse\" /> \
+ </attribute> \
+ <attribute id=\"0x0101\"> \
+ <text value=\"Mouse\" /> \
+ </attribute> \
+ <attribute id=\"0x0200\"> \
+ <uint16 value=\"0x0100\" /> \
+ </attribute> \
+ <attribute id=\"0x0201\"> \
+ <uint16 value=\"0x0111\" /> \
+ </attribute> \
+ <attribute id=\"0x0202\"> \
+ <uint8 value=\"0x40\" /> \
+ </attribute> \
+ <attribute id=\"0x0203\"> \
+ <uint8 value=\"0x00\" /> \
+ </attribute> \
+ <attribute id=\"0x0204\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ <attribute id=\"0x0205\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ <attribute id=\"0x0206\"> \
+ <sequence> \
+ <sequence> \
+ <uint8 value=\"0x22\" /> \
+ <text encoding=\"hex\" value=\"05010902a10185010901a1000509190129031500250175019503810275059501810105010930093109381581257f750895028106c0c005010906a1018502a100050719e029e71500250175019508810295087508150025650507190029658100c0c0\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0207\"> \
+ <sequence> \
+ <sequence> \
+ <uint16 value=\"0x0409\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x020b\"> \
+ <uint16 value=\"0x0100\" /> \
+ </attribute> \
+ <attribute id=\"0x020e\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ </record>"
+#endif
+
+
struct ext_io;
struct ext_profile {
char *destination;
char *app_path;
#endif
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ bool local_connect;
+#endif
};
struct ext_io {
static GSList *profiles = NULL;
static GSList *ext_profiles = NULL;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int connect_io(struct ext_io *conn, const bdaddr_t *src,
+ const bdaddr_t *dst);
+#endif
+
void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
void *data)
{
return true;
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int check_connection_psm(gconstpointer a, gconstpointer b)
+{
+ const struct ext_io *conn = a;
+ const int *psm = b;
+ DBG("conn->psm %d, psm %d", conn->psm, *psm);
+ return (conn->psm == *psm ? 0 : -1);
+}
+#endif
+
static void ext_connect(GIOChannel *io, GError *err, gpointer user_data)
{
struct ext_io *conn = user_data;
conn->io_id = g_io_add_watch(io, cond, ext_io_disconnected,
conn);
}
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (g_strcmp0(ext->uuid, HID_UUID) == 0 && ext->local_connect == TRUE) {
+ GSList *l = NULL;
+ int psm = HID_DEVICE_CTRL_PSM;
+ ext->local_connect = FALSE;
+ l = g_slist_find_custom(ext->conns, &psm, check_connection_psm);
+ if (l == NULL) {
+ struct ext_io *conn1 = g_new0(struct ext_io, 1);
+ int error = 0;
+ ext->remote_psm = psm;
+ conn1->ext = ext;
+ conn1->psm = ext->remote_psm;
+ conn1->chan = ext->remote_chan;
+ error = connect_io(conn1, btd_adapter_get_address(conn->adapter),
+ device_get_address(conn->device));
+ DBG("error from connect_io %d", error);
+ conn1->adapter = btd_adapter_ref(conn->adapter);
+ conn1->device = btd_device_ref(conn->device);
+ conn1->service = btd_service_ref(conn->service);
+ ext->conns = g_slist_append(ext->conns, conn1);
+ } else {
+ DBG("Connection Already there");
+ }
+ }
+#endif
if (send_new_connection(ext, conn))
return;
struct btd_adapter *adapter)
{
struct ext_io *l2cap = NULL;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ struct ext_io *l2cap1 = NULL;
+#endif
struct ext_io *rfcomm = NULL;
BtIOConfirm confirm;
BtIOConnect connect;
GError *err = NULL;
GIOChannel *io;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ GIOChannel *io1;
+#endif
if (ext->authorize) {
confirm = ext_confirm;
ext->servers = g_slist_append(ext->servers, l2cap);
DBG("%s listening on PSM %u", ext->name, psm);
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (g_strcmp0(ext->uuid , HID_UUID) == 0) {
+ psm = HID_DEVICE_CTRL_PSM;
+ l2cap1 = g_new0(struct ext_io, 1);
+ l2cap1->ext = ext;
+ io1 = bt_io_listen(connect, confirm, l2cap, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(adapter),
+ BT_IO_OPT_MODE, ext->mode,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_SEC_LEVEL, ext->sec_level,
+ BT_IO_OPT_INVALID);
+ l2cap1->io = io1;
+ l2cap1->proto = BTPROTO_L2CAP;
+ l2cap1->psm = psm;
+ l2cap1->adapter = btd_adapter_ref(adapter);
+ ext->servers = g_slist_append(ext->servers, l2cap1);
+ DBG("%s listening on PSM %u", ext->name, psm);
+ }
+#endif
}
if (ext->local_chan) {
conn = g_new0(struct ext_io, 1);
conn->ext = ext;
-
+#ifndef TIZEN_BT_HID_DEVICE_ENABLE
if (ext->remote_psm || ext->remote_chan) {
conn->psm = ext->remote_psm;
conn->chan = ext->remote_chan;
if (err < 0)
goto failed;
-
+#else
+ if (g_strcmp0(ext->uuid, HID_UUID) == 0) {
+ ext->local_connect = TRUE;
+ ext->remote_psm = HID_DEVICE_INTR_PSM;
+ conn->psm = ext->remote_psm;
+ conn->chan = ext->remote_chan;
+ err = connect_io(conn, btd_adapter_get_address(adapter),
+ device_get_address(dev));
+ } else {
+ if (ext->remote_psm || ext->remote_chan) {
+ conn->psm = ext->remote_psm;
+ conn->chan = ext->remote_chan;
+ err = connect_io(conn, btd_adapter_get_address(adapter),
+ device_get_address(dev));
+ } else {
+ err = resolve_service(conn, btd_adapter_get_address(adapter),
+ device_get_address(dev));
+ }
+ }
+ if (err < 0)
+ goto failed;
+#endif
conn->adapter = btd_adapter_ref(adapter);
conn->device = btd_device_ref(dev);
conn->service = btd_service_ref(service);
if (!ext)
return -ENOENT;
+#ifndef TIZEN_BT_HID_DEVICE_ENABLE
conn = find_connection(ext, dev);
if (!conn || !conn->connected)
return -ENOTCONN;
err = send_disconn_req(ext, conn);
if (err < 0)
return err;
+#else
+ if (g_strcmp0(ext->uuid, HID_UUID) != 0) {
+ conn = find_connection(ext, dev);
+ if (!conn || !conn->connected)
+ return -ENOTCONN;
+
+ if (conn->pending)
+ return -EBUSY;
+ err = send_disconn_req(ext, conn);
+ if (err < 0)
+ return err;
+ } else {
+ GSList *l;
+ /* As HID will be using two psm we need to send disconnect
+ * request for both the psms */
+ for (l = ext->conns; l != NULL; l = g_slist_next(l)) {
+ struct ext_io *conn1 = l->data;
+ if (conn1->device == dev) {
+ err = send_disconn_req(ext, conn1);
+ }
+ }
+ }
+#endif
return 0;
}
return g_strdup_printf(SYNC_RECORD, rfcomm->chan, ext->version,
ext->name);
}
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static char *get_hid_device_record(struct ext_profile *ext, struct ext_io *l2cap,
+ struct ext_io *rfcomm)
+{
+ return g_strdup(HID_DEVICE_RECORD);
+}
+#endif
static char *get_opp_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
.authorize = true,
.get_record = get_mns_record,
.version = 0x0102
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ }, {
+ .uuid = HID_UUID,
+ .name = "HID Device",
+ .psm = HID_DEVICE_INTR_PSM,
+ .authorize = TRUE,
+ .get_record = get_hid_device_record,
+ .version = 0x0100,
},
+#else
+ },
+#endif
};
static void ext_set_defaults(struct ext_profile *ext)
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;
*discovering = false;
while ((chrc_data = queue_pop_head(op->pending_chrcs))) {
- attr = gatt_db_service_add_characteristic(op->cur_svc,
+ attr = gatt_db_service_insert_characteristic(op->cur_svc,
+ chrc_data->value_handle,
&chrc_data->uuid, 0,
chrc_data->properties,
NULL, NULL, NULL);
chrc_data->value_handle)
goto failed;
+#ifdef __TIZEN_PATCH__
+ if (chrc_data->value_handle >= chrc_data->end_handle) {
+ free(chrc_data);
+ continue;
+ }
+ desc_start = chrc_data->value_handle + 1;
+#else
desc_start = chrc_data->value_handle + 1;
-
if (desc_start > chrc_data->end_handle) {
free(chrc_data);
continue;
}
+#endif
client->discovery_req = bt_gatt_discover_descriptors(
client->att, desc_start,
"handle: 0x%04x, uuid: %s",
handle, uuid_str);
- attr = gatt_db_service_add_descriptor(op->cur_svc, &uuid, 0,
- NULL, NULL, NULL);
+ attr = gatt_db_service_insert_descriptor(op->cur_svc, handle,
+ &uuid, 0, NULL, NULL,
+ NULL);
if (!attr)
goto failed;
goto failed;
}
+next:
if (!discover_descs(op, &discovering))
goto failed;
if (discovering)
return;
-next:
/* Done with the current service */
gatt_db_service_set_active(op->cur_svc, true);
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;
+
+ req = queue_remove_if(client->pending_requests, match_req_id,
+ UINT_TO_PTR(id));
+ if (!req)
+ return false;
- bt_att_cancel(req->client->att, req->att_id);
+ 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__
+ bdaddr_t unicast_addr;
+#endif
gatt_db_read_t read_func;
gatt_db_write_t write_func;
}
static struct gatt_db_attribute *new_attribute(struct gatt_db_service *service,
+ uint16_t handle,
const bt_uuid_t *type,
const uint8_t *val,
uint16_t len)
return NULL;
attribute->service = service;
+ attribute->handle = handle;
attribute->uuid = *type;
attribute->value_len = len;
if (len) {
}
static struct gatt_db_service *gatt_db_service_create(const bt_uuid_t *uuid,
+ uint16_t handle,
bool primary,
uint16_t num_handles)
{
len = uuid_to_le(uuid, value);
- service->attributes[0] = new_attribute(service, type, value, len);
+ service->attributes[0] = new_attribute(service, handle, type, value,
+ len);
if (!service->attributes[0]) {
gatt_db_service_destroy(service);
return NULL;
if (!find_insert_loc(db, handle, handle + num_handles - 1, &after))
return NULL;
- service = gatt_db_service_create(uuid, primary, num_handles);
+ service = gatt_db_service_create(uuid, handle, primary, num_handles);
if (!service)
return NULL;
attribute->user_data = user_data;
}
-struct gatt_db_attribute *
-gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib,
+static struct gatt_db_attribute *
+service_insert_characteristic(struct gatt_db_service *service,
+ uint16_t handle,
const bt_uuid_t *uuid,
uint32_t permissions,
uint8_t properties,
gatt_db_write_t write_func,
void *user_data)
{
- struct gatt_db_service *service;
uint8_t value[MAX_CHAR_DECL_VALUE_LEN];
uint16_t len = 0;
int i;
- if (!attrib)
+ /* Check if handle is in within service range */
+ if (handle && handle <= service->attributes[0]->handle)
return NULL;
- service = attrib->service;
-
i = get_attribute_index(service, 1);
if (!i)
return NULL;
+ if (!handle)
+ handle = get_handle_at_index(service, i - 1) + 2;
+
value[0] = properties;
len += sizeof(properties);
+
/* We set handle of characteristic value, which will be added next */
- put_le16(get_handle_at_index(service, i - 1) + 2, &value[1]);
+ put_le16(handle, &value[1]);
len += sizeof(uint16_t);
len += uuid_to_le(uuid, &value[3]);
- service->attributes[i] = new_attribute(service, &characteristic_uuid,
- value, len);
+ service->attributes[i] = new_attribute(service, handle - 1,
+ &characteristic_uuid,
+ value, len);
if (!service->attributes[i])
return NULL;
- attribute_update(service, i++);
+ i++;
- service->attributes[i] = new_attribute(service, uuid, NULL, 0);
+ service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0);
if (!service->attributes[i]) {
free(service->attributes[i - 1]);
return NULL;
set_attribute_data(service->attributes[i], read_func, write_func,
permissions, user_data);
- return attribute_update(service, i);
+ return service->attributes[i];
}
struct gatt_db_attribute *
-gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
+gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib,
+ uint16_t handle,
const bt_uuid_t *uuid,
uint32_t permissions,
+ uint8_t properties,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
void *user_data)
{
- struct gatt_db_service *service;
- int i;
+ if (!attrib || !handle)
+ return NULL;
+
+ return service_insert_characteristic(attrib->service, handle, uuid,
+ permissions, properties,
+ read_func, write_func,
+ user_data);
+}
+struct gatt_db_attribute *
+gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ uint8_t properties,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data)
+{
if (!attrib)
return NULL;
- service = attrib->service;
+ return service_insert_characteristic(attrib->service, 0, uuid,
+ permissions, properties,
+ read_func, write_func,
+ user_data);
+}
+
+static struct gatt_db_attribute *
+service_insert_descriptor(struct gatt_db_service *service,
+ uint16_t handle,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data)
+{
+ int i;
i = get_attribute_index(service, 0);
if (!i)
return NULL;
- service->attributes[i] = new_attribute(service, uuid, NULL, 0);
+ /* Check if handle is in within service range */
+ if (handle && handle <= service->attributes[0]->handle)
+ return NULL;
+
+ if (!handle)
+ handle = get_handle_at_index(service, i - 1) + 1;
+
+ service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0);
if (!service->attributes[i])
return NULL;
set_attribute_data(service->attributes[i], read_func, write_func,
permissions, user_data);
- return attribute_update(service, i);
+ return service->attributes[i];
+}
+
+struct gatt_db_attribute *
+gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib,
+ uint16_t handle,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data)
+{
+ if (!attrib || !handle)
+ return NULL;
+
+ return service_insert_descriptor(attrib->service, handle, uuid,
+ permissions, read_func, write_func,
+ user_data);
+}
+
+struct gatt_db_attribute *
+gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data)
+{
+ if (!attrib)
+ return NULL;
+
+ return service_insert_descriptor(attrib->service, 0, uuid,
+ permissions, read_func, write_func,
+ user_data);
}
struct gatt_db_attribute *
if (!index)
return NULL;
- service->attributes[index] = new_attribute(service,
+ service->attributes[index] = new_attribute(service, 0,
&included_service_uuid,
value, len);
if (!service->attributes[index])
service = attrib->service;
/* Start from the attribute following the value handle */
- i = attrib->handle - service->attributes[0]->handle + 2;
+ for (i = 0; i < service->num_handles; i++) {
+ if (service->attributes[i] == attrib) {
+ i += 2;
+ break;
+ }
+ }
+
for (; i < service->num_handles; i++) {
attr = service->attributes[i];
if (!attr)
uint16_t handle)
{
struct gatt_db_service *service;
- uint16_t service_handle;
+ int i;
if (!db || !handle)
return NULL;
if (!service)
return NULL;
- service_handle = service->attributes[0]->handle;
+ for (i = 0; i < service->num_handles; i++) {
+ if (!service->attributes[i])
+ continue;
- /*
- * We can safely get attribute from attributes array with offset,
- * because find_service_for_handle() check if given handle is
- * in service range.
- */
- return service->attributes[handle - service_handle];
+ if (service->attributes[i]->handle == handle)
+ return service->attributes[i];
+ }
+
+ return NULL;
}
static bool find_service_with_uuid(const void *data, const void *user_data)
return true;
}
+
+#ifdef __TIZEN_PATCH__
+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
gatt_db_read_t read_func,
gatt_db_write_t write_func,
void *user_data);
+struct gatt_db_attribute *
+gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib,
+ uint16_t handle,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ uint8_t properties,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data);
struct gatt_db_attribute *
gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
void *user_data);
+struct gatt_db_attribute *
+gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib,
+ uint16_t handle,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data);
struct gatt_db_attribute *
gatt_db_service_add_included(struct gatt_db_attribute *attrib,
unsigned int id, int err);
bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib);
+
+#ifdef __TIZEN_PATCH__
+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);
COLOR_RED "Failed: %d" COLOR_OFF ", "
COLOR_YELLOW "Not Run: %d" COLOR_OFF "\n",
not_run + passed + failed, passed,
- (float) passed * 100 / (not_run + passed + failed),
+ (not_run + passed + failed) ?
+ (float) passed * 100 / (not_run + passed + failed) : 0,
failed, not_run);
execution_time = g_timer_elapsed(test_timer, NULL);
{
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)
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;
return 0;
}
+#ifdef __TIZEN_PATCH__ /*SPRD add Start*/
+static int init_sprd_config(int fd, struct uart_t *u, struct termios *ti)
+{
+
+ return sprd_config_init(fd, u->bdaddr, ti);
+}
+#endif
+
struct uart_t uart[] = {
{ "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
FLOW_CTL, DISABLE_PM, NULL, NULL },
+#ifdef __TIZEN_PATCH__ /*SPRD*/
+ { "sprd", 0x0000, 0x0000, HCI_UART_H4, 3000000, 3000000,
+ FLOW_CTL, DISABLE_PM, NULL, init_sprd_config },
+#endif
{ "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200,
FLOW_CTL, DISABLE_PM, NULL, ericsson },
/* This commented code was present before bluez 5.25 upgrade
* printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]\n");*/
printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f]"
- " [-t timeout] [-s initial_speed]"\r
- " <tty> <type | id> [speed] [flow|noflow]"\r
+ " [-t timeout] [-s initial_speed]"
+ " <tty> <type | id> [speed] [flow|noflow]"
" [sleep|nosleep] [bdaddr]\n");
#else
printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed]"
int bgb2xx_init(int dd, bdaddr_t *bdaddr);
int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
struct termios *ti);
+#ifdef __TIZEN_PATCH__
+int sprd_config_init(int fd, char *bdaddr, struct termios *ti);
+#endif
int ath3k_post(int fd, int pm);
int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr);
int intel_init(int fd, int init_speed, int *speed, struct termios *ti);
--- /dev/null
+#include <linux/kernel.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+#include <sys/stat.h>
+
+#include "hciattach_sprd.h"
+
+//#include <android/log.h>
+//#define DBG
+#ifdef DBG
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "pskey_bt", __VA_ARGS__)
+#else
+#define LOGD(fmt, arg...) fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+#endif
+typedef unsigned char UINT8;
+
+#define UINT32_TO_STREAM(p, u32) {*(p)++ = (UINT8)(u32); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 24);}
+#define UINT24_TO_STREAM(p, u24) {*(p)++ = (UINT8)(u24); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)((u24) >> 16);}
+#define UINT16_TO_STREAM(p, u16) {*(p)++ = (UINT8)(u16); *(p)++ = (UINT8)((u16) >> 8);}
+#define UINT8_TO_STREAM(p, u8) {*(p)++ = (UINT8)(u8);}
+#define INT8_TO_STREAM(p, u8) {*(p)++ = (INT8)(u8);}
+
+#define PSKEY_PRELOAD_SIZE 0x04
+#define PSKEY_PREAMBLE_SIZE 0xA2
+
+ // for bt mac addr
+#define BT_MAC_FILE_PATH "/csa/bluetooth/"
+#define DATMISC_MAC_ADDR_PATH BT_MAC_FILE_PATH".bd_addr"
+#define MAC_ADDR_BUF_LEN (strlen("FF:FF:FF:FF:FF:FF"))
+#define MAC_ADDR_FILE_LEN 25
+#define MAC_ADDR_LEN 6
+
+#define BD_ADDR_LEN 14
+#define BD_PREFIX "0002\n"
+
+#if 0
+#ifndef VENDOR_BTWRITE_PROC_NODE
+#define VENDOR_BTWRITE_PROC_NODE "/proc/bluetooth/sleep/btwrite"
+#endif
+#endif
+
+#define MAX_BT_TMP_PSKEY_FILE_LEN 2048
+
+typedef unsigned int UWORD32;
+typedef unsigned short UWORD16;
+typedef unsigned char UWORD8;
+
+#define down_bt_is_space(c) (((c) == '\n') || ((c) == ',') || ((c) == '\r') || ((c) == ' ') || ((c) == '{') || ((c) == '}'))
+#define down_bt_is_comma(c) (((c) == ','))
+#define down_bt_is_endc(c) (((c) == '}')) // indicate end of data
+
+/* Macros to swap byte order */
+#define SWAP_BYTE_ORDER_WORD(val) ((((val) & 0x000000FF) << 24) + \
+ (((val) & 0x0000FF00) << 8) + \
+ (((val) & 0x00FF0000) >> 8) + \
+ (((val) & 0xFF000000) >> 24))
+#define INLINE static __inline
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN
+#endif
+
+
+// pskey file structure default value
+static BT_PSKEY_CONFIG_T bt_para_setting={
+ .pskey_cmd = 0x001C0101,
+
+ .g_dbg_source_sink_syn_test_data = 0,
+ .g_sys_sleep_in_standby_supported = 0,
+ .g_sys_sleep_master_supported = 0,
+ .g_sys_sleep_slave_supported = 0,
+
+ .default_ahb_clk = 26000000,
+ .device_class = 0x001F00,
+ .win_ext = 30,
+
+ .g_aGainValue = {0x0000F600, 0x0000D000, 0x0000AA00, 0x00008400, 0x00004400, 0x00000A00},
+ .g_aPowerValue = {0x0FC80000, 0x0FF80000, 0x0FDA0000, 0x0FCC0000, 0x0FFC0000},
+
+ .feature_set = {0xFF, 0xFF, 0x8D, 0xFE, 0x9B, 0x7F, 0x79, 0x83, 0xFF, 0xA7, 0xFF, 0x7F, 0x00, 0xE0, 0xF7, 0x3E},
+ .device_addr = {0x6A, 0x6B, 0x8C, 0x8A, 0x8B, 0x8C},
+
+ .g_sys_sco_transmit_mode = 0, //true tramsmit by uart, otherwise by share memory
+ .g_sys_uart0_communication_supported = 1, //true use uart0, otherwise use uart1 for debug
+ .edr_tx_edr_delay = 5,
+ .edr_rx_edr_delay = 14,
+
+ .g_wbs_nv_117 = 0x0031,
+
+ .is_wdg_supported = 0,
+
+ .share_memo_rx_base_addr = 0,
+ //.share_memo_tx_base_addr = 0,
+ .g_wbs_nv_118 = 0x0066,
+ .g_nbv_nv_117 = 0x1063,
+
+ .share_memo_tx_packet_num_addr = 0,
+ .share_memo_tx_data_base_addr = 0,
+
+ .g_PrintLevel = 0xFFFFFFFF,
+
+ .share_memo_tx_block_length = 0,
+ .share_memo_rx_block_length = 0,
+ .share_memo_tx_water_mark = 0,
+ //.share_memo_tx_timeout_value = 0,
+ .g_nbv_nv_118 = 0x0E45,
+
+ .uart_rx_watermark = 48,
+ .uart_flow_control_thld = 63,
+ .comp_id = 0,
+ .pcm_clk_divd = 0x26,
+
+
+ .reserved = {0}
+};
+
+extern int getPskeyFromFile(void *pData);
+extern int bt_getPskeyFromFile(void *pData);
+
+static int create_mac_folder(void)
+{
+ DIR *dp;
+ int err;
+
+ dp = opendir(BT_MAC_FILE_PATH);
+ if (dp == NULL) {
+ if (mkdir(BT_MAC_FILE_PATH, 0755) < 0) {
+ err = -errno;
+ LOGD("%s: mkdir: %s(%d)",__FUNCTION__, strerror(-err), -err);
+ }
+ return -1;
+ }
+
+ closedir(dp);
+ return 0;
+}
+
+static void mac_rand(char *btmac)
+{
+ int ran;
+ int i;
+ unsigned int seed;
+ struct timeval tv;
+
+ memcpy(btmac, BD_PREFIX, 5);
+ i = gettimeofday(&tv, NULL);
+
+ if (i < 0) {
+ LOGD("Fail to call gettimeofday()");
+ seed = time(NULL);
+ } else
+ seed = (unsigned int)tv.tv_usec;
+
+ for (i = 5; i < BD_ADDR_LEN; i++) {
+ if (i == 7) {
+ btmac[i] = '\n';
+ continue;
+ }
+ ran = rand_r(&seed) % 16;
+ if (ran < 10)
+ ran += 0x30;
+ else
+ ran += 0x57;
+ btmac[i] = ran;
+ }
+ LOGD("Random number is\r\n");
+ for (i = 0; i < BD_ADDR_LEN; i++) {
+ LOGD("%c", btmac[i]);
+ }
+ LOGD("\r\n");
+}
+
+static void write_btmac2file(char *btmac)
+{
+ int fd;
+ int ret;
+ fd = open(DATMISC_MAC_ADDR_PATH, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
+ LOGD("write_btmac2file open file, fd=%d", fd);
+ if(fd >= 0) {
+ if(chmod(DATMISC_MAC_ADDR_PATH,0666) != -1){
+ ret = write(fd, btmac, strlen(btmac));
+ if (ret < strlen(btmac)) {
+ LOGD("Fail to write %s", DATMISC_MAC_ADDR_PATH);
+ close(fd);
+ return;
+ }
+ }
+ close(fd);
+ }else{
+ LOGD("write bt mac to file failed!!");
+ }
+}
+
+uint8 ConvertHexToBin(
+ uint8 *hex_ptr, // in: the hexadecimal format string
+ uint16 length, // in: the length of hexadecimal string
+ uint8 *bin_ptr // out: pointer to the binary format string
+ ){
+ uint8 *dest_ptr = bin_ptr;
+ uint32 i = 0;
+ uint8 ch;
+
+ for(i=0; i<length; i+=2){
+ // the bit 8,7,6,5
+ ch = hex_ptr[i];
+ // digital 0 - 9
+ if (ch >= '0' && ch <= '9')
+ *dest_ptr =(uint8)((ch - '0') << 4);
+ // a - f
+ else if (ch >= 'a' && ch <= 'f')
+ *dest_ptr = (uint8)((ch - 'a' + 10) << 4);
+ // A - F
+ else if (ch >= 'A' && ch <= 'F')
+ *dest_ptr = (uint8)((ch -'A' + 10) << 4);
+ else{
+ return 0;
+ }
+
+ // the bit 1,2,3,4
+ ch = hex_ptr[i+1];
+ // digtial 0 - 9
+ if (ch >= '0' && ch <= '9')
+ *dest_ptr |= (uint8)(ch - '0');
+ // a - f
+ else if (ch >= 'a' && ch <= 'f')
+ *dest_ptr |= (uint8)(ch - 'a' + 10);
+ // A - F
+ else if (ch >= 'A' && ch <= 'F')
+ *dest_ptr |= (uint8)(ch -'A' + 10);
+ else{
+ return 0;
+ }
+
+ dest_ptr++;
+ }
+
+ return 1;
+}
+
+static int read_mac_address(char *file_name, uint8 *addr) {
+ char buf[MAC_ADDR_FILE_LEN] = {0};
+ uint32 addr_t[MAC_ADDR_LEN] = {0};
+ int i = 0;
+
+
+#if 1
+ int fd = open(file_name, O_RDONLY, 0666);
+ LOGD("%s read file: %s", __func__, file_name);
+ if (fd < 0) {
+ LOGD("%s open %s error reason: %s", __func__, file_name, strerror(errno));
+ return -1;
+ }
+ if (read(fd, buf, BD_ADDR_LEN) < 0) {
+ LOGD("%s read %s error reason: %s", __func__, file_name, strerror(errno));
+ goto done;
+ }
+ if (sscanf(buf, "%02X%02X\n%02X\n%02X%02X%02X", &addr_t[0], &addr_t[1], &addr_t[2], &addr_t[3], &addr_t[4], &addr_t[5]) < 0) {
+ LOGD("%s sscanf %s error reason: %s", __func__, file_name, strerror(errno));
+ goto done;
+ }
+
+ for (i = 0; i < MAC_ADDR_LEN; i++) {
+ addr[i] = addr_t[i] & 0xFF;
+ }
+ LOGD("%s %s addr: [%02X:%02X:%02X:%02X:%02X:%02X]", __func__, file_name, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+done:
+ close(fd);
+#endif
+ return 0;
+}
+
+static void mac_address_stream_compose(uint8 *addr) {
+ uint8 tmp, i, j;
+ for (i = 0, j = MAC_ADDR_LEN - 1; (i < MAC_ADDR_LEN / 2) && (i != j); i++, j--) {
+ tmp = addr[i];
+ addr[i] = addr[j];
+ addr[j] = tmp;
+ }
+}
+
+#if 0
+/*
+ * random bluetooth mac address
+ */
+static void random_mac_addr(uint8 *addr) {
+ int fd, randseed, ret, mac_rd;
+ uint8 addr_t[MAC_ADDR_LEN] = {0};
+
+ LOGD("%s", __func__);
+ /* urandom seed build */
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0){
+ LOGD("%s: open urandom fail", __func__);
+ } else {
+ ret = read(fd, &randseed, sizeof(randseed));
+ LOGD("%s urandom:0x%08X", __func__, randseed);
+ close(fd);
+ }
+
+ /* time seed build */
+ if (fd < 0 || ret < 0) {
+ struct timeval tt;
+ if (gettimeofday(&tt, (struct timezone *)0) > 0) {
+ randseed = (unsigned int) tt.tv_usec;
+ } else {
+ randseed = (unsigned int) time(NULL);
+ }
+ LOGD("urandom fail, using system time for randseed");
+ }
+
+ LOGD("%s: randseed = %u",__func__, randseed);
+ srand(randseed);
+ mac_rd = rand();
+
+ addr_t[0] = 0x40; /* FOR */
+ addr_t[1] = 0x45; /* SPRD */
+ addr_t[2] = 0xDA; /* ADDR */
+ addr_t[3] = (uint8)(mac_rd & 0xFF);
+ addr_t[4] = (uint8)((mac_rd >> 8) & 0xFF);
+ addr_t[5] = (uint8)((mac_rd >> 16) & 0xFF);
+
+ memcpy(addr, addr_t, MAC_ADDR_LEN);
+ LOGD("%s: MAC ADDR: [%02X:%02X:%02X:%02X:%02X:%02X]",__func__, addr_t[0], addr_t[1], addr_t[2], addr_t[3], addr_t[4], addr_t[5]);
+}
+#endif
+static void get_mac_address(uint8 *addr){
+ int ret = -1;
+ uint8 addr_t[6] = {0};
+ char bt_mac[BD_ADDR_LEN] = {0, };
+
+ LOGD("%s", __func__);
+ /* check misc mac file exist */
+ ret = access(DATMISC_MAC_ADDR_PATH, F_OK);
+ if (ret != 0) {
+ LOGD("%s %s miss", __func__, DATMISC_MAC_ADDR_PATH);
+
+ /* Try to make bt address file */
+ create_mac_folder();
+
+ mac_rand(bt_mac);
+ LOGD("bt random mac=%s",bt_mac);
+ write_btmac2file(bt_mac);
+
+ }
+
+ /* read mac file */
+ read_mac_address(DATMISC_MAC_ADDR_PATH, addr_t);
+
+ /* compose mac stream */
+ mac_address_stream_compose(addr_t);
+
+ memcpy(addr, addr_t, MAC_ADDR_LEN);
+
+}
+
+
+/*
+ * hci command preload stream, special order
+ */
+static void pskey_stream_compose(uint8 * buf, BT_PSKEY_CONFIG_T *bt_par) {
+ int i = 0;
+ uint8 *p = buf;
+
+ LOGD("%s", __func__);
+
+ UINT24_TO_STREAM(p, bt_par->pskey_cmd);
+ UINT8_TO_STREAM(p, (uint8)(PSKEY_PREAMBLE_SIZE & 0xFF));
+
+ UINT8_TO_STREAM(p, bt_par->g_dbg_source_sink_syn_test_data);
+ UINT8_TO_STREAM(p, bt_par->g_sys_sleep_in_standby_supported);
+ UINT8_TO_STREAM(p, bt_par->g_sys_sleep_master_supported);
+ UINT8_TO_STREAM(p, bt_par->g_sys_sleep_slave_supported);
+
+ UINT32_TO_STREAM(p, bt_par->default_ahb_clk);
+ UINT32_TO_STREAM(p, bt_par->device_class);
+ UINT32_TO_STREAM(p, bt_par->win_ext);
+
+ for (i = 0; i < 6; i++) {
+ UINT32_TO_STREAM(p, bt_par->g_aGainValue[i]);
+ }
+ for (i = 0; i < 5; i++) {
+ UINT32_TO_STREAM(p, bt_par->g_aPowerValue[i]);
+ }
+
+ for (i = 0; i < 16; i++) {
+ UINT8_TO_STREAM(p, bt_par->feature_set[i]);
+ }
+ for (i = 0; i < 6; i++) {
+ UINT8_TO_STREAM(p, bt_par->device_addr[i]);
+ }
+
+ UINT8_TO_STREAM(p, bt_par->g_sys_sco_transmit_mode);
+ UINT8_TO_STREAM(p, bt_par->g_sys_uart0_communication_supported);
+ UINT8_TO_STREAM(p, bt_par->edr_tx_edr_delay);
+ UINT8_TO_STREAM(p, bt_par->edr_rx_edr_delay);
+
+ UINT16_TO_STREAM(p, bt_par->g_wbs_nv_117);
+
+ UINT32_TO_STREAM(p, bt_par->is_wdg_supported);
+
+ UINT32_TO_STREAM(p, bt_par->share_memo_rx_base_addr);
+ //UINT32_TO_STREAM(p, bt_par->share_memo_tx_base_addr);
+ UINT16_TO_STREAM(p, bt_par->g_wbs_nv_118);
+ UINT16_TO_STREAM(p, bt_par->g_nbv_nv_117);
+
+ UINT32_TO_STREAM(p, bt_par->share_memo_tx_packet_num_addr);
+ UINT32_TO_STREAM(p, bt_par->share_memo_tx_data_base_addr);
+
+ UINT32_TO_STREAM(p, bt_par->g_PrintLevel);
+
+ UINT16_TO_STREAM(p, bt_par->share_memo_tx_block_length);
+ UINT16_TO_STREAM(p, bt_par->share_memo_rx_block_length);
+ UINT16_TO_STREAM(p, bt_par->share_memo_tx_water_mark);
+ //UINT16_TO_STREAM(p, bt_par->share_memo_tx_timeout_value);
+ UINT16_TO_STREAM(p, bt_par->g_nbv_nv_118);
+
+ UINT16_TO_STREAM(p, bt_par->uart_rx_watermark);
+ UINT16_TO_STREAM(p, bt_par->uart_flow_control_thld);
+ UINT32_TO_STREAM(p, bt_par->comp_id);
+ UINT16_TO_STREAM(p, bt_par->pcm_clk_divd);
+
+
+ for (i = 0; i < 8; i++) {
+ UINT32_TO_STREAM(p, bt_par->reserved[i]);
+ }
+}
+
+void sprd_get_pskey(BT_PSKEY_CONFIG_T * pskey_t) {
+ BT_PSKEY_CONFIG_T pskey;
+ uint8 buf[180] = {0};
+
+ LOGD("%s", __func__);
+ memset(&pskey, 0 , sizeof(BT_PSKEY_CONFIG_T));
+ if (bt_getPskeyFromFile(&pskey) < 0 ) {
+ LOGD("%s bt_getPskeyFromFile failed", __func__);
+ memcpy(pskey_t, &bt_para_setting, sizeof(BT_PSKEY_CONFIG_T));
+ return;
+ }
+
+ memset(buf, 0, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+
+ /* get bluetooth mac address */
+ get_mac_address(pskey.device_addr);
+
+ /* compose pskey hci command pkt */
+ pskey_stream_compose(buf, &pskey);
+
+ memcpy(pskey_t, &pskey, sizeof(BT_PSKEY_CONFIG_T));
+}
+
+
+int sprd_config_init(int fd, char *bdaddr, struct termios *ti)
+{
+ int ret = 0,r=0;
+ unsigned char resp[30] = {0};
+ BT_PSKEY_CONFIG_T bt_para_tmp;
+ uint8 data_tmp[5] = {'a'};
+ static int index = 0;
+ uint8 *buf = NULL;
+#if 0
+ char buffer;
+ int btsleep_fd_sprd = -1;
+#endif
+ LOGD("sprd_config_init");
+
+#if 0
+ uart_fd = open(UART_INFO_PATH, O_WRONLY);
+ if(uart_fd > 0)
+ {
+ buffer = '2';
+ if (write(uart_fd, &buffer, 1) < 0)
+ {
+ LOGD("%s write(%s) failed: %s (%d) 2", __func__,
+ UART_INFO_PATH, strerror(errno),errno);
+ }
+
+ close(uart_fd);
+ }
+#endif
+
+#if 0
+ btsleep_fd_sprd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY);
+ if (btsleep_fd_sprd < 0)
+ {
+ LOGD("%s open(%s) for write failed: %s (%d)", __func__,
+ VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno);
+ }
+ else
+ {
+ buffer = '1';
+ if (write(btsleep_fd_sprd, &buffer, 1) < 0)
+ {
+ LOGD("%s write(%s) failed: %s (%d)", __func__,
+ VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno);
+ }
+ }
+#endif
+
+ ret = bt_getPskeyFromFile(&bt_para_tmp);
+ if (ret < 0) {
+ LOGD("init_sprd_config bt_getPskeyFromFile failed\n");
+ memcpy(&bt_para_tmp, &bt_para_setting, sizeof(BT_PSKEY_CONFIG_T));
+ }
+
+ buf = (uint8 *)malloc(PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+ if (buf == NULL) {
+ LOGD("%s alloc stream memory failed", __func__);
+ return -1;
+ }
+ memset(buf, 0, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+
+ /* get bluetooth mac address */
+ get_mac_address(bt_para_tmp.device_addr);
+
+ /* compose pskey hci command pkt */
+ pskey_stream_compose(buf, &bt_para_tmp);
+
+ ret = write(fd, buf, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+ LOGD("write pskey ret = %d", ret);
+
+ free(buf);
+ buf = NULL;
+
+ if (ret < 0) {
+ LOGD("%s write pskey stream failed", __func__);
+ return -1;
+ }
+
+ memset(data_tmp, 0xff, sizeof(data_tmp));
+ while (1) {
+ r = read(fd, resp, 1);
+
+ if (r <= 0)
+ return -1;
+ else{
+ data_tmp[index] = resp[0];
+ LOGD("recive from controller 0x%x", data_tmp[index]);
+ ++index;
+ }
+
+/* if ((data_tmp[0] == 0x04) && (data_tmp[1] == 0x6f)&& (data_tmp[2] == 0x01) &&(data_tmp[3] == 0x0)){*/
+ if ((data_tmp[0] == 0x04) && (data_tmp[1] == 0xe)&& (data_tmp[2] == 0xa) &&(data_tmp[3] == 0x1)){
+ fprintf(stderr, "read response ok \n");
+
+ if (index == 12) {
+ index = 0;
+ memset(data_tmp, 0x0, sizeof(data_tmp));
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+#ifndef HCIATTACH_SPRD_H__
+#define HCIATTACH_SPRD_H__
+
+#define MAC_ERROR "FF:FF:FF:FF:FF:FF"
+
+#define BT_MAC_FILE "/productinfo/btmac.txt"
+//#define GET_BTMAC_ATCMD "AT+SNVM=0,401"
+//#define GET_BTPSKEY_ATCMD "AT+SNVM=0,415"
+//#define SET_BTMAC_ATCMD "AT+SNVM=1,401"
+#define BT_RAND_MAC_LENGTH 17
+
+// used to store BT pskey structure and default values
+#define BT_PSKEY_STRUCT_FILE "/system/lib/modules/pskey_bt.txt"
+//#define BT_PSKEY_FILE "/system/lib/modules/pskey_bt.txt"
+
+
+typedef unsigned char uint8;
+typedef unsigned int uint32;
+typedef unsigned short uint16;
+
+#define BT_ADDRESS_SIZE 6
+
+
+typedef struct SPRD_BT_PSKEY_INFO_T{
+ uint32 pskey_cmd;
+
+ uint8 g_dbg_source_sink_syn_test_data;
+ uint8 g_sys_sleep_in_standby_supported;
+ uint8 g_sys_sleep_master_supported;
+ uint8 g_sys_sleep_slave_supported;
+
+ uint32 default_ahb_clk;
+ uint32 device_class;
+ uint32 win_ext;
+
+ uint32 g_aGainValue[6];
+ uint32 g_aPowerValue[5];
+
+ uint8 feature_set[16];
+ uint8 device_addr[6];
+
+ uint8 g_sys_sco_transmit_mode; //true tramsmit by uart, otherwise by share memory
+ uint8 g_sys_uart0_communication_supported; //true use uart0, otherwise use uart1 for debug
+ uint8 edr_tx_edr_delay;
+ uint8 edr_rx_edr_delay;
+
+ uint16 g_wbs_nv_117;
+
+ uint32 is_wdg_supported;
+
+ uint32 share_memo_rx_base_addr;
+
+ // uint32 share_memo_tx_base_addr;
+ uint16 g_wbs_nv_118;
+ uint16 g_nbv_nv_117;
+
+ uint32 share_memo_tx_packet_num_addr;
+ uint32 share_memo_tx_data_base_addr;
+
+ uint32 g_PrintLevel;
+
+ uint16 share_memo_tx_block_length;
+ uint16 share_memo_rx_block_length;
+ uint16 share_memo_tx_water_mark;
+
+ //uint16 share_memo_tx_timeout_value;
+ uint16 g_nbv_nv_118;
+
+ uint16 uart_rx_watermark;
+ uint16 uart_flow_control_thld;
+ uint32 comp_id;
+ uint16 pcm_clk_divd;
+
+ uint32 reserved[8];
+}BT_PSKEY_CONFIG_T;
+
+
+#endif /* HCIATTACH_SPRD_H__ */
+
+
+
+
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();
}
#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;
--- /dev/null
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define LOG_TAG "pskey"
+// #include "cutils/log.h"
+
+#include "hciattach_sprd.h"
+//#include "bt_vendor_sprd.h"
+#define BT_PSKEY_TRACE_BUF_SIZE 256
+#define MAX_BOARD_TYPE_LEN 32
+
+#define _FILE_PARSE_DEBUG_
+#define CMD_ITEM_TABLE(ITEM, MEM_OFFSET, TYPE) { ITEM, (unsigned int)( &( ((BT_PSKEY_CONFIG_T *)(0))->MEM_OFFSET )), TYPE }
+#define ALOGI(fmt, arg...) fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+#define ALOGE(fmt, arg...) fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+
+#define PSKEY_PATH "/usr/lib/firmware/scx35_pikeavivaltove_3M_MARLIN_connectivity_configure.ini"
+
+typedef struct
+{
+ char item[64];
+ uint32 par[32];
+ int num;
+}cmd_par;
+
+typedef struct
+{
+ char *item;
+ unsigned int mem_offset;
+ int type;
+}cmd_par_table;
+
+static cmd_par_table g_pskey_table[] =
+{
+ CMD_ITEM_TABLE("pskey_cmd", pskey_cmd, 4),
+
+ CMD_ITEM_TABLE("g_dbg_source_sink_syn_test_data", g_dbg_source_sink_syn_test_data, 1),
+ CMD_ITEM_TABLE("g_sys_sleep_in_standby_supported", g_sys_sleep_in_standby_supported, 1),
+ CMD_ITEM_TABLE("g_sys_sleep_master_supported", g_sys_sleep_master_supported, 1),
+ CMD_ITEM_TABLE("g_sys_sleep_slave_supported", g_sys_sleep_slave_supported, 1),
+
+ CMD_ITEM_TABLE("default_ahb_clk", default_ahb_clk, 4),
+ CMD_ITEM_TABLE("device_class", device_class, 4),
+ CMD_ITEM_TABLE("win_ext", win_ext, 4),
+
+ CMD_ITEM_TABLE("g_aGainValue", g_aGainValue, 4),
+ CMD_ITEM_TABLE("g_aPowerValue", g_aPowerValue, 4),
+
+ CMD_ITEM_TABLE("feature_set", feature_set, 1),
+ CMD_ITEM_TABLE("device_addr", device_addr, 1),
+
+ CMD_ITEM_TABLE("g_sys_sco_transmit_mode", g_sys_sco_transmit_mode, 1), //true tramsmit by uart, otherwise by share memory
+ CMD_ITEM_TABLE("g_sys_uart0_communication_supported", g_sys_uart0_communication_supported, 1), //true use uart0, otherwise use uart1 for debug
+ CMD_ITEM_TABLE("edr_tx_edr_delay", edr_tx_edr_delay, 1),
+ CMD_ITEM_TABLE("edr_rx_edr_delay", edr_rx_edr_delay, 1),
+
+ CMD_ITEM_TABLE("g_wbs_nv_117", g_wbs_nv_117, 2),
+
+
+ CMD_ITEM_TABLE("is_wdg_supported", is_wdg_supported, 4),
+
+ CMD_ITEM_TABLE("share_memo_rx_base_addr", share_memo_rx_base_addr, 4),
+ //CMD_ITEM_TABLE("share_memo_tx_base_addr", share_memo_tx_base_addr, 4),
+
+ CMD_ITEM_TABLE("g_wbs_nv_118", g_wbs_nv_118, 2),
+ CMD_ITEM_TABLE("g_nbv_nv_117", g_nbv_nv_117, 2),
+
+
+ CMD_ITEM_TABLE("share_memo_tx_packet_num_addr", share_memo_tx_packet_num_addr, 4),
+ CMD_ITEM_TABLE("share_memo_tx_data_base_addr", share_memo_tx_data_base_addr, 4),
+
+ CMD_ITEM_TABLE("g_PrintLevel", g_PrintLevel, 4),
+
+ CMD_ITEM_TABLE("share_memo_tx_block_length", share_memo_tx_block_length, 2),
+ CMD_ITEM_TABLE("share_memo_rx_block_length", share_memo_rx_block_length, 2),
+ CMD_ITEM_TABLE("share_memo_tx_water_mark", share_memo_tx_water_mark, 2),
+ //CMD_ITEM_TABLE("share_memo_tx_timeout_value", share_memo_tx_timeout_value, 2),
+ CMD_ITEM_TABLE("g_nbv_nv_118", g_nbv_nv_118, 2),
+
+ CMD_ITEM_TABLE("uart_rx_watermark", uart_rx_watermark, 2),
+ CMD_ITEM_TABLE("uart_flow_control_thld", uart_flow_control_thld, 2),
+ CMD_ITEM_TABLE("comp_id", comp_id, 4),
+ CMD_ITEM_TABLE("pcm_clk_divd", pcm_clk_divd, 2),
+
+
+ CMD_ITEM_TABLE("bt_reserved", reserved, 4)
+};
+
+static int bt_getFileSize(char *file)
+{
+ struct stat temp;
+ stat(file, &temp);
+ return temp.st_size;
+}
+
+static int bt_find_type(char key)
+{
+ if( (key >= 'a' && key <= 'w') || (key >= 'y' && key <= 'z') || (key >= 'A' && key <= 'W') || (key >= 'Y' && key <= 'Z') || ('_' == key) )
+ return 1;
+ if( (key >= '0' && key <= '9') || ('-' == key) )
+ return 2;
+ if( ('x' == key) || ('X' == key) || ('.' == key) )
+ return 3;
+ if( (key == '\0') || ('\r' == key) || ('\n' == key) || ('#' == key) )
+ return 4;
+ return 0;
+}
+
+static void bt_getCmdOneline(unsigned char *str, cmd_par *cmd)
+{
+ int i, j, bufType, cType, flag;
+ char tmp[BT_PSKEY_TRACE_BUF_SIZE];
+ char c;
+ bufType = -1;
+ cType = 0;
+ flag = 0;
+ memset( cmd, 0, sizeof(cmd_par) );
+ for(i = 0, j = 0; ; i++)
+ {
+ c = str[i];
+ cType = bt_find_type(c);
+ if( (1 == cType) || ( 2 == cType) || (3 == cType) )
+ {
+ tmp[j] = c;
+ j++;
+ if(-1 == bufType)
+ {
+ if(2 == cType)
+ bufType = 2;
+ else
+ bufType = 1;
+ }
+ else if(2 == bufType)
+ {
+ if(1 == cType)
+ bufType = 1;
+ }
+ continue;
+ }
+ if(-1 != bufType)
+ {
+ tmp[j] = '\0';
+
+ if((1 == bufType) && (0 == flag) )
+ {
+ strcpy(cmd->item, tmp);
+ flag = 1;
+ }
+ else
+ {
+ /* compatible with HEX */
+ if (tmp[0] == '0' && (tmp[1] == 'x' || tmp[1] == 'X')) {
+ cmd->par[cmd->num] = strtoul(tmp, 0, 16) & 0xFFFFFFFF;
+ cmd->num++;
+ } else {
+ cmd->par[cmd->num] = strtoul(tmp, 0, 10) & 0xFFFFFFFF;
+ cmd->num++;
+ }
+ }
+ bufType = -1;
+ j = 0;
+ }
+ if(0 == cType )
+ continue;
+ if(4 == cType)
+ return;
+ }
+ return;
+}
+
+static int bt_getDataFromCmd(cmd_par_table *pTable, cmd_par *cmd, void *pData)
+{
+ int i;
+ unsigned char *p;
+ if( (1 != pTable->type) && (2 != pTable->type) && (4 != pTable->type) )
+ return -1;
+ p = (unsigned char *)(pData) + pTable->mem_offset;
+#ifdef _FILE_PARSE_DEBUG_
+ char tmp[BT_PSKEY_TRACE_BUF_SIZE] = {0};
+ char string[16] = {0};
+ sprintf(tmp, "###[pskey]%s, offset:%d, num:%d, value: ", pTable->item, pTable->mem_offset, cmd->num);
+ for(i=0; i<cmd->num; i++)
+ {
+ memset(string, 0, 16);
+ sprintf(string, "0x%x, ", cmd->par[i] );
+ strcat(tmp, string);
+ }
+ ALOGI("%s\n", tmp);
+#endif
+ for(i = 0; i < cmd->num; i++)
+ {
+ if(1 == pTable->type)
+ *((unsigned char *)p + i) = (unsigned char)(cmd->par[i]);
+ else if(2 == pTable->type)
+ *((unsigned short *)p + i) = (unsigned short)(cmd->par[i]);
+ else if(4 == pTable->type)
+ *( (unsigned int *)p + i) = (unsigned int)(cmd->par[i]);
+ else
+ ALOGE("%s, type err\n", __func__);
+ }
+ return 0;
+}
+
+static cmd_par_table *bt_cmd_table_match(cmd_par *cmd)
+{
+ int i;
+ cmd_par_table *pTable = NULL;
+ int len = sizeof(g_pskey_table) / sizeof(cmd_par_table);
+ if(NULL == cmd->item)
+ return NULL;
+ for(i = 0; i < len; i++)
+ {
+ if(NULL == g_pskey_table[i].item)
+ continue;
+ if( 0 != strcmp( g_pskey_table[i].item, cmd->item ) )
+ continue;
+ pTable = &g_pskey_table[i];
+ break;
+ }
+ return pTable;
+}
+
+
+static int bt_getDataFromBuf(void *pData, unsigned char *pBuf, int file_len)
+{
+ int i, p;
+ cmd_par cmd;
+ cmd_par_table *pTable = NULL;
+ if((NULL == pBuf) || (0 == file_len) || (NULL == pData) )
+ return -1;
+ for(i = 0, p = 0; i < file_len; i++)
+ {
+ if( ('\n' == *(pBuf + i)) || ( '\r' == *(pBuf + i)) || ( '\0' == *(pBuf + i) ) )
+ {
+ if(5 <= (i - p) )
+ {
+ bt_getCmdOneline((pBuf + p), &cmd);
+ pTable = bt_cmd_table_match(&cmd);
+ if(NULL != pTable)
+ {
+ bt_getDataFromCmd(pTable, &cmd, pData);
+ }
+ }
+ p = i + 1;
+ }
+
+ }
+ return 0;
+}
+
+static int bt_dumpPskey(BT_PSKEY_CONFIG_T *p)
+{
+ ALOGI("pskey_cmd: 0x%08X", p->pskey_cmd);
+
+ ALOGI("g_dbg_source_sink_syn_test_data: 0x%02X", p->g_dbg_source_sink_syn_test_data);
+ ALOGI("g_sys_sleep_in_standby_supported: 0x%02X", p->g_sys_sleep_in_standby_supported);
+ ALOGI("g_sys_sleep_master_supported: 0x%02X", p->g_sys_sleep_master_supported);
+ ALOGI("g_sys_sleep_slave_supported: 0x%02X", p->g_sys_sleep_slave_supported);
+
+ ALOGI("default_ahb_clk: %d", p->default_ahb_clk);
+ ALOGI("device_class: 0x%08X", p->device_class);
+ ALOGI("win_ext: 0x%08X", p->win_ext);
+
+ ALOGI("g_aGainValue: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", p->g_aGainValue[0], p->g_aGainValue[1], p->g_aGainValue[2], p->g_aGainValue[3], p->g_aGainValue[4], p->g_aGainValue[5]);
+ ALOGI("g_aPowerValue: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", p->g_aPowerValue[0], p->g_aPowerValue[1], p->g_aPowerValue[2], p->g_aPowerValue[3], p->g_aPowerValue[4]);
+
+
+ ALOGI("feature_set(0~7): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->feature_set[0], p->feature_set[1], p->feature_set[2],
+ p->feature_set[3], p->feature_set[4], p->feature_set[5], p->feature_set[6], p->feature_set[7]);
+ ALOGI("feature_set(8~15): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->feature_set[8], p->feature_set[9], p->feature_set[10],
+ p->feature_set[11], p->feature_set[12], p->feature_set[13], p->feature_set[14], p->feature_set[15]);
+ ALOGI("device_addr: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->device_addr[0], p->device_addr[1], p->device_addr[2], p->device_addr[3], p->device_addr[4], p->device_addr[5]);
+
+ ALOGI("g_sys_sco_transmit_mode: 0x%02X", p->g_sys_sco_transmit_mode);
+ ALOGI("g_sys_uart0_communication_supported: 0x%02X", p->g_sys_uart0_communication_supported);
+ ALOGI("edr_tx_edr_delay: %d", p->edr_tx_edr_delay);
+ ALOGI("edr_rx_edr_delay: %d", p->edr_rx_edr_delay);
+
+ ALOGI("g_wbs_nv_117 : 0x%04X", p->g_wbs_nv_117 );
+
+ ALOGI("is_wdg_supported: 0x%08X", p->is_wdg_supported);
+
+ ALOGI("share_memo_rx_base_addr: 0x%08X", p->share_memo_rx_base_addr);
+ //ALOGI("share_memo_tx_base_addr: 0x%08X", p->share_memo_tx_base_addr);
+ ALOGI("g_wbs_nv_118 : 0x%04X", p->g_wbs_nv_118 );
+ ALOGI("g_nbv_nv_117 : 0x%04X", p->g_nbv_nv_117 );
+
+
+ ALOGI("share_memo_tx_packet_num_addr: 0x%08X", p->share_memo_tx_packet_num_addr);
+ ALOGI("share_memo_tx_data_base_addr: 0x%08X", p->share_memo_tx_data_base_addr);
+
+ ALOGI("g_PrintLevel: 0x%08X", p->g_PrintLevel);
+
+ ALOGI("share_memo_tx_block_length: 0x%04X", p->share_memo_tx_block_length);
+ ALOGI("share_memo_rx_block_length: 0x%04X", p->share_memo_rx_block_length);
+ ALOGI("share_memo_tx_water_mark: 0x%04X", p->share_memo_tx_water_mark);
+ //ALOGI("share_memo_tx_timeout_value: 0x%04X", p->share_memo_tx_timeout_value);
+ ALOGI("g_nbv_nv_118 : 0x%04X", p->g_nbv_nv_118 );
+
+ ALOGI("uart_rx_watermark: %d", p->uart_rx_watermark);
+ ALOGI("uart_flow_control_thld: %d", p->uart_flow_control_thld);
+ ALOGI("comp_id: 0x%08X", p->comp_id);
+ ALOGI("pcm_clk_divd : 0x%04X", p->pcm_clk_divd );
+
+
+ ALOGI("reserved(0~7): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->reserved[0], p->reserved[1], p->reserved[2],
+ p->reserved[3], p->reserved[4], p->reserved[5], p->reserved[6], p->reserved[7]);
+ return 0;
+}
+#if 0
+static int bt_get_config_ver(unsigned char *pBuf, int len)
+{
+ int i, p;
+ cmd_par cmd;
+ int ret = -1;
+ for(i = 0, p = 0; i < len; i++)
+ {
+ if( ('\n' == *(pBuf + i)) || ( '\r' == *(pBuf + i)) || ( '\0' == *(pBuf + i) ) )
+ {
+ if(5 <= (i - p) )
+ {
+ bt_getCmdOneline((pBuf + p), &cmd);
+ if( 0 == strcmp(cmd.item, "version") )
+ {
+ ret = cmd.par[0];
+ break;
+ }
+ memset(&cmd, 0, sizeof(cmd_par) );
+ }
+ p = i + 1;
+ }
+
+ }
+ return ret;
+}
+#endif
+int bt_getPskeyFromFile(void *pData)
+{
+ int ret = -1;
+ int fd;
+ unsigned char *pBuf = NULL;
+ int len;
+
+ ALOGI("begin to bt_getPskeyFromFile");
+ fd = open(PSKEY_PATH, O_RDONLY, 0644);
+ if(-1 != fd)
+ {
+ len = bt_getFileSize(PSKEY_PATH);
+ pBuf = (unsigned char *)malloc(len);
+ ret = read(fd, pBuf, len);
+ if(-1 == ret)
+ {
+ ALOGE("%s read %s ret:%d\n", __FUNCTION__, PSKEY_PATH, ret);
+ free(pBuf);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ }
+ else
+ {
+ ALOGE("%s open %s ret:%d\n", __FUNCTION__, PSKEY_PATH, fd);
+ return -1;
+ }
+
+ ret = bt_getDataFromBuf(pData, pBuf, len);
+ if(-1 == ret)
+ {
+ free(pBuf);
+ return -1;
+ }
+ ALOGI("begin to dumpPskey");
+ bt_dumpPskey((BT_PSKEY_CONFIG_T *)pData);
+ free(pBuf);
+ return 0;
+}
+
+
#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)
{
- DBG("");
-
return true;
}
static bool handle_volume_up(struct avrcp *session, bool pressed,
void *user_data)
{
- DBG("");
-
return true;
}
static bool handle_channel_up(struct avrcp *session, bool pressed,
void *user_data)
{
- DBG("");
-
return true;
}
static bool handle_select(struct avrcp *session, bool pressed, void *user_data)
{
- DBG("");
-
return true;
}
static bool handle_vendor_uniq(struct avrcp *session, bool pressed,
void *user_data)
{
- DBG("");
-
return true;
}
static int list_attributes(struct avrcp *session, uint8_t transaction,
void *user_data)
{
- DBG("");
-
avrcp_list_player_attributes_rsp(session, transaction, 0, NULL);
return 0;
{
const char *text[number];
- DBG("");
-
if (number) {
memset(text, 0, number);
text[0] = "equalizer";
static int list_values(struct avrcp *session, uint8_t transaction,
uint8_t attr, void *user_data)
{
- DBG("");
-
avrcp_list_player_values_rsp(session, transaction, 0, NULL);
return -EINVAL;
{
const char *text[number];
- DBG("");
-
if (number) {
memset(text, 0, number);
text[0] = "on";
{
uint8_t values[number];
- DBG("");
-
memset(values, 0, number);
avrcp_get_current_player_value_rsp(session, transaction, number, attrs,
uint8_t number, uint8_t *attrs, uint8_t *values,
void *user_data)
{
- DBG("");
-
avrcp_set_player_value_rsp(session, transaction);
return 0;
static int get_play_status(struct avrcp *session, uint8_t transaction,
void *user_data)
{
- DBG("");
-
avrcp_get_play_status_rsp(session, transaction, 0xaaaaaaaa, 0xbbbbbbbb,
0x00);
{
struct context *context = user_data;
- DBG("");
-
if (g_str_has_prefix(context->data->test_name, "/TP/RCR")) {
uint8_t params[1024];
struct context *context = user_data;
uint64_t track;
- DBG("");
-
if (g_str_equal(context->data->test_name, "/TP/NFY/BV-05-C") ||
g_str_equal(context->data->test_name, "/TP/NFY/BV-08-C"))
memset(&track, 0, sizeof(track));
{
uint8_t settings[3];
- DBG("");
-
settings[0] = 0x01;
settings[1] = 0x01;
settings[2] = 0x02;
static int available_players_changed(struct avrcp *session, uint8_t transaction,
uint32_t interval, void *user_data)
{
- DBG("");
-
avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED,
NULL, 0);
{
uint16_t player[2];
- DBG("");
-
player[0] = 0x0001;
player[1] = 0x0001;
struct context *context = user_data;
uint16_t counter;
- DBG("");
-
if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-09-C"))
counter = 0x0000;
else
uint8_t transaction, uint32_t interval,
void *user_data)
{
- DBG("");
-
avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
AVRCP_EVENT_NOW_PLAYING_CONTENT_CHANGED,
NULL, 0);
{
uint8_t volume = 0x00;
- DBG("");
-
avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
AVRCP_EVENT_VOLUME_CHANGED,
&volume, sizeof(volume));
uint8_t event, uint32_t interval,
void *user_data)
{
- DBG("");
-
switch (event) {
case AVRCP_EVENT_TRACK_CHANGED:
return track_changed(session, transaction, interval, user_data);
static int set_volume(struct avrcp *session, uint8_t transaction,
uint8_t volume, void *user_data)
{
- DBG("");
-
avrcp_set_volume_rsp(session, transaction, volume);
return 0;
struct context *context = user_data;
uint8_t status;
- DBG("");
-
if (g_str_equal(context->data->test_name, "/TP/MPS/BI-01-C"))
status = AVRCP_STATUS_INVALID_PLAYER_ID;
else
struct context *context = user_data;
const char *folders[1] = { "Filesystem" };
- DBG("");
-
if (g_str_equal(context->data->test_name, "/TP/MPS/BI-02-C"))
avrcp_set_browsed_player_rsp(session, transaction,
AVRCP_STATUS_INVALID_PLAYER_ID,
{
struct context *context = user_data;
- DBG("");
-
if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BI-02-C"))
return -ERANGE;
uint16_t counter, uint8_t direction,
uint64_t uid, void *user_data)
{
- DBG("");
-
if (!uid)
return -ENOTDIR;
struct context *context = user_data;
uint8_t status;
- DBG("");
-
if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BI-05-C"))
status = AVRCP_STATUS_UID_CHANGED;
else
static int play_item(struct avrcp *session, uint8_t transaction, uint8_t scope,
uint64_t uid, uint16_t counter, void *user_data)
{
- DBG("");
-
if (!uid)
return -ENOENT;
static int search(struct avrcp *session, uint8_t transaction,
const char *string, void *user_data)
{
- DBG("");
-
avrcp_search_rsp(session, transaction, AVRCP_STATUS_SUCCESS, 0xaabb, 0);
return 0;
uint8_t scope, uint64_t uid, uint16_t counter,
void *user_data)
{
- DBG("");
-
if (!uid)
return -ENOENT;
avrcp_register_player(context->session, &control_ind, NULL, context);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void get_folder_items_rsp(struct avrcp *session, int err,
{
struct context *context = user_data;
- DBG("");
-
g_assert_cmpint(err, ==, 0);
g_assert_cmpint(counter, ==, 0xabcd);
g_assert_cmpint(number, ==, 0);
{
struct context *context = user_data;
- DBG("");
-
g_assert_cmpint(err, ==, 0);
switch (event) {
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)