diff --git a/Makefile.am b/Makefile.am
index 7415979..5009ca8 100644
--- a/Makefile.am
+++ b/Makefile.am
endif
-builtin_modules += hciops mgmtops
-builtin_sources += plugins/hciops.c plugins/mgmtops.c
+builtin_modules += mgmtops
+builtin_sources += plugins/mgmtops.c
if HAL
builtin_modules += hal
diff --git a/plugins/hciops.c b/plugins/hciops.c
deleted file mode 100644
index d74f2ea..0000000
--- a/plugins/hciops.c
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2004-2010 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 <stdlib.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
-#include <glib.h>
-
-#include "hcid.h"
-#include "sdpd.h"
-#include "btio.h"
-#include "adapter.h"
-#include "device.h"
-#include "plugin.h"
-#include "log.h"
-#include "storage.h"
-#include "event.h"
-#include "manager.h"
-#include "oob.h"
-#include "eir.h"
-
-#define DISCOV_HALTED 0
-#define DISCOV_INQ 1
-#define DISCOV_SCAN 2
-#define DISCOV_NAMES 3
-
-#define TIMEOUT_BR_LE_SCAN 5120 /* TGAP(100)/2 */
-#define TIMEOUT_LE_SCAN 10240 /* TGAP(gen_disc_scan_min) */
-
-#define LENGTH_BR_INQ 0x08
-#define LENGTH_BR_LE_INQ 0x04
-
-static int start_scanning(int index, int timeout);
-
-static int child_pipe[2] = { -1, -1 };
-
-static guint child_io_id = 0;
-static guint ctl_io_id = 0;
-
-enum adapter_type {
- BR_EDR,
- LE_ONLY,
- BR_EDR_LE,
- UNKNOWN,
-};
-
-/* Commands sent by kernel on starting an adapter */
-enum {
- PENDING_BDADDR,
- PENDING_VERSION,
- PENDING_FEATURES,
- PENDING_NAME,
-};
-
-struct bt_conn {
- struct dev_info *dev;
- bdaddr_t bdaddr;
- uint16_t handle;
- uint8_t loc_cap;
- uint8_t loc_auth;
- uint8_t rem_cap;
- uint8_t rem_auth;
- uint8_t rem_oob_data;
- gboolean bonding_initiator;
- gboolean secmode3;
- GIOChannel *io; /* For raw L2CAP socket (bonding) */
-};
-
-struct oob_data {
- bdaddr_t bdaddr;
- uint8_t hash[16];
- uint8_t randomizer[16];
-};
-
-enum name_state {
- NAME_UNKNOWN,
- NAME_NEEDED,
- NAME_NOT_NEEDED,
- NAME_PENDING,
-};
-
-struct found_dev {
- bdaddr_t bdaddr;
- int8_t rssi;
- enum name_state name_state;
-};
-
-static int max_dev = -1;
-static struct dev_info {
- int id;
- int sk;
- bdaddr_t bdaddr;
- char name[249];
- uint8_t eir[HCI_MAX_EIR_LENGTH];
- uint8_t features[8];
- uint8_t extfeatures[8];
- uint8_t ssp_mode;
-
- int8_t tx_power;
-
- int discov_state;
-
- uint32_t current_cod;
- uint32_t wanted_cod;
- uint32_t pending_cod;
- gboolean cache_enable;
- gboolean already_up;
- gboolean registered;
- gboolean pairable;
-
- uint8_t io_capability;
-
- struct hci_version ver;
-
- uint16_t did_source;
- uint16_t did_vendor;
- uint16_t did_product;
- uint16_t did_version;
-
- gboolean up;
- uint32_t pending;
-
- GIOChannel *io;
- guint watch_id;
-
- gboolean debug_keys;
- GSList *keys;
- uint8_t pin_length;
-
- GSList *oob_data;
-
- GSList *uuids;
-
- GSList *connections;
-
- GSList *found_devs;
- GSList *need_name;
-
- guint stop_scan_id;
-
- uint16_t discoverable_timeout;
- guint discoverable_id;
-} *devs = NULL;
-
-static int found_dev_rssi_cmp(gconstpointer a, gconstpointer b)
-{
- const struct found_dev *d1 = a, *d2 = b;
- int rssi1, rssi2;
-
- if (d2->name_state == NAME_NOT_NEEDED)
- return -1;
-
- rssi1 = d1->rssi < 0 ? -d1->rssi : d1->rssi;
- rssi2 = d2->rssi < 0 ? -d2->rssi : d2->rssi;
-
- return rssi1 - rssi2;
-}
-
-static int found_dev_bda_cmp(gconstpointer a, gconstpointer b)
-{
- const struct found_dev *d1 = a, *d2 = b;
-
- return bacmp(&d1->bdaddr, &d2->bdaddr);
-}
-
-static void found_dev_cleanup(struct dev_info *info)
-{
- g_slist_free_full(info->found_devs, g_free);
- info->found_devs = NULL;
-
- g_slist_free_full(info->need_name, g_free);
- info->need_name = NULL;
-}
-
-static int resolve_name(struct dev_info *info, bdaddr_t *bdaddr)
-{
- remote_name_req_cp cp;
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", info->id, addr);
-
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
- cp.pscan_rep_mode = 0x02;
-
- if (hci_send_cmd(info->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ,
- REMOTE_NAME_REQ_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static int resolve_names(struct dev_info *info, struct btd_adapter *adapter)
-{
- struct found_dev *dev;
-
- DBG("found_dev %u need_name %u", g_slist_length(info->found_devs),
- g_slist_length(info->need_name));
-
- if (g_slist_length(info->need_name) == 0)
- return -ENOENT;
-
- dev = info->need_name->data;
- resolve_name(info, &dev->bdaddr);
- dev->name_state = NAME_PENDING;
-
- return 0;
-}
-
-static void set_state(int index, int state)
-{
- struct btd_adapter *adapter;
- struct dev_info *dev = &devs[index];
-
- if (dev->discov_state == state)
- return;
-
- adapter = manager_find_adapter_by_id(index);
- if (!adapter) {
- error("No matching adapter found");
- return;
- }
-
- dev->discov_state = state;
-
- DBG("hci%d: new state %d", index, dev->discov_state);
-
- switch (dev->discov_state) {
- case DISCOV_HALTED:
- found_dev_cleanup(dev);
- adapter_set_discovering(adapter, FALSE);
- break;
- case DISCOV_INQ:
- case DISCOV_SCAN:
- adapter_set_discovering(adapter, TRUE);
- break;
- case DISCOV_NAMES:
- if (resolve_names(dev, adapter) < 0)
- set_state(index, DISCOV_HALTED);
- break;
- }
-}
-
-static inline gboolean is_le_capable(int index)
-{
- struct dev_info *dev = &devs[index];
-
- return (dev->features[4] & LMP_LE &&
- dev->extfeatures[0] & LMP_HOST_LE) ? TRUE : FALSE;
-}
-
-static inline gboolean is_bredr_capable(int index)
-{
- struct dev_info *dev = &devs[index];
-
- return (dev->features[4] & LMP_NO_BREDR) == 0 ? TRUE : FALSE;
-}
-
-static int get_adapter_type(int index)
-{
- if (is_le_capable(index) && is_bredr_capable(index))
- return BR_EDR_LE;
- else if (is_le_capable(index))
- return LE_ONLY;
- else if (is_bredr_capable(index))
- return BR_EDR;
-
- return UNKNOWN;
-}
-
-static int ignore_device(struct hci_dev_info *di)
-{
- return hci_test_bit(HCI_RAW, &di->flags) || di->type >> 4 != HCI_BREDR;
-}
-
-static struct dev_info *init_dev_info(int index, int sk, gboolean registered,
- gboolean already_up)
-{
- struct dev_info *dev = &devs[index];
-
- memset(dev, 0, sizeof(*dev));
-
- dev->id = index;
- dev->sk = sk;
- dev->cache_enable = TRUE;
- dev->registered = registered;
- dev->already_up = already_up;
- dev->io_capability = 0x03; /* No Input No Output */
- dev->discov_state = DISCOV_HALTED;
-
- return dev;
-}
-
-/* Async HCI command handling with callback support */
-
-struct hci_cmd_data {
- bt_hci_result_t cb;
- uint16_t handle;
- uint16_t ocf;
- gpointer caller_data;
-};
-
-static gboolean hci_event_watch(GIOChannel *io,
- GIOCondition cond, gpointer user_data)
-{
- unsigned char buf[HCI_MAX_EVENT_SIZE], *body;
- struct hci_cmd_data *cmd = user_data;
- evt_cmd_status *evt_status;
- evt_auth_complete *evt_auth;
- evt_encrypt_change *evt_enc;
- hci_event_hdr *hdr;
- set_conn_encrypt_cp cp;
- int dd;
- uint16_t ocf;
- uint8_t status = HCI_OE_POWER_OFF;
-
- if (cond & G_IO_NVAL) {
- cmd->cb(status, cmd->caller_data);
- return FALSE;
- }
-
- if (cond & (G_IO_ERR | G_IO_HUP))
- goto failed;
-
- dd = g_io_channel_unix_get_fd(io);
-
- if (read(dd, buf, sizeof(buf)) < 0)
- goto failed;
-
- hdr = (hci_event_hdr *) (buf + 1);
- body = buf + (1 + HCI_EVENT_HDR_SIZE);
-
- switch (hdr->evt) {
- case EVT_CMD_STATUS:
- evt_status = (evt_cmd_status *) body;
- ocf = cmd_opcode_ocf(evt_status->opcode);
- if (ocf != cmd->ocf)
- return TRUE;
- switch (ocf) {
- case OCF_AUTH_REQUESTED:
- case OCF_SET_CONN_ENCRYPT:
- if (evt_status->status != 0) {
- /* Baseband rejected command */
- status = evt_status->status;
- goto failed;
- }
- break;
- default:
- return TRUE;
- }
- /* Wait for the next event */
- return TRUE;
- case EVT_AUTH_COMPLETE:
- evt_auth = (evt_auth_complete *) body;
- if (evt_auth->handle != cmd->handle) {
- /* Skipping */
- return TRUE;
- }
-
- if (evt_auth->status != 0x00) {
- status = evt_auth->status;
- /* Abort encryption */
- goto failed;
- }
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = cmd->handle;
- cp.encrypt = 1;
-
- cmd->ocf = OCF_SET_CONN_ENCRYPT;
-
- if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT,
- SET_CONN_ENCRYPT_CP_SIZE, &cp) < 0) {
- status = HCI_COMMAND_DISALLOWED;
- goto failed;
- }
- /* Wait for encrypt change event */
- return TRUE;
- case EVT_ENCRYPT_CHANGE:
- evt_enc = (evt_encrypt_change *) body;
- if (evt_enc->handle != cmd->handle)
- return TRUE;
-
- /* Procedure finished: reporting status */
- status = evt_enc->status;
- break;
- default:
- /* Skipping */
- return TRUE;
- }
-
-failed:
- cmd->cb(status, cmd->caller_data);
- g_io_channel_shutdown(io, TRUE, NULL);
-
- return FALSE;
-}
-
-static int write_inq_mode(int index, uint8_t mode)
-{
- struct dev_info *dev = &devs[index];
- write_inquiry_mode_cp cp;
-
- memset(&cp, 0, sizeof(cp));
- cp.mode = mode;
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE,
- WRITE_INQUIRY_MODE_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static uint8_t get_inquiry_mode(int index)
-{
- struct dev_info *dev = &devs[index];
-
- if (dev->features[6] & LMP_EXT_INQ)
- return 2;
-
- if (dev->features[3] & LMP_RSSI_INQ)
- return 1;
-
- if (dev->ver.manufacturer == 11 && dev->ver.hci_rev == 0x00 &&
- dev->ver.lmp_subver == 0x0757)
- return 1;
-
- if (dev->ver.manufacturer == 15) {
- if (dev->ver.hci_rev == 0x03 &&
- dev->ver.lmp_subver == 0x6963)
- return 1;
- if (dev->ver.hci_rev == 0x09 &&
- dev->ver.lmp_subver == 0x6963)
- return 1;
- if (dev->ver.hci_rev == 0x00 &&
- dev->ver.lmp_subver == 0x6965)
- return 1;
- }
-
- if (dev->ver.manufacturer == 31 && dev->ver.hci_rev == 0x2005 &&
- dev->ver.lmp_subver == 0x1805)
- return 1;
-
- return 0;
-}
-
-static int init_ssp_mode(int index)
-{
- struct dev_info *dev = &devs[index];
- write_simple_pairing_mode_cp cp;
-
- if (ioctl(dev->sk, HCIGETAUTHINFO, NULL) < 0 && errno == EINVAL)
- return 0;
-
- memset(&cp, 0, sizeof(cp));
- cp.mode = 0x01;
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_WRITE_SIMPLE_PAIRING_MODE,
- WRITE_SIMPLE_PAIRING_MODE_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static int hciops_set_discoverable(int index, gboolean discoverable,
- uint16_t timeout)
-{
- struct dev_info *dev = &devs[index];
- uint8_t mode;
-
- if (discoverable)
- mode = (SCAN_PAGE | SCAN_INQUIRY);
- else
- mode = SCAN_PAGE;
-
- DBG("hci%d discoverable %d", index, discoverable);
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE,
- 1, &mode) < 0)
- return -errno;
-
- dev->discoverable_timeout = timeout;
-
- return 0;
-}
-
-static int hciops_set_pairable(int index, gboolean pairable)
-{
- struct btd_adapter *adapter;
-
- DBG("hci%d pairable %d", index, pairable);
-
- adapter = manager_find_adapter(&devs[index].bdaddr);
- if (adapter)
- btd_adapter_pairable_changed(adapter, pairable);
-
- devs[index].pairable = pairable;
-
- return 0;
-}
-
-static int hciops_power_off(int index)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d", index);
-
- if (ioctl(dev->sk, HCIDEVDOWN, index) < 0 && errno != EALREADY)
- return -errno;
-
- return 0;
-}
-
-static void set_event_mask(int index)
-{
- struct dev_info *dev = &devs[index];
- /* The second byte is 0xff instead of 0x9f (two reserved bits
- * disabled) since a Broadcom 1.2 dongle doesn't respond to the
- * command otherwise */
- uint8_t events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 };
-
- /* Events for 1.2 and newer controllers */
- if (dev->ver.lmp_ver > 1) {
- events[4] |= 0x01; /* Flow Specification Complete */
- events[4] |= 0x02; /* Inquiry Result with RSSI */
- events[4] |= 0x04; /* Read Remote Extended Features Complete */
- events[5] |= 0x08; /* Synchronous Connection Complete */
- events[5] |= 0x10; /* Synchronous Connection Changed */
- }
-
- if (dev->features[3] & LMP_RSSI_INQ)
- events[4] |= 0x02; /* Inquiry Result with RSSI */
-
- if (dev->features[5] & LMP_SNIFF_SUBR)
- events[5] |= 0x20; /* Sniff Subrating */
-
- if (dev->features[5] & LMP_PAUSE_ENC)
- events[5] |= 0x80; /* Encryption Key Refresh Complete */
-
- if (dev->features[6] & LMP_EXT_INQ)
- events[5] |= 0x40; /* Extended Inquiry Result */
-
- if (dev->features[6] & LMP_NFLUSH_PKTS)
- events[7] |= 0x01; /* Enhanced Flush Complete */
-
- if (dev->features[7] & LMP_LSTO)
- events[6] |= 0x80; /* Link Supervision Timeout Changed */
-
- if (dev->features[6] & LMP_SIMPLE_PAIR) {
- events[6] |= 0x01; /* IO Capability Request */
- events[6] |= 0x02; /* IO Capability Response */
- events[6] |= 0x04; /* User Confirmation Request */
- events[6] |= 0x08; /* User Passkey Request */
- events[6] |= 0x10; /* Remote OOB Data Request */
- events[6] |= 0x20; /* Simple Pairing Complete */
- events[7] |= 0x04; /* User Passkey Notification */
- events[7] |= 0x08; /* Keypress Notification */
- events[7] |= 0x10; /* Remote Host Supported
- * Features Notification */
- }
-
- if (dev->features[4] & LMP_LE)
- events[7] |= 0x20; /* LE Meta-Event */
-
- hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_SET_EVENT_MASK,
- sizeof(events), events);
-}
-
-static void start_adapter(int index)
-{
- struct dev_info *dev = &devs[index];
- uint8_t inqmode;
- uint16_t link_policy;
-
- set_event_mask(index);
-
- if (dev->features[6] & LMP_SIMPLE_PAIR)
- init_ssp_mode(index);
-
- inqmode = get_inquiry_mode(index);
- if (inqmode)
- write_inq_mode(index, inqmode);
-
- if (dev->features[7] & LMP_INQ_TX_PWR)
- hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL, 0, NULL);
-
- /* Set default link policy */
- link_policy = main_opts.link_policy;
-
- if (!(dev->features[0] & LMP_RSWITCH))
- link_policy &= ~HCI_LP_RSWITCH;
- if (!(dev->features[0] & LMP_HOLD))
- link_policy &= ~HCI_LP_HOLD;
- if (!(dev->features[0] & LMP_SNIFF))
- link_policy &= ~HCI_LP_SNIFF;
- if (!(dev->features[1] & LMP_PARK))
- link_policy &= ~HCI_LP_PARK;
-
- link_policy = htobs(link_policy);
- hci_send_cmd(dev->sk, OGF_LINK_POLICY, OCF_WRITE_DEFAULT_LINK_POLICY,
- sizeof(link_policy), &link_policy);
-
- dev->current_cod = 0;
- memset(dev->eir, 0, sizeof(dev->eir));
-}
-
-static int hciops_stop_inquiry(int index)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d", index);
-
- if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_INQUIRY_CANCEL, 0, 0) < 0)
- return -errno;
-
- return 0;
-}
-
-static void update_ext_inquiry_response(int index)
-{
- struct dev_info *dev = &devs[index];
- write_ext_inquiry_response_cp cp;
-
- DBG("hci%d", index);
-
- if (!(dev->features[6] & LMP_EXT_INQ))
- return;
-
- if (dev->ssp_mode == 0)
- return;
-
- if (dev->cache_enable)
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- eir_create(dev->name, dev->tx_power, dev->did_vendor, dev->did_product,
- dev->did_version, dev->did_source, dev->uuids,
- cp.data);
-
- if (memcmp(cp.data, dev->eir, sizeof(cp.data)) == 0)
- return;
-
- memcpy(dev->eir, cp.data, sizeof(cp.data));
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_WRITE_EXT_INQUIRY_RESPONSE,
- WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE, &cp) < 0)
- error("Unable to write EIR data: %s (%d)",
- strerror(errno), errno);
-}
-
-static int hciops_set_name(int index, const char *name)
-{
- struct dev_info *dev = &devs[index];
- change_local_name_cp cp;
-
- DBG("hci%d, name %s", index, name);
-
- memset(&cp, 0, sizeof(cp));
- strncpy((char *) cp.name, name, sizeof(cp.name));
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
- CHANGE_LOCAL_NAME_CP_SIZE, &cp) < 0)
- return -errno;
-
- memcpy(dev->name, cp.name, 248);
- update_ext_inquiry_response(index);
-
- return 0;
-}
-
-static int write_class(int index, uint32_t class)
-{
- struct dev_info *dev = &devs[index];
- write_class_of_dev_cp cp;
-
- DBG("hci%d class 0x%06x", index, class);
-
- memcpy(cp.dev_class, &class, 3);
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
- WRITE_CLASS_OF_DEV_CP_SIZE, &cp) < 0)
- return -errno;
-
- dev->pending_cod = class;
-
- return 0;
-}
-
-static int hciops_set_dev_class(int index, uint8_t major, uint8_t minor)
-{
- struct dev_info *dev = &devs[index];
- int err;
-
- DBG("hci%d major %u minor %u", index, major, minor);
-
- /* Update only the major and minor class bits keeping remaining bits
- * intact*/
- dev->wanted_cod &= 0xffe000;
- dev->wanted_cod |= ((major & 0x1f) << 8) | minor;
-
- if (dev->wanted_cod == dev->current_cod ||
- dev->cache_enable || dev->pending_cod)
- return 0;
-
- DBG("Changing Major/Minor class to 0x%06x", dev->wanted_cod);
-
- err = write_class(index, dev->wanted_cod);
- if (err < 0)
- error("Adapter class update failed: %s (%d)",
- strerror(-err), -err);
-
- return err;
-}
-
-static gboolean init_adapter(int index)
-{
- struct dev_info *dev = &devs[index];
- struct btd_adapter *adapter = NULL;
- gboolean existing_adapter = dev->registered;
- uint8_t mode, on_mode, major, minor;
- gboolean pairable, discoverable;
- const char *name;
- uint16_t discoverable_timeout;
-
- if (!dev->registered) {
- adapter = btd_manager_register_adapter(index, TRUE);
- if (adapter)
- dev->registered = TRUE;
- } else {
- adapter = manager_find_adapter(&dev->bdaddr);
- /* FIXME: manager_find_adapter should return a new ref */
- btd_adapter_ref(adapter);
- }
-
- if (adapter == NULL)
- return FALSE;
-
- btd_adapter_get_mode(adapter, &mode, &on_mode,
- &discoverable_timeout,
- &pairable);
-
- if (existing_adapter)
- mode = on_mode;
-
- if (mode == MODE_OFF) {
- hciops_power_off(index);
- goto done;
- }
-
- start_adapter(index);
-
- name = btd_adapter_get_name(adapter);
- if (name)
- hciops_set_name(index, name);
-
- btd_adapter_get_class(adapter, &major, &minor);
- hciops_set_dev_class(index, major, minor);
-
- btd_adapter_start(adapter);
-
- discoverable = (mode == MODE_DISCOVERABLE);
-
- hciops_set_discoverable(index, discoverable, discoverable_timeout);
- hciops_set_pairable(index, pairable);
-
- if (dev->already_up)
- hciops_stop_inquiry(index);
-
-done:
- btd_adapter_unref(adapter);
- return TRUE;
-}
-
-static int hciops_encrypt_link(int index, bdaddr_t *dst, bt_hci_result_t cb,
- gpointer user_data)
-{
- GIOChannel *io;
- struct hci_cmd_data *cmd;
- struct hci_conn_info_req *cr;
- auth_requested_cp cp;
- struct hci_filter nf;
- int dd, err;
- uint32_t link_mode;
- uint16_t handle;
-
- dd = hci_open_dev(index);
- if (dd < 0)
- return -errno;
-
- cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
- cr->type = ACL_LINK;
- bacpy(&cr->bdaddr, dst);
-
- err = ioctl(dd, HCIGETCONNINFO, cr);
- link_mode = cr->conn_info->link_mode;
- handle = cr->conn_info->handle;
- g_free(cr);
-
- if (err < 0) {
- err = -errno;
- goto fail;
- }
-
- if (link_mode & HCI_LM_ENCRYPT) {
- err = -EALREADY;
- goto fail;
- }
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = htobs(handle);
-
- if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
- AUTH_REQUESTED_CP_SIZE, &cp) < 0) {
- err = -errno;
- goto fail;
- }
-
- cmd = g_new0(struct hci_cmd_data, 1);
- cmd->handle = handle;
- cmd->ocf = OCF_AUTH_REQUESTED;
- cmd->cb = cb;
- cmd->caller_data = user_data;
-
- hci_filter_clear(&nf);
- hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
- hci_filter_set_event(EVT_CMD_STATUS, &nf);
- hci_filter_set_event(EVT_AUTH_COMPLETE, &nf);
- hci_filter_set_event(EVT_ENCRYPT_CHANGE, &nf);
-
- if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
- err = -errno;
- g_free(cmd);
- goto fail;
- }
-
- io = g_io_channel_unix_new(dd);
- g_io_channel_set_close_on_unref(io, FALSE);
- g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
- G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN,
- hci_event_watch, cmd, g_free);
- g_io_channel_unref(io);
-
- return 0;
-
-fail:
- close(dd);
- return err;
-}
-
-static int hciops_set_did(int index, uint16_t vendor, uint16_t product,
- uint16_t version, uint16_t source)
-{
- struct dev_info *dev = &devs[index];
-
- dev->did_vendor = vendor;
- dev->did_product = product;
- dev->did_version = version;
- dev->did_source = source;
-
- return 0;
-}
-
-/* End async HCI command handling */
-
-/* Start of HCI event callbacks */
-
-static gint conn_handle_cmp(gconstpointer a, gconstpointer b)
-{
- const struct bt_conn *conn = a;
- uint16_t handle = *((const uint16_t *) b);
-
- return (int) conn->handle - (int) handle;
-}
-
-static struct bt_conn *find_conn_by_handle(struct dev_info *dev,
- uint16_t handle)
-{
- GSList *match;
-
- match = g_slist_find_custom(dev->connections, &handle,
- conn_handle_cmp);
- if (match)
- return match->data;
-
- return NULL;
-}
-
-static gint conn_bdaddr_cmp(gconstpointer a, gconstpointer b)
-{
- const struct bt_conn *conn = a;
- const bdaddr_t *bdaddr = b;
-
- return bacmp(&conn->bdaddr, bdaddr);
-}
-
-static struct bt_conn *find_connection(struct dev_info *dev, bdaddr_t *bdaddr)
-{
- GSList *match;
-
- match = g_slist_find_custom(dev->connections, bdaddr, conn_bdaddr_cmp);
- if (match)
- return match->data;
-
- return NULL;
-}
-
-static struct bt_conn *get_connection(struct dev_info *dev, bdaddr_t *bdaddr)
-{
- struct bt_conn *conn;
-
- conn = find_connection(dev, bdaddr);
- if (conn)
- return conn;
-
- conn = g_new0(struct bt_conn, 1);
-
- conn->dev = dev;
- conn->loc_cap = dev->io_capability;
- conn->loc_auth = 0xff;
- conn->rem_auth = 0xff;
- bacpy(&conn->bdaddr, bdaddr);
-
- dev->connections = g_slist_append(dev->connections, conn);
-
- return conn;
-}
-
-static int get_handle(int index, bdaddr_t *bdaddr, uint16_t *handle)
-{
- struct dev_info *dev = &devs[index];
- struct bt_conn *conn;
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
-
- conn = find_connection(dev, bdaddr);
- if (conn == NULL)
- return -ENOENT;
-
- *handle = conn->handle;
-
- return 0;
-}
-
-static int disconnect_addr(int index, bdaddr_t *dba, uint8_t reason)
-{
- disconnect_cp cp;
- uint16_t handle;
- int err;
-
- err = get_handle(index, dba, &handle);
- if (err < 0)
- return err;
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = htobs(handle);
- cp.reason = reason;
-
- if (hci_send_cmd(devs[index].sk, OGF_LINK_CTL, OCF_DISCONNECT,
- DISCONNECT_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static void bonding_complete(struct dev_info *dev, struct bt_conn *conn,
- uint8_t status)
-{
- struct btd_adapter *adapter;
-
- DBG("status 0x%02x", status);
-
- if (conn->io != NULL) {
- /* bonding_connect_cb takes care of the successul case */
- if (status != 0)
- g_io_channel_shutdown(conn->io, TRUE, NULL);
- g_io_channel_unref(conn->io);
- conn->io = NULL;
- }
-
- conn->bonding_initiator = FALSE;
-
- adapter = manager_find_adapter(&dev->bdaddr);
- if (adapter)
- adapter_bonding_complete(adapter, &conn->bdaddr, status);
-}
-
-static int get_auth_info(int index, bdaddr_t *bdaddr, uint8_t *auth)
-{
- struct dev_info *dev = &devs[index];
- struct hci_auth_info_req req;
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
-
- memset(&req, 0, sizeof(req));
- bacpy(&req.bdaddr, bdaddr);
-
- if (ioctl(dev->sk, HCIGETAUTHINFO, (unsigned long) &req) < 0)
- return -errno;
-
- if (auth)
- *auth = req.type;
-
- return 0;
-}
-
-/* Link Key handling */
-
-static void link_key_request(int index, bdaddr_t *dba)
-{
- struct dev_info *dev = &devs[index];
- struct link_key_info *key_info;
- struct bt_conn *conn;
- GSList *match;
- char da[18];
-
- ba2str(dba, da);
- DBG("hci%d dba %s", index, da);
-
- conn = get_connection(dev, dba);
- if (conn->handle == 0)
- conn->secmode3 = TRUE;
-
- get_auth_info(index, dba, &conn->loc_auth);
-
- DBG("kernel auth requirements = 0x%02x", conn->loc_auth);
-
- match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
- if (match)
- key_info = match->data;
- else
- key_info = NULL;
-
- DBG("Matching key %s", key_info ? "found" : "not found");
-
- if (key_info == NULL || (!dev->debug_keys && key_info->type == 0x03)) {
- /* Link key not found */
- hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY,
- 6, dba);
- return;
- }
-
- /* Link key found */
-
- DBG("link key type 0x%02x", key_info->type);
-
- /* Don't use unauthenticated combination keys if MITM is
- * required */
- if (key_info->type == 0x04 && conn->loc_auth != 0xff &&
- (conn->loc_auth & 0x01))
- hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY,
- 6, dba);
- else {
- link_key_reply_cp lr;
-
- memcpy(lr.link_key, key_info->key, 16);
- bacpy(&lr.bdaddr, dba);
-
- hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_REPLY,
- LINK_KEY_REPLY_CP_SIZE, &lr);
- }
-}
-
-static void link_key_notify(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_link_key_notify *evt = ptr;
- bdaddr_t *dba = &evt->bdaddr;
- struct link_key_info *key_info;
- uint8_t old_key_type, key_type;
- struct bt_conn *conn;
- GSList *match;
- char da[18];
- uint8_t status = 0;
-
- ba2str(dba, da);
- DBG("hci%d dba %s type %d", index, da, evt->key_type);
-
- conn = get_connection(dev, &evt->bdaddr);
-
- match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
- if (match)
- key_info = match->data;
- else
- key_info = NULL;
-
- if (key_info == NULL) {
- key_info = g_new0(struct link_key_info, 1);
- bacpy(&key_info->bdaddr, &evt->bdaddr);
- old_key_type = 0xff;
- } else {
- dev->keys = g_slist_remove(dev->keys, key_info);
- old_key_type = key_info->type;
- }
-
- memcpy(key_info->key, evt->link_key, sizeof(evt->link_key));
- key_info->type = evt->key_type;
- key_info->pin_len = dev->pin_length;
-
- key_type = evt->key_type;
-
- DBG("key type 0x%02x old key type 0x%02x", key_type, old_key_type);
- DBG("local auth 0x%02x and remote auth 0x%02x",
- conn->loc_auth, conn->rem_auth);
-
- if (key_type == HCI_LK_CHANGED_COMBINATION) {
- /* Some buggy controller combinations generate a changed
- * combination key for legacy pairing even when there's no
- * previous key */
- if (conn->rem_auth == 0xff && old_key_type == 0xff)
- key_type = HCI_LK_COMBINATION;
- else if (old_key_type != 0xff)
- key_type = old_key_type;
- else
- /* This is Changed Combination Link Key for
- * a temporary link key.*/
- goto done;
- }
-
- key_info->type = key_type;
-
- /* Skip the storage check if this is a debug key */
- if (key_type == HCI_LK_DEBUG_COMBINATION)
- goto done;
-
- /* Store the link key persistently if one of the following is true:
- * 1. this is a legacy link key
- * 2. this is a changed combination key and there was a previously
- * stored one
- * 3. neither local nor remote side had no-bonding as a requirement
- * 4. the local side had dedicated bonding as a requirement
- * 5. the remote side is using dedicated bonding since in that case
- * also the local requirements are set to dedicated bonding
- * If none of the above match only keep the link key around for
- * this connection and set the temporary flag for the device.
- */
- if (key_type < HCI_LK_DEBUG_COMBINATION ||
- (key_type == HCI_LK_CHANGED_COMBINATION
- && old_key_type != HCI_LK_INVALID) ||
- (conn->loc_auth > 0x01 && conn->rem_auth > 0x01) ||
- (conn->loc_auth == 0x02 || conn->loc_auth == 0x03) ||
- (conn->rem_auth == 0x02 || conn->rem_auth == 0x03)) {
- int err;
-
- err = btd_event_link_key_notify(&dev->bdaddr, dba,
- evt->link_key, key_type,
- dev->pin_length);
-
- if (err == -ENODEV)
- status = HCI_OE_LOW_RESOURCES;
- else if (err < 0)
- status = HCI_MEMORY_FULL;
-
- goto done;
- }
-
-done:
- dev->pin_length = 0;
-
- if (status != 0) {
- g_free(key_info);
- bonding_complete(dev, conn, status);
- disconnect_addr(index, dba, status);
- return;
- }
-
- dev->keys = g_slist_prepend(dev->keys, key_info);
-
- /* If we're connected and not dedicated bonding initiators we're
- * done with the bonding process */
- if (!conn->bonding_initiator && conn->handle != 0)
- bonding_complete(dev, conn, 0);
-}
-
-static void return_link_keys(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_return_link_keys *evt = ptr;
- uint8_t num = evt->num_keys;
- unsigned char key[16];
- char da[18];
- bdaddr_t dba;
- int i;
-
- DBG("hci%d num_keys %u", index, num);
-
- ptr++;
-
- for (i = 0; i < num; i++) {
- bacpy(&dba, ptr); ba2str(&dba, da);
- memcpy(key, ptr + 6, 16);
-
- DBG("hci%d returned key for %s", index, da);
-
- btd_event_returned_link_key(&dev->bdaddr, &dba);
-
- ptr += 22;
- }
-}
-
-/* Simple Pairing handling */
-
-static int hciops_confirm_reply(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
- gboolean success)
-{
- struct dev_info *dev = &devs[index];
- user_confirm_reply_cp cp;
- char addr[18];
- int err;
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s success %d", index, addr, success);
-
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
-
- if (success)
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_USER_CONFIRM_REPLY,
- USER_CONFIRM_REPLY_CP_SIZE, &cp);
- else
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_USER_CONFIRM_NEG_REPLY,
- USER_CONFIRM_REPLY_CP_SIZE, &cp);
-
- if (err < 0)
- err = -errno;
-
- return err;
-}
-
-static void user_confirm_request(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_user_confirm_request *req = ptr;
- gboolean loc_mitm, rem_mitm;
- struct bt_conn *conn;
-
- DBG("hci%d", index);
-
- conn = find_connection(dev, &req->bdaddr);
- if (conn == NULL)
- return;
-
- loc_mitm = (conn->loc_auth & 0x01) ? TRUE : FALSE;
- rem_mitm = (conn->rem_auth & 0x01) ? TRUE : FALSE;
-
- /* If we require MITM but the remote device can't provide that
- * (it has NoInputNoOutput) then reject the confirmation
- * request. The only exception is when we're dedicated bonding
- * initiators since then we always have the MITM bit set. */
- if (!conn->bonding_initiator && loc_mitm && conn->rem_cap == 0x03) {
- error("Rejecting request: remote device can't provide MITM");
- goto fail;
- }
-
- /* If no side requires MITM protection; auto-accept */
- if ((conn->loc_auth == 0xff || !loc_mitm || conn->rem_cap == 0x03) &&
- (!rem_mitm || conn->loc_cap == 0x03)) {
- DBG("auto accept of confirmation");
-
- /* Wait 5 milliseconds before doing auto-accept */
- usleep(5000);
-
- if (hciops_confirm_reply(index, &req->bdaddr,
- BDADDR_BREDR, TRUE) < 0)
- goto fail;
-
- return;
- }
-
- if (btd_event_user_confirm(&dev->bdaddr, &req->bdaddr,
- btohl(req->passkey)) == 0)
- return;
-
-fail:
- hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_USER_CONFIRM_NEG_REPLY,
- 6, ptr);
-}
-
-static void user_passkey_request(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_user_passkey_request *req = ptr;
-
- DBG("hci%d", index);
-
- if (btd_event_user_passkey(&dev->bdaddr, &req->bdaddr) < 0)
- hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_USER_PASSKEY_NEG_REPLY, 6, ptr);
-}
-
-static void user_passkey_notify(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_user_passkey_notify *req = ptr;
-
- DBG("hci%d", index);
-
- btd_event_user_notify(&dev->bdaddr, &req->bdaddr,
- btohl(req->passkey));
-}
-
-static gint oob_bdaddr_cmp(gconstpointer a, gconstpointer b)
-{
- const struct oob_data *data = a;
- const bdaddr_t *bdaddr = b;
-
- return bacmp(&data->bdaddr, bdaddr);
-}
-
-static void remote_oob_data_request(int index, bdaddr_t *bdaddr)
-{
- struct dev_info *dev = &devs[index];
- GSList *match;
-
- DBG("hci%d", index);
-
- match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
-
- if (match) {
- struct oob_data *data;
- remote_oob_data_reply_cp cp;
-
- data = match->data;
-
- bacpy(&cp.bdaddr, &data->bdaddr);
- memcpy(cp.hash, data->hash, sizeof(cp.hash));
- memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
-
- dev->oob_data = g_slist_delete_link(dev->oob_data, match);
-
- hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_REPLY,
- REMOTE_OOB_DATA_REPLY_CP_SIZE, &cp);
-
- } else {
- hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_REMOTE_OOB_DATA_NEG_REPLY, 6, bdaddr);
- }
-}
-
-static int get_io_cap(int index, bdaddr_t *bdaddr, uint8_t *cap, uint8_t *auth)
-{
- struct dev_info *dev = &devs[index];
- struct bt_conn *conn;
- int err;
-
- conn = find_connection(dev, bdaddr);
- if (conn == NULL)
- return -ENOENT;
-
- err = get_auth_info(index, bdaddr, &conn->loc_auth);
- if (err < 0)
- return err;
-
- DBG("initial authentication requirement is 0x%02x", conn->loc_auth);
-
- if (!dev->pairable && !conn->bonding_initiator) {
- if (conn->rem_auth < 0x02) {
- DBG("Allowing no bonding in non-bondable mode");
- /* Kernel defaults to general bonding and so
- * overwrite for this special case. Otherwise
- * non-pairable test cases will fail. */
- conn->loc_auth = conn->rem_auth;
- goto done;
- }
-
- return -EPERM;
- }
-
- /* If the kernel doesn't know the local requirement just mirror
- * the remote one */
- if (conn->loc_auth == 0xff)
- conn->loc_auth = conn->rem_auth;
-
- if (conn->loc_auth == 0x00 || conn->loc_auth == 0x04) {
- /* If remote requests dedicated bonding follow that lead */
- if (conn->rem_auth == 0x02 || conn->rem_auth == 0x03) {
-
- /* If both remote and local IO capabilities allow MITM
- * then require it, otherwise don't */
- if (conn->rem_cap == 0x03 || conn->loc_cap == 0x03)
- conn->loc_auth = 0x02;
- else
- conn->loc_auth = 0x03;
- }
-
- /* If remote indicates no bonding then follow that. This
- * is important since the kernel might give general bonding
- * as default. */
- if (conn->rem_auth == 0x00 || conn->rem_auth == 0x01)
- conn->loc_auth = 0x00;
-
- /* If remote requires MITM then also require it, unless
- * our IO capability is NoInputNoOutput (so some
- * just-works security cases can be tested) */
- if (conn->rem_auth != 0xff && (conn->rem_auth & 0x01) &&
- conn->loc_cap != 0x03)
- conn->loc_auth |= 0x01;
- }
-
-done:
- *cap = conn->loc_cap;
- *auth = conn->loc_auth;
-
- DBG("final authentication requirement is 0x%02x", *auth);
-
- return 0;
-}
-
-static void io_capa_request(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- bdaddr_t *dba = ptr;
- uint8_t cap, auth = 0xff;
- char da[18];
- int err;
-
- ba2str(dba, da);
- DBG("hci%d IO capability request for %s", index, da);
-
- err = get_io_cap(index, dba, &cap, &auth);
- if (err < 0) {
- io_capability_neg_reply_cp cp;
-
- error("Getting IO capability failed: %s (%d)",
- strerror(-err), -err);
-
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, dba);
- cp.reason = HCI_PAIRING_NOT_ALLOWED;
- hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_IO_CAPABILITY_NEG_REPLY,
- IO_CAPABILITY_NEG_REPLY_CP_SIZE, &cp);
- } else {
- io_capability_reply_cp cp;
- struct bt_conn *conn;
- GSList *match;
-
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, dba);
- cp.capability = cap;
- cp.authentication = auth;
-
- conn = find_connection(dev, dba);
- match = g_slist_find_custom(dev->oob_data, dba, oob_bdaddr_cmp);
-
- if ((conn->bonding_initiator || conn->rem_oob_data == 0x01) &&
- match)
- cp.oob_data = 0x01;
- else
- cp.oob_data = 0x00;
-
- hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_IO_CAPABILITY_REPLY,
- IO_CAPABILITY_REPLY_CP_SIZE, &cp);
- }
-}
-
-static void io_capa_response(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_io_capability_response *evt = ptr;
- struct bt_conn *conn;
- char da[18];
-
- ba2str(&evt->bdaddr, da);
- DBG("hci%d IO capability response from %s", index, da);
-
- conn = find_connection(dev, &evt->bdaddr);
- if (conn) {
- conn->rem_cap = evt->capability;
- conn->rem_auth = evt->authentication;
- conn->rem_oob_data = evt->oob_data;
- }
-}
-
-/* PIN code handling */
-
-static void pin_code_request(int index, bdaddr_t *dba)
-{
- struct dev_info *dev = &devs[index];
- struct bt_conn *conn;
- char addr[18];
- int err;
-
- ba2str(dba, addr);
- DBG("hci%d PIN request for %s", index, addr);
-
- conn = get_connection(dev, dba);
- if (conn->handle == 0)
- conn->secmode3 = TRUE;
-
- /* Check if the adapter is not pairable and if there isn't a bonding in
- * progress */
- if (!dev->pairable && !conn->bonding_initiator) {
- DBG("Rejecting PIN request in non-pairable mode");
- goto reject;
- }
-
- err = btd_event_request_pin(&dev->bdaddr, dba, FALSE);
- if (err < 0) {
- error("PIN code negative reply: %s", strerror(-err));
- goto reject;
- }
-
- return;
-
-reject:
- hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, dba);
-}
-
-static inline void remote_features_notify(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_remote_host_features_notify *evt = ptr;
-
- if (evt->features[0] & 0x01)
- btd_event_set_legacy_pairing(&dev->bdaddr, &evt->bdaddr,
- FALSE);
- else
- btd_event_set_legacy_pairing(&dev->bdaddr, &evt->bdaddr,
- TRUE);
-
- write_features_info(&dev->bdaddr, &evt->bdaddr, NULL, evt->features);
-}
-
-static void read_local_version_complete(int index,
- const read_local_version_rp *rp)
-{
- struct dev_info *dev = &devs[index];
-
- if (rp->status)
- return;
-
- dev->ver.manufacturer = btohs(bt_get_unaligned(&rp->manufacturer));
- dev->ver.hci_ver = rp->hci_ver;
- dev->ver.hci_rev = btohs(bt_get_unaligned(&rp->hci_rev));
- dev->ver.lmp_ver = rp->lmp_ver;
- dev->ver.lmp_subver = btohs(bt_get_unaligned(&rp->lmp_subver));
-
- if (!dev->pending)
- return;
-
- hci_clear_bit(PENDING_VERSION, &dev->pending);
-
- DBG("Got version for hci%d", index);
-
- if (!dev->pending && dev->up)
- init_adapter(index);
-}
-
-static void read_local_features_complete(int index,
- const read_local_features_rp *rp)
-{
- struct dev_info *dev = &devs[index];
-
- if (rp->status)
- return;
-
- memcpy(dev->features, rp->features, 8);
-
- if (!dev->pending)
- return;
-
- hci_clear_bit(PENDING_FEATURES, &dev->pending);
-
- DBG("Got features for hci%d", index);
-
- if (!dev->pending && dev->up)
- init_adapter(index);
-}
-
-static void update_name(int index, const char *name)
-{
- struct btd_adapter *adapter;
-
- adapter = manager_find_adapter_by_id(index);
- if (adapter)
- adapter_name_changed(adapter, name);
-
- update_ext_inquiry_response(index);
-}
-
-static void read_local_name_complete(int index, read_local_name_rp *rp)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d status %u", index, rp->status);
-
- if (rp->status)
- return;
-
- memcpy(dev->name, rp->name, 248);
-
- if (!dev->pending) {
- update_name(index, (char *) rp->name);
- return;
- }
-
- hci_clear_bit(PENDING_NAME, &dev->pending);
-
- DBG("Got name for hci%d", index);
-
- if (!dev->pending && dev->up)
- init_adapter(index);
-}
-
-static void read_tx_power_complete(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
-
- read_inq_response_tx_power_level_rp *rp = ptr;
-
- DBG("hci%d status %u", index, rp->status);
-
- if (rp->status)
- return;
-
- dev->tx_power = rp->level;
- update_ext_inquiry_response(index);
-}
-
-static void read_simple_pairing_mode_complete(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- read_simple_pairing_mode_rp *rp = ptr;
-
- DBG("hci%d status %u", index, rp->status);
-
- if (rp->status)
- return;
-
- dev->ssp_mode = rp->mode;
- update_ext_inquiry_response(index);
-}
-
-static void read_local_ext_features_complete(int index,
- const read_local_ext_features_rp *rp)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d status %u", index, rp->status);
-
- if (rp->status)
- return;
-
- /* Local Extended feature page number is 1 */
- if (rp->page_num != 1)
- return;
-
- memcpy(dev->extfeatures, rp->features, sizeof(dev->extfeatures));
-}
-
-static void read_bd_addr_complete(int index, read_bd_addr_rp *rp)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d status %u", index, rp->status);
-
- if (rp->status)
- return;
-
- bacpy(&dev->bdaddr, &rp->bdaddr);
-
- if (!dev->pending)
- return;
-
- hci_clear_bit(PENDING_BDADDR, &dev->pending);
-
- DBG("Got bdaddr for hci%d", index);
-
- if (!dev->pending && dev->up)
- init_adapter(index);
-}
-
-static inline void cs_inquiry_evt(int index, uint8_t status)
-{
- if (status) {
- error("Inquiry Failed with status 0x%02x", status);
- return;
- }
-
- set_state(index, DISCOV_INQ);
-}
-
-static inline void cmd_status(int index, void *ptr)
-{
- evt_cmd_status *evt = ptr;
- uint16_t opcode = btohs(evt->opcode);
-
- if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))
- cs_inquiry_evt(index, evt->status);
-}
-
-static gboolean discoverable_timeout_handler(gpointer user_data)
-{
- struct dev_info *dev = user_data;
-
- hciops_set_discoverable(dev->id, FALSE, 0);
-
- return FALSE;
-}
-
-/* Limited Discoverable bit mask in CoD */
-#define LIMITED_BIT 0x002000
-
-static int hciops_set_limited_discoverable(int index, gboolean limited)
-{
- struct dev_info *dev = &devs[index];
- int num = (limited ? 2 : 1);
- uint8_t lap[] = { 0x33, 0x8b, 0x9e, 0x00, 0x8b, 0x9e };
- write_current_iac_lap_cp cp;
-
- DBG("hci%d limited %d", index, limited);
-
- /* Check if limited bit needs to be set/reset */
- if (limited)
- dev->wanted_cod |= LIMITED_BIT;
- else
- dev->wanted_cod &= ~LIMITED_BIT;
-
- /* If we dont need the toggling, save an unnecessary CoD write */
- if (dev->pending_cod || dev->wanted_cod == dev->current_cod)
- return 0;
-
- /*
- * 1: giac
- * 2: giac + liac
- */
- memset(&cp, 0, sizeof(cp));
- cp.num_current_iac = num;
- memcpy(&cp.lap, lap, num * 3);
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_CURRENT_IAC_LAP,
- (num * 3 + 1), &cp) < 0)
- return -errno;
-
- return write_class(index, dev->wanted_cod);
-}
-
-static void reset_discoverable_timeout(int index)
-{
- struct dev_info *dev = &devs[index];
-
- if (dev->discoverable_id > 0) {
- g_source_remove(dev->discoverable_id);
- dev->discoverable_id = 0;
- }
-}
-
-static void set_discoverable_timeout(int index)
-{
- struct dev_info *dev = &devs[index];
-
- reset_discoverable_timeout(index);
-
- if (dev->discoverable_timeout == 0) {
- hciops_set_limited_discoverable(index, FALSE);
- return;
- }
-
- /* Set limited discoverable if pairable and interval between 0 to 60
- sec */
- if (dev->pairable && dev->discoverable_timeout <= 60)
- hciops_set_limited_discoverable(index, TRUE);
- else
- hciops_set_limited_discoverable(index, FALSE);
-
- dev->discoverable_id = g_timeout_add_seconds(dev->discoverable_timeout,
- discoverable_timeout_handler,
- dev);
-}
-
-static void read_scan_complete(int index, uint8_t status, void *ptr)
-{
- struct btd_adapter *adapter;
- read_scan_enable_rp *rp = ptr;
-
- DBG("hci%d status %u", index, status);
-
- switch (rp->enable) {
- case (SCAN_PAGE | SCAN_INQUIRY):
- case SCAN_INQUIRY:
- set_discoverable_timeout(index);
- break;
- default:
- reset_discoverable_timeout(index);
- hciops_set_limited_discoverable(index, FALSE);
- }
-
- adapter = manager_find_adapter_by_id(index);
- if (!adapter) {
- error("Unable to find matching adapter");
- return;
- }
-
- adapter_mode_changed(adapter, rp->enable);
-}
-
-static void write_class_complete(int index, uint8_t status)
-{
- struct dev_info *dev = &devs[index];
- struct btd_adapter *adapter;
-
- if (status)
- return;
-
- if (dev->pending_cod == 0)
- return;
-
- dev->current_cod = dev->pending_cod;
- dev->pending_cod = 0;
-
- adapter = manager_find_adapter(&dev->bdaddr);
- if (adapter)
- btd_adapter_class_changed(adapter, dev->current_cod);
-
- update_ext_inquiry_response(index);
-
- if (dev->wanted_cod == dev->current_cod)
- return;
-
- if (dev->wanted_cod & LIMITED_BIT &&
- !(dev->current_cod & LIMITED_BIT))
- hciops_set_limited_discoverable(index, TRUE);
- else if (!(dev->wanted_cod & LIMITED_BIT) &&
- (dev->current_cod & LIMITED_BIT))
- hciops_set_limited_discoverable(index, FALSE);
- else
- write_class(index, dev->wanted_cod);
-}
-
-static void read_local_oob_data_complete(int index, uint8_t status,
- read_local_oob_data_rp *rp)
-{
- struct btd_adapter *adapter = manager_find_adapter_by_id(index);
-
- if (!adapter)
- return;
-
- if (status)
- oob_read_local_data_complete(adapter, NULL, NULL);
- else
- oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
-}
-
-static inline void inquiry_complete_evt(int index, uint8_t status)
-{
- int adapter_type;
- struct btd_adapter *adapter;
-
- if (status) {
- error("Inquiry Failed with status 0x%02x", status);
- return;
- }
-
- adapter = manager_find_adapter_by_id(index);
- if (!adapter) {
- error("No matching adapter found");
- return;
- }
-
- adapter_type = get_adapter_type(index);
-
- if (adapter_type == BR_EDR_LE &&
- start_scanning(index, TIMEOUT_BR_LE_SCAN) == 0)
- return;
-
- set_state(index, DISCOV_NAMES);
-}
-
-static inline void cc_inquiry_cancel(int index, uint8_t status)
-{
- if (status) {
- error("Inquiry Cancel Failed with status 0x%02x", status);
- return;
- }
-
- set_state(index, DISCOV_HALTED);
-}
-
-static inline void cc_le_set_scan_enable(int index, uint8_t status)
-{
- struct dev_info *info = &devs[index];
-
- if (status) {
- error("LE Set Scan Enable Failed with status 0x%02x", status);
- return;
- }
-
- if (info->discov_state == DISCOV_SCAN)
- set_state(index, DISCOV_HALTED);
- else
- set_state(index, DISCOV_SCAN);
-}
-
-static inline void cmd_complete(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_cmd_complete *evt = ptr;
- uint16_t opcode = btohs(evt->opcode);
- uint8_t status = *((uint8_t *) ptr + EVT_CMD_COMPLETE_SIZE);
-
- switch (opcode) {
- case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION):
- ptr += sizeof(evt_cmd_complete);
- read_local_version_complete(index, ptr);
- break;
- case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES):
- ptr += sizeof(evt_cmd_complete);
- read_local_features_complete(index, ptr);
- break;
- case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES):
- ptr += sizeof(evt_cmd_complete);
- read_local_ext_features_complete(index, ptr);
- break;
- case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR):
- ptr += sizeof(evt_cmd_complete);
- read_bd_addr_complete(index, ptr);
- break;
- case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL):
- cc_inquiry_cancel(index, status);
- break;
- case cmd_opcode_pack(OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE):
- cc_le_set_scan_enable(index, status);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
- if (!status)
- hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_READ_LOCAL_NAME, 0, 0);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE):
- hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_SCAN_ENABLE,
- 0, NULL);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE):
- ptr += sizeof(evt_cmd_complete);
- read_scan_complete(index, status, ptr);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV):
- write_class_complete(index, status);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SIMPLE_PAIRING_MODE):
- if (!status)
- hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_READ_SIMPLE_PAIRING_MODE, 0, NULL);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SIMPLE_PAIRING_MODE):
- ptr += sizeof(evt_cmd_complete);
- read_simple_pairing_mode_complete(index, ptr);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME):
- ptr += sizeof(evt_cmd_complete);
- read_local_name_complete(index, ptr);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL,
- OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL):
- ptr += sizeof(evt_cmd_complete);
- read_tx_power_complete(index, ptr);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA):
- ptr += sizeof(evt_cmd_complete);
- read_local_oob_data_complete(index, status, ptr);
- break;
- };
-}
-
-static inline void remote_name_information(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_remote_name_req_complete *evt = ptr;
- struct btd_adapter *adapter;
- char name[MAX_NAME_LENGTH + 1];
- struct found_dev *found;
-
- GSList *match;
-
- DBG("hci%d status %u", index, evt->status);
-
- memset(name, 0, sizeof(name));
-
- if (evt->status == 0) {
- memcpy(name, evt->name, MAX_NAME_LENGTH);
- btd_event_remote_name(&dev->bdaddr, &evt->bdaddr, name);
- }
-
- adapter = manager_find_adapter_by_id(index);
- if (!adapter) {
- error("No matching adapter found");
- return;
- }
-
- match = g_slist_find_custom(dev->need_name, &evt->bdaddr,
- found_dev_bda_cmp);
- if (match == NULL)
- return;
-
- found = match->data;
- found->name_state = NAME_NOT_NEEDED;
-
- dev->need_name = g_slist_remove_link(dev->need_name, match);
-
- match->next = dev->found_devs;
- dev->found_devs = match;
- dev->found_devs = g_slist_sort(dev->found_devs, found_dev_rssi_cmp);
-
- if (resolve_names(dev, adapter) < 0)
- set_state(index, DISCOV_HALTED);
-}
-
-static inline void remote_version_information(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_read_remote_version_complete *evt = ptr;
- struct bt_conn *conn;
-
- DBG("hci%d status %u", index, evt->status);
-
- if (evt->status)
- return;
-
- conn = find_conn_by_handle(dev, btohs(evt->handle));
- if (conn == NULL)
- return;
-
- write_version_info(&dev->bdaddr, &conn->bdaddr,
- btohs(evt->manufacturer), evt->lmp_ver,
- btohs(evt->lmp_subver));
-}
-
-static void dev_found(struct dev_info *info, bdaddr_t *dba, uint8_t bdaddr_type,
- uint8_t *cod, int8_t rssi, uint8_t cfm_name,
- uint8_t *eir, size_t eir_len)
-{
- struct found_dev *dev;
- GSList *match;
-
- match = g_slist_find_custom(info->found_devs, dba, found_dev_bda_cmp);
- if (match != NULL) {
- cfm_name = 0;
- goto event;
- }
-
- dev = g_new0(struct found_dev, 1);
- bacpy(&dev->bdaddr, dba);
- dev->rssi = rssi;
- if (cfm_name)
- dev->name_state = NAME_UNKNOWN;
- else
- dev->name_state = NAME_NOT_NEEDED;
-
- if (cod && !eir_has_data_type(eir, eir_len, EIR_CLASS_OF_DEV))
- eir_len = eir_append_data(eir, eir_len, EIR_CLASS_OF_DEV,
- cod, 3);
-
- info->found_devs = g_slist_prepend(info->found_devs, dev);
-
-event:
- btd_event_device_found(&info->bdaddr, dba, bdaddr_type, rssi, cfm_name,
- eir, eir_len);
-}
-
-static inline void inquiry_result(int index, int plen, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- uint8_t num = *(uint8_t *) ptr++;
- int i;
-
- for (i = 0; i < num; i++) {
- inquiry_info *info = ptr;
- uint8_t eir[5];
-
- memset(eir, 0, sizeof(eir));
- dev_found(dev, &info->bdaddr, BDADDR_BREDR, info->dev_class,
- 0, 1, eir, 0);
- ptr += INQUIRY_INFO_SIZE;
- }
-}
-
-static inline void inquiry_result_with_rssi(int index, int plen, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- uint8_t num = *(uint8_t *) ptr++;
- uint8_t eir[5];
- int i;
-
- if (!num)
- return;
-
- if ((plen - 1) / num == INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE) {
- for (i = 0; i < num; i++) {
- inquiry_info_with_rssi_and_pscan_mode *info = ptr;
-
- memset(eir, 0, sizeof(eir));
- dev_found(dev, &info->bdaddr, BDADDR_BREDR,
- info->dev_class, info->rssi,
- 1, eir, 0);
- ptr += INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
- }
- } else {
- for (i = 0; i < num; i++) {
- inquiry_info_with_rssi *info = ptr;
-
- memset(eir, 0, sizeof(eir));
- dev_found(dev, &info->bdaddr, BDADDR_BREDR,
- info->dev_class, info->rssi,
- 1, eir, 0);
- ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
- }
- }
-}
-
-static inline void extended_inquiry_result(int index, int plen, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- uint8_t num = *(uint8_t *) ptr++;
- int i;
-
- for (i = 0; i < num; i++) {
- extended_inquiry_info *info = ptr;
- uint8_t eir[sizeof(info->data) + 5];
- gboolean cfm_name;
- size_t eir_len;
-
- eir_len = eir_length(info->data, sizeof(info->data));
-
- memset(eir, 0, sizeof(eir));
- memcpy(eir, info->data, eir_len);
-
- if (eir_has_data_type(eir, eir_len, EIR_NAME_COMPLETE))
- cfm_name = FALSE;
- else
- cfm_name = TRUE;
-
- dev_found(dev, &info->bdaddr, BDADDR_BREDR, info->dev_class,
- info->rssi, cfm_name, eir, eir_len);
- ptr += EXTENDED_INQUIRY_INFO_SIZE;
- }
-}
-
-static inline void remote_features_information(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_read_remote_features_complete *evt = ptr;
- struct bt_conn *conn;
-
- DBG("hci%d status %u", index, evt->status);
-
- if (evt->status)
- return;
-
- conn = find_conn_by_handle(dev, btohs(evt->handle));
- if (conn == NULL)
- return;
-
- write_features_info(&dev->bdaddr, &conn->bdaddr, evt->features, NULL);
-}
-
-struct remote_version_req {
- int index;
- uint16_t handle;
-};
-
-static gboolean __get_remote_version(gpointer user_data)
-{
- struct remote_version_req *req = user_data;
- struct dev_info *dev = &devs[req->index];
- read_remote_version_cp cp;
-
- DBG("hci%d handle %u", req->index, req->handle);
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = htobs(req->handle);
-
- hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_READ_REMOTE_VERSION,
- READ_REMOTE_VERSION_CP_SIZE, &cp);
-
- return FALSE;
-}
-
-static void get_remote_version(int index, uint16_t handle)
-{
- struct remote_version_req *req;
-
- req = g_new0(struct remote_version_req, 1);
- req->handle = handle;
- req->index = index;
-
- g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1, __get_remote_version,
- req, g_free);
-}
-
-static void conn_free(struct bt_conn *conn)
-{
- if (conn->io != NULL) {
- g_io_channel_shutdown(conn->io, TRUE, NULL);
- g_io_channel_unref(conn->io);
- }
-
- g_free(conn);
-}
-
-static inline void conn_failed(int index, bdaddr_t *bdaddr, uint8_t status)
-{
- struct dev_info *dev = &devs[index];
- struct bt_conn *conn;
-
- btd_event_conn_failed(&dev->bdaddr, bdaddr, status);
-
- conn = find_connection(dev, bdaddr);
- if (conn == NULL)
- return;
-
- bonding_complete(dev, conn, status);
-
- dev->connections = g_slist_remove(dev->connections, conn);
- conn_free(conn);
-}
-
-static inline void conn_complete(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_conn_complete *evt = ptr;
- char filename[PATH_MAX];
- char local_addr[18], peer_addr[18], *str;
- struct bt_conn *conn;
-
- if (evt->link_type != ACL_LINK)
- return;
-
- DBG("status 0x%02x", evt->status);
-
- if (evt->status != 0) {
- conn_failed(index, &evt->bdaddr, evt->status);
- return;
- }
-
- conn = get_connection(dev, &evt->bdaddr);
- conn->handle = btohs(evt->handle);
-
- btd_event_conn_complete(&dev->bdaddr, &evt->bdaddr, BDADDR_BREDR,
- NULL, NULL);
-
- if (conn->secmode3)
- bonding_complete(dev, conn, 0);
-
- /* check if the remote version needs be requested */
- ba2str(&dev->bdaddr, local_addr);
- ba2str(&evt->bdaddr, peer_addr);
-
- create_name(filename, sizeof(filename), STORAGEDIR, local_addr,
- "manufacturers");
-
- str = textfile_get(filename, peer_addr);
- if (!str)
- get_remote_version(index, btohs(evt->handle));
- else
- free(str);
-}
-
-static inline uint8_t le_addr_type(uint8_t bdaddr_type)
-{
- switch (bdaddr_type) {
- case LE_RANDOM_ADDRESS:
- return BDADDR_LE_RANDOM;
- case LE_PUBLIC_ADDRESS:
- default:
- return BDADDR_LE_PUBLIC;
- }
-}
-
-static inline void le_conn_complete(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_le_connection_complete *evt = ptr;
- char filename[PATH_MAX];
- char local_addr[18], peer_addr[18], *str;
- struct bt_conn *conn;
- uint8_t bdaddr_type;
-
- if (evt->status) {
- btd_event_conn_failed(&dev->bdaddr, &evt->peer_bdaddr,
- evt->status);
- return;
- }
-
- conn = get_connection(dev, &evt->peer_bdaddr);
- conn->handle = btohs(evt->handle);
-
- bdaddr_type = le_addr_type(evt->peer_bdaddr_type);
- btd_event_conn_complete(&dev->bdaddr, &evt->peer_bdaddr, bdaddr_type,
- NULL, NULL);
-
- /* check if the remote version needs be requested */
- ba2str(&dev->bdaddr, local_addr);
- ba2str(&evt->peer_bdaddr, peer_addr);
-
- create_name(filename, sizeof(filename), STORAGEDIR, local_addr,
- "manufacturers");
-
- str = textfile_get(filename, peer_addr);
- if (!str)
- get_remote_version(index, btohs(evt->handle));
- else
- free(str);
-}
-
-static inline void disconn_complete(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_disconn_complete *evt = ptr;
- struct bt_conn *conn;
-
- DBG("handle %u status 0x%02x", btohs(evt->handle), evt->status);
-
- if (evt->status != 0)
- return;
-
- conn = find_conn_by_handle(dev, btohs(evt->handle));
- if (conn == NULL)
- return;
-
- dev->connections = g_slist_remove(dev->connections, conn);
-
- btd_event_disconn_complete(&dev->bdaddr, &conn->bdaddr);
-
- conn_free(conn);
-}
-
-static inline void auth_complete(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_auth_complete *evt = ptr;
- struct bt_conn *conn;
-
- DBG("hci%d status %u", index, evt->status);
-
- conn = find_conn_by_handle(dev, btohs(evt->handle));
- if (conn == NULL)
- return;
-
- bonding_complete(dev, conn, evt->status);
-}
-
-static inline void simple_pairing_complete(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_simple_pairing_complete *evt = ptr;
-
- DBG("hci%d status %u", index, evt->status);
-
- btd_event_simple_pairing_complete(&dev->bdaddr, &evt->bdaddr,
- evt->status);
-}
-
-static inline void conn_request(int index, void *ptr)
-{
- struct dev_info *dev = &devs[index];
- evt_conn_request *evt = ptr;
- uint32_t class = evt->dev_class[0] | (evt->dev_class[1] << 8)
- | (evt->dev_class[2] << 16);
-
- btd_event_remote_class(&dev->bdaddr, &evt->bdaddr, class);
-}
-
-static inline void le_advertising_report(int index, evt_le_meta_event *meta)
-{
- struct dev_info *dev = &devs[index];
- le_advertising_info *info;
- uint8_t num_reports, rssi;
- const uint8_t RSSI_SIZE = 1;
-
- num_reports = meta->data[0];
-
- info = (le_advertising_info *) &meta->data[1];
- rssi = *(info->data + info->length);
-
- dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type), NULL,
- rssi, 0, info->data, info->length);
-
- num_reports--;
-
- while (num_reports--) {
- info = (le_advertising_info *) (info->data + info->length +
- RSSI_SIZE);
- rssi = *(info->data + info->length);
-
- dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type),
- NULL, rssi, 0, info->data, info->length);
- }
-}
-
-static inline void le_metaevent(int index, void *ptr)
-{
- evt_le_meta_event *meta = ptr;
-
- DBG("hci%d LE Meta Event %u", index, meta->subevent);
-
- switch (meta->subevent) {
- case EVT_LE_ADVERTISING_REPORT:
- le_advertising_report(index, meta);
- break;
-
- case EVT_LE_CONN_COMPLETE:
- le_conn_complete(index, meta->data);
- break;
- }
-}
-
-static void stop_hci_dev(int index)
-{
- struct dev_info *dev = &devs[index];
-
- if (dev->sk < 0)
- return;
-
- info("Stopping hci%d event socket", index);
-
- if (dev->watch_id > 0)
- g_source_remove(dev->watch_id);
-
- if (dev->stop_scan_id > 0)
- g_source_remove(dev->stop_scan_id);
-
- if (dev->io != NULL)
- g_io_channel_unref(dev->io);
-
- hci_close_dev(dev->sk);
-
- g_slist_free_full(dev->keys, g_free);
- g_slist_free_full(dev->uuids, g_free);
- g_slist_free_full(dev->connections, g_free);
-
- init_dev_info(index, -1, dev->registered, dev->already_up);
-}
-
-static gboolean io_security_event(GIOChannel *chan, GIOCondition cond,
- gpointer data)
-{
- unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
- int type, index = GPOINTER_TO_INT(data);
- struct dev_info *dev = &devs[index];
- struct hci_dev_info di;
- ssize_t len;
- hci_event_hdr *eh;
- evt_cmd_status *evt;
- int fd;
-
- if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
- stop_hci_dev(index);
- return FALSE;
- }
-
- fd = g_io_channel_unix_get_fd(chan);
-
- len = read(fd, buf, sizeof(buf));
- if (len < 0) {
- if (errno == EAGAIN)
- return TRUE;
- stop_hci_dev(index);
- return FALSE;
- }
-
- type = *ptr++;
-
- if (type != HCI_EVENT_PKT)
- return TRUE;
-
- eh = (hci_event_hdr *) ptr;
- ptr += HCI_EVENT_HDR_SIZE;
-
- memset(&di, 0, sizeof(di));
- if (hci_devinfo(index, &di) == 0) {
- bacpy(&dev->bdaddr, &di.bdaddr);
-
- if (ignore_device(&di))
- return TRUE;
- }
-
- switch (eh->evt) {
- case EVT_CMD_STATUS:
- cmd_status(index, ptr);
- break;
-
- case EVT_CMD_COMPLETE:
- cmd_complete(index, ptr);
- break;
-
- case EVT_REMOTE_NAME_REQ_COMPLETE:
- remote_name_information(index, ptr);
- break;
-
- case EVT_READ_REMOTE_VERSION_COMPLETE:
- remote_version_information(index, ptr);
- break;
-
- case EVT_READ_REMOTE_FEATURES_COMPLETE:
- remote_features_information(index, ptr);
- break;
-
- case EVT_REMOTE_HOST_FEATURES_NOTIFY:
- remote_features_notify(index, ptr);
- break;
-
- case EVT_INQUIRY_COMPLETE:
- evt = (evt_cmd_status *) ptr;
- inquiry_complete_evt(index, evt->status);
- break;
-
- case EVT_INQUIRY_RESULT:
- inquiry_result(index, eh->plen, ptr);
- break;
-
- case EVT_INQUIRY_RESULT_WITH_RSSI:
- inquiry_result_with_rssi(index, eh->plen, ptr);
- break;
-
- case EVT_EXTENDED_INQUIRY_RESULT:
- extended_inquiry_result(index, eh->plen, ptr);
- break;
-
- case EVT_CONN_COMPLETE:
- conn_complete(index, ptr);
- break;
-
- case EVT_DISCONN_COMPLETE:
- disconn_complete(index, ptr);
- break;
-
- case EVT_AUTH_COMPLETE:
- auth_complete(index, ptr);
- break;
-
- case EVT_SIMPLE_PAIRING_COMPLETE:
- simple_pairing_complete(index, ptr);
- break;
-
- case EVT_CONN_REQUEST:
- conn_request(index, ptr);
- break;
- case EVT_LE_META_EVENT:
- le_metaevent(index, ptr);
- break;
- case EVT_PIN_CODE_REQ:
- pin_code_request(index, (bdaddr_t *) ptr);
- break;
-
- case EVT_LINK_KEY_REQ:
- link_key_request(index, (bdaddr_t *) ptr);
- break;
-
- case EVT_LINK_KEY_NOTIFY:
- link_key_notify(index, ptr);
- break;
-
- case EVT_RETURN_LINK_KEYS:
- return_link_keys(index, ptr);
- break;
-
- case EVT_IO_CAPABILITY_REQUEST:
- io_capa_request(index, ptr);
- break;
-
- case EVT_IO_CAPABILITY_RESPONSE:
- io_capa_response(index, ptr);
- break;
-
- case EVT_USER_CONFIRM_REQUEST:
- user_confirm_request(index, ptr);
- break;
-
- case EVT_USER_PASSKEY_REQUEST:
- user_passkey_request(index, ptr);
- break;
-
- case EVT_USER_PASSKEY_NOTIFY:
- user_passkey_notify(index, ptr);
- break;
-
- case EVT_REMOTE_OOB_DATA_REQUEST:
- remote_oob_data_request(index, (bdaddr_t *) ptr);
- break;
- }
-
- return TRUE;
-}
-
-static void start_hci_dev(int index)
-{
- struct dev_info *dev = &devs[index];
- GIOChannel *chan = dev->io;
- GIOCondition cond;
- struct hci_filter flt;
-
- if (chan)
- return;
-
- info("Listening for HCI events on hci%d", index);
-
- /* Set filter */
- hci_filter_clear(&flt);
- hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
- hci_filter_set_event(EVT_CMD_STATUS, &flt);
- hci_filter_set_event(EVT_CMD_COMPLETE, &flt);
- hci_filter_set_event(EVT_PIN_CODE_REQ, &flt);
- hci_filter_set_event(EVT_LINK_KEY_REQ, &flt);
- hci_filter_set_event(EVT_LINK_KEY_NOTIFY, &flt);
- hci_filter_set_event(EVT_RETURN_LINK_KEYS, &flt);
- hci_filter_set_event(EVT_IO_CAPABILITY_REQUEST, &flt);
- hci_filter_set_event(EVT_IO_CAPABILITY_RESPONSE, &flt);
- hci_filter_set_event(EVT_USER_CONFIRM_REQUEST, &flt);
- hci_filter_set_event(EVT_USER_PASSKEY_REQUEST, &flt);
- hci_filter_set_event(EVT_REMOTE_OOB_DATA_REQUEST, &flt);
- hci_filter_set_event(EVT_USER_PASSKEY_NOTIFY, &flt);
- hci_filter_set_event(EVT_KEYPRESS_NOTIFY, &flt);
- hci_filter_set_event(EVT_SIMPLE_PAIRING_COMPLETE, &flt);
- hci_filter_set_event(EVT_AUTH_COMPLETE, &flt);
- hci_filter_set_event(EVT_REMOTE_NAME_REQ_COMPLETE, &flt);
- hci_filter_set_event(EVT_READ_REMOTE_VERSION_COMPLETE, &flt);
- hci_filter_set_event(EVT_READ_REMOTE_FEATURES_COMPLETE, &flt);
- hci_filter_set_event(EVT_REMOTE_HOST_FEATURES_NOTIFY, &flt);
- hci_filter_set_event(EVT_INQUIRY_COMPLETE, &flt);
- hci_filter_set_event(EVT_INQUIRY_RESULT, &flt);
- hci_filter_set_event(EVT_INQUIRY_RESULT_WITH_RSSI, &flt);
- hci_filter_set_event(EVT_EXTENDED_INQUIRY_RESULT, &flt);
- hci_filter_set_event(EVT_CONN_REQUEST, &flt);
- hci_filter_set_event(EVT_CONN_COMPLETE, &flt);
- hci_filter_set_event(EVT_DISCONN_COMPLETE, &flt);
- hci_filter_set_event(EVT_LE_META_EVENT, &flt);
- if (setsockopt(dev->sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
- error("Can't set filter on hci%d: %s (%d)",
- index, strerror(errno), errno);
- return;
- }
-
- chan = g_io_channel_unix_new(dev->sk);
- cond = G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR;
- dev->watch_id = g_io_add_watch_full(chan, G_PRIORITY_LOW, cond,
- io_security_event,
- GINT_TO_POINTER(index), NULL);
- dev->io = chan;
- dev->pin_length = 0;
-
-}
-
-/* End of HCI event callbacks */
-
-static gboolean child_exit(GIOChannel *io, GIOCondition cond, void *user_data)
-{
- int status, fd = g_io_channel_unix_get_fd(io);
- pid_t child_pid;
-
- if (read(fd, &child_pid, sizeof(child_pid)) != sizeof(child_pid)) {
- error("child_exit: unable to read child pid from pipe");
- return TRUE;
- }
-
- if (waitpid(child_pid, &status, 0) != child_pid)
- error("waitpid(%d) failed", child_pid);
- else
- DBG("child %d exited", child_pid);
-
- return TRUE;
-}
-
-static void at_child_exit(void)
-{
- pid_t pid = getpid();
-
- if (write(child_pipe[1], &pid, sizeof(pid)) != sizeof(pid))
- error("unable to write to child pipe");
-}
-
-static void device_devup_setup(int index)
-{
- struct dev_info *dev = &devs[index];
- struct hci_dev_info di;
- read_stored_link_key_cp cp;
-
- DBG("hci%d", index);
-
- if (hci_devinfo(index, &di) < 0)
- return;
-
- if (ignore_device(&di))
- return;
-
- bacpy(&dev->bdaddr, &di.bdaddr);
- memcpy(dev->features, di.features, 8);
-
- if (dev->features[7] & LMP_EXT_FEAT) {
- uint8_t page_num = 0x01;
-
- hci_send_cmd(dev->sk, OGF_INFO_PARAM,
- OCF_READ_LOCAL_EXT_FEATURES, 1, &page_num);
- }
-
- /* Set page timeout */
- if ((main_opts.flags & (1 << HCID_SET_PAGETO))) {
- write_page_timeout_cp cp;
-
- cp.timeout = htobs(main_opts.pageto);
- hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_PAGE_TIMEOUT,
- WRITE_PAGE_TIMEOUT_CP_SIZE, &cp);
- }
-
- bacpy(&cp.bdaddr, BDADDR_ANY);
- cp.read_all = 1;
- hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_STORED_LINK_KEY,
- READ_STORED_LINK_KEY_CP_SIZE, &cp);
-
- if (!dev->pending) {
- init_adapter(index);
- return;
- }
-
- /* Even though it shouldn't happen (assuming the kernel behaves
- * properly) it seems like we might miss the very first
- * initialization commands that the kernel sends. So check for
- * it here and resend the ones we haven't seen their results yet */
-
- if (hci_test_bit(PENDING_FEATURES, &dev->pending))
- hci_send_cmd(dev->sk, OGF_INFO_PARAM,
- OCF_READ_LOCAL_FEATURES, 0, NULL);
-
- if (hci_test_bit(PENDING_VERSION, &dev->pending))
- hci_send_cmd(dev->sk, OGF_INFO_PARAM,
- OCF_READ_LOCAL_VERSION, 0, NULL);
-
- if (hci_test_bit(PENDING_NAME, &dev->pending))
- hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_READ_LOCAL_NAME, 0, 0);
-
- if (hci_test_bit(PENDING_BDADDR, &dev->pending))
- hci_send_cmd(dev->sk, OGF_INFO_PARAM,
- OCF_READ_BD_ADDR, 0, NULL);
-}
-
-static void init_pending(int index)
-{
- struct dev_info *dev = &devs[index];
-
- hci_set_bit(PENDING_BDADDR, &dev->pending);
- hci_set_bit(PENDING_VERSION, &dev->pending);
- hci_set_bit(PENDING_FEATURES, &dev->pending);
- hci_set_bit(PENDING_NAME, &dev->pending);
-}
-
-static struct dev_info *init_device(int index, gboolean already_up)
-{
- struct dev_info *dev;
- struct hci_dev_req dr;
- int dd;
- pid_t pid;
-
- DBG("hci%d", index);
-
- dd = hci_open_dev(index);
- if (dd < 0) {
- error("Unable to open hci%d: %s (%d)", index,
- strerror(errno), errno);
- return NULL;
- }
-
- if (index > max_dev) {
- max_dev = index;
- devs = g_realloc(devs, sizeof(devs[0]) * (max_dev + 1));
- }
-
- dev = init_dev_info(index, dd, FALSE, already_up);
- init_pending(index);
- start_hci_dev(index);
-
- /* Avoid forking if nothing else has to be done */
- if (already_up)
- return dev;
-
- /* Do initialization in the separate process */
- pid = fork();
- switch (pid) {
- case 0:
- atexit(at_child_exit);
- break;
- case -1:
- error("Fork failed. Can't init device hci%d: %s (%d)",
- index, strerror(errno), errno);
- default:
- DBG("child %d forked", pid);
- return dev;
- }
-
- memset(&dr, 0, sizeof(dr));
- dr.dev_id = index;
-
- /* Set link mode */
- dr.dev_opt = main_opts.link_mode;
- if (ioctl(dd, HCISETLINKMODE, (unsigned long) &dr) < 0)
- error("Can't set link mode on hci%d: %s (%d)",
- index, strerror(errno), errno);
-
- /* Start HCI device */
- if (ioctl(dd, HCIDEVUP, index) < 0 && errno != EALREADY) {
- error("Can't init device hci%d: %s (%d)",
- index, strerror(errno), errno);
- goto fail;
- }
-
- hci_close_dev(dd);
- exit(0);
-
-fail:
- hci_close_dev(dd);
- exit(1);
-}
-
-static void init_conn_list(int index)
-{
- struct dev_info *dev = &devs[index];
- struct hci_conn_list_req *cl;
- struct hci_conn_info *ci;
- int i;
-
- DBG("hci%d", index);
-
- cl = g_malloc0(10 * sizeof(*ci) + sizeof(*cl));
-
- cl->dev_id = index;
- cl->conn_num = 10;
- ci = cl->conn_info;
-
- if (ioctl(dev->sk, HCIGETCONNLIST, cl) < 0) {
- error("Unable to get connection list: %s (%d)",
- strerror(errno), errno);
- goto failed;
- }
-
- for (i = 0; i < cl->conn_num; i++, ci++) {
- struct bt_conn *conn;
-
- if (ci->type != ACL_LINK)
- continue;
-
- conn = get_connection(dev, &ci->bdaddr);
- conn->handle = ci->handle;
- }
-
-failed:
- g_free(cl);
-}
-
-static void device_event(int event, int index)
-{
- switch (event) {
- case HCI_DEV_REG:
- info("HCI dev %d registered", index);
- init_device(index, FALSE);
- break;
-
- case HCI_DEV_UNREG:
- info("HCI dev %d unregistered", index);
- stop_hci_dev(index);
- if (devs[index].registered)
- btd_manager_unregister_adapter(index);
- break;
-
- case HCI_DEV_UP:
- info("HCI dev %d up", index);
- devs[index].up = TRUE;
- device_devup_setup(index);
- break;
-
- case HCI_DEV_DOWN:
- info("HCI dev %d down", index);
- devs[index].up = FALSE;
- devs[index].pending_cod = 0;
- devs[index].cache_enable = TRUE;
- devs[index].discov_state = DISCOV_HALTED;
- reset_discoverable_timeout(index);
- if (!devs[index].pending) {
- struct btd_adapter *adapter;
-
- adapter = manager_find_adapter_by_id(index);
- if (adapter)
- btd_adapter_stop(adapter);
-
- init_pending(index);
- }
- break;
- }
-}
-
-static gboolean init_known_adapters(gpointer user_data)
-{
- struct hci_dev_list_req *dl;
- struct hci_dev_req *dr;
- int i, err, ctl = GPOINTER_TO_INT(user_data);
- size_t req_size;
-
- DBG("");
-
- req_size = HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t);
-
- dl = g_try_malloc0(req_size);
- if (!dl) {
- error("Can't allocate devlist buffer");
- return FALSE;
- }
-
- dl->dev_num = HCI_MAX_DEV;
- dr = dl->dev_req;
-
- if (ioctl(ctl, HCIGETDEVLIST, dl) < 0) {
- err = -errno;
- error("Can't get device list: %s (%d)", strerror(-err), -err);
- g_free(dl);
- return FALSE;
- }
-
- for (i = 0; i < dl->dev_num; i++, dr++) {
- struct dev_info *dev;
- gboolean already_up;
-
- already_up = hci_test_bit(HCI_UP, &dr->dev_opt);
-
- dev = init_device(dr->dev_id, already_up);
- if (dev == NULL)
- continue;
-
- if (!dev->already_up)
- continue;
-
- init_conn_list(dr->dev_id);
-
- dev->pending = 0;
- hci_set_bit(PENDING_VERSION, &dev->pending);
- hci_send_cmd(dev->sk, OGF_INFO_PARAM,
- OCF_READ_LOCAL_VERSION, 0, NULL);
- device_event(HCI_DEV_UP, dr->dev_id);
- }
-
- g_free(dl);
-
- return FALSE;
-}
-
-static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond,
- gpointer data)
-{
- unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
- evt_stack_internal *si;
- evt_si_device *sd;
- hci_event_hdr *eh;
- int type, fd;
- ssize_t len;
-
- ptr = buf;
-
- fd = g_io_channel_unix_get_fd(chan);
-
- len = read(fd, buf, sizeof(buf));
- if (len < 0) {
- if (errno == EAGAIN)
- return TRUE;
-
- error("Read from control socket failed: %s (%d)",
- strerror(errno), errno);
- return FALSE;
- }
-
- type = *ptr++;
-
- if (type != HCI_EVENT_PKT)
- return TRUE;
-
- eh = (hci_event_hdr *) ptr;
- if (eh->evt != EVT_STACK_INTERNAL)
- return TRUE;
-
- ptr += HCI_EVENT_HDR_SIZE;
-
- si = (evt_stack_internal *) ptr;
- switch (si->type) {
- case EVT_SI_DEVICE:
- sd = (void *) &si->data;
- device_event(sd->event, sd->dev_id);
- break;
- }
-
- return TRUE;
-}
-
-static int hciops_setup(void)
-{
- struct sockaddr_hci addr;
- struct hci_filter flt;
- GIOChannel *ctl_io, *child_io;
- int sock, err;
-
- DBG("");
-
- if (child_pipe[0] != -1)
- return -EALREADY;
-
- if (pipe(child_pipe) < 0) {
- err = -errno;
- error("pipe(): %s (%d)", strerror(-err), -err);
- return err;
- }
-
- child_io = g_io_channel_unix_new(child_pipe[0]);
- g_io_channel_set_close_on_unref(child_io, TRUE);
- child_io_id = g_io_add_watch(child_io,
- G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
- child_exit, NULL);
- g_io_channel_unref(child_io);
-
- /* Create and bind HCI socket */
- sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
- if (sock < 0) {
- err = -errno;
- error("Can't open HCI socket: %s (%d)", strerror(-err),
- -err);
- return err;
- }
-
- /* Set filter */
- hci_filter_clear(&flt);
- hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
- hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
- if (setsockopt(sock, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
- err = -errno;
- error("Can't set filter: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.hci_family = AF_BLUETOOTH;
- addr.hci_dev = HCI_DEV_NONE;
- if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = -errno;
- error("Can't bind HCI socket: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- ctl_io = g_io_channel_unix_new(sock);
- g_io_channel_set_close_on_unref(ctl_io, TRUE);
-
- ctl_io_id = g_io_add_watch(ctl_io, G_IO_IN, io_stack_event, NULL);
-
- g_io_channel_unref(ctl_io);
-
- g_idle_add(init_known_adapters, GINT_TO_POINTER(sock));
-
- return 0;
-}
-
-static void hciops_cleanup(void)
-{
- int i;
-
- DBG("");
-
- for (i = 0; i <= max_dev; i++)
- stop_hci_dev(i);
-
- g_free(devs);
- devs = NULL;
- max_dev = -1;
-
- if (child_io_id) {
- g_source_remove(child_io_id);
- child_io_id = 0;
- }
-
- if (ctl_io_id) {
- g_source_remove(ctl_io_id);
- ctl_io_id = 0;
- }
-
- if (child_pipe[0] >= 0) {
- close(child_pipe[0]);
- child_pipe[0] = -1;
- }
-
- if (child_pipe[1] >= 0) {
- close(child_pipe[1]);
- child_pipe[1] = -1;
- }
-}
-
-static int hciops_set_powered(int index, gboolean powered)
-{
- struct dev_info *dev = &devs[index];
- int err;
-
- DBG("hci%d powered %d", index, powered);
-
- if (powered == FALSE)
- return hciops_power_off(index);
-
- if (ioctl(dev->sk, HCIDEVUP, index) == 0)
- return 0;
-
- if (errno == EALREADY)
- return 0;
-
- err = -errno;
- error("Can't init device hci%d: %s (%d)",
- index, strerror(-err), -err);
-
- return err;
-}
-
-static int start_inquiry(int index, uint8_t length)
-{
- struct dev_info *dev = &devs[index];
- uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
- inquiry_cp inq_cp;
-
- DBG("hci%d length %u", index, length);
-
- memset(&inq_cp, 0, sizeof(inq_cp));
- memcpy(&inq_cp.lap, lap, 3);
- inq_cp.length = length;
- inq_cp.num_rsp = 0x00;
-
- if (hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static int le_set_scan_enable(int index, uint8_t enable)
-{
- struct dev_info *dev = &devs[index];
- le_set_scan_enable_cp cp;
-
- DBG("hci%d enable %u", index, enable);
-
- memset(&cp, 0, sizeof(cp));
- cp.enable = enable;
- cp.filter_dup = 0;
-
- if (hci_send_cmd(dev->sk, OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE,
- LE_SET_SCAN_ENABLE_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static gboolean stop_le_scan_cb(gpointer user_data)
-{
- struct dev_info *dev = user_data;
- int err;
-
- err = le_set_scan_enable(dev->id, 0);
- if (err < 0)
- return TRUE;
-
- dev->stop_scan_id = 0;
-
- return FALSE;
-}
-
-static int start_scanning(int index, int timeout)
-{
- struct dev_info *dev = &devs[index];
- le_set_scan_parameters_cp cp;
- int err;
-
- DBG("hci%d", index);
-
- memset(&cp, 0, sizeof(cp));
- cp.type = 0x01; /* Active scanning */
- /* The recommended value for scan interval and window is 11.25 msec.
- * It is calculated by: time = n * 0.625 msec */
- cp.interval = htobs(0x0012);
- cp.window = htobs(0x0012);
- cp.own_bdaddr_type = 0; /* Public address */
- cp.filter = 0; /* Accept all adv packets */
-
- if (hci_send_cmd(dev->sk, OGF_LE_CTL, OCF_LE_SET_SCAN_PARAMETERS,
- LE_SET_SCAN_PARAMETERS_CP_SIZE, &cp) < 0)
- return -errno;
-
- err = le_set_scan_enable(index, 1);
- if (err < 0)
- return err;
-
- /* Schedule a le scan disable in 'timeout' milliseconds */
- dev->stop_scan_id = g_timeout_add(timeout, stop_le_scan_cb, dev);
-
- return 0;
-}
-
-static int hciops_stop_scanning(int index)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d", index);
-
- if (dev->stop_scan_id > 0) {
- g_source_remove(dev->stop_scan_id);
- dev->stop_scan_id = 0;
- }
-
- return le_set_scan_enable(index, 0);
-}
-
-static int cancel_resolve_name(int index)
-{
- struct dev_info *info = &devs[index];
- struct found_dev *dev;
- remote_name_req_cancel_cp cp;
- struct btd_adapter *adapter;
-
- DBG("hci%d", index);
-
- if (g_slist_length(info->need_name) == 0)
- return 0;
-
- dev = info->need_name->data;
- if (dev->name_state != NAME_PENDING)
- return 0;
-
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, &dev->bdaddr);
-
- adapter = manager_find_adapter_by_id(index);
- if (adapter)
- adapter_set_discovering(adapter, FALSE);
-
- found_dev_cleanup(info);
-
- if (hci_send_cmd(info->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL,
- REMOTE_NAME_REQ_CANCEL_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static int hciops_start_discovery(int index)
-{
- int adapter_type = get_adapter_type(index);
-
- DBG("hci%u", index);
-
- switch (adapter_type) {
- case BR_EDR_LE:
- return start_inquiry(index, LENGTH_BR_LE_INQ);
- case BR_EDR:
- return start_inquiry(index, LENGTH_BR_INQ);
- case LE_ONLY:
- return start_scanning(index, TIMEOUT_LE_SCAN);
- default:
- return -EINVAL;
- }
-}
-
-static int hciops_stop_discovery(int index)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("index %d", index);
-
- switch (dev->discov_state) {
- case DISCOV_INQ:
- return hciops_stop_inquiry(index);
- case DISCOV_SCAN:
- return hciops_stop_scanning(index);
- case DISCOV_NAMES:
- cancel_resolve_name(index);
- default:
- return -EINVAL;
- }
-}
-
-static int hciops_set_fast_connectable(int index, gboolean enable)
-{
- struct dev_info *dev = &devs[index];
- write_page_activity_cp cp;
- uint8_t type;
-
- DBG("hci%d enable %d", index, enable);
-
- if (enable) {
- type = PAGE_SCAN_TYPE_INTERLACED;
- cp.interval = 0x0024; /* 22.5 msec page scan interval */
- } else {
- type = PAGE_SCAN_TYPE_STANDARD; /* default */
- cp.interval = 0x0800; /* default 1.28 sec page scan */
- }
-
- cp.window = 0x0012; /* default 11.25 msec page scan window */
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_PAGE_ACTIVITY,
- WRITE_PAGE_ACTIVITY_CP_SIZE, &cp) < 0)
- return -errno;
- else if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_WRITE_PAGE_SCAN_TYPE, 1, &type) < 0)
- return -errno;
-
- return 0;
-}
-
-static int hciops_read_clock(int index, bdaddr_t *bdaddr, int which,
- int timeout, uint32_t *clock,
- uint16_t *accuracy)
-{
- struct dev_info *dev = &devs[index];
- uint16_t handle = 0;
- char addr[18];
- int ret;
-
- ba2str(bdaddr, addr);
- DBG("hci%d addr %s which %d timeout %d", index, addr, which, timeout);
-
- ret = get_handle(index, bdaddr, &handle);
- if (ret < 0)
- return ret;
-
- if (hci_read_clock(dev->sk, htobs(handle), which, clock, accuracy,
- timeout) < 0)
- return -errno;
-
- return 0;
-}
-
-static int hciops_read_bdaddr(int index, bdaddr_t *bdaddr)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d", index);
-
- bacpy(bdaddr, &dev->bdaddr);
-
- return 0;
-}
-
-static int hciops_block_device(int index, bdaddr_t *bdaddr,
- uint8_t bdaddr_type)
-{
- struct dev_info *dev = &devs[index];
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
-
- if (ioctl(dev->sk, HCIBLOCKADDR, bdaddr) < 0)
- return -errno;
-
- return 0;
-}
-
-static int hciops_unblock_device(int index, bdaddr_t *bdaddr,
- uint8_t bdaddr_type)
-{
- struct dev_info *dev = &devs[index];
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
-
- if (ioctl(dev->sk, HCIUNBLOCKADDR, bdaddr) < 0)
- return -errno;
-
- return 0;
-}
-
-static int hciops_get_conn_list(int index, GSList **conns)
-{
- struct dev_info *dev = &devs[index];
- GSList *l;
-
- DBG("hci%d", index);
-
- *conns = NULL;
-
- for (l = dev->connections; l != NULL; l = g_slist_next(l)) {
- struct bt_conn *conn = l->data;
-
- *conns = g_slist_append(*conns,
- g_memdup(&conn->bdaddr, sizeof(bdaddr_t)));
- }
-
- return 0;
-}
-
-static int hciops_disconnect(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
-{
- DBG("hci%d", index);
-
- return disconnect_addr(index, bdaddr, HCI_OE_USER_ENDED_CONNECTION);
-}
-
-static int hciops_remove_bonding(int index, bdaddr_t *bdaddr,
- uint8_t bdaddr_type)
-{
- struct dev_info *dev = &devs[index];
- delete_stored_link_key_cp cp;
- GSList *match;
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
-
- match = g_slist_find_custom(dev->keys, bdaddr, (GCompareFunc) bacmp);
- if (match) {
- g_free(match->data);
- dev->keys = g_slist_delete_link(dev->keys, match);
- }
-
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
-
- /* Delete the link key from the Bluetooth chip */
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_DELETE_STORED_LINK_KEY,
- DELETE_STORED_LINK_KEY_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static int hciops_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin,
- size_t pin_len)
-{
- struct dev_info *dev = &devs[index];
- char addr[18];
- int err;
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
-
- if (pin) {
- pin_code_reply_cp pr;
-
- dev->pin_length = pin_len;
-
- memset(&pr, 0, sizeof(pr));
- bacpy(&pr.bdaddr, bdaddr);
- memcpy(pr.pin_code, pin, pin_len);
- pr.pin_len = pin_len;
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_PIN_CODE_REPLY,
- PIN_CODE_REPLY_CP_SIZE, &pr);
- } else
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_PIN_CODE_NEG_REPLY, 6, bdaddr);
-
- if (err < 0)
- err = -errno;
-
- return err;
-}
-
-static int hciops_passkey_reply(int index, bdaddr_t *bdaddr,
- uint8_t bdaddr_type, uint32_t passkey)
-{
- struct dev_info *dev = &devs[index];
- char addr[18];
- int err;
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
-
- if (passkey != INVALID_PASSKEY) {
- user_passkey_reply_cp cp;
-
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
- cp.passkey = passkey;
-
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_USER_PASSKEY_REPLY,
- USER_PASSKEY_REPLY_CP_SIZE, &cp);
- } else
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_USER_PASSKEY_NEG_REPLY, 6, bdaddr);
-
- if (err < 0)
- err = -errno;
-
- return err;
-}
-
-static uint8_t generate_service_class(int index)
-{
- struct dev_info *dev = &devs[index];
- GSList *l;
- uint8_t val = 0;
-
- for (l = dev->uuids; l != NULL; l = g_slist_next(l)) {
- struct uuid_info *uuid = l->data;
-
- val |= uuid->svc_hint;
- }
-
- return val;
-}
-
-static int update_service_classes(int index)
-{
- struct dev_info *dev = &devs[index];
- uint8_t value;
- int err;
-
- value = generate_service_class(index);
-
- DBG("hci%d value %u", index, value);
-
- /* Update only the service class, keep the limited bit,
- * major/minor class bits intact */
- dev->wanted_cod &= 0x00ffff;
- dev->wanted_cod |= (value << 16);
-
- /* If the cache is enabled or an existing CoD write is in progress
- * just bail out */
- if (dev->cache_enable || dev->pending_cod)
- return 0;
-
- /* If we already have the CoD we want, update EIR and return */
- if (dev->current_cod == dev->wanted_cod) {
- update_ext_inquiry_response(index);
- return 0;
- }
-
- DBG("Changing service classes to 0x%06x", dev->wanted_cod);
-
- err = write_class(index, dev->wanted_cod);
- if (err < 0)
- error("Adapter class update failed: %s (%d)",
- strerror(-err), -err);
-
- return err;
-}
-
-static int hciops_add_uuid(int index, uuid_t *uuid, uint8_t svc_hint)
-{
- struct dev_info *dev = &devs[index];
- struct uuid_info *info;
-
- DBG("hci%d", index);
-
- info = g_new0(struct uuid_info, 1);
- memcpy(&info->uuid, uuid, sizeof(*uuid));
- info->svc_hint = svc_hint;
-
- dev->uuids = g_slist_append(dev->uuids, info);
-
- return update_service_classes(index);
-}
-
-static int hciops_remove_uuid(int index, uuid_t *uuid)
-{
- struct dev_info *dev = &devs[index];
- GSList *match;
-
- match = g_slist_find_custom(dev->uuids, uuid, sdp_uuid_cmp);
- if (match) {
- g_free(match->data);
- dev->uuids = g_slist_delete_link(dev->uuids, match);
- }
-
- DBG("hci%d", index);
-
- return update_service_classes(index);
-}
-
-static int hciops_disable_cod_cache(int index)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d cache_enable %d", index, dev->cache_enable);
-
- if (!dev->cache_enable)
- return 0;
-
- DBG("hci%d current_cod 0x%06x wanted_cod 0x%06x", index,
- dev->current_cod, dev->wanted_cod);
-
- /* Disable and flush svc cache. All successive service class
- * updates * will be written to the device */
- dev->cache_enable = FALSE;
-
- if (dev->current_cod == dev->wanted_cod) {
- update_ext_inquiry_response(index);
- return 0;
- }
-
- return write_class(index, dev->wanted_cod);
-}
-
-static int hciops_restore_powered(int index)
-{
- struct dev_info *dev = &devs[index];
-
- if (!dev->already_up && dev->up)
- return hciops_power_off(index);
-
- return 0;
-}
-
-static int hciops_load_keys(int index, GSList *keys, gboolean debug_keys)
-{
- struct dev_info *dev = &devs[index];
- GSList *l;
-
- DBG("hci%d keys %d debug_keys %d", index, g_slist_length(keys),
- debug_keys);
-
- if (dev->keys != NULL)
- return -EEXIST;
-
- for (l = keys; l; l = l->next) {
- struct link_key_info *orig, *dup;
-
- orig = l->data;
-
- dup = g_memdup(orig, sizeof(*orig));
-
- dev->keys = g_slist_prepend(dev->keys, dup);
- }
-
- dev->debug_keys = debug_keys;
-
- return 0;
-}
-
-static int hciops_set_io_capability(int index, uint8_t io_capability)
-{
- struct dev_info *dev = &devs[index];
-
- /* hciops is not to be used for SMP pairing for LE devices. So
- * change the IO capability from KeyboardDisplay to DisplayYesNo
- * in case it is set. */
- dev->io_capability = (io_capability == 0x04) ? 0x01 : io_capability;
-
- return 0;
-}
-
-static int request_authentication(int index, bdaddr_t *bdaddr)
-{
- struct dev_info *dev = &devs[index];
- auth_requested_cp cp;
- uint16_t handle;
- int err;
-
- DBG("hci%d", index);
-
- err = get_handle(index, bdaddr, &handle);
- if (err < 0)
- return err;
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = htobs(handle);
-
- if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
- AUTH_REQUESTED_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
-static void bonding_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
-{
- struct bt_conn *conn = user_data;
- struct dev_info *dev = conn->dev;
-
- if (!conn->io) {
- if (!err)
- g_io_channel_shutdown(io, TRUE, NULL);
- return;
- }
-
- if (err)
- /* Wait proper error to be propagated by bonding complete */
- return;
-
- if (request_authentication(dev->id, &conn->bdaddr) < 0)
- goto failed;
-
- return;
-
-failed:
- bonding_complete(dev, conn, HCI_UNSPECIFIED_ERROR);
-}
-
-static int hciops_create_bonding(int index, bdaddr_t *bdaddr,
- uint8_t bdaddr_type, uint8_t io_cap)
-{
- struct dev_info *dev = &devs[index];
- BtIOSecLevel sec_level;
- struct bt_conn *conn;
- GError *err = NULL;
-
- conn = get_connection(dev, bdaddr);
-
- if (conn->io != NULL)
- return -EBUSY;
-
- /* hciops is not to be used for SMP pairing for LE devices. So
- * change the IO capability from KeyboardDisplay to DisplayYesNo
- * in case it is set. */
- conn->loc_cap = (io_cap == 0x04 ? 0x01 : io_cap);
-
- /* If our IO capability is NoInputNoOutput use medium security
- * level (i.e. don't require MITM protection) else use high
- * security level */
- if (io_cap == 0x03)
- sec_level = BT_IO_SEC_MEDIUM;
- else
- sec_level = BT_IO_SEC_HIGH;
-
- conn->io = bt_io_connect(BT_IO_L2RAW, bonding_connect_cb, conn,
- NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &dev->bdaddr,
- BT_IO_OPT_DEST_BDADDR, bdaddr,
- BT_IO_OPT_SEC_LEVEL, sec_level,
- BT_IO_OPT_INVALID);
- if (conn->io == NULL) {
- error("bt_io_connect: %s", err->message);
- g_error_free(err);
- return -EIO;
- }
-
- conn->bonding_initiator = TRUE;
-
- return 0;
-}
-
-static int hciops_cancel_bonding(int index, bdaddr_t *bdaddr)
-{
- struct dev_info *dev = &devs[index];
- struct bt_conn *conn;
-
- DBG("hci%d", index);
-
- conn = find_connection(dev, bdaddr);
- if (conn == NULL || conn->io == NULL)
- return -ENOTCONN;
-
- g_io_channel_shutdown(conn->io, TRUE, NULL);
- g_io_channel_unref(conn->io);
- conn->io = NULL;
-
- return 0;
-}
-
-static int hciops_read_local_oob_data(int index)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d", index);
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA, 0, 0)
- < 0)
- return -errno;
-
- return 0;
-}
-
-static int hciops_add_remote_oob_data(int index, bdaddr_t *bdaddr,
- uint8_t *hash, uint8_t *randomizer)
-{
- char addr[18];
- struct dev_info *dev = &devs[index];
- GSList *match;
- struct oob_data *data;
-
- ba2str(bdaddr, addr);
- DBG("hci%d bdaddr %s", index, addr);
-
- match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
-
- if (match) {
- data = match->data;
- } else {
- data = g_new(struct oob_data, 1);
- bacpy(&data->bdaddr, bdaddr);
- dev->oob_data = g_slist_prepend(dev->oob_data, data);
- }
-
- memcpy(data->hash, hash, sizeof(data->hash));
- memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
-
- return 0;
-}
-
-static int hciops_remove_remote_oob_data(int index, bdaddr_t *bdaddr)
-{
- char addr[18];
- struct dev_info *dev = &devs[index];
- GSList *match;
-
- ba2str(bdaddr, addr);
- DBG("hci%d bdaddr %s", index, addr);
-
- match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
-
- if (!match)
- return -ENOENT;
-
- g_free(match->data);
- dev->oob_data = g_slist_delete_link(dev->oob_data, match);
-
- return 0;
-}
-
-static int hciops_confirm_name(int index, bdaddr_t *bdaddr,
- uint8_t bdaddr_type, gboolean name_known)
-{
- struct dev_info *info = &devs[index];
- struct found_dev *dev;
- GSList *match;
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%u %s name_known %u", index, addr, name_known);
-
- match = g_slist_find_custom(info->found_devs, bdaddr,
- found_dev_bda_cmp);
- if (match == NULL)
- return -ENOENT;
-
- dev = match->data;
-
- if (name_known) {
- dev->name_state = NAME_NOT_NEEDED;
- info->found_devs = g_slist_sort(info->found_devs,
- found_dev_rssi_cmp);
- return 0;
- }
-
- dev->name_state = NAME_NEEDED;
- info->found_devs = g_slist_remove_link(info->found_devs, match);
-
- match->next = info->need_name;
- info->need_name = match;
- info->need_name = g_slist_sort(info->need_name, found_dev_rssi_cmp);
-
- return 0;
-}
-
-static int hciops_load_ltks(int index, GSList *keys)
-{
- return -ENOSYS;
-}
-
-static struct btd_adapter_ops hci_ops = {
- .setup = hciops_setup,
- .cleanup = hciops_cleanup,
- .set_powered = hciops_set_powered,
- .set_discoverable = hciops_set_discoverable,
- .set_pairable = hciops_set_pairable,
- .start_discovery = hciops_start_discovery,
- .stop_discovery = hciops_stop_discovery,
- .set_name = hciops_set_name,
- .set_dev_class = hciops_set_dev_class,
- .set_fast_connectable = hciops_set_fast_connectable,
- .read_clock = hciops_read_clock,
- .read_bdaddr = hciops_read_bdaddr,
- .block_device = hciops_block_device,
- .unblock_device = hciops_unblock_device,
- .get_conn_list = hciops_get_conn_list,
- .disconnect = hciops_disconnect,
- .remove_bonding = hciops_remove_bonding,
- .pincode_reply = hciops_pincode_reply,
- .confirm_reply = hciops_confirm_reply,
- .passkey_reply = hciops_passkey_reply,
- .encrypt_link = hciops_encrypt_link,
- .set_did = hciops_set_did,
- .add_uuid = hciops_add_uuid,
- .remove_uuid = hciops_remove_uuid,
- .disable_cod_cache = hciops_disable_cod_cache,
- .restore_powered = hciops_restore_powered,
- .load_keys = hciops_load_keys,
- .set_io_capability = hciops_set_io_capability,
- .create_bonding = hciops_create_bonding,
- .cancel_bonding = hciops_cancel_bonding,
- .read_local_oob_data = hciops_read_local_oob_data,
- .add_remote_oob_data = hciops_add_remote_oob_data,
- .remove_remote_oob_data = hciops_remove_remote_oob_data,
- .confirm_name = hciops_confirm_name,
- .load_ltks = hciops_load_ltks,
-};
-
-static int hciops_init(void)
-{
- DBG("");
- return btd_register_adapter_ops(&hci_ops, FALSE);
-}
-
-static void hciops_exit(void)
-{
- DBG("");
- btd_adapter_cleanup_ops(&hci_ops);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(hciops, VERSION,
- BLUETOOTH_PLUGIN_PRIORITY_LOW, hciops_init, hciops_exit)