summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2006-09-19 08:29:22 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2006-09-19 08:29:22 +0000
commitef6a5df5d623b88685dfee801b39d19333e7a206 (patch)
tree6fae3fbf63ab450c29d0aaad03984b3034d8f827
parent7d039bf19c88e031eb97733e96e34049ee43c238 (diff)
Add periodic discovery support to the D-Bus API
-rw-r--r--hcid/dbus-adapter.c200
-rw-r--r--hcid/dbus-api.txt20
-rw-r--r--hcid/dbus.c395
-rw-r--r--hcid/dbus.h34
-rw-r--r--hcid/hcid.h2
-rw-r--r--hcid/security.c11
6 files changed, 542 insertions, 120 deletions
diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c
index 80da4ef8..31411578 100644
--- a/hcid/dbus-adapter.c
+++ b/hcid/dbus-adapter.c
@@ -203,6 +203,61 @@ static int check_address(const char *addr)
return 0;
}
+static int cancel_remote_name(struct hci_dbus_data *pdata)
+{
+ struct discovered_dev_info *dev, match;
+ struct slist *l;
+ struct hci_request rq;
+ remote_name_req_cancel_cp cp;
+ int dd, err = 0;
+ uint8_t status;
+
+ /* find the pending remote name request */
+ memset(&match, 0, sizeof(struct discovered_dev_info));
+ bacpy(&match.bdaddr, BDADDR_ANY);
+ match.name_status = NAME_REQUESTED;
+
+ l = slist_find(pdata->disc_devices, &match, (cmp_func_t) disc_device_find);
+ if (!l) /* no pending request */
+ return 0;
+
+ dd = hci_open_dev(pdata->dev_id);
+ if (dd < 0)
+ return -ENODEV;
+
+ dev = l->data;
+
+ bacpy(&cp.bdaddr, &dev->bdaddr);
+
+ rq.ogf = OGF_LINK_CTL;
+ rq.rparam = &status;
+ rq.rlen = sizeof(status);
+ rq.event = EVT_CMD_COMPLETE;
+ rq.ocf = OCF_REMOTE_NAME_REQ_CANCEL;
+ rq.cparam = &cp;
+ rq.clen = REMOTE_NAME_REQ_CANCEL_CP_SIZE;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ error("Sending command failed: %s (%d)", strerror(errno), errno);
+ err = -errno;
+ goto failed;
+ }
+
+ if (status) {
+ error("Cancel failed with status 0x%02x", status);
+ err = -bt_error(status);
+ }
+failed:
+
+ /* free discovered devices list */
+ slist_foreach(pdata->disc_devices, (slist_func_t) free, NULL);
+ slist_free(pdata->disc_devices);
+ pdata->disc_devices = NULL;
+
+ hci_close_dev(dd);
+ return err;
+}
+
static struct bonding_request_info *bonding_request_new(bdaddr_t *peer, DBusConnection *conn,
DBusMessage *msg)
{
@@ -1399,13 +1454,13 @@ static DBusHandlerResult handle_dev_get_remote_name_req(DBusConnection *conn, DB
/* put the request name in the queue to resolve name */
str2ba(peer_addr, &peer_bdaddr);
- disc_device_append(&dbus_data->disc_devices, &peer_bdaddr, NAME_PENDING, RESOLVE_NAME);
+ disc_device_append(&dbus_data->disc_devices, &peer_bdaddr, NAME_REQUIRED);
/*
* if there is a discover process running, just queue the request.
* Otherwise, send the HCI cmd to get the remote name
*/
- if (dbus_data->discover_state == STATE_IDLE)
+ if (!(dbus_data->inq_active || dbus_data->pinq_active))
disc_device_req_name(dbus_data);
return error_request_deferred(conn, msg);
@@ -1875,8 +1930,10 @@ static DBusHandlerResult handle_dev_create_bonding_req(DBusConnection *conn, DBu
str2ba(peer_addr, &peer_bdaddr);
/* check if there is a pending discover: requested by D-Bus/non clients */
- if (dbus_data->discover_state != STATE_IDLE || dbus_data->discovery_requestor)
- return error_discover_in_progress(conn, msg);
+ if (dbus_data->inq_active || !dbus_data->pinq_idle)
+ return error_discover_in_progress(conn, msg);
+
+ cancel_remote_name(dbus_data);
if (dbus_data->bonding)
return error_bonding_in_progress(conn, msg);
@@ -2229,15 +2286,118 @@ static DBusHandlerResult handle_dev_get_encryption_key_size_req(DBusConnection *
return send_reply_and_unref(conn, reply);
}
+static DBusHandlerResult handle_dev_start_periodic_req(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ periodic_inquiry_cp cp;
+ struct hci_request rq;
+ struct hci_dbus_data *dbus_data = data;
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ uint8_t status;
+ int dd;
+
+ if (!dbus_data->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ if (dbus_data->inq_active || dbus_data->pinq_active)
+ return error_discover_in_progress(conn, msg);
+
+ cancel_remote_name(dbus_data);
+
+ dd = hci_open_dev(dbus_data->dev_id);
+ if (dd < 0)
+ return error_no_such_adapter(conn, msg);
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(&cp.lap, lap, 3);
+ cp.max_period = 24;
+ cp.min_period = 16;
+ cp.length = 0x08;
+ cp.num_rsp = 0x00;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_PERIODIC_INQUIRY;
+ rq.cparam = &cp;
+ rq.clen = PERIODIC_INQUIRY_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = sizeof(status);
+ rq.event = EVT_CMD_COMPLETE;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ int err = errno;
+ error("Unable to start periodic inquiry: %s (%d)",
+ strerror(errno), errno);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, err);
+ }
+
+ if (status) {
+ error("HCI_Periodic_Inquiry_Mode command failed with status 0x%02x", status);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, bt_error(status));
+ }
+
+ dbus_data->pdiscovery_requestor = strdup(dbus_message_get_sender(msg));
+ dbus_data->discover_type = PERIODIC_INQUIRY | RESOLVE_NAME;
+
+ reply = dbus_message_new_method_return(msg);
+
+ hci_close_dev(dd);
+
+ /* track the request owner to cancel it automatically if the owner exits */
+ name_listener_add(conn, dbus_message_get_sender(msg),
+ (name_cb_t) periodic_discover_req_exit, dbus_data);
+
+ return send_reply_and_unref(conn, reply);
+}
+
+static DBusHandlerResult handle_dev_stop_periodic_req(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ struct hci_dbus_data *dbus_data = data;
+ int err;
+
+ if (!dbus_data->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ /* allow cancel if the periodic inquiry was requested by a NON D-Bus client */
+ if (dbus_data->pdiscovery_requestor && strcmp(dbus_data->pdiscovery_requestor, dbus_message_get_sender(msg)))
+ return error_not_authorized(conn, msg);
+
+ if (!dbus_data->pinq_active)
+ return error_not_authorized(conn, msg); /* find a better name */
+
+ /*
+ * Cleanup the discovered devices list and send the cmd to exit
+ * from periodic inquiry mode or cancel remote name request.
+ */
+ err = cancel_periodic_discovery(dbus_data);
+ if (err < 0) {
+ if (err == -ENODEV)
+ return error_no_such_adapter(conn, msg);
+ else
+ return error_failed(conn, msg, -err);
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ return send_reply_and_unref(conn, reply);
+}
+
static DBusHandlerResult handle_dev_discover_devices_req(DBusConnection *conn, DBusMessage *msg, void *data)
{
- DBusMessage *reply = NULL;
+ DBusMessage *reply;
const char *method;
inquiry_cp cp;
evt_cmd_status rp;
struct hci_request rq;
struct hci_dbus_data *dbus_data = data;
- uint8_t length = 8, num_rsp = 0;
uint32_t lap = 0x9e8b33;
int dd;
@@ -2247,9 +2407,11 @@ static DBusHandlerResult handle_dev_discover_devices_req(DBusConnection *conn, D
if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
return error_invalid_arguments(conn, msg);
- if (dbus_data->discover_state != STATE_IDLE)
+ if (dbus_data->inq_active || dbus_data->pinq_active)
return error_discover_in_progress(conn, msg);
+ cancel_remote_name(dbus_data);
+
if (dbus_data->bonding)
return error_bonding_in_progress(conn, msg);
@@ -2261,8 +2423,8 @@ static DBusHandlerResult handle_dev_discover_devices_req(DBusConnection *conn, D
cp.lap[0] = lap & 0xff;
cp.lap[1] = (lap >> 8) & 0xff;
cp.lap[2] = (lap >> 16) & 0xff;
- cp.length = length;
- cp.num_rsp = num_rsp;
+ cp.length = 0x08;
+ cp.num_rsp = 0x00;
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_LINK_CTL;
@@ -2288,10 +2450,14 @@ static DBusHandlerResult handle_dev_discover_devices_req(DBusConnection *conn, D
}
method = dbus_message_get_member(msg);
+ /*
+ * In the future, if we allow standard inquiry when there is active
+ * periodic inquiry: the name resolve flag must NOT be overwritten.
+ */
if (strcmp("DiscoverDevicesWithoutNameResolving", method) == 0)
- dbus_data->discover_type = WITHOUT_NAME_RESOLVING;
+ dbus_data->discover_type |= STD_INQUIRY;
else
- dbus_data->discover_type = RESOLVE_NAME;
+ dbus_data->discover_type |= (STD_INQUIRY | RESOLVE_NAME);
dbus_data->discovery_requestor = strdup(dbus_message_get_sender(msg));
@@ -2308,7 +2474,6 @@ static DBusHandlerResult handle_dev_discover_devices_req(DBusConnection *conn, D
static DBusHandlerResult handle_dev_cancel_discovery_req(DBusConnection *conn, DBusMessage *msg, void *data)
{
- const char *requestor_name;
struct hci_dbus_data *dbus_data = data;
int err;
@@ -2318,17 +2483,13 @@ static DBusHandlerResult handle_dev_cancel_discovery_req(DBusConnection *conn, D
if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
return error_invalid_arguments(conn, msg);
- requestor_name = dbus_message_get_sender(msg);
-
/* is there discover pending? or discovery cancel was requested previously */
- if ((dbus_data->discover_state != STATE_DISCOVER &&
- dbus_data->discover_state != STATE_RESOLVING_NAMES) ||
- dbus_data->discovery_cancel)
+ if (!dbus_data->inq_active || dbus_data->discovery_cancel)
return error_not_authorized(conn, msg); /* FIXME: find a better error name */
/* only the discover requestor can cancel the inquiry process */
if (!dbus_data->discovery_requestor ||
- strcmp(dbus_data->discovery_requestor, requestor_name))
+ strcmp(dbus_data->discovery_requestor, dbus_message_get_sender(msg)))
return error_not_authorized(conn, msg);
/*
* Cleanup the discovered devices list and send the cmd
@@ -2469,6 +2630,9 @@ static struct service_data dev_services[] = {
{ "GetPinCodeLength", handle_dev_get_pin_code_length_req },
{ "GetEncryptionKeySize", handle_dev_get_encryption_key_size_req },
+ { "StartPeriodicDiscovery", handle_dev_start_periodic_req },
+ { "StopPeriodicDiscovery", handle_dev_stop_periodic_req },
+
{ "DiscoverDevices", handle_dev_discover_devices_req },
{ "DiscoverDevicesWithoutNameResolving", handle_dev_discover_devices_req },
{ "CancelDiscovery", handle_dev_cancel_discovery_req },
diff --git a/hcid/dbus-api.txt b/hcid/dbus-api.txt
index 40e25733..528c3794 100644
--- a/hcid/dbus-api.txt
+++ b/hcid/dbus-api.txt
@@ -698,6 +698,26 @@ Methods string GetAddress()
org.bluez.Error.NotAuthorized
org.bluez.Error.NoSuchAdapter
+ void StartPeriodicDiscovery()
+
+ This method starts a periodic discovery.
+
+ Possible errors:
+ org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+ org.bluez.Error.NoSuchAdapter
+
+ void StopPeriodicDiscovery()
+
+ This method stops a periodic discovery that was started using
+ the StartPeriodicDiscovery method.
+
+ Possible errors:
+ org.bluez.Error.Failed
+ org.bluez.Error.NotAuthorized
+ org.bluez.Error.NotInProgress
+ org.bluez.Error.NoSuchAdapter
+
array{uint32} GetRemoteServiceHandles(string address, string match)
This method will request the SDP database of a remote
diff --git a/hcid/dbus.c b/hcid/dbus.c
index 3a15ad0a..0d90aaeb 100644
--- a/hcid/dbus.c
+++ b/hcid/dbus.c
@@ -93,7 +93,7 @@ void bonding_request_free(struct bonding_request_info *bonding)
free(bonding);
}
-static int disc_device_find(const struct discovered_dev_info *d1, const struct discovered_dev_info *d2)
+int disc_device_find(const struct discovered_dev_info *d1, const struct discovered_dev_info *d2)
{
int ret;
@@ -110,13 +110,10 @@ static int disc_device_find(const struct discovered_dev_info *d1, const struct d
return ret;
}
- if (d2->discover_type)
- return (d1->discover_type - d2->discover_type);
-
return 0;
}
-int disc_device_append(struct slist **list, bdaddr_t *bdaddr, name_status_t name_status, int discover_type)
+int disc_device_append(struct slist **list, bdaddr_t *bdaddr, name_status_t name_status)
{
struct discovered_dev_info *dev, match;
struct slist *l;
@@ -130,10 +127,13 @@ int disc_device_append(struct slist **list, bdaddr_t *bdaddr, name_status_t name
if (l) {
/* device found, update the attributes */
dev = l->data;
- dev->name_status = name_status;
- /* get remote name received while discovering */
- if (dev->discover_type != RESOLVE_NAME)
- dev->discover_type = discover_type;
+
+ /* Get remote name can be received while inquiring.
+ * Keep in mind that multiple inquiry result events can
+ * be received from the same remote device.
+ */
+ if (name_status != NAME_NOT_REQUIRED)
+ dev->name_status = name_status;
return -EALREADY;
}
@@ -144,7 +144,6 @@ int disc_device_append(struct slist **list, bdaddr_t *bdaddr, name_status_t name
memset(dev, 0, sizeof(*dev));
bacpy(&dev->bdaddr, bdaddr);
dev->name_status = name_status;
- dev->discover_type = discover_type;
*list = slist_append(*list, dev);
@@ -391,19 +390,26 @@ static void reply_pending_requests(const char *path, struct hci_dbus_data *pdata
bonding_request_free(pdata->bonding);
pdata->bonding = NULL;
}
- else if (pdata->discover_state != STATE_IDLE) {
- /* If there is a pending reply for discovery cancel */
- if (pdata->discovery_cancel) {
- message = dbus_message_new_method_return(pdata->discovery_cancel);
- send_reply_and_unref(connection, message);
- dbus_message_unref(pdata->discovery_cancel);
- pdata->discovery_cancel = NULL;
- }
+ /* If there is a pending reply for discovery cancel */
+ if (pdata->discovery_cancel) {
+ message = dbus_message_new_method_return(pdata->discovery_cancel);
+ send_reply_and_unref(connection, message);
+ dbus_message_unref(pdata->discovery_cancel);
+ pdata->discovery_cancel = NULL;
+ }
+ if (pdata->discovery_requestor) {
/* Send discovery completed signal if there isn't name to resolve */
message = dbus_message_new_signal(path, ADAPTER_INTERFACE,
- "DiscoveryCompleted");
+ "DiscoveryCompleted");
+ send_reply_and_unref(connection, message);
+ }
+
+ if (pdata->pdiscovery_requestor) {
+ /* Send periodic discovery signal */
+ message = dbus_message_new_signal(path, ADAPTER_INTERFACE,
+ "PeriodicDiscoveryStopped");
send_reply_and_unref(connection, message);
}
}
@@ -430,6 +436,13 @@ static int unregister_dbus_path(const char *path)
pdata->discovery_requestor = NULL;
}
+ if (pdata->pdiscovery_requestor) {
+ name_listener_remove(connection, pdata->pdiscovery_requestor,
+ (name_cb_t) periodic_discover_req_exit, pdata);
+ free(pdata->pdiscovery_requestor);
+ pdata->pdiscovery_requestor = NULL;
+ }
+
if (pdata->disc_devices) {
slist_foreach(pdata->disc_devices, (slist_func_t) free, NULL);
slist_free(pdata->disc_devices);
@@ -585,10 +598,15 @@ int hcid_dbus_start_device(uint16_t id)
goto failed;
}
+ if (hci_test_bit(HCI_INQUIRY, &di.flags))
+ pdata->inq_active = 1;
+ else
+ pdata->inq_active = 0;
+
pdata->mode = rp.enable; /* Keep the current scan status */
pdata->up = 1;
pdata->discoverable_timeout = get_discoverable_timeout(id);
- pdata->discover_type = WITHOUT_NAME_RESOLVING; /* default discover type */
+ pdata->discover_type = DISCOVER_TYPE_NONE;
/*
* Get the adapter Bluetooth address
@@ -674,6 +692,13 @@ int hcid_dbus_stop_device(uint16_t id)
pdata->discovery_requestor = NULL;
}
+ if (pdata->pdiscovery_requestor) {
+ name_listener_remove(connection, pdata->pdiscovery_requestor,
+ (name_cb_t) periodic_discover_req_exit, pdata);
+ free(pdata->pdiscovery_requestor);
+ pdata->pdiscovery_requestor = NULL;
+ }
+
if (pdata->disc_devices) {
slist_foreach(pdata->disc_devices, (slist_func_t) free, NULL);
slist_free(pdata->disc_devices);
@@ -693,8 +718,11 @@ int hcid_dbus_stop_device(uint16_t id)
}
pdata->up = 0;
- pdata->discover_state = STATE_IDLE;
pdata->mode = SCAN_DISABLED;
+ pdata->inq_active = 0;
+ pdata->pinq_active = 0;
+ pdata->pinq_idle = 0;
+ pdata->discover_type = DISCOVER_TYPE_NONE;
return 0;
}
@@ -834,7 +862,7 @@ void hcid_dbus_inquiry_start(bdaddr_t *local)
snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, id);
if (dbus_connection_get_object_path_data(connection, path, (void *) &pdata))
- pdata->discover_state = STATE_DISCOVER;
+ pdata->inq_active = 1;
message = dbus_message_new_signal(path, ADAPTER_INTERFACE,
"DiscoveryStarted");
@@ -872,8 +900,7 @@ int disc_device_req_name(struct hci_dbus_data *dbus_data)
memset(&match, 0, sizeof(struct discovered_dev_info));
bacpy(&match.bdaddr, BDADDR_ANY);
- match.name_status = NAME_PENDING;
- match.discover_type = RESOLVE_NAME;
+ match.name_status = NAME_REQUIRED;
l = slist_find(dbus_data->disc_devices, &match, (cmp_func_t) disc_device_find);
if (!l)
@@ -975,51 +1002,141 @@ void hcid_dbus_inquiry_complete(bdaddr_t *local)
goto done;
}
- /* reset the discover type to be able to handle D-Bus and non D-Bus requests */
- pdata->discover_type = WITHOUT_NAME_RESOLVING;
+ pdata->pinq_idle = 1;
- if (!disc_device_req_name(pdata)) {
- pdata->discover_state = STATE_RESOLVING_NAMES;
+ if (!disc_device_req_name(pdata))
goto done; /* skip - there is name to resolve */
- }
-
- pdata->discover_state = STATE_IDLE;
/* free discovered devices list */
slist_foreach(pdata->disc_devices, (slist_func_t) free, NULL);
slist_free(pdata->disc_devices);
pdata->disc_devices = NULL;
- if (pdata->discovery_requestor) {
+ /* Don't send signal if it is a periodic inquiry */
+ if (pdata->pinq_active)
+ goto done;
+
+ if (pdata->discovery_requestor) {
name_listener_remove(connection, pdata->discovery_requestor,
- (name_cb_t) discover_devices_req_exit, pdata);
+ (name_cb_t) discover_devices_req_exit, pdata);
free(pdata->discovery_requestor);
pdata->discovery_requestor = NULL;
+
+ /* If there is a pending reply for discovery cancel */
+ if (pdata->discovery_cancel) {
+ message = dbus_message_new_method_return(pdata->discovery_cancel);
+ send_reply_and_unref(connection, message);
+ dbus_message_unref(pdata->discovery_cancel);
+ pdata->discovery_cancel = NULL;
+ }
+
+ if (pdata->inq_active) {
+ /* Send discovery completed signal if there isn't name to resolve */
+ message = dbus_message_new_signal(path, ADAPTER_INTERFACE,
+ "DiscoveryCompleted");
+ send_reply_and_unref(connection, message);
+ }
+
+ /* reset the discover type for standard inquiry only */
+ pdata->discover_type &= ~STD_INQUIRY;
}
- /* If there is a pending reply for discovery cancel */
- if (pdata->discovery_cancel) {
- message = dbus_message_new_method_return(pdata->discovery_cancel);
- send_reply_and_unref(connection, message);
- dbus_message_unref(pdata->discovery_cancel);
- pdata->discovery_cancel = NULL;
+ /* tracks D-Bus and NON D-Bus */
+ pdata->inq_active = 0;
+done:
+ bt_free(local_addr);
+}
+
+void hcid_dbus_periodic_inquiry_start(bdaddr_t *local, uint8_t status)
+{
+ struct hci_dbus_data *pdata;
+ DBusMessage *message;
+ char path[MAX_PATH_LENGTH];
+ char *local_addr;
+ bdaddr_t tmp;
+ int id;
+
+ /* Don't send the signal if the cmd failed */
+ if (status)
+ return;
+
+ baswap(&tmp, local); local_addr = batostr(&tmp);
+
+ id = hci_devid(local_addr);
+ if (id < 0) {
+ error("No matching device id for %s", local_addr);
+ goto failed;
+ }
+
+ snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, id);
+
+ if (dbus_connection_get_object_path_data(connection, path, (void *) &pdata))
+ pdata->pinq_active = 1;
+
+ message = dbus_message_new_signal(path, ADAPTER_INTERFACE,
+ "PeriodicDiscoveryStarted");
+ send_reply_and_unref(connection, message);
+failed:
+ bt_free(local_addr);
+}
+
+void hcid_dbus_periodic_inquiry_exit(bdaddr_t *local, uint8_t status)
+{
+ DBusMessage *message;
+ struct hci_dbus_data *pdata;
+ char path[MAX_PATH_LENGTH];
+ char *local_addr;
+ bdaddr_t tmp;
+ int id;
+
+ /* Don't send the signal if the cmd failed */
+ if (status)
+ return;
+
+ baswap(&tmp, local); local_addr = batostr(&tmp);
+
+ id = hci_devid(local_addr);
+ if (id < 0) {
+ error("No matching device id for %s", local_addr);
+ goto done;
+ }
+
+ snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, id);
+
+ if (!dbus_connection_get_object_path_data(connection, path, (void *) &pdata)) {
+ error("Getting %s path data failed!", path);
+ goto done;
+ }
+
+ /* reset the discover type to be able to handle D-Bus and non D-Bus requests */
+ pdata->pinq_active = 0;
+ pdata->discover_type &= ~(PERIODIC_INQUIRY | RESOLVE_NAME);
+
+ /* free discovered devices list */
+ slist_foreach(pdata->disc_devices, (slist_func_t) free, NULL);
+ slist_free(pdata->disc_devices);
+ pdata->disc_devices = NULL;
+
+ if (pdata->pdiscovery_requestor) {
+ name_listener_remove(connection, pdata->pdiscovery_requestor,
+ (name_cb_t) periodic_discover_req_exit, pdata);
+ free(pdata->pdiscovery_requestor);
+ pdata->pdiscovery_requestor = NULL;
}
/* Send discovery completed signal if there isn't name to resolve */
message = dbus_message_new_signal(path, ADAPTER_INTERFACE,
- "DiscoveryCompleted");
+ "PeriodicDiscoveryStopped");
send_reply_and_unref(connection, message);
done:
bt_free(local_addr);
}
-
void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, int8_t rssi)
{
char filename[PATH_MAX + 1];
DBusMessage *signal_device;
DBusMessage *signal_name;
- DBusMessageIter iter;
char path[MAX_PATH_LENGTH];
struct hci_dbus_data *pdata;
struct slist *l;
@@ -1028,7 +1145,7 @@ void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, i
const dbus_uint32_t tmp_class = class;
const dbus_int16_t tmp_rssi = rssi;
bdaddr_t tmp;
- name_status_t name_status = NAME_PENDING;
+ name_status_t name_status;
int id;
baswap(&tmp, local); local_addr = batostr(&tmp);
@@ -1049,18 +1166,32 @@ void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, i
write_remote_class(local, peer, class);
- /* send the device found signal */
- signal_device = dbus_message_new_signal(path, ADAPTER_INTERFACE,
- "RemoteDeviceFound");
- if (signal_device == NULL) {
- error("Can't allocate D-Bus message");
- goto failed;
+ /*
+ * workaround to identify situation when the daemon started and
+ * a standard inquiry or periodic inquiry was already running
+ */
+ if (!pdata->inq_active && !pdata->pinq_active) {
+ pdata->discover_type |= (PERIODIC_INQUIRY | RESOLVE_NAME);
+ pdata->pinq_active = 1;
}
- dbus_message_iter_init_append(signal_device, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &peer_addr);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &tmp_class);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT16, &tmp_rssi);
+ /* reset the idle flag when the inquiry complete event arrives */
+ if (pdata->pinq_active)
+ pdata->pinq_idle = 0;
+
+ /* the inquiry result can be triggered by NON D-Bus client */
+ if (pdata->discover_type & RESOLVE_NAME)
+ name_status = NAME_REQUIRED;
+ else
+ name_status = NAME_NOT_REQUIRED;
+
+
+ /* send the device found signal */
+ signal_device = dev_signal_factory(pdata->dev_id, "RemoteDeviceFound",
+ DBUS_TYPE_STRING, &peer_addr,
+ DBUS_TYPE_UINT32, &tmp_class,
+ DBUS_TYPE_INT16, &tmp_rssi,
+ DBUS_TYPE_INVALID);
send_reply_and_unref(connection, signal_device);
@@ -1084,9 +1215,8 @@ void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, i
free(name);
name_status = NAME_SENT;
}
-
/* add in the list to track name sent/pending */
- disc_device_append(&pdata->disc_devices, peer, name_status, pdata->discover_type);
+ disc_device_append(&pdata->disc_devices, peer, name_status);
failed:
bt_free(local_addr);
@@ -1154,11 +1284,11 @@ void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char
snprintf(path, sizeof(path), "%s/hci%d", BASE_PATH, id);
- /* remove from remote name request list */
- if (dbus_connection_get_object_path_data(connection, path, (void *) &pdata))
- disc_device_remove(&pdata->disc_devices, peer);
+ if (!dbus_connection_get_object_path_data(connection, path, (void *) &pdata)) {
+ error("Getting %s path data failed!", path);
+ goto done;
+ }
- /* if the requested name failed, don't send signal and request the next name */
if (status)
message = dev_signal_factory(pdata->dev_id, "RemoteNameFailed",
DBUS_TYPE_STRING, &peer_addr,
@@ -1171,6 +1301,9 @@ void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char
send_reply_and_unref(connection, message);
+ /* remove from remote name request list */
+ disc_device_remove(&pdata->disc_devices, peer);
+
/* check if there is more devices to request names */
if (!disc_device_req_name(pdata))
goto done; /* skip if a new request has been sent */
@@ -1184,14 +1317,11 @@ void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char
* The discovery completed signal must be sent only for discover
* devices request WITH name resolving
*/
- if (pdata->discover_state == STATE_RESOLVING_NAMES) {
-
- if (pdata->discovery_requestor) {
- name_listener_remove(connection, pdata->discovery_requestor,
- (name_cb_t) discover_devices_req_exit, pdata);
- free(pdata->discovery_requestor);
- pdata->discovery_requestor = NULL;
- }
+ if (pdata->discovery_requestor) {
+ name_listener_remove(connection, pdata->discovery_requestor,
+ (name_cb_t) discover_devices_req_exit, pdata);
+ free(pdata->discovery_requestor);
+ pdata->discovery_requestor = NULL;
/* If there is a pending reply for discovery cancel */
if (pdata->discovery_cancel) {
@@ -1201,13 +1331,13 @@ void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char
pdata->discovery_cancel = NULL;
}
- message = dbus_message_new_signal(path, ADAPTER_INTERFACE,
- "DiscoveryCompleted");
- send_reply_and_unref(connection, message);
+ if (pdata->discover_type & RESOLVE_NAME) {
+ message = dbus_message_new_signal(path, ADAPTER_INTERFACE,
+ "DiscoveryCompleted");
+ send_reply_and_unref(connection, message);
+ }
}
-
- pdata->discover_state = STATE_IDLE;
-
+ pdata->inq_active = 0;
done:
bt_free(local_addr);
bt_free(peer_addr);
@@ -1974,7 +2104,7 @@ void create_bond_req_exit(const char *name, struct hci_dbus_data *pdata)
void discover_devices_req_exit(const char *name, struct hci_dbus_data *pdata)
{
- debug("DiscoverDevices requestor at %s exited before the operation finishes", name);
+ debug("DiscoverDevices requestor at %s exited before the operation finished", name);
/*
* Cleanup the discovered devices list and send the cmd to cancel inquiry
@@ -1989,8 +2119,8 @@ int cancel_discovery(struct hci_dbus_data *pdata)
struct slist *l;
struct hci_request rq;
remote_name_req_cancel_cp cp;
- int dd = -1, err = 0;
- uint8_t status = 0x00;
+ int dd, err = 0;
+ uint8_t status;
dd = hci_open_dev(pdata->dev_id);
if (dd < 0) {
@@ -2007,13 +2137,13 @@ int cancel_discovery(struct hci_dbus_data *pdata)
rq.rlen = sizeof(status);
rq.event = EVT_CMD_COMPLETE;
- switch (pdata->discover_state) {
- case STATE_RESOLVING_NAMES:
+ if (pdata->inq_active) {
+ rq.ocf = OCF_INQUIRY_CANCEL;
+ } else {
/* find the pending remote name request */
memset(&match, 0, sizeof(struct discovered_dev_info));
bacpy(&match.bdaddr, BDADDR_ANY);
match.name_status = NAME_REQUESTED;
- match.discover_type = RESOLVE_NAME;
l = slist_find(pdata->disc_devices, &match, (cmp_func_t) disc_device_find);
if (!l)
@@ -2026,13 +2156,6 @@ int cancel_discovery(struct hci_dbus_data *pdata)
rq.ocf = OCF_REMOTE_NAME_REQ_CANCEL;
rq.cparam = &cp;
rq.clen = REMOTE_NAME_REQ_CANCEL_CP_SIZE;
- break;
- case STATE_DISCOVER:
- rq.ocf = OCF_INQUIRY_CANCEL;
- break;
- default:
- /* discover is not pending */
- goto cleanup;
}
if (hci_send_req(dd, &rq, 100) < 0) {
@@ -2061,3 +2184,103 @@ cleanup:
return err;
}
+
+void periodic_discover_req_exit(const char *name, struct hci_dbus_data *pdata)
+{
+ debug("Periodic Discover requestor at %s exited before the operation finishes", name);
+
+ /*
+ * Cleanup the discovered devices list and send the cmd to exit from periodic inquiry
+ * or cancel remote name request. The return value can be ignored.
+ */
+ cancel_periodic_discovery(pdata);
+
+ if (pdata->pdiscovery_requestor) {
+ free(pdata->pdiscovery_requestor);
+ pdata->pdiscovery_requestor = NULL;
+ }
+
+ pdata->pinq_active = 0;
+}
+
+int cancel_periodic_discovery(struct hci_dbus_data *pdata)
+{
+ struct discovered_dev_info *dev, match;
+ struct slist *l;
+ struct hci_request rq;
+ remote_name_req_cancel_cp cp;
+ int dd, err = 0;
+ uint8_t status = 0x00;
+
+ dd = hci_open_dev(pdata->dev_id);
+ if (dd < 0) {
+ err = -ENODEV;
+ goto cleanup;
+ }
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_EXIT_PERIODIC_INQUIRY;
+ rq.cparam = 0;
+ rq.clen = 0;
+ rq.rparam = &status;
+ rq.rlen = sizeof(status);
+ rq.event = EVT_CMD_COMPLETE;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ error("Sending command failed: %s (%d)", strerror(errno), errno);
+ err = -errno;
+ goto cleanup;
+ }
+
+ if (status) {
+ error("exit periodic inquiry failed with status 0x%02x", status);
+ err = -bt_error(status);
+ goto cleanup;
+ }
+
+ /* find the pending remote name request */
+ memset(&match, 0, sizeof(struct discovered_dev_info));
+ bacpy(&match.bdaddr, BDADDR_ANY);
+ match.name_status = NAME_REQUESTED;
+
+ l = slist_find(pdata->disc_devices, &match, (cmp_func_t) disc_device_find);
+ if (!l)
+ goto cleanup; /* no request pending */
+
+ dev = l->data;
+
+ bacpy(&cp.bdaddr, &dev->bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ memset(&cp, 0, sizeof(cp));
+
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_REMOTE_NAME_REQ_CANCEL;
+ rq.cparam = &cp;
+ rq.clen = REMOTE_NAME_REQ_CANCEL_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = sizeof(status);
+ rq.event = EVT_CMD_COMPLETE;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ error("Sending command failed: %s (%d)", strerror(errno), errno);
+ err = -errno;
+ goto cleanup;
+ }
+
+ if (status) {
+ error("Remote name cancel failed with status 0x%02x", status);
+ err = -bt_error(status);
+ goto cleanup;
+ }
+
+cleanup:
+ slist_foreach(pdata->disc_devices, (slist_func_t) free, NULL);
+ slist_free(pdata->disc_devices);
+ pdata->disc_devices = NULL;
+
+ hci_close_dev(dd);
+
+ return err;
+}
diff --git a/hcid/dbus.h b/hcid/dbus.h
index ebd634a2..02ebf6b1 100644
--- a/hcid/dbus.h
+++ b/hcid/dbus.h
@@ -61,27 +61,25 @@ struct service_data {
service_handler_func_t handler_func;
};
-typedef enum {
- STATE_IDLE,
- STATE_DISCOVER,
- STATE_RESOLVING_NAMES
-} discover_state_t;
+/* Discover types */
+#define DISCOVER_TYPE_NONE 0x00
+#define STD_INQUIRY 0x01
+#define PERIODIC_INQUIRY 0x02
-/* discover type */
-#define WITHOUT_NAME_RESOLVING 1 /* D-Bus and non D-Bus request */
-#define RESOLVE_NAME 2
+/* Actions executed after inquiry complete */
+#define RESOLVE_NAME 0x10
typedef enum {
NAME_ANY,
- NAME_PENDING, /* remote name needs be resolved */
- NAME_REQUESTED, /* HCI remote name request was sent */
- NAME_SENT /* D-Bus signal RemoteNameUpdated sent */
+ NAME_NOT_REQUIRED, /* used by get remote name without name resolving */
+ NAME_REQUIRED, /* remote name needs be resolved */
+ NAME_REQUESTED, /* HCI remote name request was sent */
+ NAME_SENT /* D-Bus signal RemoteNameUpdated sent */
} name_status_t;
struct discovered_dev_info {
bdaddr_t bdaddr;
name_status_t name_status;
- int discover_type;
};
struct bonding_request_info {
@@ -110,9 +108,12 @@ struct hci_dbus_data {
uint32_t timeout_id; /* discoverable timeout id */
uint32_t discoverable_timeout; /* discoverable time(msec) */
uint8_t mode; /* scan mode */
- discover_state_t discover_state; /* discover states */
- int discover_type; /* with/without name resolving */
+ int inq_active; /* standard inquiry active: includes name resolution step */
+ int pinq_active; /* periodic inquiry active */
+ int pinq_idle; /* tracks the idle time for periodic inquiry */
+ int discover_type; /* type requested */
struct slist *disc_devices;
+ char *pdiscovery_requestor; /* periodic discovery requestor unique name */
char *discovery_requestor; /* discovery requestor unique name */
DBusMessage *discovery_cancel; /* discovery cancel message request */
struct slist *passkey_agents;
@@ -209,6 +210,8 @@ service_handler_func_t find_service_handler(struct service_data *services, DBusM
void create_bond_req_exit(const char *name, struct hci_dbus_data *pdata);
void discover_devices_req_exit(const char *name, struct hci_dbus_data *pdata);
int cancel_discovery(struct hci_dbus_data *pdata);
+void periodic_discover_req_exit(const char *name, struct hci_dbus_data *pdata);
+int cancel_periodic_discovery(struct hci_dbus_data *pdata);
int handle_passkey_request(DBusConnection *conn, int dev, const char *path, bdaddr_t *sba, bdaddr_t *dba);
void release_default_agent(void);
@@ -229,7 +232,8 @@ static inline DBusHandlerResult send_reply_and_unref(DBusConnection *conn, DBusM
int active_conn_find_by_bdaddr(const void *data, const void *user_data);
void bonding_request_free(struct bonding_request_info *dev);
int pin_req_cmp(const void *p1, const void *p2);
-int disc_device_append(struct slist **list, bdaddr_t *bdaddr, name_status_t name_status, int discover_type);
+int disc_device_find(const struct discovered_dev_info *d1, const struct discovered_dev_info *d2);
+int disc_device_append(struct slist **list, bdaddr_t *bdaddr, name_status_t name_status);
int disc_device_req_name(struct hci_dbus_data *dbus_data);
int discoverable_timeout_handler(void *data);
diff --git a/hcid/hcid.h b/hcid/hcid.h
index 48041166..271677f6 100644
--- a/hcid/hcid.h
+++ b/hcid/hcid.h
@@ -153,6 +153,8 @@ int hcid_dbus_request_pin(int dev, bdaddr_t *sba, struct hci_conn_info *ci);
void hcid_dbus_inquiry_start(bdaddr_t *local);
void hcid_dbus_inquiry_complete(bdaddr_t *local);
+void hcid_dbus_periodic_inquiry_start(bdaddr_t *local, uint8_t status);
+void hcid_dbus_periodic_inquiry_exit(bdaddr_t *local, uint8_t status);
void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, int8_t rssi);
void hcid_dbus_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class);
void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char *name);
diff --git a/hcid/security.c b/hcid/security.c
index cd78dd05..7f1ad703 100644
--- a/hcid/security.c
+++ b/hcid/security.c
@@ -427,7 +427,17 @@ static inline void cmd_status(int dev, bdaddr_t *sba, void *ptr)
static inline void cmd_complete(int dev, bdaddr_t *sba, void *ptr)
{
evt_cmd_complete *evt = ptr;
+ uint8_t status;
+
switch (evt->opcode) {
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY):
+ status = *((uint8_t *) ptr + sizeof(evt_cmd_complete));
+ hcid_dbus_periodic_inquiry_start(sba, status);
+ break;
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY):
+ status = *((uint8_t *) ptr + sizeof(evt_cmd_complete));
+ hcid_dbus_periodic_inquiry_exit(sba, status);
+ break;
case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL):
hcid_dbus_inquiry_complete(sba);
break;
@@ -441,7 +451,6 @@ static inline void cmd_complete(int dev, bdaddr_t *sba, void *ptr)
case cmd_opcode_pack(OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY):
hcid_dbus_pin_code_reply(sba, ptr);
break;
-
};
}