aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/bluetooth/hci_core.h16
-rw-r--r--include/net/bluetooth/mgmt.h12
-rw-r--r--net/bluetooth/mgmt.c133
3 files changed, 161 insertions, 0 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d5d8454236b..506f2508920 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -248,6 +248,10 @@ struct hci_conn {
248 void *priv; 248 void *priv;
249 249
250 struct hci_conn *link; 250 struct hci_conn *link;
251
252 void (*connect_cfm_cb) (struct hci_conn *conn, u8 status);
253 void (*security_cfm_cb) (struct hci_conn *conn, u8 status);
254 void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason);
251}; 255};
252 256
253extern struct hci_proto *hci_proto[]; 257extern struct hci_proto *hci_proto[];
@@ -571,6 +575,9 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
571 hp = hci_proto[HCI_PROTO_SCO]; 575 hp = hci_proto[HCI_PROTO_SCO];
572 if (hp && hp->connect_cfm) 576 if (hp && hp->connect_cfm)
573 hp->connect_cfm(conn, status); 577 hp->connect_cfm(conn, status);
578
579 if (conn->connect_cfm_cb)
580 conn->connect_cfm_cb(conn, status);
574} 581}
575 582
576static inline int hci_proto_disconn_ind(struct hci_conn *conn) 583static inline int hci_proto_disconn_ind(struct hci_conn *conn)
@@ -600,6 +607,9 @@ static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
600 hp = hci_proto[HCI_PROTO_SCO]; 607 hp = hci_proto[HCI_PROTO_SCO];
601 if (hp && hp->disconn_cfm) 608 if (hp && hp->disconn_cfm)
602 hp->disconn_cfm(conn, reason); 609 hp->disconn_cfm(conn, reason);
610
611 if (conn->disconn_cfm_cb)
612 conn->disconn_cfm_cb(conn, reason);
603} 613}
604 614
605static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) 615static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
@@ -619,6 +629,9 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
619 hp = hci_proto[HCI_PROTO_SCO]; 629 hp = hci_proto[HCI_PROTO_SCO];
620 if (hp && hp->security_cfm) 630 if (hp && hp->security_cfm)
621 hp->security_cfm(conn, status, encrypt); 631 hp->security_cfm(conn, status, encrypt);
632
633 if (conn->security_cfm_cb)
634 conn->security_cfm_cb(conn, status);
622} 635}
623 636
624static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) 637static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt)
@@ -632,6 +645,9 @@ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u
632 hp = hci_proto[HCI_PROTO_SCO]; 645 hp = hci_proto[HCI_PROTO_SCO];
633 if (hp && hp->security_cfm) 646 if (hp && hp->security_cfm)
634 hp->security_cfm(conn, status, encrypt); 647 hp->security_cfm(conn, status, encrypt);
648
649 if (conn->security_cfm_cb)
650 conn->security_cfm_cb(conn, status);
635} 651}
636 652
637int hci_register_proto(struct hci_proto *hproto); 653int hci_register_proto(struct hci_proto *hproto);
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 44ac55c8507..1d25c59be2e 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -160,6 +160,18 @@ struct mgmt_cp_set_io_capability {
160 __u8 io_capability; 160 __u8 io_capability;
161} __packed; 161} __packed;
162 162
163#define MGMT_OP_PAIR_DEVICE 0x0014
164struct mgmt_cp_pair_device {
165 __le16 index;
166 bdaddr_t bdaddr;
167 __u8 io_cap;
168} __packed;
169struct mgmt_rp_pair_device {
170 __le16 index;
171 bdaddr_t bdaddr;
172 __u8 status;
173} __packed;
174
163#define MGMT_EV_CMD_COMPLETE 0x0001 175#define MGMT_EV_CMD_COMPLETE 0x0001
164struct mgmt_ev_cmd_complete { 176struct mgmt_ev_cmd_complete {
165 __le16 opcode; 177 __le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 52e5f88b753..d7fc54dcbc9 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -38,6 +38,7 @@ struct pending_cmd {
38 int index; 38 int index;
39 void *cmd; 39 void *cmd;
40 struct sock *sk; 40 struct sock *sk;
41 void *user_data;
41}; 42};
42 43
43LIST_HEAD(cmd_list); 44LIST_HEAD(cmd_list);
@@ -1063,6 +1064,135 @@ static int set_io_capability(struct sock *sk, unsigned char *data, u16 len)
1063 &dev_id, sizeof(dev_id)); 1064 &dev_id, sizeof(dev_id));
1064} 1065}
1065 1066
1067static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
1068{
1069 struct hci_dev *hdev = conn->hdev;
1070 struct list_head *p;
1071
1072 list_for_each(p, &cmd_list) {
1073 struct pending_cmd *cmd;
1074
1075 cmd = list_entry(p, struct pending_cmd, list);
1076
1077 if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
1078 continue;
1079
1080 if (cmd->index != hdev->id)
1081 continue;
1082
1083 if (cmd->user_data != conn)
1084 continue;
1085
1086 return cmd;
1087 }
1088
1089 return NULL;
1090}
1091
1092static void pairing_complete(struct pending_cmd *cmd, u8 status)
1093{
1094 struct mgmt_rp_pair_device rp;
1095 struct hci_conn *conn = cmd->user_data;
1096
1097 rp.index = cmd->index;
1098 bacpy(&rp.bdaddr, &conn->dst);
1099 rp.status = status;
1100
1101 cmd_complete(cmd->sk, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp));
1102
1103 /* So we don't get further callbacks for this connection */
1104 conn->connect_cfm_cb = NULL;
1105 conn->security_cfm_cb = NULL;
1106 conn->disconn_cfm_cb = NULL;
1107
1108 hci_conn_put(conn);
1109
1110 list_del(&cmd->list);
1111 mgmt_pending_free(cmd);
1112}
1113
1114static void pairing_complete_cb(struct hci_conn *conn, u8 status)
1115{
1116 struct pending_cmd *cmd;
1117
1118 BT_DBG("status %u", status);
1119
1120 cmd = find_pairing(conn);
1121 if (!cmd) {
1122 BT_DBG("Unable to find a pending command");
1123 return;
1124 }
1125
1126 pairing_complete(cmd, status);
1127}
1128
1129static int pair_device(struct sock *sk, unsigned char *data, u16 len)
1130{
1131 struct hci_dev *hdev;
1132 struct mgmt_cp_pair_device *cp;
1133 struct pending_cmd *cmd;
1134 u8 sec_level, auth_type;
1135 struct hci_conn *conn;
1136 u16 dev_id;
1137 int err;
1138
1139 BT_DBG("");
1140
1141 cp = (void *) data;
1142 dev_id = get_unaligned_le16(&cp->index);
1143
1144 hdev = hci_dev_get(dev_id);
1145 if (!hdev)
1146 return cmd_status(sk, MGMT_OP_PAIR_DEVICE, ENODEV);
1147
1148 hci_dev_lock_bh(hdev);
1149
1150 if (cp->io_cap == 0x03) {
1151 sec_level = BT_SECURITY_MEDIUM;
1152 auth_type = HCI_AT_DEDICATED_BONDING;
1153 } else {
1154 sec_level = BT_SECURITY_HIGH;
1155 auth_type = HCI_AT_DEDICATED_BONDING_MITM;
1156 }
1157
1158 conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, auth_type);
1159 if (!conn) {
1160 err = -ENOMEM;
1161 goto unlock;
1162 }
1163
1164 if (conn->connect_cfm_cb) {
1165 hci_conn_put(conn);
1166 err = cmd_status(sk, MGMT_OP_PAIR_DEVICE, EBUSY);
1167 goto unlock;
1168 }
1169
1170 cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, dev_id, data, len);
1171 if (!cmd) {
1172 err = -ENOMEM;
1173 hci_conn_put(conn);
1174 goto unlock;
1175 }
1176
1177 conn->connect_cfm_cb = pairing_complete_cb;
1178 conn->security_cfm_cb = pairing_complete_cb;
1179 conn->disconn_cfm_cb = pairing_complete_cb;
1180 conn->io_capability = cp->io_cap;
1181 cmd->user_data = conn;
1182
1183 if (conn->state == BT_CONNECTED &&
1184 hci_conn_security(conn, sec_level, auth_type))
1185 pairing_complete(cmd, 0);
1186
1187 err = 0;
1188
1189unlock:
1190 hci_dev_unlock_bh(hdev);
1191 hci_dev_put(hdev);
1192
1193 return err;
1194}
1195
1066int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) 1196int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
1067{ 1197{
1068 unsigned char *buf; 1198 unsigned char *buf;
@@ -1148,6 +1278,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
1148 case MGMT_OP_SET_IO_CAPABILITY: 1278 case MGMT_OP_SET_IO_CAPABILITY:
1149 err = set_io_capability(sk, buf + sizeof(*hdr), len); 1279 err = set_io_capability(sk, buf + sizeof(*hdr), len);
1150 break; 1280 break;
1281 case MGMT_OP_PAIR_DEVICE:
1282 err = pair_device(sk, buf + sizeof(*hdr), len);
1283 break;
1151 default: 1284 default:
1152 BT_DBG("Unknown op %u", opcode); 1285 BT_DBG("Unknown op %u", opcode);
1153 err = cmd_status(sk, opcode, 0x01); 1286 err = cmd_status(sk, opcode, 0x01);