From ef6a5df5d623b88685dfee801b39d19333e7a206 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 19 Sep 2006 08:29:22 +0000 Subject: Add periodic discovery support to the D-Bus API --- hcid/dbus-adapter.c | 200 +++++++++++++++++++++++--- hcid/dbus-api.txt | 20 +++ hcid/dbus.c | 395 ++++++++++++++++++++++++++++++++++++++++------------ hcid/dbus.h | 34 +++-- hcid/hcid.h | 2 + hcid/security.c | 11 +- 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; - }; } -- cgit