diff options
-rw-r--r-- | include/net/bluetooth/hci_core.h | 16 | ||||
-rw-r--r-- | include/net/bluetooth/mgmt.h | 12 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 133 |
3 files changed, 161 insertions, 0 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d5d8454236bf..506f25089207 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 | ||
253 | extern struct hci_proto *hci_proto[]; | 257 | extern 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 | ||
576 | static inline int hci_proto_disconn_ind(struct hci_conn *conn) | 583 | static 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 | ||
605 | static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) | 615 | static 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 | ||
624 | static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) | 637 | static 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 | ||
637 | int hci_register_proto(struct hci_proto *hproto); | 653 | int hci_register_proto(struct hci_proto *hproto); |
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 44ac55c85079..1d25c59be2e3 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 | ||
164 | struct mgmt_cp_pair_device { | ||
165 | __le16 index; | ||
166 | bdaddr_t bdaddr; | ||
167 | __u8 io_cap; | ||
168 | } __packed; | ||
169 | struct 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 |
164 | struct mgmt_ev_cmd_complete { | 176 | struct mgmt_ev_cmd_complete { |
165 | __le16 opcode; | 177 | __le16 opcode; |
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 52e5f88b753a..d7fc54dcbc9e 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 | ||
43 | LIST_HEAD(cmd_list); | 44 | LIST_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 | ||
1067 | static 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 | |||
1092 | static 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 | |||
1114 | static 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 | |||
1129 | static 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 | |||
1189 | unlock: | ||
1190 | hci_dev_unlock_bh(hdev); | ||
1191 | hci_dev_put(hdev); | ||
1192 | |||
1193 | return err; | ||
1194 | } | ||
1195 | |||
1066 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | 1196 | int 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); |