From: Gustavo Padovan <gustavo.padovan@xxxxxxxxxxxxxxx> hciops was replaced by mgmtops, it should not be maintained anymore and is now removed. --- Makefile.am | 4 +- plugins/hciops.c | 3943 ------------------------------------------------------ 2 files changed, 2 insertions(+), 3945 deletions(-) delete mode 100644 plugins/hciops.c diff --git a/Makefile.am b/Makefile.am index 7415979..5009ca8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -236,8 +236,8 @@ builtin_sources += thermometer/main.c \ 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 @@ -1,3943 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann <marcel@xxxxxxxxxxxx> - * - * 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) -- 1.7.10.2 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html