summaryrefslogtreecommitdiffstats
path: root/hcid/adapter.c
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2007-08-15 22:32:33 +0000
committerMarcel Holtmann <marcel@holtmann.org>2007-08-15 22:32:33 +0000
commit3b320a0355706a98f824f4d0abf2d14f820bbd81 (patch)
treea39f0747d87df78dc4d24b96292b6c22c0084ab1 /hcid/adapter.c
parentbe9288b048e92fd34978dd13ba13ab37d0f22fae (diff)
Rename files for adapter interface
Diffstat (limited to 'hcid/adapter.c')
-rw-r--r--hcid/adapter.c3367
1 files changed, 3367 insertions, 0 deletions
diff --git a/hcid/adapter.c b/hcid/adapter.c
new file mode 100644
index 00000000..17e63f99
--- /dev/null
+++ b/hcid/adapter.c
@@ -0,0 +1,3367 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2007 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "hcid.h"
+#include "dbus.h"
+
+#include "textfile.h"
+#include "oui.h"
+#include "adapter.h"
+#include "dbus-common.h"
+#include "dbus-helper.h"
+#include "dbus-hci.h"
+#include "dbus-sdp.h"
+#include "dbus-error.h"
+
+#define NUM_ELEMENTS(table) (sizeof(table)/sizeof(const char *))
+
+static const char *service_cls[] = {
+ "positioning",
+ "networking",
+ "rendering",
+ "capturing",
+ "object transfer",
+ "audio",
+ "telephony",
+ "information"
+};
+
+static const char *major_cls[] = {
+ "miscellaneous",
+ "computer",
+ "phone",
+ "access point",
+ "audio/video",
+ "peripheral",
+ "imaging",
+ "wearable",
+ "toy",
+ "uncategorized"
+};
+
+static const char *computer_minor_cls[] = {
+ "uncategorized",
+ "desktop",
+ "server",
+ "laptop",
+ "handheld",
+ "palm",
+ "wearable"
+};
+
+static const char *phone_minor_cls[] = {
+ "uncategorized",
+ "cellular",
+ "cordless",
+ "smart phone",
+ "modem",
+ "isdn"
+};
+
+static const char *access_point_minor_cls[] = {
+ "fully",
+ "1-17 percent",
+ "17-33 percent",
+ "33-50 percent",
+ "50-67 percent",
+ "67-83 percent",
+ "83-99 percent",
+ "not available"
+};
+
+static const char *audio_video_minor_cls[] = {
+ "uncategorized",
+ "headset",
+ "handsfree",
+ "unknown",
+ "microphone",
+ "loudspeaker",
+ "headphones",
+ "portable audio",
+ "car audio",
+ "set-top box",
+ "hifi audio",
+ "vcr",
+ "video camera",
+ "camcorder",
+ "video monitor",
+ "video display and loudspeaker",
+ "video conferencing",
+ "unknown",
+ "gaming/toy"
+};
+
+static const char *peripheral_minor_cls[] = {
+ "uncategorized",
+ "keyboard",
+ "pointing",
+ "combo"
+};
+
+#if 0
+static const char *peripheral_2_minor_cls[] = {
+ "uncategorized",
+ "joystick",
+ "gamepad",
+ "remote control",
+ "sensing",
+ "digitizer tablet",
+ "card reader"
+};
+#endif
+
+static const char *imaging_minor_cls[] = {
+ "display",
+ "camera",
+ "scanner",
+ "printer"
+};
+
+static const char *wearable_minor_cls[] = {
+ "wrist watch",
+ "pager",
+ "jacket",
+ "helmet",
+ "glasses"
+};
+
+static const char *toy_minor_cls[] = {
+ "robot",
+ "vehicle",
+ "doll",
+ "controller",
+ "game"
+};
+
+int pending_remote_name_cancel(struct adapter *adapter)
+{
+ struct remote_dev_info *dev, match;
+ GSList *l;
+ int dd, err = 0;
+
+ /* find the pending remote name request */
+ memset(&match, 0, sizeof(struct remote_dev_info));
+ bacpy(&match.bdaddr, BDADDR_ANY);
+ match.name_status = NAME_REQUESTED;
+
+ l = g_slist_find_custom(adapter->found_devices, &match,
+ (GCompareFunc) found_device_cmp);
+ if (!l) /* no pending request */
+ return 0;
+
+ dd = hci_open_dev(adapter->dev_id);
+ if (dd < 0)
+ return -ENODEV;
+
+ dev = l->data;
+
+ if (hci_read_remote_name_cancel(dd, &dev->bdaddr, 1000) < 0) {
+ error("Remote name cancel failed: %s(%d)", strerror(errno), errno);
+ err = -errno;
+ }
+
+ /* free discovered devices list */
+ g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL);
+ g_slist_free(adapter->found_devices);
+ adapter->found_devices = NULL;
+
+ hci_close_dev(dd);
+ return err;
+}
+
+static struct bonding_request_info *bonding_request_new(bdaddr_t *peer,
+ DBusConnection *conn,
+ DBusMessage *msg)
+{
+ struct bonding_request_info *bonding;
+
+ bonding = g_new0(struct bonding_request_info, 1);
+
+ bacpy(&bonding->bdaddr, peer);
+
+ bonding->conn = dbus_connection_ref(conn);
+ bonding->rq = dbus_message_ref(msg);
+
+ return bonding;
+}
+
+const char *mode2str(uint8_t mode)
+{
+ switch(mode) {
+ case MODE_OFF:
+ return "off";
+ case MODE_CONNECTABLE:
+ return "connectable";
+ case MODE_DISCOVERABLE:
+ return "discoverable";
+ case MODE_LIMITED:
+ return "limited";
+ default:
+ return "unknown";
+ }
+}
+
+uint8_t str2mode(const char *mode)
+{
+ if (strcasecmp("off", mode) == 0)
+ return MODE_OFF;
+ else if (strcasecmp("connectable", mode) == 0)
+ return MODE_CONNECTABLE;
+ else if (strcasecmp("discoverable", mode) == 0)
+ return MODE_DISCOVERABLE;
+ else if (strcasecmp("limited", mode) == 0)
+ return MODE_LIMITED;
+ else
+ return MODE_UNKNOWN;
+}
+
+static DBusHandlerResult adapter_get_info(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ const char *property = adapter->address;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ bdaddr_t ba;
+ char str[249];
+ uint8_t cls[3];
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dbus_message_iter_append_dict_entry(&dict, "address",
+ DBUS_TYPE_STRING, &property);
+
+ memset(str, 0, sizeof(str));
+ property = str;
+ str2ba(adapter->address, &ba);
+
+ if (!read_local_name(&ba, str))
+ dbus_message_iter_append_dict_entry(&dict, "name",
+ DBUS_TYPE_STRING, &property);
+
+ get_device_version(adapter->dev_id, str, sizeof(str));
+ dbus_message_iter_append_dict_entry(&dict, "version",
+ DBUS_TYPE_STRING, &property);
+
+ get_device_revision(adapter->dev_id, str, sizeof(str));
+ dbus_message_iter_append_dict_entry(&dict, "revision",
+ DBUS_TYPE_STRING, &property);
+
+ get_device_manufacturer(adapter->dev_id, str, sizeof(str));
+ dbus_message_iter_append_dict_entry(&dict, "manufacturer",
+ DBUS_TYPE_STRING, &property);
+
+ get_device_company(adapter->dev_id, str, sizeof(str));
+ dbus_message_iter_append_dict_entry(&dict, "company",
+ DBUS_TYPE_STRING, &property);
+
+ property = mode2str(adapter->mode);
+
+ dbus_message_iter_append_dict_entry(&dict, "mode",
+ DBUS_TYPE_STRING, &property);
+
+ dbus_message_iter_append_dict_entry(&dict, "discoverable_timeout",
+ DBUS_TYPE_UINT32, &adapter->discov_timeout);
+
+ if (!read_local_class(&ba, cls)) {
+ uint32_t class;
+
+ memcpy(&class, cls, 3);
+ dbus_message_iter_append_dict_entry(&dict, "class",
+ DBUS_TYPE_UINT32, &class);
+
+ property = major_class_str(class);
+ dbus_message_iter_append_dict_entry(&dict, "major_class",
+ DBUS_TYPE_STRING, &property);
+
+ property = minor_class_str(class);
+ dbus_message_iter_append_dict_entry(&dict, "minor_class",
+ DBUS_TYPE_STRING, &property);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_address(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ const char *paddr = adapter->address;
+ DBusMessage *reply;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_version(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char str[20], *str_ptr = str;
+ int err;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ err = get_device_version(adapter->dev_id, str, sizeof(str));
+ if (err < 0)
+ return error_failed(conn, msg, -err);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_revision(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char str[64], *str_ptr = str;
+ int err;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ err = get_device_revision(adapter->dev_id, str, sizeof(str));
+ if (err < 0)
+ return error_failed(conn, msg, -err);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_manufacturer(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char str[64], *str_ptr = str;
+ int err;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ err = get_device_manufacturer(adapter->dev_id, str, sizeof(str));
+ if (err < 0)
+ return error_failed(conn, msg, -err);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_company(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char str[64], *str_ptr = str;
+ int err;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ err = get_device_company(adapter->dev_id, str, sizeof(str));
+ if (err < 0)
+ return error_failed(conn, msg, -err);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_list_modes(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ const char *mode_ptr[] = { "off", "connectable", "discoverable", "limited" };
+ int i;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ 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 < 4; i++)
+ dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING,
+ &mode_ptr[i]);
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_mode(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const struct adapter *adapter = data;
+ DBusMessage *reply = NULL;
+ const char *mode;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ mode = mode2str(adapter->mode);
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_set_mode(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ const char *mode;
+ uint8_t scan_enable;
+ uint8_t new_mode, current_scan = adapter->scan_enable;
+ bdaddr_t local;
+ gboolean limited;
+ int err, dd;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (!mode)
+ return error_invalid_arguments(conn, msg);
+
+ new_mode = str2mode(mode);
+ switch(new_mode) {
+ case MODE_OFF:
+ scan_enable = SCAN_DISABLED;
+ break;
+ case MODE_CONNECTABLE:
+ scan_enable = SCAN_PAGE;
+ break;
+ case MODE_DISCOVERABLE:
+ case MODE_LIMITED:
+ scan_enable = (SCAN_PAGE | SCAN_INQUIRY);
+ break;
+ default:
+ return error_invalid_arguments(conn, msg);
+ }
+
+ dd = hci_open_dev(adapter->dev_id);
+ if (dd < 0)
+ return error_no_such_adapter(conn, msg);
+
+ if (!adapter->up &&
+ (hcid.offmode == HCID_OFFMODE_NOSCAN ||
+ (hcid.offmode == HCID_OFFMODE_DEVDOWN &&
+ scan_enable != SCAN_DISABLED))) {
+ /* Start HCI device */
+ if (ioctl(dd, HCIDEVUP, adapter->dev_id) == 0)
+ goto done; /* on success */
+
+ if (errno != EALREADY) {
+ err = errno;
+ error("Can't init device hci%d: %s (%d)\n",
+ adapter->dev_id, strerror(errno), errno);
+
+ hci_close_dev(dd);
+ return error_failed(conn, msg, err);
+ }
+ }
+
+ if (adapter->up && scan_enable == SCAN_DISABLED &&
+ hcid.offmode == HCID_OFFMODE_DEVDOWN) {
+ if (ioctl(dd, HCIDEVDOWN, adapter->dev_id) < 0) {
+ hci_close_dev(dd);
+ return error_failed(conn, msg, errno);
+ }
+
+ goto done;
+ }
+
+ limited = (new_mode == MODE_LIMITED ? TRUE : FALSE);
+ err = set_limited_discoverable(dd, adapter->class, limited);
+ if (err < 0) {
+ hci_close_dev(dd);
+ return error_failed(conn, msg, -err);
+ }
+
+ if (current_scan != scan_enable) {
+ 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 = &scan_enable;
+ rq.clen = sizeof(scan_enable);
+ rq.rparam = &status;
+ rq.rlen = sizeof(status);
+ rq.event = EVT_CMD_COMPLETE;
+
+ if (hci_send_req(dd, &rq, 1000) < 0) {
+ err = errno;
+ error("Sending write scan enable command failed: %s (%d)",
+ strerror(errno), errno);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, err);
+ }
+
+ if (status) {
+ error("Setting scan enable failed with status 0x%02x",
+ status);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, bt_error(status));
+ }
+ } else {
+ /* discoverable or limited */
+ if ((scan_enable & SCAN_INQUIRY) && (new_mode != adapter->mode)) {
+ dbus_connection_emit_signal(conn,
+ dbus_message_get_path(msg),
+ ADAPTER_INTERFACE,
+ "ModeChanged",
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID);
+
+ if (adapter->timeout_id)
+ g_source_remove(adapter->timeout_id);
+
+ if (adapter->discov_timeout != 0)
+ adapter->timeout_id = g_timeout_add(adapter->discov_timeout * 1000,
+ discov_timeout_handler, adapter);
+ }
+ }
+done:
+ str2ba(adapter->address, &local);
+ write_device_mode(&local, mode);
+
+ hci_close_dev(dd);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ adapter->mode = new_mode;
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_discoverable_to(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ const struct adapter *adapter = data;
+ DBusMessage *reply;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_UINT32, &adapter->discov_timeout,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_set_discoverable_to(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ uint32_t timeout;
+ bdaddr_t bdaddr;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &timeout,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ if (adapter->timeout_id) {
+ g_source_remove(adapter->timeout_id);
+ adapter->timeout_id = 0;
+ }
+
+ if ((timeout != 0) && (adapter->scan_enable & SCAN_INQUIRY))
+ adapter->timeout_id = g_timeout_add(timeout * 1000,
+ discov_timeout_handler,
+ adapter);
+
+ adapter->discov_timeout = timeout;
+
+ str2ba(adapter->address, &bdaddr);
+ write_discoverable_timeout(&bdaddr, timeout);
+
+ dbus_connection_emit_signal(conn, dbus_message_get_path(msg),
+ ADAPTER_INTERFACE,
+ "DiscoverableTimeoutChanged",
+ DBUS_TYPE_UINT32, &timeout,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_is_connectable(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const struct adapter *adapter = data;
+ DBusMessage *reply;
+ const uint8_t scan_enable = adapter->scan_enable;
+ dbus_bool_t connectable = FALSE;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ if (scan_enable & SCAN_PAGE)
+ connectable = TRUE;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connectable,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_is_discoverable(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const struct adapter *adapter = data;
+ DBusMessage *reply;
+ const uint8_t scan_enable = adapter->scan_enable;
+ dbus_bool_t discoverable = FALSE;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ if (scan_enable & SCAN_INQUIRY)
+ discoverable = TRUE;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &discoverable,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_is_connected(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ dbus_bool_t connected = FALSE;
+
+ struct adapter *adapter = data;
+ GSList *l = adapter->active_conn;
+
+ const char *peer_addr;
+ bdaddr_t peer_bdaddr;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &peer_addr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(peer_addr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(peer_addr, &peer_bdaddr);
+
+ l = g_slist_find_custom(l, &peer_bdaddr, active_conn_find_by_bdaddr);
+ if (l)
+ connected = TRUE;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_list_connections(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ struct adapter *adapter = data;
+ GSList *l = adapter->active_conn;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array_iter);
+
+ while (l) {
+ char peer_addr[18];
+ const char *paddr = peer_addr;
+ struct active_conn_info *dev = l->data;
+
+ ba2str(&dev->bdaddr, peer_addr);
+
+ dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING,
+ &paddr);
+
+ l = l->next;
+ }
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_major_class(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const struct adapter *adapter = data;
+ DBusMessage *reply;
+ const char *str_ptr = "computer";
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ /* FIXME: Currently, only computer major class is supported */
+ if ((adapter->class[1] & 0x1f) != 1)
+ return error_unsupported_major_class(conn, msg);
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_list_minor_classes(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const struct adapter *adapter = data;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ const char **minor_ptr;
+ uint8_t major_class;
+ int size, i;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ major_class = adapter->class[1] & 0x1F;
+
+ switch (major_class) {
+ case 1: /* computer */
+ minor_ptr = computer_minor_cls;
+ size = sizeof(computer_minor_cls) / sizeof(*computer_minor_cls);
+ break;
+ case 2: /* phone */
+ minor_ptr = phone_minor_cls;
+ size = sizeof(phone_minor_cls) / sizeof(*phone_minor_cls);
+ break;
+ default:
+ return error_unsupported_major_class(conn, msg);
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ 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 < size; i++)
+ dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING,
+ &minor_ptr[i]);
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_minor_class(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ const char *str_ptr = "";
+ uint8_t minor_class;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ /* FIXME: Currently, only computer major class is supported */
+ if ((adapter->class[1] & 0x1f) != 1)
+ return error_unsupported_major_class(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ minor_class = adapter->class[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 send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_set_minor_class(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ const char *minor;
+ uint32_t dev_class = 0xFFFFFFFF;
+ int i, dd;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &minor,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (!minor)
+ return error_invalid_arguments(conn, msg);
+
+ dd = hci_open_dev(adapter->dev_id);
+ if (dd < 0)
+ return error_no_such_adapter(conn, msg);
+
+ /* Currently, only computer major class is supported */
+ if ((adapter->class[1] & 0x1f) != 1) {
+ hci_close_dev(dd);
+ return error_unsupported_major_class(conn, msg);
+ }
+ 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) {
+ hci_close_dev(dd);
+ return error_invalid_arguments(conn, msg);
+ }
+
+ /* set the service class and major class */
+ dev_class |= (adapter->class[2] << 16) | (adapter->class[1] << 8);
+
+ if (hci_write_class_of_dev(dd, dev_class, 2000) < 0) {
+ int err = errno;
+ error("Can't write class of device on hci%d: %s(%d)",
+ adapter->dev_id, strerror(errno), errno);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, err);
+ }
+
+ dbus_connection_emit_signal(conn, dbus_message_get_path(msg),
+ ADAPTER_INTERFACE, "MinorClassChanged",
+ DBUS_TYPE_STRING, &minor,
+ DBUS_TYPE_INVALID);
+
+ reply = dbus_message_new_method_return(msg);
+
+ hci_close_dev(dd);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_service_classes(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ const char *str_ptr;
+ int i;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ 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 (adapter->class[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);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_name(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char str[249], *str_ptr = str;
+ int err;
+ bdaddr_t ba;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(adapter->address, &ba);
+
+ err = read_local_name(&ba, str);
+ if (err < 0) {
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ err = get_device_name(adapter->dev_id, str, sizeof(str));
+ if (err < 0)
+ return error_failed(conn, msg, -err);
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_set_name(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ bdaddr_t bdaddr;
+ char *str_ptr;
+ int ecode;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &str_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (!g_utf8_validate(str_ptr, -1, NULL)) {
+ error("Name change failed: the supplied name isn't valid UTF-8");
+ return error_invalid_arguments(conn, msg);
+ }
+
+ hci_devba(adapter->dev_id, &bdaddr);
+
+ write_local_name(&bdaddr, str_ptr);
+
+ ecode = set_device_name(adapter->dev_id, str_ptr);
+ if (ecode < 0)
+ return error_failed(conn, msg, -ecode);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_info(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ bdaddr_t src, dst;
+ const char *addr_ptr;
+ char filename[PATH_MAX + 1];
+ char buf[64];
+ const char *ptr;
+ char *str;
+ dbus_bool_t boolean;
+ uint32_t class;
+ int compid, ver, subver;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Name */
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "names");
+ str = textfile_caseget(filename, addr_ptr);
+ if (str) {
+ dbus_message_iter_append_dict_entry(&dict, "name",
+ DBUS_TYPE_STRING, &str);
+ free(str);
+ }
+
+ str2ba(adapter->address, &src);
+ str2ba(addr_ptr, &dst);
+
+ /* Remote device class */
+ if (read_remote_class(&src, &dst, &class) == 0) {
+
+ dbus_message_iter_append_dict_entry(&dict, "class",
+ DBUS_TYPE_UINT32, &class);
+
+ ptr = major_class_str(class);
+ dbus_message_iter_append_dict_entry(&dict, "major_class",
+ DBUS_TYPE_STRING, &ptr);
+
+ ptr = minor_class_str(class);
+ dbus_message_iter_append_dict_entry(&dict, "minor_class",
+ DBUS_TYPE_STRING, &ptr);
+ }
+
+ /* Alias */
+ if (get_device_alias(adapter->dev_id, &dst, buf, sizeof(buf)) > 0) {
+ ptr = buf;
+ dbus_message_iter_append_dict_entry(&dict, "alias",
+ DBUS_TYPE_STRING, &ptr);
+ }
+
+ /* Bonded */
+ create_name(filename, PATH_MAX, STORAGEDIR,
+ adapter->address, "linkkeys");
+ str = textfile_caseget(filename, addr_ptr);
+ if (str) {
+ boolean = TRUE;
+ free(str);
+ } else {
+ boolean = FALSE;
+ }
+
+ dbus_message_iter_append_dict_entry(&dict, "bonded",
+ DBUS_TYPE_BOOLEAN, &boolean);
+
+ /* Trusted */
+ boolean = read_trust(&src, addr_ptr, GLOBAL_TRUST);
+ dbus_message_iter_append_dict_entry(&dict, "trusted",
+ DBUS_TYPE_BOOLEAN, &boolean);
+
+ /* Connected */
+ if (g_slist_find_custom(adapter->active_conn, &dst,
+ active_conn_find_by_bdaddr))
+ boolean = TRUE;
+ else
+ boolean = FALSE;
+
+ dbus_message_iter_append_dict_entry(&dict, "connected",
+ DBUS_TYPE_BOOLEAN, &boolean);
+
+ /* HCI Revision/Manufacturer/Version */
+ create_name(filename, PATH_MAX, STORAGEDIR,
+ adapter->address, "manufacturers");
+
+ str = textfile_caseget(filename, addr_ptr);
+ if (!str)
+ goto done;
+
+ if (sscanf(str, "%d %d %d", &compid, &ver, &subver) != 3) {
+ /* corrupted file data */
+ free(str);
+ goto done;
+ }
+
+ free(str);
+
+ ptr = buf;
+ snprintf(buf, 16, "HCI 0x%X", subver);
+ dbus_message_iter_append_dict_entry(&dict, "revision",
+ DBUS_TYPE_STRING, &ptr);
+
+ ptr = bt_compidtostr(compid);
+ dbus_message_iter_append_dict_entry(&dict, "manufacturer",
+ DBUS_TYPE_STRING, &ptr);
+
+ str = lmp_vertostr(ver);
+ snprintf(buf, 64, "Bluetooth %s", str);
+ bt_free(str);
+
+ create_name(filename, PATH_MAX, STORAGEDIR,
+ adapter->address, "features");
+
+ str = textfile_caseget(filename, addr_ptr);
+ if (str) {
+ if (strlen(str) == 16) {
+ uint8_t features;
+ /* Getting the third byte */
+ features = ((str[6] - 48) << 4) | (str[7] - 48);
+ if (features & (LMP_EDR_ACL_2M | LMP_EDR_ACL_3M))
+ snprintf(buf, 64, "Bluetooth %s + EDR", ptr);
+
+ }
+ free(str);
+ }
+ ptr = buf;
+ dbus_message_iter_append_dict_entry(&dict, "version",
+ DBUS_TYPE_STRING, &ptr);
+
+done:
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_svc(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return get_remote_svc_rec(conn, msg, data, SDP_FORMAT_BINARY);
+}
+
+static DBusHandlerResult adapter_get_remote_svc_xml(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return get_remote_svc_rec(conn, msg, data, SDP_FORMAT_XML);
+}
+
+static DBusHandlerResult adapter_get_remote_svc_handles(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ return get_remote_svc_handles(conn, msg, data);
+}
+
+static DBusHandlerResult adapter_get_remote_svc_identifiers(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ return get_remote_svc_identifiers(conn, msg, data);
+}
+
+static DBusHandlerResult adapter_finish_sdp_transact(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ return finish_remote_svc_transact(conn, msg, data);
+}
+
+static DBusHandlerResult adapter_get_remote_version(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ char *addr_ptr, *str;
+ char *str_ver = NULL;
+ char info_array[64], *info = info_array;
+ int compid, ver, subver;
+
+ memset(info_array, 0, 64);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "manufacturers");
+
+ str = textfile_caseget(filename, addr_ptr);
+ if (!str)
+ return error_not_available(conn, msg);
+
+ if (sscanf(str, "%d %d %d", &compid, &ver, &subver) != 3) {
+ /* corrupted file data */
+ free(str);
+ goto failed;
+ }
+
+ free(str);
+
+ str_ver = lmp_vertostr(ver);
+
+ /* Default value */
+ snprintf(info, 64, "Bluetooth %s", str_ver);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "features");
+
+ str = textfile_caseget(filename, addr_ptr);
+ if (!str)
+ goto failed;
+
+ /* Check if the data is not corrupted */
+ if (strlen(str) == 16) {
+ uint8_t features;
+ /* Getting the third byte */
+ features = ((str[6] - 48) << 4) | (str[7] - 48);
+ if (features & (LMP_EDR_ACL_2M | LMP_EDR_ACL_3M))
+ snprintf(info, 64, "Bluetooth %s + EDR", str_ver);
+ }
+
+ free(str);
+
+failed:
+ if (str_ver)
+ bt_free(str_ver);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &info,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_revision(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ char *addr_ptr, *str;
+ char info_array[16], *info = info_array;
+ int compid, ver, subver;
+
+ memset(info_array, 0, 16);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "manufacturers");
+
+ str = textfile_caseget(filename, addr_ptr);
+ if (!str)
+ return error_not_available(conn, msg);
+
+ if (sscanf(str, "%d %d %d", &compid, &ver, &subver) == 3)
+ snprintf(info, 16, "HCI 0x%X", subver);
+
+ free(str);
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &info,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_manufacturer(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ char *addr_ptr, *str;
+ char info_array[64], *info = info_array;
+ int compid, ver, subver;
+
+ memset(info_array, 0, 64);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "manufacturers");
+
+ str = textfile_caseget(filename, addr_ptr);
+ if (!str)
+ return error_not_available(conn, msg);
+
+ if (sscanf(str, "%d %d %d", &compid, &ver, &subver) == 3)
+ info = bt_compidtostr(compid);
+
+ free(str);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &info,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_company(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ bdaddr_t bdaddr;
+ char oui[9], *str_bdaddr, *tmp;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &str_bdaddr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(str_bdaddr, &bdaddr);
+ ba2oui(&bdaddr, oui);
+
+ tmp = ouitocomp(oui);
+ if (!tmp)
+ return error_not_available(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ free(tmp);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &tmp,
+ DBUS_TYPE_INVALID);
+
+ free(tmp);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static int get_remote_class(DBusConnection *conn, DBusMessage *msg, void *data,
+ uint32_t *class)
+{
+ struct adapter *adapter = data;
+ char *addr_peer;
+ bdaddr_t local, peer;
+ int ecode;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_peer,
+ DBUS_TYPE_INVALID)) {
+ error_invalid_arguments(conn, msg);
+ return -1;
+ }
+
+ if (check_address(addr_peer) < 0) {
+ error_invalid_arguments(conn, msg);
+ return -1;
+ }
+
+ str2ba(addr_peer, &peer);
+ str2ba(adapter->address, &local);
+
+ ecode = read_remote_class(&local, &peer, class);
+ if (ecode < 0) {
+ error_not_available(conn, msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+static DBusHandlerResult adapter_get_remote_major_class(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ DBusMessage *reply;
+ const char *major_class;
+ uint32_t class;
+
+ if (get_remote_class(conn, msg, data, &class) < 0)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ major_class = major_class_str(class);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &major_class,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_minor_class(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ DBusMessage *reply;
+ const char *minor_class;
+ uint32_t class;
+
+ if (get_remote_class(conn, msg, data, &class) < 0)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ minor_class = minor_class_str(class);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &minor_class,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static void append_class_string(const char *class, DBusMessageIter *iter)
+{
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &class);
+}
+
+static DBusHandlerResult adapter_get_remote_service_cls(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, array_iter;
+ GSList *service_classes;
+ uint32_t class;
+
+ if (get_remote_class(conn, msg, data, &class) < 0)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ service_classes = service_classes_str(class);
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array_iter);
+
+ g_slist_foreach(service_classes, (GFunc) append_class_string,
+ &array_iter);
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ g_slist_free(service_classes);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_class(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ uint32_t class;
+
+ if (get_remote_class(conn, msg, data, &class) < 0)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_UINT32, &class,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_features(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ char filename[PATH_MAX + 1];
+ struct adapter *adapter = data;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter, array_iter;
+ uint8_t features[8], *ptr = features;
+ const char *addr;
+ char *str;
+ int i;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "features");
+
+ str = textfile_caseget(filename, addr);
+ if (!str)
+ return error_not_available(conn, msg);
+
+ memset(features, 0, sizeof(features));
+ for (i = 0; i < sizeof(features); i++) {
+ char tmp[3];
+
+ memcpy(tmp, str + (i * 2), 2);
+ tmp[2] = '\0';
+
+ features[i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ free(str);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array_iter);
+
+ dbus_message_iter_append_fixed_array(&array_iter,
+ DBUS_TYPE_BYTE, &ptr, sizeof(features));
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ free(str);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_remote_name(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ char filename[PATH_MAX + 1];
+ struct adapter *adapter = data;
+ DBusMessage *reply = NULL;
+ const char *peer_addr;
+ bdaddr_t peer_bdaddr;
+ char *str;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &peer_addr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(peer_addr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ /* check if it is in the cache */
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "names");
+
+ str = textfile_caseget(filename, peer_addr);
+
+ if (str) {
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ free(str);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ /* send the cached name */
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID);
+
+ free(str);
+ return send_message_and_unref(conn, reply);
+ }
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ /* If the discover process is not running, return an error */
+ if (!adapter->discov_active && !adapter->pdiscov_active)
+ return error_not_available(conn, msg);
+
+ /* Queue the request when there is a discovery running */
+ str2ba(peer_addr, &peer_bdaddr);
+ found_device_add(&adapter->found_devices, &peer_bdaddr, 0, NAME_REQUIRED);
+
+ return error_request_deferred(conn, msg);
+}
+
+static DBusHandlerResult adapter_get_remote_alias(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char str[249], *str_ptr = str, *addr_ptr;
+ bdaddr_t bdaddr;
+ int ecode;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(addr_ptr, &bdaddr);
+
+ ecode = get_device_alias(adapter->dev_id, &bdaddr, str, sizeof(str));
+ if (ecode < 0)
+ return error_not_available(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_ptr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_set_remote_alias(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char *alias, *addr;
+ bdaddr_t bdaddr;
+ int ecode;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_STRING, &alias,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if ((strlen(alias) == 0) || (check_address(addr) < 0)) {
+ error("Alias change failed: Invalid parameter");
+ return error_invalid_arguments(conn, msg);
+ }
+
+ str2ba(addr, &bdaddr);
+
+ ecode = set_device_alias(adapter->dev_id, &bdaddr, alias);
+ if (ecode < 0)
+ return error_failed(conn, msg, -ecode);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_connection_emit_signal(conn, dbus_message_get_path(msg),
+ ADAPTER_INTERFACE, "RemoteAliasChanged",
+ DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_STRING, &alias,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_clear_remote_alias(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char *addr_ptr;
+ bdaddr_t bdaddr;
+ int ecode, had_alias = 1;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0) {
+ error("Alias clear failed: Invalid parameter");
+ return error_invalid_arguments(conn, msg);
+ }
+
+ str2ba(addr_ptr, &bdaddr);
+
+ ecode = get_device_alias(adapter->dev_id, &bdaddr, NULL, 0);
+ if (ecode == -ENXIO)
+ had_alias = 0;
+
+ ecode = set_device_alias(adapter->dev_id, &bdaddr, NULL);
+ if (ecode < 0)
+ return error_failed(conn, msg, -ecode);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ if (had_alias)
+ dbus_connection_emit_signal(conn, dbus_message_get_path(msg),
+ ADAPTER_INTERFACE,
+ "RemoteAliasCleared",
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_last_seen(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ char *addr_ptr, *str;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "lastseen");
+
+ str = textfile_caseget(filename, addr_ptr);
+ if (!str)
+ return error_not_available(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ free(str);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID);
+
+ free(str);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_last_used(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ char *addr_ptr, *str;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "lastused");
+
+ str = textfile_caseget(filename, addr_ptr);
+ if (!str)
+ return error_not_available(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ free(str);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID);
+
+ free(str);
+
+ return send_message_and_unref(conn, reply);
+}
+
+
+gboolean dc_pending_timeout_handler(void *data)
+{
+ int dd;
+ struct adapter *adapter = data;
+ struct pending_dc_info *pending_dc = adapter->pending_dc;
+ DBusMessage *reply;
+
+ dd = hci_open_dev(adapter->dev_id);
+
+ if (dd < 0) {
+ error_no_such_adapter(pending_dc->conn,
+ pending_dc->msg);
+ dc_pending_timeout_cleanup(adapter);
+ return FALSE;
+ }
+
+ /* Send the HCI disconnect command */
+ if (hci_disconnect(dd, pending_dc->conn_handle,
+ HCI_OE_USER_ENDED_CONNECTION,
+ 500) < 0) {
+ int err = errno;
+ error("Disconnect failed");
+ error_failed(pending_dc->conn, pending_dc->msg, err);
+ } else {
+ reply = dbus_message_new_method_return(pending_dc->msg);
+ if (!reply)
+ error("Failed to allocate disconnect reply");
+ else
+ send_message_and_unref(pending_dc->conn, reply);
+ }
+
+ hci_close_dev(dd);
+ dc_pending_timeout_cleanup(adapter);
+
+ return FALSE;
+}
+
+void dc_pending_timeout_cleanup(struct adapter *adapter)
+{
+ dbus_connection_unref(adapter->pending_dc->conn);
+ dbus_message_unref(adapter->pending_dc->msg);
+ g_free(adapter->pending_dc);
+ adapter->pending_dc = NULL;
+}
+
+static DBusHandlerResult adapter_dc_remote_device(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ GSList *l = adapter->active_conn;
+ const char *peer_addr;
+ bdaddr_t peer_bdaddr;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &peer_addr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(peer_addr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(peer_addr, &peer_bdaddr);
+
+ l = g_slist_find_custom(l, &peer_bdaddr, active_conn_find_by_bdaddr);
+ if (!l)
+ return error_not_connected(conn, msg);
+
+ if (adapter->pending_dc)
+ return error_disconnect_in_progress(conn, msg);
+
+ adapter->pending_dc = g_new0(struct pending_dc_info, 1);
+
+ /* Start waiting... */
+ adapter->pending_dc->timeout_id =
+ g_timeout_add(DC_PENDING_TIMEOUT,
+ dc_pending_timeout_handler,
+ adapter);
+
+ if (!adapter->pending_dc->timeout_id) {
+ g_free(adapter->pending_dc);
+ adapter->pending_dc = NULL;
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ adapter->pending_dc->conn = dbus_connection_ref(conn);
+ adapter->pending_dc->msg = dbus_message_ref(msg);
+ adapter->pending_dc->conn_handle =
+ ((struct active_conn_info *) l->data)->handle;
+
+ dbus_connection_emit_signal(conn, dbus_message_get_path(msg),
+ ADAPTER_INTERFACE,
+ "RemoteDeviceDisconnectRequested",
+ DBUS_TYPE_STRING, &peer_addr,
+ DBUS_TYPE_INVALID);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void reply_authentication_failure(struct bonding_request_info *bonding)
+{
+ DBusMessage *reply;
+ int status;
+
+ status = bonding->hci_status ?
+ bonding->hci_status : HCI_AUTHENTICATION_FAILURE;
+
+ reply = new_authentication_return(bonding->rq, status);
+ if (reply)
+ send_message_and_unref(bonding->conn, reply);
+}
+
+static gboolean create_bonding_conn_complete(GIOChannel *io, GIOCondition cond,
+ struct adapter *adapter)
+{
+ struct hci_request rq;
+ auth_requested_cp cp;
+ evt_cmd_status rp;
+ struct l2cap_conninfo cinfo;
+ socklen_t len;
+ int sk, dd, ret;
+
+ if (!adapter->bonding) {
+ /* If we come here it implies a bug somewhere */
+ debug("create_bonding_conn_complete: no pending bonding!");
+ g_io_channel_close(io);
+ g_io_channel_unref(io);
+ return FALSE;
+ }
+
+ if (cond & G_IO_NVAL) {
+ error_authentication_canceled(adapter->bonding->conn,
+ adapter->bonding->rq);
+ goto cleanup;
+ }
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ debug("Hangup or error on bonding IO channel");
+
+ if (!adapter->bonding->auth_active)
+ error_connection_attempt_failed(adapter->bonding->conn,
+ adapter->bonding->rq,
+ ENETDOWN);
+ else
+ reply_authentication_failure(adapter->bonding);
+
+ goto failed;
+ }
+
+ sk = g_io_channel_unix_get_fd(io);
+
+ len = sizeof(ret);
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {
+ error("Can't get socket error: %s (%d)",
+ strerror(errno), errno);
+ error_failed(adapter->bonding->conn, adapter->bonding->rq,
+ errno);
+ goto failed;
+ }
+
+ if (ret != 0) {
+ if (adapter->bonding->auth_active)
+ reply_authentication_failure(adapter->bonding);
+ else
+ error_connection_attempt_failed(adapter->bonding->conn,
+ adapter->bonding->rq,
+ ret);
+ goto failed;
+ }
+
+ len = sizeof(cinfo);
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &cinfo, &len) < 0) {
+ error("Can't get connection info: %s (%d)",
+ strerror(errno), errno);
+ error_failed(adapter->bonding->conn, adapter->bonding->rq,
+ errno);
+ goto failed;
+ }
+
+ dd = hci_open_dev(adapter->dev_id);
+ if (dd < 0) {
+ error_no_such_adapter(adapter->bonding->conn,
+ adapter->bonding->rq);
+ goto failed;
+ }
+
+ memset(&rp, 0, sizeof(rp));
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = htobs(cinfo.hci_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, 500) < 0) {
+ error("Unable to send HCI request: %s (%d)",
+ strerror(errno), errno);
+ error_failed(adapter->bonding->conn, adapter->bonding->rq,
+ errno);
+ hci_close_dev(dd);
+ goto failed;
+ }
+
+ if (rp.status) {
+ error("HCI_Authentication_Requested failed with status 0x%02x",
+ rp.status);
+ error_failed(adapter->bonding->conn, adapter->bonding->rq,
+ bt_error(rp.status));
+ hci_close_dev(dd);
+ goto failed;
+ }
+
+ hci_close_dev(dd);
+
+ adapter->bonding->auth_active = 1;
+
+ adapter->bonding->io_id = g_io_add_watch(io,
+ G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ (GIOFunc) create_bonding_conn_complete,
+ adapter);
+
+ return FALSE;
+
+failed:
+ g_io_channel_close(io);
+
+cleanup:
+ name_listener_remove(adapter->bonding->conn,
+ dbus_message_get_sender(adapter->bonding->rq),
+ (name_cb_t) create_bond_req_exit, adapter);
+
+ bonding_request_free(adapter->bonding);
+ adapter->bonding = NULL;
+
+ return FALSE;
+}
+
+static DBusHandlerResult adapter_create_bonding(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ char filename[PATH_MAX + 1];
+ char *str, *peer_addr = NULL;
+ struct adapter *adapter = data;
+ bdaddr_t peer_bdaddr;
+ int sk;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &peer_addr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(peer_addr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(peer_addr, &peer_bdaddr);
+
+ /* check if there is a pending discover: requested by D-Bus/non clients */
+ if (adapter->discov_active || (adapter->pdiscov_active && !adapter->pinq_idle))
+ return error_discover_in_progress(conn, msg);
+
+ pending_remote_name_cancel(adapter);
+
+ if (adapter->bonding)
+ return error_bonding_in_progress(conn, msg);
+
+ if (g_slist_find_custom(adapter->pin_reqs, &peer_bdaddr, pin_req_cmp))
+ return error_bonding_in_progress(conn, msg);
+
+ /* check if a link key already exists */
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "linkkeys");
+
+ str = textfile_caseget(filename, peer_addr);
+ if (str) {
+ free(str);
+ return error_bonding_already_exists(conn, msg);
+ }
+
+ sk = l2raw_connect(adapter->address, &peer_bdaddr);
+ if (sk < 0)
+ return error_connection_attempt_failed(conn, msg, 0);
+
+ adapter->bonding = bonding_request_new(&peer_bdaddr, conn, msg);
+ if (!adapter->bonding) {
+ close(sk);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ adapter->bonding->io = g_io_channel_unix_new(sk);
+ adapter->bonding->io_id = g_io_add_watch(adapter->bonding->io,
+ G_IO_OUT | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ (GIOFunc) create_bonding_conn_complete,
+ adapter);
+
+ name_listener_add(conn, dbus_message_get_sender(msg),
+ (name_cb_t) create_bond_req_exit, adapter);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult adapter_cancel_bonding(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ bdaddr_t peer_bdaddr;
+ const char *peer_addr;
+ GSList *l;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &peer_addr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(peer_addr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(peer_addr, &peer_bdaddr);
+
+ if (!adapter->bonding || bacmp(&adapter->bonding->bdaddr, &peer_bdaddr))
+ return error_bonding_not_in_progress(conn, msg);
+
+ if (strcmp(dbus_message_get_sender(adapter->bonding->rq),
+ dbus_message_get_sender(msg)))
+ return error_not_authorized(conn, msg);
+
+ adapter->bonding->cancel = 1;
+
+ l = g_slist_find_custom(adapter->pin_reqs, &peer_bdaddr, pin_req_cmp);
+ if (l) {
+ struct pending_pin_info *pin_req = l->data;
+
+ if (pin_req->replied) {
+ /*
+ * If disconnect can't be applied and the PIN code
+ * request was already replied it doesn't make sense
+ * cancel the remote passkey: return not authorized.
+ */
+ g_io_channel_close(adapter->bonding->io);
+ return error_not_authorized(conn, msg);
+ } else {
+ int dd = hci_open_dev(adapter->dev_id);
+ if (dd < 0) {
+ error("Can't open hci%d: %s (%d)",
+ adapter->dev_id, strerror(errno), errno);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ hci_send_cmd(dd, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY,
+ 6, &peer_bdaddr);
+
+ hci_close_dev(dd);
+ }
+
+ adapter->pin_reqs = g_slist_remove(adapter->pin_reqs, pin_req);
+ g_free(pin_req);
+ }
+
+ g_io_channel_close(adapter->bonding->io);
+
+ reply = dbus_message_new_method_return(msg);
+ send_message_and_unref(conn, reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult adapter_remove_bonding(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ GSList *l;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ char *addr_ptr, *str;
+ bdaddr_t bdaddr;
+ int dd;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ dd = hci_open_dev(adapter->dev_id);
+ if (dd < 0)
+ return error_no_such_adapter(conn, msg);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "linkkeys");
+
+ /* textfile_del doesn't return an error when the key is not found */
+ str = textfile_caseget(filename, addr_ptr);
+ if (!str) {
+ hci_close_dev(dd);
+ return error_bonding_does_not_exist(conn, msg);
+ }
+
+ free(str);
+
+ /* Delete the link key from storage */
+ if (textfile_casedel(filename, addr_ptr) < 0) {
+ int err = errno;
+ hci_close_dev(dd);
+ return error_failed(conn, msg, err);
+ }
+
+ str2ba(addr_ptr, &bdaddr);
+
+ /* Delete the link key from the Bluetooth chip */
+ hci_delete_stored_link_key(dd, &bdaddr, 0, 1000);
+
+ /* find the connection */
+ l = g_slist_find_custom(adapter->active_conn, &bdaddr,
+ active_conn_find_by_bdaddr);
+ if (l) {
+ struct active_conn_info *con = l->data;
+ /* Send the HCI disconnect command */
+ if (hci_disconnect(dd, htobs(con->handle),
+ HCI_OE_USER_ENDED_CONNECTION, 500) < 0) {
+ int err = errno;
+ error("Disconnect failed");
+ hci_close_dev(dd);
+ return error_failed(conn, msg, err);
+ }
+ }
+
+ dbus_connection_emit_signal(conn, dbus_message_get_path(msg),
+ ADAPTER_INTERFACE, "BondingRemoved",
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID);
+
+ reply = dbus_message_new_method_return(msg);
+
+ hci_close_dev(dd);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_has_bonding(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ char *addr_ptr, *str;
+ dbus_bool_t result;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "linkkeys");
+
+ str = textfile_caseget(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 send_message_and_unref(conn, reply);
+}
+
+static void list_bondings_do_append(char *key, char *value, void *data)
+{
+ DBusMessageIter *iter = data;
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+}
+
+static DBusHandlerResult adapter_list_bondings(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address,
+ "linkkeys");
+
+ 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, list_bondings_do_append, &array_iter);
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_pin_code_length(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ bdaddr_t local, peer;
+ char *addr_ptr;
+ uint8_t length;
+ int len;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(adapter->address, &local);
+
+ str2ba(addr_ptr, &peer);
+
+ len = read_pin_length(&local, &peer);
+ if (len < 0)
+ return error_record_does_not_exist(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+
+ length = len;
+
+ dbus_message_append_args(reply, DBUS_TYPE_BYTE, &length,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_encryption_key_size(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ bdaddr_t bdaddr;
+ char *addr_ptr;
+ uint8_t size;
+ int val;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr_ptr,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(addr_ptr) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(addr_ptr, &bdaddr);
+
+ val = get_encryption_key_size(adapter->dev_id, &bdaddr);
+ if (val < 0)
+ return error_failed(conn, msg, -val);
+
+ reply = dbus_message_new_method_return(msg);
+
+ size = val;
+
+ dbus_message_append_args(reply, DBUS_TYPE_BYTE, &size,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_start_periodic(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ periodic_inquiry_cp cp;
+ struct hci_request rq;
+ struct adapter *adapter = data;
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ uint8_t status;
+ int dd;
+
+ if (!adapter->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 (adapter->discov_active || adapter->pdiscov_active)
+ return error_discover_in_progress(conn, msg);
+
+ pending_remote_name_cancel(adapter);
+
+ dd = hci_open_dev(adapter->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 = htobs(24);
+ cp.min_period = htobs(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, 1000) < 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 failed with status 0x%02x",
+ status);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, bt_error(status));
+ }
+
+ adapter->pdiscov_requestor = g_strdup(dbus_message_get_sender(msg));
+
+ if (adapter->pdiscov_resolve_names)
+ adapter->discov_type = PERIODIC_INQUIRY | RESOLVE_NAME;
+ else
+ adapter->discov_type = PERIODIC_INQUIRY;
+
+ 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,
+ adapter);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_stop_periodic(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ struct adapter *adapter = data;
+ int err;
+
+ if (!adapter->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 (!adapter->pdiscov_active)
+ return error_not_authorized(conn, msg);
+
+ /*
+ * Cleanup the discovered devices list and send the cmd to exit
+ * from periodic inquiry mode or cancel remote name request.
+ */
+ err = cancel_periodic_discovery(adapter);
+ 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_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_is_periodic(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ struct adapter *adapter = data;
+ dbus_bool_t active = adapter->pdiscov_active;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &active,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_set_pdiscov_resolve(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ DBusMessage *reply;
+ struct adapter *adapter = data;
+ dbus_bool_t resolve;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_BOOLEAN, &resolve,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ debug("SetPeriodicDiscoveryNameResolving(%s)",
+ resolve ? "TRUE" : "FALSE");
+
+ adapter->pdiscov_resolve_names = resolve;
+
+ if (adapter->pdiscov_active) {
+ if (resolve)
+ adapter->discov_type |= RESOLVE_NAME;
+ else
+ adapter->discov_type &= ~RESOLVE_NAME;
+ }
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_get_pdiscov_resolve(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ DBusMessage *reply;
+ struct adapter *adapter = data;
+ dbus_bool_t resolve = adapter->pdiscov_resolve_names;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &resolve,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_discover_devices(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *method;
+ inquiry_cp cp;
+ evt_cmd_status rp;
+ struct hci_request rq;
+ struct adapter *adapter = data;
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ int dd;
+
+ if (!adapter->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 (adapter->discov_active)
+ return error_discover_in_progress(conn, msg);
+
+ pending_remote_name_cancel(adapter);
+
+ if (adapter->bonding)
+ return error_bonding_in_progress(conn, msg);
+
+ dd = hci_open_dev(adapter->dev_id);
+ if (dd < 0)
+ return error_no_such_adapter(conn, msg);
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(&cp.lap, lap, 3);
+ cp.length = 0x08;
+ cp.num_rsp = 0x00;
+
+ 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, 500) < 0) {
+ int err = errno;
+ error("Unable to start inquiry: %s (%d)",
+ strerror(errno), errno);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, err);
+ }
+
+ if (rp.status) {
+ error("HCI_Inquiry command failed with status 0x%02x",
+ rp.status);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, bt_error(rp.status));
+ }
+
+ method = dbus_message_get_member(msg);
+ if (strcmp("DiscoverDevicesWithoutNameResolving", method) == 0)
+ adapter->discov_type |= STD_INQUIRY;
+ else
+ adapter->discov_type |= (STD_INQUIRY | RESOLVE_NAME);
+
+ adapter->discov_requestor = g_strdup(dbus_message_get_sender(msg));
+
+ 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) discover_devices_req_exit, adapter);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_cancel_discovery(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct adapter *adapter = data;
+ int err;
+
+ if (!adapter->up)
+ return error_not_ready(conn, msg);
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ /* is there discover pending? or discovery cancel was requested
+ * previously */
+ if (!adapter->discov_active || adapter->discovery_cancel)
+ return error_not_authorized(conn, msg);
+
+ /* only the discover requestor can cancel the inquiry process */
+ if (!adapter->discov_requestor ||
+ strcmp(adapter->discov_requestor, dbus_message_get_sender(msg)))
+ return error_not_authorized(conn, msg);
+
+ /*
+ * Cleanup the discovered devices list and send the cmd
+ * to cancel inquiry or cancel remote name request
+ */
+ err = cancel_discovery(adapter);
+ if (err < 0) {
+ if (err == -ENODEV)
+ return error_no_such_adapter(conn, msg);
+ else
+ return error_failed(conn, msg, -err);
+ }
+
+ /* Reply before send DiscoveryCompleted */
+ adapter->discovery_cancel = dbus_message_ref(msg);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+struct remote_device_list_t {
+ GSList *list;
+ time_t time;
+};
+
+static void list_remote_devices_do_append(char *key, char *value, void *data)
+{
+ struct remote_device_list_t *param = data;
+ char *address;
+ struct tm date;
+
+ if (g_slist_find_custom(param->list, key, (GCompareFunc) strcasecmp))
+ return;
+
+ if (param->time){
+ strptime(value, "%Y-%m-%d %H:%M:%S %Z", &date);
+ if (difftime(mktime(&date), param->time) < 0)
+ return;
+ }
+
+ address = g_strdup(key);
+
+ param->list = g_slist_append(param->list, address);
+}
+
+static void remote_devices_do_append(void *data, void *user_data)
+{
+ DBusMessageIter *iter = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data);
+}
+
+static DBusHandlerResult adapter_list_remote_devices(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ struct remote_device_list_t param = { NULL, 0 };
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return error_invalid_arguments(conn, msg);
+
+ /* Add Bonded devices to the list */
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "linkkeys");
+ textfile_foreach(filename, list_remote_devices_do_append, &param);
+
+ /* Add Trusted devices to the list */
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "trusts");
+ textfile_foreach(filename, list_remote_devices_do_append, &param);
+
+ /* Add Last Used devices to the list */
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "lastused");
+ textfile_foreach(filename, list_remote_devices_do_append, &param);
+
+ 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);
+
+ g_slist_foreach(param.list, remote_devices_do_append, &array_iter);
+
+ g_slist_foreach(param.list, (GFunc) free, NULL);
+ g_slist_free(param.list);
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static void append_connected(struct active_conn_info *dev, GSList *list)
+{
+ char address[18];
+
+ ba2str(&dev->bdaddr, address);
+ if (g_slist_find_custom(list, address, (GCompareFunc) strcasecmp))
+ return;
+
+ list = g_slist_append(list, g_strdup(address));
+}
+
+static DBusHandlerResult adapter_list_recent_remote_devices(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ struct tm date;
+ const char *string;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ DBusMessage *reply;
+ char filename[PATH_MAX + 1];
+ struct remote_device_list_t param = { NULL, 0 };
+ int len;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &string,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ /* Date format is "YYYY-MM-DD HH:MM:SS GMT" */
+ len = strlen(string);
+ if (len && (strptime(string, "%Y-%m-%d %H:%M:%S", &date) == NULL))
+ return error_invalid_arguments(conn, msg);
+
+ /* Bonded and trusted: mandatory entries(no matter the date/time) */
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "linkkeys");
+ textfile_foreach(filename, list_remote_devices_do_append, &param);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "trusts");
+ textfile_foreach(filename, list_remote_devices_do_append, &param);
+
+ /* Last seen/used: append devices since the date informed */
+ if (len)
+ param.time = mktime(&date);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "lastseen");
+ textfile_foreach(filename, list_remote_devices_do_append, &param);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter->address, "lastused");
+ textfile_foreach(filename, list_remote_devices_do_append, &param);
+
+ /* connected: force appending connected devices, lastused might not match */
+ g_slist_foreach(adapter->active_conn, (GFunc) append_connected, param.list);
+
+ 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);
+
+ g_slist_foreach(param.list, remote_devices_do_append, &array_iter);
+
+ g_slist_foreach(param.list, (GFunc) free, NULL);
+ g_slist_free(param.list);
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return send_message_and_unref(conn, reply);
+}
+
+
+static DBusHandlerResult adapter_set_trusted(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ bdaddr_t local;
+ const char *address;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(address) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ str2ba(adapter->address, &local);
+
+ write_trust(&local, address, GLOBAL_TRUST, TRUE);
+
+ dbus_connection_emit_signal(conn, dbus_message_get_path(msg),
+ ADAPTER_INTERFACE, "TrustAdded",
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_is_trusted(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ const char *address;
+ dbus_bool_t trusted;
+ bdaddr_t local;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(address) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ str2ba(adapter->address, &local);
+
+ trusted = read_trust(&local, address, GLOBAL_TRUST);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_BOOLEAN, &trusted,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_remove_trust(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ const char *address;
+ bdaddr_t local;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ if (check_address(address) < 0)
+ return error_invalid_arguments(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ str2ba(adapter->address, &local);
+
+ write_trust(&local, address, GLOBAL_TRUST, FALSE);
+
+ dbus_connection_emit_signal(conn, dbus_message_get_path(msg),
+ ADAPTER_INTERFACE, "TrustRemoved",
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID);
+
+ return send_message_and_unref(conn, reply);
+}
+
+static DBusHandlerResult adapter_list_trusts(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct adapter *adapter = data;
+ DBusMessage *reply;
+ GSList *trusts, *l;
+ char **addrs;
+ bdaddr_t local;
+ int len;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ str2ba(adapter->address, &local);
+
+ trusts = list_trusts(&local, GLOBAL_TRUST);
+
+ addrs = g_new(char *, g_slist_length(trusts));
+
+ for (l = trusts, len = 0; l; l = l->next, len++)
+ addrs[len] = l->data;
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+ &addrs, len,
+ DBUS_TYPE_INVALID);
+
+ g_free(addrs);
+ g_slist_foreach(trusts, (GFunc) g_free, NULL);
+ g_slist_free(trusts);
+
+ return send_message_and_unref(conn, reply);
+}
+
+const char *major_class_str(uint32_t class)
+{
+ uint8_t index = (class >> 8) & 0x1F;
+
+ if (index > 8)
+ return major_cls[9]; /* uncategorized */
+
+ return major_cls[index];
+}
+
+const char *minor_class_str(uint32_t class)
+{
+ uint8_t major_index = (class >> 8) & 0x1F;
+ uint8_t minor_index;
+
+ switch (major_index) {
+ case 1: /* computer */
+ minor_index = (class >> 2) & 0x3F;
+ if (minor_index < NUM_ELEMENTS(computer_minor_cls))
+ return computer_minor_cls[minor_index];
+ else
+ return "";
+ case 2: /* phone */
+ minor_index = (class >> 2) & 0x3F;
+ if (minor_index < NUM_ELEMENTS(phone_minor_cls))
+ return phone_minor_cls[minor_index];
+ return "";
+ case 3: /* access point */
+ minor_index = (class >> 5) & 0x07;
+ if (minor_index < NUM_ELEMENTS(access_point_minor_cls))
+ return access_point_minor_cls[minor_index];
+ else
+ return "";
+ case 4: /* audio/video */
+ minor_index = (class >> 2) & 0x3F;
+ if (minor_index < NUM_ELEMENTS(audio_video_minor_cls))
+ return audio_video_minor_cls[minor_index];
+ else
+ return "";
+ case 5: /* peripheral */
+ minor_index = (class >> 6) & 0x03;
+ if (minor_index < NUM_ELEMENTS(peripheral_minor_cls))
+ return peripheral_minor_cls[minor_index];
+ else
+ return "";
+ case 6: /* imaging */
+ {
+ uint8_t shift_minor = 0;
+
+ minor_index = (class >> 4) & 0x0F;
+ while (shift_minor < (sizeof(imaging_minor_cls) / sizeof(*imaging_minor_cls))) {
+ if (((minor_index >> shift_minor) & 0x01) == 0x01)
+ return imaging_minor_cls[shift_minor];
+ shift_minor++;
+ }
+ }
+ break;
+ case 7: /* wearable */
+ minor_index = (class >> 2) & 0x3F;
+ if (minor_index < NUM_ELEMENTS(wearable_minor_cls))
+ return wearable_minor_cls[minor_index];
+ else
+ return "";
+ case 8: /* toy */
+ minor_index = (class >> 2) & 0x3F;
+ if (minor_index < NUM_ELEMENTS(toy_minor_cls))
+ return toy_minor_cls[minor_index];
+ else
+ return "";
+ }
+
+ return "";
+}
+
+GSList *service_classes_str(uint32_t class)
+{
+ uint8_t services = class >> 16;
+ GSList *l = NULL;
+ int i;
+
+ for (i = 0; i < (sizeof(service_cls) / sizeof(*service_cls)); i++) {
+ if (!(services & (1 << i)))
+ continue;
+
+ l = g_slist_append(l, (void *) service_cls[i]);
+ }
+
+ return l;
+}
+
+static DBusMethodVTable adapter_methods[] = {
+ { "GetInfo", adapter_get_info,
+ "", "{sv}" },
+ { "GetAddress", adapter_get_address,
+ "", "s" },
+ { "GetVersion", adapter_get_version,
+ "", "s" },
+ { "GetRevision", adapter_get_revision,
+ "", "s" },
+ { "GetManufacturer", adapter_get_manufacturer,
+ "", "s" },
+ { "GetCompany", adapter_get_company,
+ "", "s" },
+ { "ListAvailableModes", adapter_list_modes,
+ "", "as" },
+ { "GetMode", adapter_get_mode,
+ "", "s" },
+ { "SetMode", adapter_set_mode,
+ "s", "" },
+ { "GetDiscoverableTimeout", adapter_get_discoverable_to,
+ "", "u" },
+ { "SetDiscoverableTimeout", adapter_set_discoverable_to,
+ "u", "" },
+ { "IsConnectable", adapter_is_connectable,
+ "", "b" },
+ { "IsDiscoverable", adapter_is_discoverable,
+ "", "b" },
+ { "IsConnected", adapter_is_connected,
+ "s", "b" },
+ { "ListConnections", adapter_list_connections,
+ "", "as" },
+ { "GetMajorClass", adapter_get_major_class,
+ "", "s" },
+ { "ListAvailableMinorClasses", adapter_list_minor_classes,
+ "", "as" },
+ { "GetMinorClass", adapter_get_minor_class,
+ "", "s" },
+ { "SetMinorClass", adapter_set_minor_class,
+ "s", "" },
+ { "GetServiceClasses", adapter_get_service_classes,
+ "", "as" },
+ { "GetName", adapter_get_name,
+ "", "s" },
+ { "SetName", adapter_set_name,
+ "s", "" },
+
+ { "GetRemoteInfo", adapter_get_remote_info,
+ "s", "{sv}" },
+ { "GetRemoteServiceRecord", adapter_get_remote_svc,
+ "su", "ay" },
+ { "GetRemoteServiceRecordAsXML", adapter_get_remote_svc_xml,
+ "su", "s" },
+ { "GetRemoteServiceHandles", adapter_get_remote_svc_handles,
+ "ss", "au" },
+ { "GetRemoteServiceIdentifiers", adapter_get_remote_svc_identifiers,
+ "s", "as" },
+ { "FinishRemoteServiceTransaction", adapter_finish_sdp_transact,
+ "s", "" },
+
+ { "GetRemoteVersion", adapter_get_remote_version,
+ "s", "s" },
+ { "GetRemoteRevision", adapter_get_remote_revision,
+ "s", "s" },
+ { "GetRemoteManufacturer", adapter_get_remote_manufacturer,
+ "s", "s" },
+ { "GetRemoteCompany", adapter_get_remote_company,
+ "s", "s" },
+ { "GetRemoteMajorClass", adapter_get_remote_major_class,
+ "s", "s" },
+ { "GetRemoteMinorClass", adapter_get_remote_minor_class,
+ "s", "s" },
+ { "GetRemoteServiceClasses", adapter_get_remote_service_cls,
+ "s", "as" },
+ { "GetRemoteClass", adapter_get_remote_class,
+ "s", "u" },
+ { "GetRemoteFeatures", adapter_get_remote_features,
+ "s", "ay" },
+ { "GetRemoteName", adapter_get_remote_name,
+ "s", "s" },
+ { "GetRemoteAlias", adapter_get_remote_alias,
+ "s", "s" },
+ { "SetRemoteAlias", adapter_set_remote_alias,
+ "ss", "" },
+ { "ClearRemoteAlias", adapter_clear_remote_alias,
+ "s", "" },
+
+ { "LastSeen", adapter_last_seen,
+ "s", "s" },
+ { "LastUsed", adapter_last_used,
+ "s", "s" },
+
+ { "DisconnectRemoteDevice", adapter_dc_remote_device,
+ "s", "" },
+
+ { "CreateBonding", adapter_create_bonding,
+ "s", "" },
+ { "CancelBondingProcess", adapter_cancel_bonding,
+ "s", "" },
+ { "RemoveBonding", adapter_remove_bonding,
+ "s", "" },
+ { "HasBonding", adapter_has_bonding,
+ "s", "b" },
+ { "ListBondings", adapter_list_bondings,
+ "", "as" },
+ { "GetPinCodeLength", adapter_get_pin_code_length,
+ "s", "y" },
+ { "GetEncryptionKeySize", adapter_get_encryption_key_size,
+ "s", "y" },
+
+ { "StartPeriodicDiscovery", adapter_start_periodic,
+ "", "" },
+ { "StopPeriodicDiscovery", adapter_stop_periodic,
+ "", "" },
+ { "IsPeriodicDiscovery", adapter_is_periodic,
+ "", "b" },
+ { "SetPeriodicDiscoveryNameResolving", adapter_set_pdiscov_resolve,
+ "b", "" },
+ { "GetPeriodicDiscoveryNameResolving", adapter_get_pdiscov_resolve,
+ "", "b" },
+
+ { "DiscoverDevices", adapter_discover_devices,
+ "", "" },
+ { "DiscoverDevicesWithoutNameResolving", adapter_discover_devices,
+ "", "" },
+ { "CancelDiscovery", adapter_cancel_discovery,
+ "", "" },
+
+ { "ListRemoteDevices", adapter_list_remote_devices,
+ "", "as" },
+ { "ListRecentRemoteDevices", adapter_list_recent_remote_devices,
+ "s", "as" },
+
+ { "SetTrusted", adapter_set_trusted,
+ "s", "" },
+ { "IsTrusted", adapter_is_trusted,
+ "s", "b" },
+ { "RemoveTrust", adapter_remove_trust,
+ "s", "" },
+ { "ListTrusts", adapter_list_trusts,
+ "", "as" },
+
+ { NULL, NULL, NULL, NULL }
+};
+
+static DBusSignalVTable adapter_signals[] = {
+ { "ModeChanged", "s" },
+ { "DiscoverableTimeoutChanged", "u" },
+ { "MinorClassChanged", "s" },
+ { "NameChanged", "s" },
+ { "DiscoveryStarted", "" },
+ { "DiscoveryCompleted", "" },
+ { "PeriodicDiscoveryStarted", "" },
+ { "PeriodicDiscoveryStopped", "" },
+ { "RemoteDeviceFound", "sun" },
+ { "RemoteDeviceDisappeared", "s" },
+ { "RemoteClassUpdated", "su" },
+ { "RemoteNameUpdated", "ss" },
+ { "RemoteNameFailed", "s" },
+ { "RemoteNameRequested", "s" },
+ { "RemoteAliasChanged", "ss" },
+ { "RemoteAliasCleared", "s" },
+ { "RemoteDeviceConnected", "s" },
+ { "RemoteDeviceDisconnectRequested", "s" },
+ { "RemoteDeviceDisconnected", "s" },
+ { "RemoteIdentifiersUpdated", "sas" },
+ { "BondingCreated", "s" },
+ { "BondingRemoved", "s" },
+ { "TrustAdded", "s" },
+ { "TrustRemoved", "s" },
+ { NULL, NULL }
+};
+
+dbus_bool_t adapter_init(DBusConnection *conn, const char *path)
+{
+ return dbus_connection_register_interface(conn, path, ADAPTER_INTERFACE,
+ adapter_methods,
+ adapter_signals, NULL);
+}