|
@@ -108,7 +108,7 @@ struct bt_uuid {
|
|
struct smp_csrk {
|
|
struct smp_csrk {
|
|
bdaddr_t bdaddr;
|
|
bdaddr_t bdaddr;
|
|
u8 bdaddr_type;
|
|
u8 bdaddr_type;
|
|
- u8 master;
|
|
|
|
|
|
+ u8 type;
|
|
u8 val[16];
|
|
u8 val[16];
|
|
};
|
|
};
|
|
|
|
|
|
@@ -373,6 +373,7 @@ struct hci_dev {
|
|
int (*close)(struct hci_dev *hdev);
|
|
int (*close)(struct hci_dev *hdev);
|
|
int (*flush)(struct hci_dev *hdev);
|
|
int (*flush)(struct hci_dev *hdev);
|
|
int (*setup)(struct hci_dev *hdev);
|
|
int (*setup)(struct hci_dev *hdev);
|
|
|
|
+ int (*shutdown)(struct hci_dev *hdev);
|
|
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
|
|
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
|
|
void (*notify)(struct hci_dev *hdev, unsigned int evt);
|
|
void (*notify)(struct hci_dev *hdev, unsigned int evt);
|
|
void (*hw_error)(struct hci_dev *hdev, u8 code);
|
|
void (*hw_error)(struct hci_dev *hdev, u8 code);
|
|
@@ -498,19 +499,14 @@ struct hci_conn_params {
|
|
extern struct list_head hci_dev_list;
|
|
extern struct list_head hci_dev_list;
|
|
extern struct list_head hci_cb_list;
|
|
extern struct list_head hci_cb_list;
|
|
extern rwlock_t hci_dev_list_lock;
|
|
extern rwlock_t hci_dev_list_lock;
|
|
-extern rwlock_t hci_cb_list_lock;
|
|
|
|
|
|
+extern struct mutex hci_cb_list_lock;
|
|
|
|
|
|
/* ----- HCI interface to upper protocols ----- */
|
|
/* ----- HCI interface to upper protocols ----- */
|
|
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
|
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
|
-void l2cap_connect_cfm(struct hci_conn *hcon, u8 status);
|
|
|
|
int l2cap_disconn_ind(struct hci_conn *hcon);
|
|
int l2cap_disconn_ind(struct hci_conn *hcon);
|
|
-void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason);
|
|
|
|
-int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt);
|
|
|
|
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
|
|
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
|
|
|
|
|
|
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
|
|
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
|
|
-void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
|
|
|
|
-void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
|
|
|
|
int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
|
|
int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
|
|
|
|
|
|
/* ----- Inquiry cache ----- */
|
|
/* ----- Inquiry cache ----- */
|
|
@@ -1050,28 +1046,6 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
|
|
|
|
-{
|
|
|
|
- switch (conn->type) {
|
|
|
|
- case ACL_LINK:
|
|
|
|
- case LE_LINK:
|
|
|
|
- l2cap_connect_cfm(conn, status);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case SCO_LINK:
|
|
|
|
- case ESCO_LINK:
|
|
|
|
- sco_connect_cfm(conn, status);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- BT_ERR("unknown link type %d", conn->type);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (conn->connect_cfm_cb)
|
|
|
|
- conn->connect_cfm_cb(conn, status);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static inline int hci_proto_disconn_ind(struct hci_conn *conn)
|
|
static inline int hci_proto_disconn_ind(struct hci_conn *conn)
|
|
{
|
|
{
|
|
if (conn->type != ACL_LINK && conn->type != LE_LINK)
|
|
if (conn->type != ACL_LINK && conn->type != LE_LINK)
|
|
@@ -1080,91 +1054,69 @@ static inline int hci_proto_disconn_ind(struct hci_conn *conn)
|
|
return l2cap_disconn_ind(conn);
|
|
return l2cap_disconn_ind(conn);
|
|
}
|
|
}
|
|
|
|
|
|
-static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
|
|
|
|
-{
|
|
|
|
- switch (conn->type) {
|
|
|
|
- case ACL_LINK:
|
|
|
|
- case LE_LINK:
|
|
|
|
- l2cap_disconn_cfm(conn, reason);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case SCO_LINK:
|
|
|
|
- case ESCO_LINK:
|
|
|
|
- sco_disconn_cfm(conn, reason);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- /* L2CAP would be handled for BREDR chan */
|
|
|
|
- case AMP_LINK:
|
|
|
|
- break;
|
|
|
|
|
|
+/* ----- HCI callbacks ----- */
|
|
|
|
+struct hci_cb {
|
|
|
|
+ struct list_head list;
|
|
|
|
|
|
- default:
|
|
|
|
- BT_ERR("unknown link type %d", conn->type);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
|
|
+ char *name;
|
|
|
|
|
|
- if (conn->disconn_cfm_cb)
|
|
|
|
- conn->disconn_cfm_cb(conn, reason);
|
|
|
|
-}
|
|
|
|
|
|
+ void (*connect_cfm) (struct hci_conn *conn, __u8 status);
|
|
|
|
+ void (*disconn_cfm) (struct hci_conn *conn, __u8 status);
|
|
|
|
+ void (*security_cfm) (struct hci_conn *conn, __u8 status,
|
|
|
|
+ __u8 encrypt);
|
|
|
|
+ void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
|
|
|
|
+ void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
|
|
|
|
+};
|
|
|
|
|
|
-static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
|
|
|
|
|
|
+static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
|
|
{
|
|
{
|
|
- __u8 encrypt;
|
|
|
|
-
|
|
|
|
- if (conn->type != ACL_LINK && conn->type != LE_LINK)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
|
|
|
|
- return;
|
|
|
|
|
|
+ struct hci_cb *cb;
|
|
|
|
|
|
- encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
|
|
|
|
- l2cap_security_cfm(conn, status, encrypt);
|
|
|
|
|
|
+ mutex_lock(&hci_cb_list_lock);
|
|
|
|
+ list_for_each_entry(cb, &hci_cb_list, list) {
|
|
|
|
+ if (cb->connect_cfm)
|
|
|
|
+ cb->connect_cfm(conn, status);
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&hci_cb_list_lock);
|
|
|
|
|
|
- if (conn->security_cfm_cb)
|
|
|
|
- conn->security_cfm_cb(conn, status);
|
|
|
|
|
|
+ if (conn->connect_cfm_cb)
|
|
|
|
+ conn->connect_cfm_cb(conn, status);
|
|
}
|
|
}
|
|
|
|
|
|
-static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
|
|
|
- __u8 encrypt)
|
|
|
|
|
|
+static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
|
|
{
|
|
{
|
|
- if (conn->type != ACL_LINK && conn->type != LE_LINK)
|
|
|
|
- return;
|
|
|
|
|
|
+ struct hci_cb *cb;
|
|
|
|
|
|
- l2cap_security_cfm(conn, status, encrypt);
|
|
|
|
|
|
+ mutex_lock(&hci_cb_list_lock);
|
|
|
|
+ list_for_each_entry(cb, &hci_cb_list, list) {
|
|
|
|
+ if (cb->disconn_cfm)
|
|
|
|
+ cb->disconn_cfm(conn, reason);
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&hci_cb_list_lock);
|
|
|
|
|
|
- if (conn->security_cfm_cb)
|
|
|
|
- conn->security_cfm_cb(conn, status);
|
|
|
|
|
|
+ if (conn->disconn_cfm_cb)
|
|
|
|
+ conn->disconn_cfm_cb(conn, reason);
|
|
}
|
|
}
|
|
|
|
|
|
-/* ----- HCI callbacks ----- */
|
|
|
|
-struct hci_cb {
|
|
|
|
- struct list_head list;
|
|
|
|
-
|
|
|
|
- char *name;
|
|
|
|
-
|
|
|
|
- void (*security_cfm) (struct hci_conn *conn, __u8 status,
|
|
|
|
- __u8 encrypt);
|
|
|
|
- void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
|
|
|
|
- void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
|
|
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
|
|
{
|
|
{
|
|
struct hci_cb *cb;
|
|
struct hci_cb *cb;
|
|
__u8 encrypt;
|
|
__u8 encrypt;
|
|
|
|
|
|
- hci_proto_auth_cfm(conn, status);
|
|
|
|
-
|
|
|
|
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
|
|
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
|
|
return;
|
|
return;
|
|
|
|
|
|
encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
|
|
encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
|
|
|
|
|
|
- read_lock(&hci_cb_list_lock);
|
|
|
|
|
|
+ mutex_lock(&hci_cb_list_lock);
|
|
list_for_each_entry(cb, &hci_cb_list, list) {
|
|
list_for_each_entry(cb, &hci_cb_list, list) {
|
|
if (cb->security_cfm)
|
|
if (cb->security_cfm)
|
|
cb->security_cfm(conn, status, encrypt);
|
|
cb->security_cfm(conn, status, encrypt);
|
|
}
|
|
}
|
|
- read_unlock(&hci_cb_list_lock);
|
|
|
|
|
|
+ mutex_unlock(&hci_cb_list_lock);
|
|
|
|
+
|
|
|
|
+ if (conn->security_cfm_cb)
|
|
|
|
+ conn->security_cfm_cb(conn, status);
|
|
}
|
|
}
|
|
|
|
|
|
static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
|
static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
|
@@ -1178,26 +1130,27 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
|
if (conn->pending_sec_level > conn->sec_level)
|
|
if (conn->pending_sec_level > conn->sec_level)
|
|
conn->sec_level = conn->pending_sec_level;
|
|
conn->sec_level = conn->pending_sec_level;
|
|
|
|
|
|
- hci_proto_encrypt_cfm(conn, status, encrypt);
|
|
|
|
-
|
|
|
|
- read_lock(&hci_cb_list_lock);
|
|
|
|
|
|
+ mutex_lock(&hci_cb_list_lock);
|
|
list_for_each_entry(cb, &hci_cb_list, list) {
|
|
list_for_each_entry(cb, &hci_cb_list, list) {
|
|
if (cb->security_cfm)
|
|
if (cb->security_cfm)
|
|
cb->security_cfm(conn, status, encrypt);
|
|
cb->security_cfm(conn, status, encrypt);
|
|
}
|
|
}
|
|
- read_unlock(&hci_cb_list_lock);
|
|
|
|
|
|
+ mutex_unlock(&hci_cb_list_lock);
|
|
|
|
+
|
|
|
|
+ if (conn->security_cfm_cb)
|
|
|
|
+ conn->security_cfm_cb(conn, status);
|
|
}
|
|
}
|
|
|
|
|
|
static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
|
|
static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
|
|
{
|
|
{
|
|
struct hci_cb *cb;
|
|
struct hci_cb *cb;
|
|
|
|
|
|
- read_lock(&hci_cb_list_lock);
|
|
|
|
|
|
+ mutex_lock(&hci_cb_list_lock);
|
|
list_for_each_entry(cb, &hci_cb_list, list) {
|
|
list_for_each_entry(cb, &hci_cb_list, list) {
|
|
if (cb->key_change_cfm)
|
|
if (cb->key_change_cfm)
|
|
cb->key_change_cfm(conn, status);
|
|
cb->key_change_cfm(conn, status);
|
|
}
|
|
}
|
|
- read_unlock(&hci_cb_list_lock);
|
|
|
|
|
|
+ mutex_unlock(&hci_cb_list_lock);
|
|
}
|
|
}
|
|
|
|
|
|
static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
|
|
static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
|
|
@@ -1205,12 +1158,12 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
|
|
{
|
|
{
|
|
struct hci_cb *cb;
|
|
struct hci_cb *cb;
|
|
|
|
|
|
- read_lock(&hci_cb_list_lock);
|
|
|
|
|
|
+ mutex_lock(&hci_cb_list_lock);
|
|
list_for_each_entry(cb, &hci_cb_list, list) {
|
|
list_for_each_entry(cb, &hci_cb_list, list) {
|
|
if (cb->role_switch_cfm)
|
|
if (cb->role_switch_cfm)
|
|
cb->role_switch_cfm(conn, status, role);
|
|
cb->role_switch_cfm(conn, status, role);
|
|
}
|
|
}
|
|
- read_unlock(&hci_cb_list_lock);
|
|
|
|
|
|
+ mutex_unlock(&hci_cb_list_lock);
|
|
}
|
|
}
|
|
|
|
|
|
static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
|
|
static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
|
|
@@ -1312,7 +1265,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
|
|
|
|
|
|
/* ----- HCI Sockets ----- */
|
|
/* ----- HCI Sockets ----- */
|
|
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
|
|
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
|
|
-void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk);
|
|
|
|
|
|
+void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
|
|
|
|
+ struct sock *skip_sk);
|
|
void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
|
|
void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
|
|
|
|
|
|
void hci_sock_dev_event(struct hci_dev *hdev, int event);
|
|
void hci_sock_dev_event(struct hci_dev *hdev, int event);
|