diff options
| -rw-r--r-- | hcid/dbus-adapter.c | 200 | ||||
| -rw-r--r-- | hcid/dbus-api.txt | 20 | ||||
| -rw-r--r-- | hcid/dbus.c | 395 | ||||
| -rw-r--r-- | hcid/dbus.h | 34 | ||||
| -rw-r--r-- | hcid/hcid.h | 2 | ||||
| -rw-r--r-- | 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; -  	};  }  | 
