diff options
| author | Marcel Holtmann <marcel@holtmann.org> | 2006-02-27 10:18:05 +0000 | 
|---|---|---|
| committer | Marcel Holtmann <marcel@holtmann.org> | 2006-02-27 10:18:05 +0000 | 
| commit | d7dd0c224080fc072fff6f37fe7cfe727a43245e (patch) | |
| tree | dd28df7ebc6ae2f64e2ee22e2588dfcb0f5bac4e /hcid/dbus-adapter.c | |
| parent | b7d94f7d97e66877db9778b05faa205831e8e0d4 (diff) | |
Move all local device related functions into dbus-adapter.c
Diffstat (limited to 'hcid/dbus-adapter.c')
| -rw-r--r-- | hcid/dbus-adapter.c | 1342 | 
1 files changed, 1342 insertions, 0 deletions
| diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c new file mode 100644 index 00000000..050aab69 --- /dev/null +++ b/hcid/dbus-adapter.c @@ -0,0 +1,1342 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2004-2006  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> + +#include <dbus/dbus.h> + +#include "hcid.h" +#include "dbus.h" + +#include "textfile.h" +#include "oui.h" + +static const char *service_cls[] = { +	"positioning", +	"networking", +	"rendering", +	"capturing", +	"object transfer", +	"audio", +	"telephony", +	"information" +}; + +static const char *computer_minor_cls[] = { +	"uncategorized", +	"desktop", +	"server", +	"laptop", +	"handheld", +	"palm", +	"wearable" +}; + +static char *get_peer_name(const bdaddr_t *local, const bdaddr_t *peer) +{ +	char filename[PATH_MAX + 1], addr[18]; + +	ba2str(local, addr); +	snprintf(filename, PATH_MAX, "%s/%s/names", STORAGEDIR, addr); + +	ba2str(peer, addr); +	return textfile_get(filename, addr); +} + +static DBusMessage *handle_dev_get_address_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	char str[18], *str_ptr = str; +	int err; + +	err = get_device_address(dbus_data->dev_id, str, sizeof(str)); +	if (err < 0) +		return error_generic(msg, -err); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_get_version_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	char str[20], *str_ptr = str; +	int err; + +	err = get_device_version(dbus_data->dev_id, str, sizeof(str)); +	if (err < 0) +		return error_generic(msg, -err); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_get_revision_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	char str[64], *str_ptr = str; +	int err; + +	err = get_device_revision(dbus_data->dev_id, str, sizeof(str)); +	if (err < 0) +		return error_generic(msg, -err); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_get_manufacturer_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	char str[64], *str_ptr = str; +	int err; + +	err = get_device_manufacturer(dbus_data->dev_id, str, sizeof(str)); +	if (err < 0) +		return error_generic(msg, -err); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_get_company_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	char str[64], *str_ptr = str; +	int err; + +	err = get_device_company(dbus_data->dev_id, str, sizeof(str)); +	if (err < 0) +		return error_generic(msg, -err); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_get_features_req(DBusMessage *msg, void *data) +{ +	DBusMessage *reply; +	DBusMessageIter iter; +	DBusMessageIter array_iter; + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_iter_init_append(reply, &iter); + +	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, +				DBUS_TYPE_STRING_AS_STRING, &array_iter); + +	dbus_message_iter_close_container(&iter, &array_iter); + +	return reply; +} + +static DBusMessage *handle_dev_get_mode_req(DBusMessage *msg, void *data) +{ +	const struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply = NULL; +	const uint8_t hci_mode = dbus_data->mode; +	const char *scan_mode; + +	switch (hci_mode) { +	case SCAN_DISABLED: +		scan_mode = MODE_OFF; +		break; +	case SCAN_PAGE: +		scan_mode = MODE_CONNECTABLE; +		break; +	case (SCAN_PAGE | SCAN_INQUIRY): +		scan_mode = MODE_DISCOVERABLE; +		break; +	case SCAN_INQUIRY: +	/* inquiry scan mode is not handled, return unknown */ +	default: +		/* reserved */ +		scan_mode = MODE_UNKNOWN; +	} + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &scan_mode, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_set_mode_req(DBusMessage *msg, void *data) +{ +	const struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	const char* scan_mode; +	uint8_t hci_mode; +	const uint8_t current_mode = dbus_data->mode; +	int dd; + +	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &scan_mode, +							DBUS_TYPE_INVALID); + +	if (!scan_mode) +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_WRONG_PARAM); + +	if (strcasecmp(MODE_OFF, scan_mode) == 0) +		hci_mode = SCAN_DISABLED; +	else if (strcasecmp(MODE_CONNECTABLE, scan_mode) == 0) +		hci_mode = SCAN_PAGE; +	else if (strcasecmp(MODE_DISCOVERABLE, scan_mode) == 0) +		hci_mode = (SCAN_PAGE | SCAN_INQUIRY); +	else +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_WRONG_PARAM); + +	dd = hci_open_dev(dbus_data->dev_id); +	if (dd < 0) +		return error_no_such_device(msg); + +	/* Check if the new requested mode is different from the current */ +	if (current_mode != hci_mode) { +		struct hci_request rq; +		uint8_t status = 0; + +		memset(&rq, 0, sizeof(rq)); +		rq.ogf    = OGF_HOST_CTL; +		rq.ocf    = OCF_WRITE_SCAN_ENABLE; +		rq.cparam = &hci_mode; +		rq.clen   = sizeof(hci_mode); +		rq.rparam = &status; +		rq.rlen   = sizeof(status); + +		if (hci_send_req(dd, &rq, 100) < 0) { +			syslog(LOG_ERR, "Sending write scan enable command failed: %s (%d)", +							strerror(errno), errno); +			hci_close_dev(dd); +			return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | errno); +		} + +		if (status) { +			syslog(LOG_ERR, "Setting scan enable failed with status 0x%02x", status); +			hci_close_dev(dd); +			return bluez_new_failure_msg(msg, BLUEZ_EBT_OFFSET | status); +		} +	} + +	hci_close_dev(dd); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	return reply; +} + +static DBusMessage *handle_dev_get_discoverable_to_req(DBusMessage *msg, void *data) +{ +	const struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_UINT32, &dbus_data->discoverable_timeout, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_set_discoverable_to_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	DBusMessageIter iter; +	uint32_t timeout; + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &timeout); + +	dbus_data->discoverable_timeout = timeout; + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	return reply; +} + +static DBusMessage *handle_dev_is_connectable_req(DBusMessage *msg, void *data) +{ +	const struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	const uint8_t hci_mode = dbus_data->mode; +	dbus_bool_t connectable = FALSE; + +	if (hci_mode & SCAN_PAGE) +		connectable = TRUE; + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connectable, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_is_discoverable_req(DBusMessage *msg, void *data) +{ +	const struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	const uint8_t hci_mode = dbus_data->mode; +	dbus_bool_t discoverable = FALSE; + +	if (hci_mode & SCAN_INQUIRY) +		discoverable = TRUE; + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &discoverable, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_get_major_class_req(DBusMessage *msg, void *data) +{ +	DBusMessage *reply; +	const char *str_ptr = "computer"; + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	/*FIXME: Check the real device major class */ +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_get_minor_class_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	const char *str_ptr = ""; +	uint8_t cls[3]; +	uint8_t minor_class; +	int dd; + +	dd = hci_open_dev(dbus_data->dev_id); +	if (dd < 0) +		return error_no_such_device(msg); + +	if (hci_read_class_of_dev(dd, cls, 1000) < 0) { +		syslog(LOG_ERR, "Can't read class of device on hci%d: %s(%d)", +				dbus_data->dev_id, strerror(errno), errno); +		hci_close_dev(dd); +		return error_generic(msg, -errno); +	} + +	hci_close_dev(dd); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	/* FIXME: Currently, only computer major class is supported */ +	if ((cls[1] & 0x1f) != 1) +		goto failed; + +	minor_class = cls[0] >> 2; + +	/* Validate computer minor class */ +	if (minor_class > (sizeof(computer_minor_cls) / sizeof(*computer_minor_cls))) +		goto failed; + +	str_ptr = computer_minor_cls[minor_class]; + +failed: +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_set_minor_class_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusConnection *connection = get_dbus_connection(); +	DBusMessageIter iter; +	DBusMessage *reply, *signal; +	bdaddr_t bdaddr; +	const char *minor; +	uint8_t cls[3]; +	uint32_t dev_class = 0xFFFFFFFF; +	int i, dd; + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &minor); + +	if (!minor) +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_WRONG_PARAM); + +	/* FIXME: currently, only computer minor classes are allowed */ +	for (i = 0; i < sizeof(computer_minor_cls) / sizeof(*computer_minor_cls); i++) +		if (!strcasecmp(minor, computer_minor_cls[i])) { +			/* Remove the format type */ +			dev_class = i << 2; +			break; +		} + +	/* Check if it's a valid minor class */ +	if (dev_class == 0xFFFFFFFF) +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_WRONG_PARAM); + +	dd = hci_open_dev(dbus_data->dev_id); +	if (dd < 0) { +		syslog(LOG_ERR, "HCI device open failed: hci%d", dbus_data->dev_id); +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_ENODEV); +	} + +	if (hci_read_class_of_dev(dd, cls, 1000) < 0) { +		syslog(LOG_ERR, "Can't read class of device on hci%d: %s(%d)", +				dbus_data->dev_id, strerror(errno), errno); +		reply = bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | errno); +		goto failed; +	} + +	dev_class |= (cls[2] << 16) | (cls[1] << 8); + +	cls[2] = 0x00;	/* no service classes */ +	cls[1] = 0x01;	/* major class computer */ +	cls[0] = (dev_class & 0xff); + +	hci_devba(dbus_data->dev_id, &bdaddr); + +	write_local_class(&bdaddr, cls); + +	if (hci_write_class_of_dev(dd, dev_class, 2000) < 0) { +		syslog(LOG_ERR, "Can't write class of device on hci%d: %s(%d)", +				dbus_data->dev_id, strerror(errno), errno); +		reply = bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | errno); +		goto failed; +	} + +	signal = dev_signal_factory(dbus_data->dev_id, DEV_SIG_MINOR_CLASS_CHANGED, +						DBUS_TYPE_STRING, &minor, +						DBUS_TYPE_INVALID); +	if (signal) { +		dbus_connection_send(connection, signal, NULL); +		dbus_connection_flush(connection); +		dbus_message_unref(signal); +	} + +	reply = dbus_message_new_method_return(msg); + +failed: +	hci_close_dev(dd); + +	return reply; +} + +static DBusMessage *handle_dev_get_service_classes_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	DBusMessageIter iter; +	DBusMessageIter array_iter; +	const char *str_ptr; +	uint8_t cls[3]; +	int dd, i; + +	dd = hci_open_dev(dbus_data->dev_id); +	if (dd < 0) { +		syslog(LOG_ERR, "HCI device open failed: hci%d", dbus_data->dev_id); +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_ENODEV); +	} + +	if (hci_read_class_of_dev(dd, cls, 1000) < 0) { +		syslog(LOG_ERR, "Can't read class of device on hci%d: %s(%d)", +				dbus_data->dev_id, strerror(errno), errno); +		hci_close_dev(dd); +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | errno); +	} + +	reply = dbus_message_new_method_return(msg); + +	dbus_message_iter_init_append(reply, &iter); + +	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, +				DBUS_TYPE_STRING_AS_STRING, &array_iter); + +	for (i = 0; i < (sizeof(service_cls) / sizeof(*service_cls)); i++) { +		if (cls[2] & (1 << i)) { +			str_ptr = service_cls[i]; +			dbus_message_iter_append_basic(&array_iter, +						DBUS_TYPE_STRING, &str_ptr); +		} +	} + +	dbus_message_iter_close_container(&iter, &array_iter); + +	hci_close_dev(dd); + +	return reply; +} + +static DBusMessage *handle_dev_get_name_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	char str[249], *str_ptr = str; +	int err; + +	err = get_device_name(dbus_data->dev_id, str, sizeof(str)); +	if (err < 0) +		return error_generic(msg, -err); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_set_name_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessage *reply; +	bdaddr_t bdaddr; +	char *str_ptr; +	int err; + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &str_ptr); + +	if (strlen(str_ptr) == 0) { +		syslog(LOG_ERR, "Name change failed: Invalid parameter"); +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_WRONG_PARAM); +	} + +	hci_devba(dbus_data->dev_id, &bdaddr); + +	write_local_name(&bdaddr, str_ptr); + +	err = set_device_name(dbus_data->dev_id, str_ptr); +	if (err < 0) +		return error_generic(msg, -err); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	return reply; +} + +static DBusMessage *handle_dev_get_remote_version_req(DBusMessage *msg, void *data) +{ +	/*FIXME: */ +	return bluez_new_failure_msg(msg, BLUEZ_EDBUS_NOT_IMPLEMENTED); +} + +static DBusMessage *handle_dev_get_remote_revision_req(DBusMessage *msg, void *data) +{ +	/*FIXME: */ +	return bluez_new_failure_msg(msg, BLUEZ_EDBUS_NOT_IMPLEMENTED); +} + +static DBusMessage *handle_dev_get_remote_manufacturer_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessage *reply; +	char filename[PATH_MAX + 1]; +	char addr[18], *addr_ptr, *str; +	int compid; + +	get_device_address(dbus_data->dev_id, addr, sizeof(addr)); + +	snprintf(filename, PATH_MAX, "%s/%s/manufacturers", STORAGEDIR, addr); + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); + +	str = textfile_get(filename, addr_ptr); +	if (!str) +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | ENXIO); + +	compid = atoi(str); + +	free(str); + +	str = bt_compidtostr(compid); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_get_remote_company_req(DBusMessage *msg, void *data) +{ +	DBusMessage *reply; +	bdaddr_t bdaddr; +	char oui[9], *str_bdaddr, *tmp; + +	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str_bdaddr, +							DBUS_TYPE_INVALID); + + +	str2ba(str_bdaddr, &bdaddr); +	ba2oui(&bdaddr, oui); + +	tmp = ouitocomp(oui); +	if (!tmp) +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_RECORD_NOT_FOUND); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) { +		reply = error_out_of_memory(msg); +		goto done; +	} + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &tmp, +					DBUS_TYPE_INVALID); + +done: +	free(tmp); + +	return reply; +} + +static DBusMessage *handle_dev_get_remote_name_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply = NULL; +	const char *str_bdaddr; +	char *name; +	bdaddr_t bdaddr; +	struct hci_dev_info di; + +	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str_bdaddr, +							DBUS_TYPE_INVALID); + +	str2ba(str_bdaddr, &bdaddr); + +	if (hci_devinfo(dbus_data->dev_id, &di) < 0) { +		syslog(LOG_ERR, "Can't get device info"); +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_ENODEV); +	} + +	name = get_peer_name(&di.bdaddr, &bdaddr); +	if (!name) +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_RECORD_NOT_FOUND); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) { +		reply = error_out_of_memory(msg); +		goto done; +	} + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, +					DBUS_TYPE_INVALID); + +done: +	free(name); + +	return reply; +} + +static DBusMessage *handle_dev_get_remote_alias_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessage *reply; +	char str[249], *str_ptr = str, *addr_ptr; +	bdaddr_t bdaddr; +	int err; + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); + +	str2ba(addr_ptr, &bdaddr); + +	err = get_device_alias(dbus_data->dev_id, &bdaddr, str, sizeof(str)); +	if (err < 0) +		return error_generic(msg, -err); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_set_remote_alias_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusConnection *connection = get_dbus_connection(); +	DBusMessageIter iter; +	DBusMessage *reply, *signal; +	char *str_ptr, *addr_ptr; +	bdaddr_t bdaddr; +	int err; + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); +	dbus_message_iter_next(&iter); +	dbus_message_iter_get_basic(&iter, &str_ptr); + +	if (strlen(str_ptr) == 0) { +		syslog(LOG_ERR, "Alias change failed: Invalid parameter"); +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_WRONG_PARAM); +	} + +	str2ba(addr_ptr, &bdaddr); + +	err = set_device_alias(dbus_data->dev_id, &bdaddr, str_ptr); +	if (err < 0) +		return error_generic(msg, -err); + +	signal = dev_signal_factory(dbus_data->dev_id, DEV_SIG_REMOTE_ALIAS_CHANGED, +						DBUS_TYPE_STRING, &addr_ptr, +						DBUS_TYPE_STRING, &str_ptr, +						DBUS_TYPE_INVALID); +	if (signal) { +		dbus_connection_send(connection, signal, NULL); +		dbus_connection_flush(connection); +		dbus_message_unref(signal); +	} + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return error_out_of_memory(msg); + +	return reply; +} + +static DBusMessage *handle_dev_last_seen_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessage *reply; +	char filename[PATH_MAX + 1]; +	char addr[18], *addr_ptr, *str; + +	get_device_address(dbus_data->dev_id, addr, sizeof(addr)); + +	snprintf(filename, PATH_MAX, "%s/%s/lastseen", STORAGEDIR, addr); + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); + +	str = textfile_get(filename, addr_ptr); +	if (!str) +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | ENXIO); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) { +		reply = error_out_of_memory(msg); +		goto done; +	} + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str, +					DBUS_TYPE_INVALID); + +done: +	free(str); + +	return reply; +} + +static DBusMessage *handle_dev_last_used_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessage *reply; +	char filename[PATH_MAX + 1]; +	char addr[18], *addr_ptr, *str; + +	get_device_address(dbus_data->dev_id, addr, sizeof(addr)); + +	snprintf(filename, PATH_MAX, "%s/%s/lastused", STORAGEDIR, addr); + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); + +	str = textfile_get(filename, addr_ptr); +	if (!str) +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | ENXIO); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) { +		reply = error_out_of_memory(msg); +		goto done; +	} + +	dbus_message_append_args(reply, DBUS_TYPE_STRING, &str, +					DBUS_TYPE_INVALID); + +done: +	free(str); + +	return reply; +} + +static DBusMessage *handle_dev_create_bonding_req(DBusMessage *msg, void *data) +{ +	struct hci_request rq; +	auth_requested_cp cp; +	evt_cmd_status rp; +	DBusMessage *reply; +	char *str_bdaddr; +	struct hci_dbus_data *dbus_data = data; +	struct hci_conn_info_req *cr; +	bdaddr_t bdaddr; +	int dd, dev_id; + +	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str_bdaddr, +							DBUS_TYPE_INVALID); + +	str2ba(str_bdaddr, &bdaddr); + +	dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr); + +	if (dev_id < 0) +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_CONN_NOT_FOUND); + +	if (dbus_data->dev_id != dev_id) +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_CONN_NOT_FOUND); + +	dd = hci_open_dev(dev_id); +	if (dd < 0) +		return error_no_such_device(msg); + +	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); +	if (!cr) { +		reply = bluez_new_failure_msg(msg, BLUEZ_EDBUS_NO_MEM); +		goto failed; +	} + +	bacpy(&cr->bdaddr, &bdaddr); +	cr->type = ACL_LINK; + +	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { +		reply = bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET + errno); +		goto done; +	} + +	memset(&cp, 0, sizeof(cp)); +	cp.handle = cr->conn_info->handle; + +	memset(&rq, 0, sizeof(rq)); +	rq.ogf    = OGF_LINK_CTL; +	rq.ocf    = OCF_AUTH_REQUESTED; +	rq.cparam = &cp; +	rq.clen   = AUTH_REQUESTED_CP_SIZE; +	rq.rparam = &rp; +	rq.rlen   = EVT_CMD_STATUS_SIZE; +	rq.event  = EVT_CMD_STATUS; + +	if (hci_send_req(dd, &rq, 100) < 0) { +		syslog(LOG_ERR, "Unable to send authentication request: %s (%d)", +							strerror(errno), errno); +		reply = bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET + errno); +		goto done; +	} + +	reply = dbus_message_new_method_return(msg); + +done: +	free(cr); + +failed: +	close(dd); + +	return reply; +} + +static DBusMessage *handle_dev_remove_bonding_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusConnection *connection = get_dbus_connection(); +	DBusMessageIter iter; +	DBusMessage *reply; +	DBusMessage *signal; +	char filename[PATH_MAX + 1]; +	char addr[18], *addr_ptr; +	struct hci_conn_info_req *cr; +	bdaddr_t bdaddr; +	int dd; + +	dd = hci_open_dev(dbus_data->dev_id); +	if (dd < 0) +		return error_no_such_device(msg); + +	get_device_address(dbus_data->dev_id, addr, sizeof(addr)); + +	snprintf(filename, PATH_MAX, "%s/%s/linkkeys", STORAGEDIR, addr); + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); + +	/* Delete the link key from storage */ +	textfile_del(filename, addr_ptr); + +	str2ba(addr_ptr, &bdaddr); + +	/* Delete the link key from the Bluetooth chip */ +	hci_delete_stored_link_key(dd, &bdaddr, 0, 1000); + +	/* Close active connections for the remote device */ +	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); +	if (!cr) { +		syslog(LOG_ERR, "Can't allocate memory"); +		hci_close_dev(dd); +		return bluez_new_failure_msg(msg, BLUEZ_EDBUS_NO_MEM); +	} + +	bacpy(&cr->bdaddr, &bdaddr); +	cr->type = ACL_LINK; +	if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { +		/* Ignore when there isn't active connections, return success */ +		reply = dbus_message_new_method_return(msg); +		goto failed; +	} + +	/* Send the HCI disconnect command */ +	if (hci_disconnect(dd, htobs(cr->conn_info->handle), HCI_OE_USER_ENDED_CONNECTION, 1000) < 0) { +		syslog(LOG_ERR, "Disconnect failed"); +		reply = bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | errno); +		goto failed; +	} + +	/* FIXME: which condition must be verified before send the signal */ +	signal = dev_signal_factory(dbus_data->dev_id, +					DEV_SIG_BONDING_REMOVED, +					DBUS_TYPE_STRING, &addr_ptr, +					DBUS_TYPE_INVALID); +	if (signal) { +		dbus_connection_send(connection, signal, NULL); +		dbus_connection_flush(connection); +		dbus_message_unref(signal); +	} + +	reply = dbus_message_new_method_return(msg); + +failed: +	free(cr); + +	close(dd); + +	return reply; +} + +static DBusMessage *handle_dev_has_bonding_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessage *reply; +	char filename[PATH_MAX + 1]; +	char addr[18], *addr_ptr, *str; +	dbus_bool_t result; + +	get_device_address(dbus_data->dev_id, addr, sizeof(addr)); + +	snprintf(filename, PATH_MAX, "%s/%s/linkkeys", STORAGEDIR, addr); + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); + +	str = textfile_get(filename, addr_ptr); +	if (str) { +		result = TRUE; +		free(str); +	} else +		result = FALSE; + +	reply = dbus_message_new_method_return(msg); + +	dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &result, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_list_bondings_req(DBusMessage *msg, void *data) +{ +	void do_append(char *key, char *value, void *data) +	{ +		DBusMessageIter *iter = data; + +		dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); +	} + +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessageIter array_iter; +	DBusMessage *reply; +	char filename[PATH_MAX + 1]; +	char addr[18]; + +	get_device_address(dbus_data->dev_id, addr, sizeof(addr)); + +	snprintf(filename, PATH_MAX, "%s/%s/linkkeys", STORAGEDIR, addr); + +	reply = dbus_message_new_method_return(msg); + +	dbus_message_iter_init_append(reply, &iter); + +	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, +				DBUS_TYPE_STRING_AS_STRING, &array_iter); + +	textfile_foreach(filename, do_append, &array_iter); + +	dbus_message_iter_close_container(&iter, &array_iter); + +	return reply; +} + +static DBusMessage *handle_dev_pin_code_length_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessage *reply; +	bdaddr_t local, peer; +	char addr[18], *addr_ptr; +	uint8_t length; +	int len; + +	get_device_address(dbus_data->dev_id, addr, sizeof(addr)); + +	str2ba(addr, &local); + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); + +	str2ba(addr_ptr, &peer); + +	len = read_pin_length(&local, &peer); +	if (len < 0) +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | -len); + +	reply = dbus_message_new_method_return(msg); + +	length = len; + +	dbus_message_append_args(reply, DBUS_TYPE_BYTE, &length, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_encryption_key_size_req(DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	DBusMessageIter iter; +	DBusMessage *reply; +	bdaddr_t bdaddr; +	char *addr_ptr; +	uint8_t size; +	int val; + +	dbus_message_iter_init(msg, &iter); +	dbus_message_iter_get_basic(&iter, &addr_ptr); + +	str2ba(addr_ptr, &bdaddr); + +	val = get_encryption_key_size(dbus_data->dev_id, &bdaddr); +	if (val < 0) +		return bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET | -val); + +	reply = dbus_message_new_method_return(msg); + +	size = val; + +	dbus_message_append_args(reply, DBUS_TYPE_BYTE, &size, +					DBUS_TYPE_INVALID); + +	return reply; +} + +static DBusMessage *handle_dev_discover_req(DBusMessage *msg, void *data) +{ +	DBusMessage *reply = NULL; +	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; + +	dd = hci_open_dev(dbus_data->dev_id); +	if (dd < 0) +		return error_no_such_device(msg); + +	memset(&cp, 0, sizeof(cp)); +	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; + +	memset(&rq, 0, sizeof(rq)); +	rq.ogf    = OGF_LINK_CTL; +	rq.ocf    = OCF_INQUIRY; +	rq.cparam = &cp; +	rq.clen   = INQUIRY_CP_SIZE; +	rq.rparam = &rp; +	rq.rlen   = EVT_CMD_STATUS_SIZE; +	rq.event  = EVT_CMD_STATUS; + +	if (hci_send_req(dd, &rq, 100) < 0) { +		syslog(LOG_ERR, "Unable to start inquiry: %s (%d)", +							strerror(errno), errno); +		reply = bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET + errno); +		goto failed; +	} + +	reply = dbus_message_new_method_return(msg); + +failed: +	hci_close_dev(dd); + +	return reply; +} + +static DBusMessage *handle_dev_discover_cancel_req(DBusMessage *msg, void *data) +{ +	DBusMessage *reply = NULL; +	struct hci_request rq; +	struct hci_dbus_data *dbus_data = data; +	uint8_t status; +	int dd; + +	dd = hci_open_dev(dbus_data->dev_id); +	if (dd < 0) +		return error_no_such_device(msg); + +	memset(&rq, 0, sizeof(rq)); +	rq.ogf    = OGF_LINK_CTL; +	rq.ocf    = OCF_INQUIRY_CANCEL; +	rq.rparam = &status; +	rq.rlen   = sizeof(status); + +	if (hci_send_req(dd, &rq, 100) < 0) { +		syslog(LOG_ERR, "Sending cancel inquiry failed: %s (%d)", +							strerror(errno), errno); +		reply = bluez_new_failure_msg(msg, BLUEZ_ESYSTEM_OFFSET + errno); +		goto failed; +	} + +	if (status) { +		syslog(LOG_ERR, "Cancel inquiry failed with status 0x%02x", status); +		reply = bluez_new_failure_msg(msg, BLUEZ_EBT_OFFSET + status); +		goto failed; +	} + +	reply = dbus_message_new_method_return(msg); + +failed: +	hci_close_dev(dd); + +	return reply; +} + +static DBusMessage *handle_dev_discover_cache_req(DBusMessage *msg, void *data) +{ +	/*FIXME: */ +	return bluez_new_failure_msg(msg, BLUEZ_EDBUS_NOT_IMPLEMENTED); +} + +static DBusMessage *handle_dev_discover_service_req(DBusMessage *msg, void *data) +{ +	/*FIXME: */ +	return bluez_new_failure_msg(msg, BLUEZ_EDBUS_NOT_IMPLEMENTED); +} + +static const struct service_data dev_services[] = { +	{ DEV_GET_ADDRESS,		handle_dev_get_address_req,		DEV_GET_ADDRESS_SIGNATURE		}, +	{ DEV_GET_VERSION,		handle_dev_get_version_req,		DEV_GET_VERSION_SIGNATURE		}, +	{ DEV_GET_REVISION,		handle_dev_get_revision_req,		DEV_GET_REVISION_SIGNATURE		}, +	{ DEV_GET_MANUFACTURER,		handle_dev_get_manufacturer_req,	DEV_GET_MANUFACTURER_SIGNATURE		}, +	{ DEV_GET_COMPANY,		handle_dev_get_company_req,		DEV_GET_COMPANY_SIGNATURE		}, +	{ DEV_GET_FEATURES,		handle_dev_get_features_req,		DEV_GET_FEATURES_SIGNATURE		}, +	{ DEV_GET_MODE,			handle_dev_get_mode_req,		DEV_GET_MODE_SIGNATURE			}, +	{ DEV_SET_MODE,			handle_dev_set_mode_req,		DEV_SET_MODE_SIGNATURE			}, +	{ DEV_GET_DISCOVERABLE_TO,	handle_dev_get_discoverable_to_req,	DEV_GET_DISCOVERABLE_TO_SIGNATURE	}, +	{ DEV_SET_DISCOVERABLE_TO,	handle_dev_set_discoverable_to_req,	DEV_SET_DISCOVERABLE_TO_SIGNATURE	}, +	{ DEV_IS_CONNECTABLE,		handle_dev_is_connectable_req,		DEV_IS_CONNECTABLE_SIGNATURE		}, +	{ DEV_IS_DISCOVERABLE,		handle_dev_is_discoverable_req,		DEV_IS_DISCOVERABLE_SIGNATURE		}, +	{ DEV_GET_MAJOR_CLASS,		handle_dev_get_major_class_req,		DEV_GET_MAJOR_CLASS_SIGNATURE		}, +	{ DEV_GET_MINOR_CLASS,		handle_dev_get_minor_class_req,		DEV_GET_MINOR_CLASS_SIGNATURE		}, +	{ DEV_SET_MINOR_CLASS,		handle_dev_set_minor_class_req,		DEV_SET_MINOR_CLASS_SIGNATURE		}, +	{ DEV_GET_SERVICE_CLASSES,	handle_dev_get_service_classes_req,	DEV_GET_SERVICE_CLASSES_SIGNATURE	}, +	{ DEV_GET_NAME,			handle_dev_get_name_req,		DEV_GET_NAME_SIGNATURE			}, +	{ DEV_SET_NAME,			handle_dev_set_name_req,		DEV_SET_NAME_SIGNATURE			}, + +	{ DEV_GET_REMOTE_VERSION,	handle_dev_get_remote_version_req,	DEV_GET_REMOTE_VERSION_SIGNATURE	}, +	{ DEV_GET_REMOTE_REVISION,	handle_dev_get_remote_revision_req,	DEV_GET_REMOTE_REVISION_SIGNATURE	}, +	{ DEV_GET_REMOTE_MANUFACTURER,	handle_dev_get_remote_manufacturer_req,	DEV_GET_REMOTE_MANUFACTURER_SIGNATURE	}, +	{ DEV_GET_REMOTE_COMPANY,	handle_dev_get_remote_company_req,	DEV_GET_REMOTE_COMPANY_SIGNATURE	}, +	{ DEV_GET_REMOTE_NAME,		handle_dev_get_remote_name_req,		DEV_GET_REMOTE_NAME_SIGNATURE		}, +	{ DEV_GET_REMOTE_ALIAS,		handle_dev_get_remote_alias_req,	DEV_GET_REMOTE_ALIAS_SIGNATURE		}, +	{ DEV_SET_REMOTE_ALIAS,		handle_dev_set_remote_alias_req,	DEV_SET_REMOTE_ALIAS_SIGNATURE		}, + +	{ DEV_LAST_SEEN,		handle_dev_last_seen_req,		DEV_LAST_SEEN_SIGNATURE			}, +	{ DEV_LAST_USED,		handle_dev_last_used_req,		DEV_LAST_USED_SIGNATURE			}, + +	{ DEV_CREATE_BONDING,		handle_dev_create_bonding_req,		DEV_CREATE_BONDING_SIGNATURE		}, +	{ DEV_REMOVE_BONDING,		handle_dev_remove_bonding_req,		DEV_REMOVE_BONDING_SIGNATURE		}, +	{ DEV_HAS_BONDING_NAME,		handle_dev_has_bonding_req,		DEV_HAS_BONDING_SIGNATURE		}, +	{ DEV_LIST_BONDINGS,		handle_dev_list_bondings_req,		DEV_LIST_BONDINGS_SIGNATURE		}, +	{ DEV_PIN_CODE_LENGTH,		handle_dev_pin_code_length_req,		DEV_PIN_CODE_LENGTH_SIGNATURE		}, +	{ DEV_ENCRYPTION_KEY_SIZE,	handle_dev_encryption_key_size_req,	DEV_ENCRYPTION_KEY_SIZE_SIGNATURE	}, + +	{ DEV_DISCOVER,			handle_dev_discover_req,		DEV_DISCOVER_SIGNATURE			}, +	{ DEV_DISCOVER_CANCEL,		handle_dev_discover_cancel_req,		DEV_DISCOVER_CANCEL_SIGNATURE		}, +	{ DEV_DISCOVER_CACHE,		handle_dev_discover_cache_req,		DEV_DISCOVER_CACHE_SIGNATURE		}, +	{ DEV_DISCOVER_SERVICE,		handle_dev_discover_service_req,	DEV_DISCOVER_SERVICE_SIGNATURE		}, + +	{ NULL, NULL, NULL} +}; + +DBusHandlerResult msg_func_device(DBusConnection *conn, DBusMessage *msg, void *data) +{ +	const struct service_data *handlers = dev_services; +	DBusMessage *reply = NULL; +	struct hci_dbus_data *dbus_data = data; +	const char *method; +	const char *signature; +	const char *iface; +	uint32_t error = BLUEZ_EDBUS_UNKNOWN_METHOD; + +	method = dbus_message_get_member(msg); +	signature = dbus_message_get_signature(msg); +	iface = dbus_message_get_interface(msg); + +	syslog(LOG_INFO, "Adapter path:%s method:%s", +					dbus_message_get_path(msg), method); + +	if (strcmp(ADAPTER_INTERFACE, iface)) +		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +	if (dbus_data->path_id == ADAPTER_ROOT_ID) { +		/* Adapter is down(path unregistered) or the path is wrong */ +		error = BLUEZ_EDBUS_UNKNOWN_PATH; +		goto failed; +	} + +	/* It's a device path id */ +	for (; handlers->name != NULL; handlers++) { +		if (strcmp(handlers->name, method)) +			continue; + +		if (!strcmp(handlers->signature, signature)) { +			reply = handlers->handler_func(msg, data); +			error = 0; +			break; +		} else { +			/* Set the error, but continue looping incase there is +			 * another method with the same name but a different +			 * signature */ +			error = BLUEZ_EDBUS_WRONG_SIGNATURE; +			continue; +		} +	} + +failed: +	if (error) +		reply = bluez_new_failure_msg(msg, error); + +	if (reply) { +		if (!dbus_connection_send (conn, reply, NULL)) +			syslog(LOG_ERR, "Can't send reply message"); + +		dbus_message_unref(reply); +	} + +	return DBUS_HANDLER_RESULT_HANDLED; +} | 
