static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_disconn_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); if (ev->status) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { conn->state = BT_CLOSED; hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); } hci_dev_unlock(hdev); }
static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) { struct hci_cp_create_conn *cp; struct hci_conn *conn; BT_DBG("%s status 0x%x", hdev->name, status); cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_CONN); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->bdaddr), conn); if (status) { if (conn && conn->state == BT_CONNECT) { if (status != 0x0c || conn->attempt > 2) { conn->state = BT_CLOSED; hci_proto_connect_cfm(conn, status); hci_conn_del(conn); } else conn->state = BT_CONNECT2; } } else { if (!conn) { conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); if (conn) { conn->out = 1; conn->link_mode |= HCI_LM_MASTER; } else BT_ERR("No memmory for new connection"); } } hci_dev_unlock(hdev); }
static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_role_discovery *rp = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status 0x%x", hdev->name, rp->status); if (rp->status) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); if (conn) { if (rp->role) conn->link_mode &= ~HCI_LM_MASTER; else conn->link_mode |= HCI_LM_MASTER; } hci_dev_unlock(hdev); }
/* Disconnect Complete */ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { evt_disconn_complete *dc = (evt_disconn_complete *) skb->data; struct hci_conn *conn = NULL; __u16 handle = __le16_to_cpu(dc->handle); BT_DBG("%s status %d", hdev->name, dc->status); if (dc->status) return; hci_dev_lock(hdev); conn = conn_hash_lookup_handle(hdev, handle); if (conn) { conn->state = BT_CLOSED; hci_proto_disconn_ind(conn, dc->reason); hci_conn_del(conn); } hci_dev_unlock(hdev); }
static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) { struct hci_cp_exit_sniff_mode *cp; struct hci_conn *conn; BT_DBG("%s status 0x%x", hdev->name, status); if (!status) return; cp = hci_sent_cmd_data(hdev, HCI_OP_EXIT_SNIFF_MODE); if (!cp) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); if (conn) clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); hci_dev_unlock(hdev); }
static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_clock_offset *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn && !ev->status) { struct inquiry_entry *ie; ie = hci_inquiry_cache_lookup(hdev, &conn->dst); if (ie) { ie->data.clock_offset = ev->clock_offset; ie->timestamp = jiffies; } } hci_dev_unlock(hdev); }
/* Role Change */ static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { evt_role_change *rc = (evt_role_change *) skb->data; struct hci_conn *conn = NULL; BT_DBG("%s status %d", hdev->name, rc->status); if (rc->status) return; hci_dev_lock(hdev); conn = conn_hash_lookup_ba(hdev, ACL_LINK, &rc->bdaddr); if (conn) { if (rc->role) conn->link_mode &= ~HCI_LM_MASTER; else conn->link_mode |= HCI_LM_MASTER; } hci_dev_unlock(hdev); }
//BT_TIK 2011.09.23 SH Start : QoS patch from IL static inline void hci_flowspec_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_flowspec_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); if (!ev->status) { hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { /* conn->qos_info.delay_variation = le32_to_cpu(ev->qos.delay_variation); conn->qos_info.latency = le32_to_cpu(ev->qos.latency); conn->qos_info.peak_bandwidth = le32_to_cpu(ev->qos.peak_bandwidth); conn->qos_info.service_type = ev->qos.service_type; conn->qos_info.token_rate = le32_to_cpu(ev->qos.token_rate); */ conn->flowspec.bucket_size = le32_to_cpu(ev->flowspec.bucket_size); conn->flowspec.flowdir = ev->flowspec.flowdir; conn->flowspec.latency = le32_to_cpu(ev->flowspec.latency); conn->flowspec.peak_bandwidth = le32_to_cpu(ev->flowspec.peak_bandwidth); conn->flowspec.service_type = ev->flowspec.service_type; conn->flowspec.token_rate = le32_to_cpu(ev->flowspec.token_rate); } hci_dev_unlock(hdev); } //BT_TIK 2011.09.23 SH : Removed HCI_OP_SET_FLOW_SPEC because kernel version of // GT-P1010 is different from one of patch from IL //hci_req_complete(hdev, HCI_OP_SET_FLOW_SPEC, ev->status); hci_req_complete(hdev, ev->status); }
static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_remote_ext_features *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status && ev->page == 0x01) { struct inquiry_entry *ie; if ((ie = hci_inquiry_cache_lookup(hdev, &conn->dst))) ie->data.ssp_mode = (ev->features[0] & 0x01); conn->ssp_mode = (ev->features[0] & 0x01); } if (conn->state == BT_CONFIG) { if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0 && conn->out && conn->sec_level != BT_SECURITY_SDP) { struct hci_cp_auth_requested cp; cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } else { conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } } } hci_dev_unlock(hdev); }
static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_write_link_policy *rp = (void *) skb->data; struct hci_conn *conn; void *sent; BT_DBG("%s status 0x%x", hdev->name, rp->status); if (rp->status) return; sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LINK_POLICY); if (!sent) return; hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); if (conn) conn->link_policy = get_unaligned_le16(sent + 2); hci_dev_unlock(hdev); }
static void confirm_work(struct work_struct *work) { struct smp_chan *smp = container_of(work, struct smp_chan, confirm); struct l2cap_conn *conn = smp->conn; struct hci_dev *hdev = conn->hcon->hdev; struct crypto_blkcipher *tfm = hdev->tfm_aes; struct smp_cmd_pairing_confirm cp; int ret; u8 reason; BT_DBG("conn %p", conn); /* Prevent mutual access to hdev->tfm_aes */ hci_dev_lock(hdev); ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, conn->hcon->init_addr_type, &conn->hcon->init_addr, conn->hcon->resp_addr_type, &conn->hcon->resp_addr, cp.confirm_val); hci_dev_unlock(hdev); if (ret) { reason = SMP_UNSPECIFIED; goto error; } clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); return; error: smp_failure(conn, reason); }
/* Mode Change */ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_mode_change *ev = (struct hci_ev_mode_change *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { conn->mode = ev->mode; conn->interval = __le16_to_cpu(ev->interval); if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { if (conn->mode == HCI_CM_ACTIVE) conn->power_save = 1; else conn->power_save = 0; } } hci_dev_unlock(hdev); }
/* Authentication Complete */ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_auth_complete *ev = (struct hci_ev_auth_complete *) skb->data; struct hci_conn *conn = NULL; __u16 handle = __le16_to_cpu(ev->handle); BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, handle); if (conn) { if (!ev->status) conn->link_mode |= HCI_LM_AUTH; clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); hci_auth_cfm(conn, ev->status); if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { if (!ev->status) { struct hci_cp_set_conn_encrypt cp; cp.handle = __cpu_to_le16(conn->handle); cp.encrypt = 1; hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); hci_encrypt_cfm(conn, ev->status, 0x00); } } } hci_dev_unlock(hdev); }
static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_sync_conn_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { if (ev->link_type == ESCO_LINK) goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr); if (!conn) goto unlock; conn->type = SCO_LINK; } if (!ev->status) { conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; hci_conn_add_sysfs(conn); } else conn->state = BT_CLOSED; hci_proto_connect_cfm(conn, ev->status); if (ev->status) hci_conn_del(conn); unlock: hci_dev_unlock(hdev); }
/* Connect Complete */ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_complete *ev = (struct hci_ev_conn_complete *) skb->data; struct hci_conn *conn, *pend; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { hci_dev_unlock(hdev); return; } if (!ev->status) { conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; if (test_bit(HCI_AUTH, &hdev->flags)) conn->link_mode |= HCI_LM_AUTH; if (test_bit(HCI_ENCRYPT, &hdev->flags)) conn->link_mode |= HCI_LM_ENCRYPT; /* Get remote features */ if (conn->type == ACL_LINK) { struct hci_cp_read_remote_features cp; cp.handle = ev->handle; hci_send_cmd(hdev, OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES, sizeof(cp), &cp); } /* Set link policy */ if (conn->type == ACL_LINK && hdev->link_policy) { struct hci_cp_write_link_policy cp; cp.handle = ev->handle; cp.policy = __cpu_to_le16(hdev->link_policy); hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY, sizeof(cp), &cp); } /* Set packet type for incoming connection */ if (!conn->out) { struct hci_cp_change_conn_ptype cp; cp.handle = ev->handle; cp.pkt_type = (conn->type == ACL_LINK) ? __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK): __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp); } else { /* Update disconnect timer */ hci_conn_hold(conn); hci_conn_put(conn); } } else conn->state = BT_CLOSED; if (conn->type == ACL_LINK) { struct hci_conn *sco = conn->link; if (sco) { if (!ev->status) hci_add_sco(sco, conn->handle); else { hci_proto_connect_cfm(sco, ev->status); hci_conn_del(sco); } } } hci_proto_connect_cfm(conn, ev->status); if (ev->status) hci_conn_del(conn); pend = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2); if (pend) hci_acl_connect(pend); hci_dev_unlock(hdev); }
static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, u8 local_io, u8 remote_io) { struct hci_conn *hcon = conn->hcon; u8 method; u32 passkey = 0; int ret = 0; /* Initialize key to JUST WORKS */ memset(hcon->tk, 0, sizeof(hcon->tk)); hcon->tk_valid = FALSE; hcon->auth = auth; /* By definition, OOB data will be used if both sides have it available */ if (remote_oob && hcon->oob) { method = SMP_REQ_OOB; goto agent_request; } BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); /* If neither side wants MITM, use JUST WORKS */ /* If either side has unknown io_caps, use JUST_WORKS */ if (!(auth & SMP_AUTH_MITM) || local_io > SMP_IO_KEYBOARD_DISPLAY || remote_io > SMP_IO_KEYBOARD_DISPLAY) { hcon->auth &= ~SMP_AUTH_MITM; hcon->tk_valid = TRUE; return 0; } /* MITM is now officially requested, but not required */ /* Determine what we need (if anything) from the agent */ method = gen_method[local_io][remote_io]; BT_DBG("tk_method: %d", method); if (method == SMP_JUST_WORKS || method == SMP_JUST_CFM) hcon->auth &= ~SMP_AUTH_MITM; /* Don't bother confirming unbonded JUST_WORKS */ if (!(auth & SMP_AUTH_BONDING) && method == SMP_JUST_CFM) { hcon->tk_valid = TRUE; return 0; } else if (method == SMP_JUST_WORKS) { hcon->tk_valid = TRUE; return 0; } else if (method == SMP_OVERLAP) { if (hcon->link_mode & HCI_LM_MASTER) method = SMP_CFM_PASSKEY; else method = SMP_REQ_PASSKEY; } BT_DBG("tk_method-2: %d", method); if (method == SMP_CFM_PASSKEY) { u8 key[16]; /* Generate a passkey for display. It is not valid until * confirmed. */ memset(key, 0, sizeof(key)); get_random_bytes(&passkey, sizeof(passkey)); passkey %= 1000000; put_unaligned_le32(passkey, key); swap128(key, hcon->tk); BT_DBG("PassKey: %d", passkey); } agent_request: hci_dev_lock(hcon->hdev); switch (method) { case SMP_REQ_PASSKEY: ret = mgmt_user_confirm_request(hcon->hdev->id, HCI_EV_USER_PASSKEY_REQUEST, conn->dst, 0); break; case SMP_CFM_PASSKEY: default: ret = mgmt_user_confirm_request(hcon->hdev->id, HCI_EV_USER_CONFIRM_REQUEST, conn->dst, passkey); break; } hci_dev_unlock(hcon->hdev); return ret; }
static void random_work(struct work_struct *work) { struct smp_chan *smp = container_of(work, struct smp_chan, random); struct l2cap_conn *conn = smp->conn; struct hci_conn *hcon = conn->hcon; struct hci_dev *hdev = hcon->hdev; struct crypto_blkcipher *tfm = hdev->tfm_aes; u8 reason, confirm[16]; int ret; if (IS_ERR_OR_NULL(tfm)) { reason = SMP_UNSPECIFIED; goto error; } BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); /* Prevent mutual access to hdev->tfm_aes */ hci_dev_lock(hdev); ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, hcon->init_addr_type, &hcon->init_addr, hcon->resp_addr_type, &hcon->resp_addr, confirm); hci_dev_unlock(hdev); if (ret) { reason = SMP_UNSPECIFIED; goto error; } if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { BT_ERR("Pairing failed (confirmation values mismatch)"); reason = SMP_CONFIRM_FAILED; goto error; } if (hcon->out) { u8 stk[16]; __le64 rand = 0; __le16 ediv = 0; smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, stk); memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) { reason = SMP_UNSPECIFIED; goto error; } hci_le_start_enc(hcon, ediv, rand, stk); hcon->enc_key_size = smp->enc_key_size; } else { u8 stk[16], auth; __le64 rand = 0; __le16 ediv = 0; smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, stk); memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); if (hcon->pending_sec_level == BT_SECURITY_HIGH) auth = 1; else auth = 0; hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, HCI_SMP_STK_SLAVE, auth, stk, smp->enc_key_size, ediv, rand); } return; error: smp_failure(conn, reason); }
static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_remote_features *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) memcpy(conn->features, ev->features, 8); if (conn->state == BT_CONFIG) { if (!ev->status && lmp_ssp_capable(hdev) && lmp_ssp_capable(conn)) { struct hci_cp_read_remote_ext_features cp; cp.handle = ev->handle; cp.page = 0x01; hci_send_cmd(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES, sizeof(cp), &cp); } else { if (!(conn->features[3]&(0x02|0x04))) /* not support 2M/3M EDR. 0x02=2M 0x04=3M */ { if (!(conn->link_mode & HCI_LM_MASTER)) /* act as slave */ { if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->pend)) { struct hci_cp_switch_role cp; cp.bdaddr.b[0]= 0; /* init */ cp.bdaddr.b[1]= 0; cp.bdaddr.b[2]= 0; cp.bdaddr.b[3]= 0; cp.bdaddr.b[4]= 0; cp.bdaddr.b[5]= 0; cp.role = 0; bacpy(&cp.bdaddr, &conn->dst); /* switch to master */ cp.role = 0x0; BT_DBG("switch to master"); hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp); } } else { struct hci_cp_write_link_policy cp; cp.handle = 0; /* init */ cp.policy = 0; /* disable role_switch if already act as master */ cp.handle = cpu_to_le16(conn->handle); conn->link_policy |= HCI_LP_SNIFF; conn->link_policy |= HCI_LP_HOLD; conn->link_policy &= ~HCI_LP_RSWITCH; cp.policy = cpu_to_le16(conn->link_policy); hci_send_cmd(conn->hdev, HCI_OP_WRITE_LINK_POLICY, sizeof(cp), &cp); } } conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } } } hci_dev_unlock(hdev); }
static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, u8 local_io, u8 remote_io) { struct hci_conn *hcon = conn->hcon; u8 method; u32 passkey = 0; int ret = 0; memset(hcon->tk, 0, sizeof(hcon->tk)); hcon->tk_valid = FALSE; hcon->auth = auth; if (remote_oob && hcon->oob) { method = SMP_REQ_OOB; goto agent_request; } BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); if (!(auth & SMP_AUTH_MITM) || local_io > SMP_IO_KEYBOARD_DISPLAY || remote_io > SMP_IO_KEYBOARD_DISPLAY) { hcon->auth &= ~SMP_AUTH_MITM; hcon->tk_valid = TRUE; return 0; } method = gen_method[local_io][remote_io]; BT_DBG("tk_method: %d", method); if (method == SMP_JUST_WORKS || method == SMP_JUST_CFM) hcon->auth &= ~SMP_AUTH_MITM; if (!(auth & SMP_AUTH_BONDING) && method == SMP_JUST_CFM) { hcon->tk_valid = TRUE; return 0; } else if (method == SMP_JUST_WORKS) { hcon->tk_valid = TRUE; return 0; } else if (method == SMP_OVERLAP) { if (hcon->link_mode & HCI_LM_MASTER) method = SMP_CFM_PASSKEY; else method = SMP_REQ_PASSKEY; } BT_DBG("tk_method-2: %d", method); if (method == SMP_CFM_PASSKEY) { u8 key[16]; memset(key, 0, sizeof(key)); get_random_bytes(&passkey, sizeof(passkey)); passkey %= 1000000; put_unaligned_le32(passkey, key); swap128(key, hcon->tk); BT_DBG("PassKey: %d", passkey); } agent_request: hci_dev_lock(hcon->hdev); switch (method) { case SMP_REQ_PASSKEY: ret = mgmt_user_confirm_request(hcon->hdev->id, HCI_EV_USER_PASSKEY_REQUEST, conn->dst, 0); break; case SMP_CFM_PASSKEY: default: ret = mgmt_user_confirm_request(hcon->hdev->id, HCI_EV_USER_CONFIRM_REQUEST, conn->dst, passkey); break; } hci_dev_unlock(hcon->hdev); return ret; }
static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; BT_DBG("%s bdaddr %s type 0x%x", hdev->name, batostr(&ev->bdaddr), ev->link_type); mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); if (mask & HCI_LM_ACCEPT) { /* Connection accepted */ struct inquiry_entry *ie; struct hci_conn *conn; hci_dev_lock(hdev); if ((ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr))) memcpy(ie->data.dev_class, ev->dev_class, 3); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { /* pkt_type not yet used for incoming connections */ if (!(conn = hci_conn_add(hdev, ev->link_type, 0, &ev->bdaddr))) { BT_ERR("No memmory for new connection"); hci_dev_unlock(hdev); return; } } memcpy(conn->dev_class, ev->dev_class, 3); conn->state = BT_CONNECT; hci_dev_unlock(hdev); if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) { struct hci_cp_accept_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) cp.role = 0x00; /* Become master */ else cp.role = 0x01; /* Remain slave */ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); } else { struct hci_cp_accept_sync_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); cp.pkt_type = cpu_to_le16(conn->pkt_type); cp.tx_bandwidth = cpu_to_le32(0x00001f40); cp.rx_bandwidth = cpu_to_le32(0x00001f40); cp.max_latency = cpu_to_le16(0xffff); cp.content_format = cpu_to_le16(hdev->voice_setting); cp.retrans_effort = 0xff; hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(cp), &cp); } } else { /* Connection rejected */ struct hci_cp_reject_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); cp.reason = 0x0f; hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); } }
static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_auth_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) conn->link_mode |= HCI_LM_AUTH; else conn->sec_level = BT_SECURITY_LOW; //modify the problem of PIN or KEY MISSIMG ZTE_BT_QXX_20101118 begin if (ev->status == 0x06 && hdev->ssp_mode > 0 && conn->ssp_mode > 0 && conn->out) { // PIN or KEY MISSING struct hci_cp_auth_requested cp; cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); goto done; } //modify the problem of PIN or KEY MISSIMG ZTE_BT_QXX_20101118 end clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); if (conn->state == BT_CONFIG) { if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; cp.encrypt = 0x01; hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } } else { hci_auth_cfm(conn, ev->status); hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; hci_conn_put(conn); } if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { if (!ev->status) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; cp.encrypt = 0x01; hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); hci_encrypt_cfm(conn, ev->status, 0x00); } } } //modify the problem of PIN or KEY MISSIMG ZTE_BT_QXX_20101118 done: hci_dev_unlock(hdev); }
static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { if (ev->link_type != SCO_LINK) goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr); if (!conn) goto unlock; conn->type = SCO_LINK; } if (!ev->status) { conn->handle = __le16_to_cpu(ev->handle); if (conn->type == ACL_LINK) { conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; } else conn->state = BT_CONNECTED; hci_conn_hold_device(conn); hci_conn_add_sysfs(conn); if (test_bit(HCI_AUTH, &hdev->flags)) conn->link_mode |= HCI_LM_AUTH; if (test_bit(HCI_ENCRYPT, &hdev->flags)) conn->link_mode |= HCI_LM_ENCRYPT; /* Get remote features */ if (conn->type == ACL_LINK) { struct hci_cp_read_remote_features cp; cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, sizeof(cp), &cp); } /* Set packet type for incoming connection */ if (!conn->out && hdev->hci_ver < 3) { struct hci_cp_change_conn_ptype cp; cp.handle = ev->handle; cp.pkt_type = cpu_to_le16(conn->pkt_type); hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), &cp); } } else conn->state = BT_CLOSED; if (conn->type == ACL_LINK) { struct hci_conn *sco = conn->link; if (sco) { if (!ev->status) { if (lmp_esco_capable(hdev)) hci_setup_sync(sco, conn->handle); else hci_add_sco(sco, conn->handle); } else { hci_proto_connect_cfm(sco, ev->status); hci_conn_del(sco); } } } if (ev->status) { hci_proto_connect_cfm(conn, ev->status); hci_conn_del(conn); } else if (ev->link_type != ACL_LINK) hci_proto_connect_cfm(conn, ev->status); unlock: hci_dev_unlock(hdev); hci_conn_check_pending(hdev); }
/* Connect Complete */ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { evt_conn_complete *cc = (evt_conn_complete *) skb->data; struct hci_conn *conn = NULL; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); conn = conn_hash_lookup_ba(hdev, cc->link_type, &cc->bdaddr); if (!conn) { hci_dev_unlock(hdev); return; } if (!cc->status) { conn->handle = __le16_to_cpu(cc->handle); conn->state = BT_CONNECTED; if (test_bit(HCI_AUTH, &hdev->flags)) conn->link_mode |= HCI_LM_AUTH; if (test_bit(HCI_ENCRYPT, &hdev->flags)) conn->link_mode |= HCI_LM_ENCRYPT; /* Set link policy */ if (conn->type == ACL_LINK && hdev->link_policy) { write_link_policy_cp lp; lp.handle = cc->handle; lp.policy = __cpu_to_le16(hdev->link_policy); hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY, WRITE_LINK_POLICY_CP_SIZE, &lp); } /* Set packet type for incomming connection */ if (!conn->out) { change_conn_ptype_cp cp; cp.handle = cc->handle; cp.pkt_type = (conn->type == ACL_LINK) ? __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK): __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE, CHANGE_CONN_PTYPE_CP_SIZE, &cp); } } else conn->state = BT_CLOSED; if (conn->type == ACL_LINK) { struct hci_conn *sco = conn->link; if (sco) { if (!cc->status) hci_add_sco(sco, conn->handle); else { hci_proto_connect_cfm(sco, cc->status); hci_conn_del(sco); } } } hci_proto_connect_cfm(conn, cc->status); if (cc->status) hci_conn_del(conn); hci_dev_unlock(hdev); }
static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, u8 local_io, u8 remote_io) { struct hci_conn *hcon = conn->hcon; struct smp_chan *smp = conn->smp_chan; u8 method; u32 passkey = 0; int ret = 0; /* Initialize key for JUST WORKS */ memset(smp->tk, 0, sizeof(smp->tk)); clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); /* If neither side wants MITM, use JUST WORKS */ /* If either side has unknown io_caps, use JUST WORKS */ /* Otherwise, look up method from the table */ if (!(auth & SMP_AUTH_MITM) || local_io > SMP_IO_KEYBOARD_DISPLAY || remote_io > SMP_IO_KEYBOARD_DISPLAY) method = JUST_WORKS; else method = gen_method[remote_io][local_io]; /* If not bonding, don't ask user to confirm a Zero TK */ if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM) method = JUST_WORKS; /* If Just Works, Continue with Zero TK */ if (method == JUST_WORKS) { set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); return 0; } /* Not Just Works/Confirm results in MITM Authentication */ if (method != JUST_CFM) set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags); /* If both devices have Keyoard-Display I/O, the master * Confirms and the slave Enters the passkey. */ if (method == OVERLAP) { if (hcon->link_mode & HCI_LM_MASTER) method = CFM_PASSKEY; else method = REQ_PASSKEY; } /* Generate random passkey. Not valid until confirmed. */ if (method == CFM_PASSKEY) { u8 key[16]; memset(key, 0, sizeof(key)); get_random_bytes(&passkey, sizeof(passkey)); passkey %= 1000000; put_unaligned_le32(passkey, key); swap128(key, smp->tk); BT_DBG("PassKey: %d", passkey); } hci_dev_lock(hcon->hdev); if (method == REQ_PASSKEY) ret = mgmt_user_passkey_request(hcon->hdev, conn->dst, hcon->type, hcon->dst_type); else ret = mgmt_user_confirm_request(hcon->hdev, conn->dst, hcon->type, hcon->dst_type, cpu_to_le32(passkey), 0); hci_dev_unlock(hcon->hdev); return ret; }
static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_auth_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) conn->link_mode |= HCI_LM_AUTH; else conn->sec_level = BT_SECURITY_LOW; // BEGIN SS_BLUEZ_BT +kjh 2011.03.17 : // workaround for nobonding. // cond 1. local has link key. remote doesn't have link key. // cond 2. local & remote are ssp mode // cond 3. send opp file from local to remote // result -> pin or key missing err returned. // so send "HCI_OP_AUTH_REQUESTED" one more time. if (ev->status == 0x06 && hdev->ssp_mode > 0 && // 0x06 Error code (PIN or key missing) conn->ssp_mode > 0 && conn->out) { // hdev: remote device, conn: connection struct hci_cp_auth_requested cp; cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); printk(KERN_ERR "error code = 0x06, sspmode => HCI_OP_AUTH_REQUESTED"); goto done; } // END SS_BLUEZ_BT clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); if (conn->state == BT_CONFIG) { if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; cp.encrypt = 0x01; hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { conn->state = BT_CONNECTED; hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } } else { hci_auth_cfm(conn, ev->status); hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; hci_conn_put(conn); } if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { if (!ev->status) { struct hci_cp_set_conn_encrypt cp; cp.handle = ev->handle; cp.encrypt = 0x01; hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); hci_encrypt_cfm(conn, ev->status, 0x00); } } } // BEGIN SS_BLUEZ_BT +kjh 2011.03.17 : done: // END SS_BLUEZ_BT hci_dev_unlock(hdev); }