Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
This commit is contained in:
commit
bc27d5f143
@ -492,7 +492,7 @@ done:
|
|||||||
static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
||||||
{
|
{
|
||||||
u16 buf_len = 0;
|
u16 buf_len = 0;
|
||||||
int ret, buf_block_len, blksz;
|
int ret, num_blocks, blksz;
|
||||||
struct sk_buff *skb = NULL;
|
struct sk_buff *skb = NULL;
|
||||||
u32 type;
|
u32 type;
|
||||||
u8 *payload = NULL;
|
u8 *payload = NULL;
|
||||||
@ -514,18 +514,17 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
blksz = SDIO_BLOCK_SIZE;
|
blksz = SDIO_BLOCK_SIZE;
|
||||||
buf_block_len = (buf_len + blksz - 1) / blksz;
|
num_blocks = DIV_ROUND_UP(buf_len, blksz);
|
||||||
|
|
||||||
if (buf_len <= SDIO_HEADER_LEN
|
if (buf_len <= SDIO_HEADER_LEN
|
||||||
|| (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
|
|| (num_blocks * blksz) > ALLOC_BUF_SIZE) {
|
||||||
BT_ERR("invalid packet length: %d", buf_len);
|
BT_ERR("invalid packet length: %d", buf_len);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate buffer */
|
/* Allocate buffer */
|
||||||
skb = bt_skb_alloc(buf_block_len * blksz + BTSDIO_DMA_ALIGN,
|
skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
|
||||||
GFP_ATOMIC);
|
|
||||||
if (skb == NULL) {
|
if (skb == NULL) {
|
||||||
BT_ERR("No free skb");
|
BT_ERR("No free skb");
|
||||||
goto exit;
|
goto exit;
|
||||||
@ -541,7 +540,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
|||||||
payload = skb->data;
|
payload = skb->data;
|
||||||
|
|
||||||
ret = sdio_readsb(card->func, payload, card->ioport,
|
ret = sdio_readsb(card->func, payload, card->ioport,
|
||||||
buf_block_len * blksz);
|
num_blocks * blksz);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
BT_ERR("readsb failed: %d", ret);
|
BT_ERR("readsb failed: %d", ret);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
@ -553,7 +552,16 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
buf_len = payload[0];
|
buf_len = payload[0];
|
||||||
buf_len |= (u16) payload[1] << 8;
|
buf_len |= payload[1] << 8;
|
||||||
|
buf_len |= payload[2] << 16;
|
||||||
|
|
||||||
|
if (buf_len > blksz * num_blocks) {
|
||||||
|
BT_ERR("Skip incorrect packet: hdrlen %d buffer %d",
|
||||||
|
buf_len, blksz * num_blocks);
|
||||||
|
ret = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
type = payload[3];
|
type = payload[3];
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -589,8 +597,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
BT_ERR("Unknown packet type:%d", type);
|
BT_ERR("Unknown packet type:%d", type);
|
||||||
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload,
|
BT_ERR("hex: %*ph", blksz * num_blocks, payload);
|
||||||
blksz * buf_block_len);
|
|
||||||
|
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
skb = NULL;
|
skb = NULL;
|
||||||
@ -849,8 +856,7 @@ static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
i++;
|
i++;
|
||||||
BT_ERR("i=%d writesb failed: %d", i, ret);
|
BT_ERR("i=%d writesb failed: %d", i, ret);
|
||||||
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
|
BT_ERR("hex: %*ph", nb, payload);
|
||||||
payload, nb);
|
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
if (i > MAX_WRITE_IOMEM_RETRY)
|
if (i > MAX_WRITE_IOMEM_RETRY)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -19,13 +19,25 @@
|
|||||||
|
|
||||||
#define A2MP_FEAT_EXT 0x8000
|
#define A2MP_FEAT_EXT 0x8000
|
||||||
|
|
||||||
|
enum amp_mgr_state {
|
||||||
|
READ_LOC_AMP_INFO,
|
||||||
|
READ_LOC_AMP_ASSOC,
|
||||||
|
READ_LOC_AMP_ASSOC_FINAL,
|
||||||
|
};
|
||||||
|
|
||||||
struct amp_mgr {
|
struct amp_mgr {
|
||||||
|
struct list_head list;
|
||||||
struct l2cap_conn *l2cap_conn;
|
struct l2cap_conn *l2cap_conn;
|
||||||
struct l2cap_chan *a2mp_chan;
|
struct l2cap_chan *a2mp_chan;
|
||||||
|
struct l2cap_chan *bredr_chan;
|
||||||
struct kref kref;
|
struct kref kref;
|
||||||
__u8 ident;
|
__u8 ident;
|
||||||
__u8 handle;
|
__u8 handle;
|
||||||
|
enum amp_mgr_state state;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
struct list_head amp_ctrls;
|
||||||
|
struct mutex amp_ctrls_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct a2mp_cmd {
|
struct a2mp_cmd {
|
||||||
@ -118,9 +130,19 @@ struct a2mp_physlink_rsp {
|
|||||||
#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
|
#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
|
||||||
#define A2MP_STATUS_SECURITY_VIOLATION 0x06
|
#define A2MP_STATUS_SECURITY_VIOLATION 0x06
|
||||||
|
|
||||||
void amp_mgr_get(struct amp_mgr *mgr);
|
extern struct list_head amp_mgr_list;
|
||||||
|
extern struct mutex amp_mgr_list_lock;
|
||||||
|
|
||||||
|
struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
|
||||||
int amp_mgr_put(struct amp_mgr *mgr);
|
int amp_mgr_put(struct amp_mgr *mgr);
|
||||||
|
u8 __next_ident(struct amp_mgr *mgr);
|
||||||
struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
|
struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
|
||||||
struct sk_buff *skb);
|
struct sk_buff *skb);
|
||||||
|
struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
|
||||||
|
void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
|
||||||
|
void a2mp_discover_amp(struct l2cap_chan *chan);
|
||||||
|
void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
|
||||||
|
void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
|
||||||
|
void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
|
||||||
|
|
||||||
#endif /* __A2MP_H */
|
#endif /* __A2MP_H */
|
||||||
|
50
include/net/bluetooth/amp.h
Normal file
50
include/net/bluetooth/amp.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011,2012 Intel Corp.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License version 2 and
|
||||||
|
only version 2 as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __AMP_H
|
||||||
|
#define __AMP_H
|
||||||
|
|
||||||
|
struct amp_ctrl {
|
||||||
|
struct list_head list;
|
||||||
|
struct kref kref;
|
||||||
|
__u8 id;
|
||||||
|
__u16 assoc_len_so_far;
|
||||||
|
__u16 assoc_rem_len;
|
||||||
|
__u16 assoc_len;
|
||||||
|
__u8 *assoc;
|
||||||
|
};
|
||||||
|
|
||||||
|
int amp_ctrl_put(struct amp_ctrl *ctrl);
|
||||||
|
void amp_ctrl_get(struct amp_ctrl *ctrl);
|
||||||
|
struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id);
|
||||||
|
struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id);
|
||||||
|
void amp_ctrl_list_flush(struct amp_mgr *mgr);
|
||||||
|
|
||||||
|
struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||||
|
u8 remote_id, bool out);
|
||||||
|
|
||||||
|
int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
|
||||||
|
|
||||||
|
void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr);
|
||||||
|
void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
|
||||||
|
void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
|
||||||
|
void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||||
|
struct hci_conn *hcon);
|
||||||
|
void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||||
|
struct hci_conn *hcon);
|
||||||
|
void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||||
|
struct hci_conn *hcon);
|
||||||
|
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
|
||||||
|
void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
|
||||||
|
|
||||||
|
#endif /* __AMP_H */
|
@ -180,7 +180,6 @@ static inline void bacpy(bdaddr_t *dst, bdaddr_t *src)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void baswap(bdaddr_t *dst, bdaddr_t *src);
|
void baswap(bdaddr_t *dst, bdaddr_t *src);
|
||||||
char *batostr(bdaddr_t *ba);
|
|
||||||
|
|
||||||
/* Common socket structures and functions */
|
/* Common socket structures and functions */
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
#define HCI_LINK_KEY_SIZE 16
|
#define HCI_LINK_KEY_SIZE 16
|
||||||
#define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE)
|
#define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE)
|
||||||
|
|
||||||
|
#define HCI_MAX_AMP_ASSOC_SIZE 672
|
||||||
|
|
||||||
/* HCI dev events */
|
/* HCI dev events */
|
||||||
#define HCI_DEV_REG 1
|
#define HCI_DEV_REG 1
|
||||||
#define HCI_DEV_UNREG 2
|
#define HCI_DEV_UNREG 2
|
||||||
@ -196,6 +198,7 @@ enum {
|
|||||||
#define ACL_START_NO_FLUSH 0x00
|
#define ACL_START_NO_FLUSH 0x00
|
||||||
#define ACL_CONT 0x01
|
#define ACL_CONT 0x01
|
||||||
#define ACL_START 0x02
|
#define ACL_START 0x02
|
||||||
|
#define ACL_COMPLETE 0x03
|
||||||
#define ACL_ACTIVE_BCAST 0x04
|
#define ACL_ACTIVE_BCAST 0x04
|
||||||
#define ACL_PICO_BCAST 0x08
|
#define ACL_PICO_BCAST 0x08
|
||||||
|
|
||||||
@ -205,6 +208,7 @@ enum {
|
|||||||
#define ESCO_LINK 0x02
|
#define ESCO_LINK 0x02
|
||||||
/* Low Energy links do not have defined link type. Use invented one */
|
/* Low Energy links do not have defined link type. Use invented one */
|
||||||
#define LE_LINK 0x80
|
#define LE_LINK 0x80
|
||||||
|
#define AMP_LINK 0x81
|
||||||
|
|
||||||
/* LMP features */
|
/* LMP features */
|
||||||
#define LMP_3SLOT 0x01
|
#define LMP_3SLOT 0x01
|
||||||
@ -556,12 +560,46 @@ struct hci_cp_accept_phy_link {
|
|||||||
__u8 key[HCI_AMP_LINK_KEY_SIZE];
|
__u8 key[HCI_AMP_LINK_KEY_SIZE];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
#define HCI_OP_DISCONN_PHY_LINK 0x0437
|
#define HCI_OP_DISCONN_PHY_LINK 0x0437
|
||||||
struct hci_cp_disconn_phy_link {
|
struct hci_cp_disconn_phy_link {
|
||||||
__u8 phy_handle;
|
__u8 phy_handle;
|
||||||
__u8 reason;
|
__u8 reason;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct ext_flow_spec {
|
||||||
|
__u8 id;
|
||||||
|
__u8 stype;
|
||||||
|
__le16 msdu;
|
||||||
|
__le32 sdu_itime;
|
||||||
|
__le32 acc_lat;
|
||||||
|
__le32 flush_to;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_CREATE_LOGICAL_LINK 0x0438
|
||||||
|
#define HCI_OP_ACCEPT_LOGICAL_LINK 0x0439
|
||||||
|
struct hci_cp_create_accept_logical_link {
|
||||||
|
__u8 phy_handle;
|
||||||
|
struct ext_flow_spec tx_flow_spec;
|
||||||
|
struct ext_flow_spec rx_flow_spec;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_DISCONN_LOGICAL_LINK 0x043a
|
||||||
|
struct hci_cp_disconn_logical_link {
|
||||||
|
__le16 log_handle;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_LOGICAL_LINK_CANCEL 0x043b
|
||||||
|
struct hci_cp_logical_link_cancel {
|
||||||
|
__u8 phy_handle;
|
||||||
|
__u8 flow_spec_id;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct hci_rp_logical_link_cancel {
|
||||||
|
__u8 status;
|
||||||
|
__u8 phy_handle;
|
||||||
|
__u8 flow_spec_id;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#define HCI_OP_SNIFF_MODE 0x0803
|
#define HCI_OP_SNIFF_MODE 0x0803
|
||||||
struct hci_cp_sniff_mode {
|
struct hci_cp_sniff_mode {
|
||||||
__le16 handle;
|
__le16 handle;
|
||||||
|
@ -73,6 +73,7 @@ struct discovery_state {
|
|||||||
struct hci_conn_hash {
|
struct hci_conn_hash {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
unsigned int acl_num;
|
unsigned int acl_num;
|
||||||
|
unsigned int amp_num;
|
||||||
unsigned int sco_num;
|
unsigned int sco_num;
|
||||||
unsigned int le_num;
|
unsigned int le_num;
|
||||||
};
|
};
|
||||||
@ -124,6 +125,14 @@ struct le_scan_params {
|
|||||||
|
|
||||||
#define HCI_MAX_SHORT_NAME_LENGTH 10
|
#define HCI_MAX_SHORT_NAME_LENGTH 10
|
||||||
|
|
||||||
|
struct amp_assoc {
|
||||||
|
__u16 len;
|
||||||
|
__u16 offset;
|
||||||
|
__u16 rem_len;
|
||||||
|
__u16 len_so_far;
|
||||||
|
__u8 data[HCI_MAX_AMP_ASSOC_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
#define NUM_REASSEMBLY 4
|
#define NUM_REASSEMBLY 4
|
||||||
struct hci_dev {
|
struct hci_dev {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
@ -177,6 +186,8 @@ struct hci_dev {
|
|||||||
__u32 amp_max_flush_to;
|
__u32 amp_max_flush_to;
|
||||||
__u32 amp_be_flush_to;
|
__u32 amp_be_flush_to;
|
||||||
|
|
||||||
|
struct amp_assoc loc_assoc;
|
||||||
|
|
||||||
__u8 flow_ctl_mode;
|
__u8 flow_ctl_mode;
|
||||||
|
|
||||||
unsigned int auto_accept_delay;
|
unsigned int auto_accept_delay;
|
||||||
@ -252,8 +263,6 @@ struct hci_dev {
|
|||||||
|
|
||||||
struct sk_buff_head driver_init;
|
struct sk_buff_head driver_init;
|
||||||
|
|
||||||
void *core_data;
|
|
||||||
|
|
||||||
atomic_t promisc;
|
atomic_t promisc;
|
||||||
|
|
||||||
struct dentry *debugfs;
|
struct dentry *debugfs;
|
||||||
@ -277,6 +286,8 @@ struct hci_dev {
|
|||||||
int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
|
int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define HCI_PHY_HANDLE(handle) (handle & 0xff)
|
||||||
|
|
||||||
struct hci_conn {
|
struct hci_conn {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
|
||||||
@ -310,6 +321,7 @@ struct hci_conn {
|
|||||||
|
|
||||||
__u8 remote_cap;
|
__u8 remote_cap;
|
||||||
__u8 remote_auth;
|
__u8 remote_auth;
|
||||||
|
__u8 remote_id;
|
||||||
bool flush_key;
|
bool flush_key;
|
||||||
|
|
||||||
unsigned int sent;
|
unsigned int sent;
|
||||||
@ -339,7 +351,7 @@ struct hci_conn {
|
|||||||
|
|
||||||
struct hci_chan {
|
struct hci_chan {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
__u16 handle;
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
struct sk_buff_head data_q;
|
struct sk_buff_head data_q;
|
||||||
unsigned int sent;
|
unsigned int sent;
|
||||||
@ -438,6 +450,9 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
|
|||||||
case ACL_LINK:
|
case ACL_LINK:
|
||||||
h->acl_num++;
|
h->acl_num++;
|
||||||
break;
|
break;
|
||||||
|
case AMP_LINK:
|
||||||
|
h->amp_num++;
|
||||||
|
break;
|
||||||
case LE_LINK:
|
case LE_LINK:
|
||||||
h->le_num++;
|
h->le_num++;
|
||||||
break;
|
break;
|
||||||
@ -459,6 +474,9 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
|
|||||||
case ACL_LINK:
|
case ACL_LINK:
|
||||||
h->acl_num--;
|
h->acl_num--;
|
||||||
break;
|
break;
|
||||||
|
case AMP_LINK:
|
||||||
|
h->amp_num--;
|
||||||
|
break;
|
||||||
case LE_LINK:
|
case LE_LINK:
|
||||||
h->le_num--;
|
h->le_num--;
|
||||||
break;
|
break;
|
||||||
@ -475,6 +493,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case ACL_LINK:
|
case ACL_LINK:
|
||||||
return h->acl_num;
|
return h->acl_num;
|
||||||
|
case AMP_LINK:
|
||||||
|
return h->amp_num;
|
||||||
case LE_LINK:
|
case LE_LINK:
|
||||||
return h->le_num;
|
return h->le_num;
|
||||||
case SCO_LINK:
|
case SCO_LINK:
|
||||||
@ -556,6 +576,7 @@ void hci_conn_check_pending(struct hci_dev *hdev);
|
|||||||
struct hci_chan *hci_chan_create(struct hci_conn *conn);
|
struct hci_chan *hci_chan_create(struct hci_conn *conn);
|
||||||
void hci_chan_del(struct hci_chan *chan);
|
void hci_chan_del(struct hci_chan *chan);
|
||||||
void hci_chan_list_flush(struct hci_conn *conn);
|
void hci_chan_list_flush(struct hci_conn *conn);
|
||||||
|
struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
|
||||||
|
|
||||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||||
__u8 dst_type, __u8 sec_level, __u8 auth_type);
|
__u8 dst_type, __u8 sec_level, __u8 auth_type);
|
||||||
@ -584,7 +605,10 @@ static inline void hci_conn_put(struct hci_conn *conn)
|
|||||||
|
|
||||||
if (atomic_dec_and_test(&conn->refcnt)) {
|
if (atomic_dec_and_test(&conn->refcnt)) {
|
||||||
unsigned long timeo;
|
unsigned long timeo;
|
||||||
if (conn->type == ACL_LINK || conn->type == LE_LINK) {
|
|
||||||
|
switch (conn->type) {
|
||||||
|
case ACL_LINK:
|
||||||
|
case LE_LINK:
|
||||||
del_timer(&conn->idle_timer);
|
del_timer(&conn->idle_timer);
|
||||||
if (conn->state == BT_CONNECTED) {
|
if (conn->state == BT_CONNECTED) {
|
||||||
timeo = conn->disc_timeout;
|
timeo = conn->disc_timeout;
|
||||||
@ -593,12 +617,20 @@ static inline void hci_conn_put(struct hci_conn *conn)
|
|||||||
} else {
|
} else {
|
||||||
timeo = msecs_to_jiffies(10);
|
timeo = msecs_to_jiffies(10);
|
||||||
}
|
}
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
case AMP_LINK:
|
||||||
|
timeo = conn->disc_timeout;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
timeo = msecs_to_jiffies(10);
|
timeo = msecs_to_jiffies(10);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel_delayed_work(&conn->disc_work);
|
cancel_delayed_work(&conn->disc_work);
|
||||||
queue_delayed_work(conn->hdev->workqueue,
|
queue_delayed_work(conn->hdev->workqueue,
|
||||||
&conn->disc_work, timeo);
|
&conn->disc_work, timeo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,6 +821,10 @@ static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
|
|||||||
sco_disconn_cfm(conn, reason);
|
sco_disconn_cfm(conn, reason);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* L2CAP would be handled for BREDR chan */
|
||||||
|
case AMP_LINK:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_ERR("unknown link type %d", conn->type);
|
BT_ERR("unknown link type %d", conn->type);
|
||||||
break;
|
break;
|
||||||
|
@ -32,13 +32,14 @@
|
|||||||
/* L2CAP defaults */
|
/* L2CAP defaults */
|
||||||
#define L2CAP_DEFAULT_MTU 672
|
#define L2CAP_DEFAULT_MTU 672
|
||||||
#define L2CAP_DEFAULT_MIN_MTU 48
|
#define L2CAP_DEFAULT_MIN_MTU 48
|
||||||
#define L2CAP_DEFAULT_FLUSH_TO 0xffff
|
#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
|
||||||
|
#define L2CAP_EFS_DEFAULT_FLUSH_TO 0xFFFFFFFF
|
||||||
#define L2CAP_DEFAULT_TX_WINDOW 63
|
#define L2CAP_DEFAULT_TX_WINDOW 63
|
||||||
#define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF
|
#define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF
|
||||||
#define L2CAP_DEFAULT_MAX_TX 3
|
#define L2CAP_DEFAULT_MAX_TX 3
|
||||||
#define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */
|
#define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */
|
||||||
#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
|
#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
|
||||||
#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */
|
#define L2CAP_DEFAULT_MAX_PDU_SIZE 1492 /* Sized for AMP packet */
|
||||||
#define L2CAP_DEFAULT_ACK_TO 200
|
#define L2CAP_DEFAULT_ACK_TO 200
|
||||||
#define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF
|
#define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF
|
||||||
#define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF
|
#define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF
|
||||||
@ -508,6 +509,8 @@ struct l2cap_chan {
|
|||||||
__u32 remote_acc_lat;
|
__u32 remote_acc_lat;
|
||||||
__u32 remote_flush_to;
|
__u32 remote_flush_to;
|
||||||
|
|
||||||
|
__u8 ctrl_id;
|
||||||
|
|
||||||
struct delayed_work chan_timer;
|
struct delayed_work chan_timer;
|
||||||
struct delayed_work retrans_timer;
|
struct delayed_work retrans_timer;
|
||||||
struct delayed_work monitor_timer;
|
struct delayed_work monitor_timer;
|
||||||
@ -538,6 +541,7 @@ struct l2cap_ops {
|
|||||||
void (*state_change) (struct l2cap_chan *chan,
|
void (*state_change) (struct l2cap_chan *chan,
|
||||||
int state);
|
int state);
|
||||||
void (*ready) (struct l2cap_chan *chan);
|
void (*ready) (struct l2cap_chan *chan);
|
||||||
|
void (*defer) (struct l2cap_chan *chan);
|
||||||
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
|
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
|
||||||
unsigned long len, int nb);
|
unsigned long len, int nb);
|
||||||
};
|
};
|
||||||
@ -745,6 +749,10 @@ static inline void l2cap_chan_no_ready(struct l2cap_chan *chan)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void l2cap_chan_no_defer(struct l2cap_chan *chan)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
extern bool disable_ertm;
|
extern bool disable_ertm;
|
||||||
|
|
||||||
int l2cap_init_sockets(void);
|
int l2cap_init_sockets(void);
|
||||||
@ -767,6 +775,8 @@ int l2cap_chan_check_security(struct l2cap_chan *chan);
|
|||||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan);
|
void l2cap_chan_set_defaults(struct l2cap_chan *chan);
|
||||||
int l2cap_ertm_init(struct l2cap_chan *chan);
|
int l2cap_ertm_init(struct l2cap_chan *chan);
|
||||||
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||||
|
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||||
void l2cap_chan_del(struct l2cap_chan *chan, int err);
|
void l2cap_chan_del(struct l2cap_chan *chan, int err);
|
||||||
|
void l2cap_send_conn_req(struct l2cap_chan *chan);
|
||||||
|
|
||||||
#endif /* __L2CAP_H */
|
#endif /* __L2CAP_H */
|
||||||
|
@ -11,6 +11,7 @@ menuconfig BT
|
|||||||
select CRYPTO_BLKCIPHER
|
select CRYPTO_BLKCIPHER
|
||||||
select CRYPTO_AES
|
select CRYPTO_AES
|
||||||
select CRYPTO_ECB
|
select CRYPTO_ECB
|
||||||
|
select CRYPTO_SHA256
|
||||||
help
|
help
|
||||||
Bluetooth is low-cost, low-power, short-range wireless technology.
|
Bluetooth is low-cost, low-power, short-range wireless technology.
|
||||||
It was designed as a replacement for cables and other short-range
|
It was designed as a replacement for cables and other short-range
|
||||||
|
@ -10,4 +10,4 @@ obj-$(CONFIG_BT_HIDP) += hidp/
|
|||||||
|
|
||||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
||||||
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
|
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
|
||||||
a2mp.o
|
a2mp.o amp.o
|
||||||
|
@ -16,6 +16,11 @@
|
|||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
#include <net/bluetooth/l2cap.h>
|
#include <net/bluetooth/l2cap.h>
|
||||||
#include <net/bluetooth/a2mp.h>
|
#include <net/bluetooth/a2mp.h>
|
||||||
|
#include <net/bluetooth/amp.h>
|
||||||
|
|
||||||
|
/* Global AMP Manager list */
|
||||||
|
LIST_HEAD(amp_mgr_list);
|
||||||
|
DEFINE_MUTEX(amp_mgr_list_lock);
|
||||||
|
|
||||||
/* A2MP build & send command helper functions */
|
/* A2MP build & send command helper functions */
|
||||||
static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
|
static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
|
||||||
@ -37,8 +42,7 @@ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
|
|||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
|
void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
|
||||||
void *data)
|
|
||||||
{
|
{
|
||||||
struct l2cap_chan *chan = mgr->a2mp_chan;
|
struct l2cap_chan *chan = mgr->a2mp_chan;
|
||||||
struct a2mp_cmd *cmd;
|
struct a2mp_cmd *cmd;
|
||||||
@ -63,6 +67,14 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
|
|||||||
kfree(cmd);
|
kfree(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u8 __next_ident(struct amp_mgr *mgr)
|
||||||
|
{
|
||||||
|
if (++mgr->ident == 0)
|
||||||
|
mgr->ident = 1;
|
||||||
|
|
||||||
|
return mgr->ident;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
|
static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
|
||||||
{
|
{
|
||||||
cl->id = 0;
|
cl->id = 0;
|
||||||
@ -161,6 +173,83 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||||
|
struct a2mp_cmd *hdr)
|
||||||
|
{
|
||||||
|
struct a2mp_discov_rsp *rsp = (void *) skb->data;
|
||||||
|
u16 len = le16_to_cpu(hdr->len);
|
||||||
|
struct a2mp_cl *cl;
|
||||||
|
u16 ext_feat;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (len < sizeof(*rsp))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
len -= sizeof(*rsp);
|
||||||
|
skb_pull(skb, sizeof(*rsp));
|
||||||
|
|
||||||
|
ext_feat = le16_to_cpu(rsp->ext_feat);
|
||||||
|
|
||||||
|
BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(rsp->mtu), ext_feat);
|
||||||
|
|
||||||
|
/* check that packet is not broken for now */
|
||||||
|
while (ext_feat & A2MP_FEAT_EXT) {
|
||||||
|
if (len < sizeof(ext_feat))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ext_feat = get_unaligned_le16(skb->data);
|
||||||
|
BT_DBG("efm 0x%4.4x", ext_feat);
|
||||||
|
len -= sizeof(ext_feat);
|
||||||
|
skb_pull(skb, sizeof(ext_feat));
|
||||||
|
}
|
||||||
|
|
||||||
|
cl = (void *) skb->data;
|
||||||
|
while (len >= sizeof(*cl)) {
|
||||||
|
BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type,
|
||||||
|
cl->status);
|
||||||
|
|
||||||
|
if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) {
|
||||||
|
struct a2mp_info_req req;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
req.id = cl->id;
|
||||||
|
a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr),
|
||||||
|
sizeof(req), &req);
|
||||||
|
}
|
||||||
|
|
||||||
|
len -= sizeof(*cl);
|
||||||
|
cl = (void *) skb_pull(skb, sizeof(*cl));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fall back to L2CAP init sequence */
|
||||||
|
if (!found) {
|
||||||
|
struct l2cap_conn *conn = mgr->l2cap_conn;
|
||||||
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
|
mutex_lock(&conn->chan_lock);
|
||||||
|
|
||||||
|
list_for_each_entry(chan, &conn->chan_l, list) {
|
||||||
|
|
||||||
|
BT_DBG("chan %p state %s", chan,
|
||||||
|
state_to_string(chan->state));
|
||||||
|
|
||||||
|
if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
l2cap_chan_lock(chan);
|
||||||
|
|
||||||
|
if (chan->state == BT_CONNECT)
|
||||||
|
l2cap_send_conn_req(chan);
|
||||||
|
|
||||||
|
l2cap_chan_unlock(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&conn->chan_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
|
static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||||
struct a2mp_cmd *hdr)
|
struct a2mp_cmd *hdr)
|
||||||
{
|
{
|
||||||
@ -181,7 +270,6 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
|||||||
struct a2mp_cmd *hdr)
|
struct a2mp_cmd *hdr)
|
||||||
{
|
{
|
||||||
struct a2mp_info_req *req = (void *) skb->data;
|
struct a2mp_info_req *req = (void *) skb->data;
|
||||||
struct a2mp_info_rsp rsp;
|
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
|
|
||||||
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
||||||
@ -189,53 +277,93 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
|||||||
|
|
||||||
BT_DBG("id %d", req->id);
|
BT_DBG("id %d", req->id);
|
||||||
|
|
||||||
rsp.id = req->id;
|
|
||||||
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
|
||||||
|
|
||||||
hdev = hci_dev_get(req->id);
|
hdev = hci_dev_get(req->id);
|
||||||
if (hdev && hdev->amp_type != HCI_BREDR) {
|
if (!hdev || hdev->dev_type != HCI_AMP) {
|
||||||
rsp.status = 0;
|
struct a2mp_info_rsp rsp;
|
||||||
rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
|
|
||||||
rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
|
rsp.id = req->id;
|
||||||
rsp.min_latency = cpu_to_le32(hdev->amp_min_latency);
|
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
||||||
rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap);
|
|
||||||
rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size);
|
a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp),
|
||||||
|
&rsp);
|
||||||
|
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mgr->state = READ_LOC_AMP_INFO;
|
||||||
|
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
|
||||||
|
|
||||||
|
done:
|
||||||
if (hdev)
|
if (hdev)
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp);
|
|
||||||
|
|
||||||
skb_pull(skb, sizeof(*req));
|
skb_pull(skb, sizeof(*req));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||||
|
struct a2mp_cmd *hdr)
|
||||||
|
{
|
||||||
|
struct a2mp_info_rsp *rsp = (struct a2mp_info_rsp *) skb->data;
|
||||||
|
struct a2mp_amp_assoc_req req;
|
||||||
|
struct amp_ctrl *ctrl;
|
||||||
|
|
||||||
|
if (le16_to_cpu(hdr->len) < sizeof(*rsp))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
BT_DBG("id %d status 0x%2.2x", rsp->id, rsp->status);
|
||||||
|
|
||||||
|
if (rsp->status)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ctrl = amp_ctrl_add(mgr, rsp->id);
|
||||||
|
if (!ctrl)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
req.id = rsp->id;
|
||||||
|
a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req),
|
||||||
|
&req);
|
||||||
|
|
||||||
|
skb_pull(skb, sizeof(*rsp));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||||
struct a2mp_cmd *hdr)
|
struct a2mp_cmd *hdr)
|
||||||
{
|
{
|
||||||
struct a2mp_amp_assoc_req *req = (void *) skb->data;
|
struct a2mp_amp_assoc_req *req = (void *) skb->data;
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
|
struct amp_mgr *tmp;
|
||||||
|
|
||||||
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
BT_DBG("id %d", req->id);
|
BT_DBG("id %d", req->id);
|
||||||
|
|
||||||
|
/* Make sure that other request is not processed */
|
||||||
|
tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
|
||||||
|
|
||||||
hdev = hci_dev_get(req->id);
|
hdev = hci_dev_get(req->id);
|
||||||
if (!hdev || hdev->amp_type == HCI_BREDR) {
|
if (!hdev || hdev->amp_type == HCI_BREDR || tmp) {
|
||||||
struct a2mp_amp_assoc_rsp rsp;
|
struct a2mp_amp_assoc_rsp rsp;
|
||||||
rsp.id = req->id;
|
rsp.id = req->id;
|
||||||
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
|
||||||
|
if (tmp) {
|
||||||
|
rsp.status = A2MP_STATUS_COLLISION_OCCURED;
|
||||||
|
amp_mgr_put(tmp);
|
||||||
|
} else {
|
||||||
|
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
||||||
|
}
|
||||||
|
|
||||||
a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
|
a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
|
||||||
&rsp);
|
&rsp);
|
||||||
goto clean;
|
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Placeholder for HCI Read AMP Assoc */
|
amp_read_loc_assoc(hdev, mgr);
|
||||||
|
|
||||||
clean:
|
done:
|
||||||
if (hdev)
|
if (hdev)
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
@ -243,6 +371,68 @@ clean:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||||
|
struct a2mp_cmd *hdr)
|
||||||
|
{
|
||||||
|
struct a2mp_amp_assoc_rsp *rsp = (void *) skb->data;
|
||||||
|
u16 len = le16_to_cpu(hdr->len);
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
struct amp_ctrl *ctrl;
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
size_t assoc_len;
|
||||||
|
|
||||||
|
if (len < sizeof(*rsp))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
assoc_len = len - sizeof(*rsp);
|
||||||
|
|
||||||
|
BT_DBG("id %d status 0x%2.2x assoc len %zu", rsp->id, rsp->status,
|
||||||
|
assoc_len);
|
||||||
|
|
||||||
|
if (rsp->status)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Save remote ASSOC data */
|
||||||
|
ctrl = amp_ctrl_lookup(mgr, rsp->id);
|
||||||
|
if (ctrl) {
|
||||||
|
u8 *assoc;
|
||||||
|
|
||||||
|
assoc = kzalloc(assoc_len, GFP_KERNEL);
|
||||||
|
if (!assoc) {
|
||||||
|
amp_ctrl_put(ctrl);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(assoc, rsp->amp_assoc, assoc_len);
|
||||||
|
ctrl->assoc = assoc;
|
||||||
|
ctrl->assoc_len = assoc_len;
|
||||||
|
ctrl->assoc_rem_len = assoc_len;
|
||||||
|
ctrl->assoc_len_so_far = 0;
|
||||||
|
|
||||||
|
amp_ctrl_put(ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create Phys Link */
|
||||||
|
hdev = hci_dev_get(rsp->id);
|
||||||
|
if (!hdev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
hcon = phylink_add(hdev, mgr, rsp->id, true);
|
||||||
|
if (!hcon)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id);
|
||||||
|
|
||||||
|
mgr->bredr_chan->ctrl_id = rsp->id;
|
||||||
|
|
||||||
|
amp_create_phylink(hdev, mgr, hcon);
|
||||||
|
|
||||||
|
done:
|
||||||
|
hci_dev_put(hdev);
|
||||||
|
skb_pull(skb, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||||
struct a2mp_cmd *hdr)
|
struct a2mp_cmd *hdr)
|
||||||
{
|
{
|
||||||
@ -250,6 +440,8 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
|||||||
|
|
||||||
struct a2mp_physlink_rsp rsp;
|
struct a2mp_physlink_rsp rsp;
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
struct amp_ctrl *ctrl;
|
||||||
|
|
||||||
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -265,9 +457,43 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
|||||||
goto send_rsp;
|
goto send_rsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO process physlink create */
|
ctrl = amp_ctrl_lookup(mgr, rsp.remote_id);
|
||||||
|
if (!ctrl) {
|
||||||
|
ctrl = amp_ctrl_add(mgr, rsp.remote_id);
|
||||||
|
if (ctrl) {
|
||||||
|
amp_ctrl_get(ctrl);
|
||||||
|
} else {
|
||||||
|
rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
|
||||||
|
goto send_rsp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rsp.status = A2MP_STATUS_SUCCESS;
|
if (ctrl) {
|
||||||
|
size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req);
|
||||||
|
u8 *assoc;
|
||||||
|
|
||||||
|
assoc = kzalloc(assoc_len, GFP_KERNEL);
|
||||||
|
if (!assoc) {
|
||||||
|
amp_ctrl_put(ctrl);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(assoc, req->amp_assoc, assoc_len);
|
||||||
|
ctrl->assoc = assoc;
|
||||||
|
ctrl->assoc_len = assoc_len;
|
||||||
|
ctrl->assoc_rem_len = assoc_len;
|
||||||
|
ctrl->assoc_len_so_far = 0;
|
||||||
|
|
||||||
|
amp_ctrl_put(ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
hcon = phylink_add(hdev, mgr, req->local_id, false);
|
||||||
|
if (hcon) {
|
||||||
|
amp_accept_phylink(hdev, mgr, hcon);
|
||||||
|
rsp.status = A2MP_STATUS_SUCCESS;
|
||||||
|
} else {
|
||||||
|
rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
|
||||||
|
}
|
||||||
|
|
||||||
send_rsp:
|
send_rsp:
|
||||||
if (hdev)
|
if (hdev)
|
||||||
@ -286,6 +512,7 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
|||||||
struct a2mp_physlink_req *req = (void *) skb->data;
|
struct a2mp_physlink_req *req = (void *) skb->data;
|
||||||
struct a2mp_physlink_rsp rsp;
|
struct a2mp_physlink_rsp rsp;
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
|
||||||
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -296,14 +523,22 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
|||||||
rsp.remote_id = req->local_id;
|
rsp.remote_id = req->local_id;
|
||||||
rsp.status = A2MP_STATUS_SUCCESS;
|
rsp.status = A2MP_STATUS_SUCCESS;
|
||||||
|
|
||||||
hdev = hci_dev_get(req->local_id);
|
hdev = hci_dev_get(req->remote_id);
|
||||||
if (!hdev) {
|
if (!hdev) {
|
||||||
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
||||||
goto send_rsp;
|
goto send_rsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, mgr->l2cap_conn->dst);
|
||||||
|
if (!hcon) {
|
||||||
|
BT_ERR("No phys link exist");
|
||||||
|
rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO Disconnect Phys Link here */
|
/* TODO Disconnect Phys Link here */
|
||||||
|
|
||||||
|
clean:
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
send_rsp:
|
send_rsp:
|
||||||
@ -377,10 +612,19 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
|
|||||||
err = a2mp_discphyslink_req(mgr, skb, hdr);
|
err = a2mp_discphyslink_req(mgr, skb, hdr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case A2MP_CHANGE_RSP:
|
|
||||||
case A2MP_DISCOVER_RSP:
|
case A2MP_DISCOVER_RSP:
|
||||||
|
err = a2mp_discover_rsp(mgr, skb, hdr);
|
||||||
|
break;
|
||||||
|
|
||||||
case A2MP_GETINFO_RSP:
|
case A2MP_GETINFO_RSP:
|
||||||
|
err = a2mp_getinfo_rsp(mgr, skb, hdr);
|
||||||
|
break;
|
||||||
|
|
||||||
case A2MP_GETAMPASSOC_RSP:
|
case A2MP_GETAMPASSOC_RSP:
|
||||||
|
err = a2mp_getampassoc_rsp(mgr, skb, hdr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case A2MP_CHANGE_RSP:
|
||||||
case A2MP_CREATEPHYSLINK_RSP:
|
case A2MP_CREATEPHYSLINK_RSP:
|
||||||
case A2MP_DISCONNPHYSLINK_RSP:
|
case A2MP_DISCONNPHYSLINK_RSP:
|
||||||
err = a2mp_cmd_rsp(mgr, skb, hdr);
|
err = a2mp_cmd_rsp(mgr, skb, hdr);
|
||||||
@ -455,9 +699,10 @@ static struct l2cap_ops a2mp_chan_ops = {
|
|||||||
.new_connection = l2cap_chan_no_new_connection,
|
.new_connection = l2cap_chan_no_new_connection,
|
||||||
.teardown = l2cap_chan_no_teardown,
|
.teardown = l2cap_chan_no_teardown,
|
||||||
.ready = l2cap_chan_no_ready,
|
.ready = l2cap_chan_no_ready,
|
||||||
|
.defer = l2cap_chan_no_defer,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
|
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
|
||||||
{
|
{
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
int err;
|
int err;
|
||||||
@ -492,7 +737,10 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
|
|||||||
|
|
||||||
chan->conf_state = 0;
|
chan->conf_state = 0;
|
||||||
|
|
||||||
l2cap_chan_add(conn, chan);
|
if (locked)
|
||||||
|
__l2cap_chan_add(conn, chan);
|
||||||
|
else
|
||||||
|
l2cap_chan_add(conn, chan);
|
||||||
|
|
||||||
chan->remote_mps = chan->omtu;
|
chan->remote_mps = chan->omtu;
|
||||||
chan->mps = chan->omtu;
|
chan->mps = chan->omtu;
|
||||||
@ -503,11 +751,13 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* AMP Manager functions */
|
/* AMP Manager functions */
|
||||||
void amp_mgr_get(struct amp_mgr *mgr)
|
struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr)
|
||||||
{
|
{
|
||||||
BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount));
|
BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount));
|
||||||
|
|
||||||
kref_get(&mgr->kref);
|
kref_get(&mgr->kref);
|
||||||
|
|
||||||
|
return mgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void amp_mgr_destroy(struct kref *kref)
|
static void amp_mgr_destroy(struct kref *kref)
|
||||||
@ -516,6 +766,11 @@ static void amp_mgr_destroy(struct kref *kref)
|
|||||||
|
|
||||||
BT_DBG("mgr %p", mgr);
|
BT_DBG("mgr %p", mgr);
|
||||||
|
|
||||||
|
mutex_lock(&_mgr_list_lock);
|
||||||
|
list_del(&mgr->list);
|
||||||
|
mutex_unlock(&_mgr_list_lock);
|
||||||
|
|
||||||
|
amp_ctrl_list_flush(mgr);
|
||||||
kfree(mgr);
|
kfree(mgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,7 +781,7 @@ int amp_mgr_put(struct amp_mgr *mgr)
|
|||||||
return kref_put(&mgr->kref, &_mgr_destroy);
|
return kref_put(&mgr->kref, &_mgr_destroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
|
static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn, bool locked)
|
||||||
{
|
{
|
||||||
struct amp_mgr *mgr;
|
struct amp_mgr *mgr;
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
@ -539,7 +794,7 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
|
|||||||
|
|
||||||
mgr->l2cap_conn = conn;
|
mgr->l2cap_conn = conn;
|
||||||
|
|
||||||
chan = a2mp_chan_open(conn);
|
chan = a2mp_chan_open(conn, locked);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
kfree(mgr);
|
kfree(mgr);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -552,6 +807,14 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
|
|||||||
|
|
||||||
kref_init(&mgr->kref);
|
kref_init(&mgr->kref);
|
||||||
|
|
||||||
|
/* Remote AMP ctrl list initialization */
|
||||||
|
INIT_LIST_HEAD(&mgr->amp_ctrls);
|
||||||
|
mutex_init(&mgr->amp_ctrls_lock);
|
||||||
|
|
||||||
|
mutex_lock(&_mgr_list_lock);
|
||||||
|
list_add(&mgr->list, &_mgr_list);
|
||||||
|
mutex_unlock(&_mgr_list_lock);
|
||||||
|
|
||||||
return mgr;
|
return mgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,7 +823,7 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
|
|||||||
{
|
{
|
||||||
struct amp_mgr *mgr;
|
struct amp_mgr *mgr;
|
||||||
|
|
||||||
mgr = amp_mgr_create(conn);
|
mgr = amp_mgr_create(conn, false);
|
||||||
if (!mgr) {
|
if (!mgr) {
|
||||||
BT_ERR("Could not create AMP manager");
|
BT_ERR("Could not create AMP manager");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -570,3 +833,139 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
return mgr->a2mp_chan;
|
return mgr->a2mp_chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
|
||||||
|
{
|
||||||
|
struct amp_mgr *mgr;
|
||||||
|
|
||||||
|
mutex_lock(&_mgr_list_lock);
|
||||||
|
list_for_each_entry(mgr, &_mgr_list, list) {
|
||||||
|
if (mgr->state == state) {
|
||||||
|
amp_mgr_get(mgr);
|
||||||
|
mutex_unlock(&_mgr_list_lock);
|
||||||
|
return mgr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&_mgr_list_lock);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
struct amp_mgr *mgr;
|
||||||
|
struct a2mp_info_rsp rsp;
|
||||||
|
|
||||||
|
mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_INFO);
|
||||||
|
if (!mgr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BT_DBG("%s mgr %p", hdev->name, mgr);
|
||||||
|
|
||||||
|
rsp.id = hdev->id;
|
||||||
|
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
|
||||||
|
|
||||||
|
if (hdev->amp_type != HCI_BREDR) {
|
||||||
|
rsp.status = 0;
|
||||||
|
rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
|
||||||
|
rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
|
||||||
|
rsp.min_latency = cpu_to_le32(hdev->amp_min_latency);
|
||||||
|
rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap);
|
||||||
|
rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp);
|
||||||
|
amp_mgr_put(mgr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
struct amp_mgr *mgr;
|
||||||
|
struct amp_assoc *loc_assoc = &hdev->loc_assoc;
|
||||||
|
struct a2mp_amp_assoc_rsp *rsp;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
|
||||||
|
if (!mgr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BT_DBG("%s mgr %p", hdev->name, mgr);
|
||||||
|
|
||||||
|
len = sizeof(struct a2mp_amp_assoc_rsp) + loc_assoc->len;
|
||||||
|
rsp = kzalloc(len, GFP_KERNEL);
|
||||||
|
if (!rsp) {
|
||||||
|
amp_mgr_put(mgr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp->id = hdev->id;
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
rsp->status = A2MP_STATUS_INVALID_CTRL_ID;
|
||||||
|
} else {
|
||||||
|
rsp->status = A2MP_STATUS_SUCCESS;
|
||||||
|
memcpy(rsp->amp_assoc, loc_assoc->data, loc_assoc->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, mgr->ident, len, rsp);
|
||||||
|
amp_mgr_put(mgr);
|
||||||
|
kfree(rsp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
struct amp_mgr *mgr;
|
||||||
|
struct amp_assoc *loc_assoc = &hdev->loc_assoc;
|
||||||
|
struct a2mp_physlink_req *req;
|
||||||
|
struct l2cap_chan *bredr_chan;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC_FINAL);
|
||||||
|
if (!mgr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
len = sizeof(*req) + loc_assoc->len;
|
||||||
|
|
||||||
|
BT_DBG("%s mgr %p assoc_len %zu", hdev->name, mgr, len);
|
||||||
|
|
||||||
|
req = kzalloc(len, GFP_KERNEL);
|
||||||
|
if (!req) {
|
||||||
|
amp_mgr_put(mgr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bredr_chan = mgr->bredr_chan;
|
||||||
|
if (!bredr_chan)
|
||||||
|
goto clean;
|
||||||
|
|
||||||
|
req->local_id = hdev->id;
|
||||||
|
req->remote_id = bredr_chan->ctrl_id;
|
||||||
|
memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len);
|
||||||
|
|
||||||
|
a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req);
|
||||||
|
|
||||||
|
clean:
|
||||||
|
amp_mgr_put(mgr);
|
||||||
|
kfree(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void a2mp_discover_amp(struct l2cap_chan *chan)
|
||||||
|
{
|
||||||
|
struct l2cap_conn *conn = chan->conn;
|
||||||
|
struct amp_mgr *mgr = conn->hcon->amp_mgr;
|
||||||
|
struct a2mp_discov_req req;
|
||||||
|
|
||||||
|
BT_DBG("chan %p conn %p mgr %p", chan, conn, mgr);
|
||||||
|
|
||||||
|
if (!mgr) {
|
||||||
|
mgr = amp_mgr_create(conn, true);
|
||||||
|
if (!mgr)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr->bredr_chan = chan;
|
||||||
|
|
||||||
|
req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
|
||||||
|
req.ext_feat = 0;
|
||||||
|
a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req);
|
||||||
|
}
|
||||||
|
@ -569,7 +569,6 @@ static int bt_seq_show(struct seq_file *seq, void *v)
|
|||||||
{
|
{
|
||||||
struct bt_seq_state *s = seq->private;
|
struct bt_seq_state *s = seq->private;
|
||||||
struct bt_sock_list *l = s->l;
|
struct bt_sock_list *l = s->l;
|
||||||
bdaddr_t src_baswapped, dst_baswapped;
|
|
||||||
|
|
||||||
if (v == SEQ_START_TOKEN) {
|
if (v == SEQ_START_TOKEN) {
|
||||||
seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent");
|
seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent");
|
||||||
@ -583,18 +582,17 @@ static int bt_seq_show(struct seq_file *seq, void *v)
|
|||||||
} else {
|
} else {
|
||||||
struct sock *sk = sk_entry(v);
|
struct sock *sk = sk_entry(v);
|
||||||
struct bt_sock *bt = bt_sk(sk);
|
struct bt_sock *bt = bt_sk(sk);
|
||||||
baswap(&src_baswapped, &bt->src);
|
|
||||||
baswap(&dst_baswapped, &bt->dst);
|
|
||||||
|
|
||||||
seq_printf(seq, "%pK %-6d %-6u %-6u %-6u %-6lu %pM %pM %-6lu",
|
seq_printf(seq,
|
||||||
|
"%pK %-6d %-6u %-6u %-6u %-6lu %pMR %pMR %-6lu",
|
||||||
sk,
|
sk,
|
||||||
atomic_read(&sk->sk_refcnt),
|
atomic_read(&sk->sk_refcnt),
|
||||||
sk_rmem_alloc_get(sk),
|
sk_rmem_alloc_get(sk),
|
||||||
sk_wmem_alloc_get(sk),
|
sk_wmem_alloc_get(sk),
|
||||||
from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
|
from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
|
||||||
sock_i_ino(sk),
|
sock_i_ino(sk),
|
||||||
&src_baswapped,
|
&bt->src,
|
||||||
&dst_baswapped,
|
&bt->dst,
|
||||||
bt->parent? sock_i_ino(bt->parent): 0LU);
|
bt->parent? sock_i_ino(bt->parent): 0LU);
|
||||||
|
|
||||||
if (l->custom_seq_show) {
|
if (l->custom_seq_show) {
|
||||||
|
374
net/bluetooth/amp.c
Normal file
374
net/bluetooth/amp.c
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2011,2012 Intel Corp.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License version 2 and
|
||||||
|
only version 2 as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <net/bluetooth/bluetooth.h>
|
||||||
|
#include <net/bluetooth/hci.h>
|
||||||
|
#include <net/bluetooth/hci_core.h>
|
||||||
|
#include <net/bluetooth/a2mp.h>
|
||||||
|
#include <net/bluetooth/amp.h>
|
||||||
|
#include <crypto/hash.h>
|
||||||
|
|
||||||
|
/* Remote AMP Controllers interface */
|
||||||
|
void amp_ctrl_get(struct amp_ctrl *ctrl)
|
||||||
|
{
|
||||||
|
BT_DBG("ctrl %p orig refcnt %d", ctrl,
|
||||||
|
atomic_read(&ctrl->kref.refcount));
|
||||||
|
|
||||||
|
kref_get(&ctrl->kref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amp_ctrl_destroy(struct kref *kref)
|
||||||
|
{
|
||||||
|
struct amp_ctrl *ctrl = container_of(kref, struct amp_ctrl, kref);
|
||||||
|
|
||||||
|
BT_DBG("ctrl %p", ctrl);
|
||||||
|
|
||||||
|
kfree(ctrl->assoc);
|
||||||
|
kfree(ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
int amp_ctrl_put(struct amp_ctrl *ctrl)
|
||||||
|
{
|
||||||
|
BT_DBG("ctrl %p orig refcnt %d", ctrl,
|
||||||
|
atomic_read(&ctrl->kref.refcount));
|
||||||
|
|
||||||
|
return kref_put(&ctrl->kref, &_ctrl_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id)
|
||||||
|
{
|
||||||
|
struct amp_ctrl *ctrl;
|
||||||
|
|
||||||
|
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
||||||
|
if (!ctrl)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
kref_init(&ctrl->kref);
|
||||||
|
ctrl->id = id;
|
||||||
|
|
||||||
|
mutex_lock(&mgr->amp_ctrls_lock);
|
||||||
|
list_add(&ctrl->list, &mgr->amp_ctrls);
|
||||||
|
mutex_unlock(&mgr->amp_ctrls_lock);
|
||||||
|
|
||||||
|
BT_DBG("mgr %p ctrl %p", mgr, ctrl);
|
||||||
|
|
||||||
|
return ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amp_ctrl_list_flush(struct amp_mgr *mgr)
|
||||||
|
{
|
||||||
|
struct amp_ctrl *ctrl, *n;
|
||||||
|
|
||||||
|
BT_DBG("mgr %p", mgr);
|
||||||
|
|
||||||
|
mutex_lock(&mgr->amp_ctrls_lock);
|
||||||
|
list_for_each_entry_safe(ctrl, n, &mgr->amp_ctrls, list) {
|
||||||
|
list_del(&ctrl->list);
|
||||||
|
amp_ctrl_put(ctrl);
|
||||||
|
}
|
||||||
|
mutex_unlock(&mgr->amp_ctrls_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id)
|
||||||
|
{
|
||||||
|
struct amp_ctrl *ctrl;
|
||||||
|
|
||||||
|
BT_DBG("mgr %p id %d", mgr, id);
|
||||||
|
|
||||||
|
mutex_lock(&mgr->amp_ctrls_lock);
|
||||||
|
list_for_each_entry(ctrl, &mgr->amp_ctrls, list) {
|
||||||
|
if (ctrl->id == id) {
|
||||||
|
amp_ctrl_get(ctrl);
|
||||||
|
mutex_unlock(&mgr->amp_ctrls_lock);
|
||||||
|
return ctrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&mgr->amp_ctrls_lock);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Physical Link interface */
|
||||||
|
static u8 __next_handle(struct amp_mgr *mgr)
|
||||||
|
{
|
||||||
|
if (++mgr->handle == 0)
|
||||||
|
mgr->handle = 1;
|
||||||
|
|
||||||
|
return mgr->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||||
|
u8 remote_id, bool out)
|
||||||
|
{
|
||||||
|
bdaddr_t *dst = mgr->l2cap_conn->dst;
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
|
||||||
|
hcon = hci_conn_add(hdev, AMP_LINK, dst);
|
||||||
|
if (!hcon)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
BT_DBG("hcon %p dst %pMR", hcon, dst);
|
||||||
|
|
||||||
|
hcon->state = BT_CONNECT;
|
||||||
|
hcon->attempt++;
|
||||||
|
hcon->handle = __next_handle(mgr);
|
||||||
|
hcon->remote_id = remote_id;
|
||||||
|
hcon->amp_mgr = amp_mgr_get(mgr);
|
||||||
|
hcon->out = out;
|
||||||
|
|
||||||
|
return hcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AMP crypto key generation interface */
|
||||||
|
static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct crypto_shash *tfm;
|
||||||
|
|
||||||
|
if (!ksize)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
|
||||||
|
if (IS_ERR(tfm)) {
|
||||||
|
BT_DBG("crypto_alloc_ahash failed: err %ld", PTR_ERR(tfm));
|
||||||
|
return PTR_ERR(tfm);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = crypto_shash_setkey(tfm, key, ksize);
|
||||||
|
if (ret) {
|
||||||
|
BT_DBG("crypto_ahash_setkey failed: err %d", ret);
|
||||||
|
} else {
|
||||||
|
struct {
|
||||||
|
struct shash_desc shash;
|
||||||
|
char ctx[crypto_shash_descsize(tfm)];
|
||||||
|
} desc;
|
||||||
|
|
||||||
|
desc.shash.tfm = tfm;
|
||||||
|
desc.shash.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||||
|
|
||||||
|
ret = crypto_shash_digest(&desc.shash, plaintext, psize,
|
||||||
|
output);
|
||||||
|
}
|
||||||
|
|
||||||
|
crypto_free_shash(tfm);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
struct link_key *key;
|
||||||
|
u8 keybuf[HCI_AMP_LINK_KEY_SIZE];
|
||||||
|
u8 gamp_key[HCI_AMP_LINK_KEY_SIZE];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!hci_conn_check_link_mode(conn))
|
||||||
|
return -EACCES;
|
||||||
|
|
||||||
|
BT_DBG("conn %p key_type %d", conn, conn->key_type);
|
||||||
|
|
||||||
|
/* Legacy key */
|
||||||
|
if (conn->key_type < 3) {
|
||||||
|
BT_ERR("Legacy key type %d", conn->key_type);
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
*type = conn->key_type;
|
||||||
|
*len = HCI_AMP_LINK_KEY_SIZE;
|
||||||
|
|
||||||
|
key = hci_find_link_key(hdev, &conn->dst);
|
||||||
|
if (!key) {
|
||||||
|
BT_DBG("No Link key for conn %p dst %pMR", conn, &conn->dst);
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BR/EDR Link Key concatenated together with itself */
|
||||||
|
memcpy(&keybuf[0], key->val, HCI_LINK_KEY_SIZE);
|
||||||
|
memcpy(&keybuf[HCI_LINK_KEY_SIZE], key->val, HCI_LINK_KEY_SIZE);
|
||||||
|
|
||||||
|
/* Derive Generic AMP Link Key (gamp) */
|
||||||
|
err = hmac_sha256(keybuf, HCI_AMP_LINK_KEY_SIZE, "gamp", 4, gamp_key);
|
||||||
|
if (err) {
|
||||||
|
BT_ERR("Could not derive Generic AMP Key: err %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->key_type == HCI_LK_DEBUG_COMBINATION) {
|
||||||
|
BT_DBG("Use Generic AMP Key (gamp)");
|
||||||
|
memcpy(data, gamp_key, HCI_AMP_LINK_KEY_SIZE);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Derive Dedicated AMP Link Key: "802b" is 802.11 PAL keyID */
|
||||||
|
return hmac_sha256(gamp_key, HCI_AMP_LINK_KEY_SIZE, "802b", 4, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle)
|
||||||
|
{
|
||||||
|
struct hci_cp_read_local_amp_assoc cp;
|
||||||
|
struct amp_assoc *loc_assoc = &hdev->loc_assoc;
|
||||||
|
|
||||||
|
BT_DBG("%s handle %d", hdev->name, phy_handle);
|
||||||
|
|
||||||
|
cp.phy_handle = phy_handle;
|
||||||
|
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||||
|
cp.len_so_far = cpu_to_le16(loc_assoc->offset);
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
|
||||||
|
{
|
||||||
|
struct hci_cp_read_local_amp_assoc cp;
|
||||||
|
|
||||||
|
memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc));
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
|
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||||
|
|
||||||
|
mgr->state = READ_LOC_AMP_ASSOC;
|
||||||
|
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||||
|
struct hci_conn *hcon)
|
||||||
|
{
|
||||||
|
struct hci_cp_read_local_amp_assoc cp;
|
||||||
|
struct amp_mgr *mgr = hcon->amp_mgr;
|
||||||
|
|
||||||
|
cp.phy_handle = hcon->handle;
|
||||||
|
cp.len_so_far = cpu_to_le16(0);
|
||||||
|
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||||
|
|
||||||
|
mgr->state = READ_LOC_AMP_ASSOC_FINAL;
|
||||||
|
|
||||||
|
/* Read Local AMP Assoc final link information data */
|
||||||
|
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write AMP Assoc data fragments, returns true with last fragment written*/
|
||||||
|
static bool amp_write_rem_assoc_frag(struct hci_dev *hdev,
|
||||||
|
struct hci_conn *hcon)
|
||||||
|
{
|
||||||
|
struct hci_cp_write_remote_amp_assoc *cp;
|
||||||
|
struct amp_mgr *mgr = hcon->amp_mgr;
|
||||||
|
struct amp_ctrl *ctrl;
|
||||||
|
u16 frag_len, len;
|
||||||
|
|
||||||
|
ctrl = amp_ctrl_lookup(mgr, hcon->remote_id);
|
||||||
|
if (!ctrl)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ctrl->assoc_rem_len) {
|
||||||
|
BT_DBG("all fragments are written");
|
||||||
|
ctrl->assoc_rem_len = ctrl->assoc_len;
|
||||||
|
ctrl->assoc_len_so_far = 0;
|
||||||
|
|
||||||
|
amp_ctrl_put(ctrl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
frag_len = min_t(u16, 248, ctrl->assoc_rem_len);
|
||||||
|
len = frag_len + sizeof(*cp);
|
||||||
|
|
||||||
|
cp = kzalloc(len, GFP_KERNEL);
|
||||||
|
if (!cp) {
|
||||||
|
amp_ctrl_put(ctrl);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_DBG("hcon %p ctrl %p frag_len %u assoc_len %u rem_len %u",
|
||||||
|
hcon, ctrl, frag_len, ctrl->assoc_len, ctrl->assoc_rem_len);
|
||||||
|
|
||||||
|
cp->phy_handle = hcon->handle;
|
||||||
|
cp->len_so_far = cpu_to_le16(ctrl->assoc_len_so_far);
|
||||||
|
cp->rem_len = cpu_to_le16(ctrl->assoc_rem_len);
|
||||||
|
memcpy(cp->frag, ctrl->assoc, frag_len);
|
||||||
|
|
||||||
|
ctrl->assoc_len_so_far += frag_len;
|
||||||
|
ctrl->assoc_rem_len -= frag_len;
|
||||||
|
|
||||||
|
amp_ctrl_put(ctrl);
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_WRITE_REMOTE_AMP_ASSOC, len, cp);
|
||||||
|
|
||||||
|
kfree(cp);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle)
|
||||||
|
{
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
|
||||||
|
BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle);
|
||||||
|
|
||||||
|
hcon = hci_conn_hash_lookup_handle(hdev, handle);
|
||||||
|
if (!hcon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
amp_write_rem_assoc_frag(hdev, hcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle)
|
||||||
|
{
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
|
||||||
|
BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle);
|
||||||
|
|
||||||
|
hcon = hci_conn_hash_lookup_handle(hdev, handle);
|
||||||
|
if (!hcon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BT_DBG("%s phy handle 0x%2.2x hcon %p", hdev->name, handle, hcon);
|
||||||
|
|
||||||
|
amp_write_rem_assoc_frag(hdev, hcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||||
|
struct hci_conn *hcon)
|
||||||
|
{
|
||||||
|
struct hci_cp_create_phy_link cp;
|
||||||
|
|
||||||
|
cp.phy_handle = hcon->handle;
|
||||||
|
|
||||||
|
BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon,
|
||||||
|
hcon->handle);
|
||||||
|
|
||||||
|
if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len,
|
||||||
|
&cp.key_type)) {
|
||||||
|
BT_DBG("Cannot create link key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||||
|
struct hci_conn *hcon)
|
||||||
|
{
|
||||||
|
struct hci_cp_accept_phy_link cp;
|
||||||
|
|
||||||
|
cp.phy_handle = hcon->handle;
|
||||||
|
|
||||||
|
BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon,
|
||||||
|
hcon->handle);
|
||||||
|
|
||||||
|
if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len,
|
||||||
|
&cp.key_type)) {
|
||||||
|
BT_DBG("Cannot create link key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
|
||||||
|
}
|
@ -182,8 +182,7 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
|
|||||||
a2 = data;
|
a2 = data;
|
||||||
data += ETH_ALEN;
|
data += ETH_ALEN;
|
||||||
|
|
||||||
BT_DBG("mc filter %s -> %s",
|
BT_DBG("mc filter %pMR -> %pMR", a1, a2);
|
||||||
batostr((void *) a1), batostr((void *) a2));
|
|
||||||
|
|
||||||
/* Iterate from a1 to a2 */
|
/* Iterate from a1 to a2 */
|
||||||
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
|
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
|
||||||
|
@ -353,7 +353,7 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
|
|||||||
|
|
||||||
BT_DBG("mtu %d", session->mtu);
|
BT_DBG("mtu %d", session->mtu);
|
||||||
|
|
||||||
sprintf(session->name, "%s", batostr(&bt_sk(sock->sk)->dst));
|
sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst);
|
||||||
|
|
||||||
session->sock = sock;
|
session->sock = sock;
|
||||||
session->state = BT_CONFIG;
|
session->state = BT_CONFIG;
|
||||||
|
@ -130,6 +130,20 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
|
|||||||
hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
|
hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_amp_disconn(struct hci_conn *conn, __u8 reason)
|
||||||
|
{
|
||||||
|
struct hci_cp_disconn_phy_link cp;
|
||||||
|
|
||||||
|
BT_DBG("hcon %p", conn);
|
||||||
|
|
||||||
|
conn->state = BT_DISCONN;
|
||||||
|
|
||||||
|
cp.phy_handle = HCI_PHY_HANDLE(conn->handle);
|
||||||
|
cp.reason = reason;
|
||||||
|
hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK,
|
||||||
|
sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_add_sco(struct hci_conn *conn, __u16 handle)
|
static void hci_add_sco(struct hci_conn *conn, __u16 handle)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = conn->hdev;
|
struct hci_dev *hdev = conn->hdev;
|
||||||
@ -230,11 +244,24 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_conn_disconnect(struct hci_conn *conn)
|
||||||
|
{
|
||||||
|
__u8 reason = hci_proto_disconn_ind(conn);
|
||||||
|
|
||||||
|
switch (conn->type) {
|
||||||
|
case ACL_LINK:
|
||||||
|
hci_acl_disconn(conn, reason);
|
||||||
|
break;
|
||||||
|
case AMP_LINK:
|
||||||
|
hci_amp_disconn(conn, reason);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_conn_timeout(struct work_struct *work)
|
static void hci_conn_timeout(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct hci_conn *conn = container_of(work, struct hci_conn,
|
struct hci_conn *conn = container_of(work, struct hci_conn,
|
||||||
disc_work.work);
|
disc_work.work);
|
||||||
__u8 reason;
|
|
||||||
|
|
||||||
BT_DBG("hcon %p state %s", conn, state_to_string(conn->state));
|
BT_DBG("hcon %p state %s", conn, state_to_string(conn->state));
|
||||||
|
|
||||||
@ -253,8 +280,7 @@ static void hci_conn_timeout(struct work_struct *work)
|
|||||||
break;
|
break;
|
||||||
case BT_CONFIG:
|
case BT_CONFIG:
|
||||||
case BT_CONNECTED:
|
case BT_CONNECTED:
|
||||||
reason = hci_proto_disconn_ind(conn);
|
hci_conn_disconnect(conn);
|
||||||
hci_acl_disconn(conn, reason);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
conn->state = BT_CLOSED;
|
conn->state = BT_CLOSED;
|
||||||
@ -320,7 +346,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
|||||||
{
|
{
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
|
|
||||||
BT_DBG("%s dst %s", hdev->name, batostr(dst));
|
BT_DBG("%s dst %pMR", hdev->name, dst);
|
||||||
|
|
||||||
conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL);
|
conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL);
|
||||||
if (!conn)
|
if (!conn)
|
||||||
@ -437,7 +463,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
|
|||||||
int use_src = bacmp(src, BDADDR_ANY);
|
int use_src = bacmp(src, BDADDR_ANY);
|
||||||
struct hci_dev *hdev = NULL, *d;
|
struct hci_dev *hdev = NULL, *d;
|
||||||
|
|
||||||
BT_DBG("%s -> %s", batostr(src), batostr(dst));
|
BT_DBG("%pMR -> %pMR", src, dst);
|
||||||
|
|
||||||
read_lock(&hci_dev_list_lock);
|
read_lock(&hci_dev_list_lock);
|
||||||
|
|
||||||
@ -567,7 +593,7 @@ static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
|
|||||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||||
__u8 dst_type, __u8 sec_level, __u8 auth_type)
|
__u8 dst_type, __u8 sec_level, __u8 auth_type)
|
||||||
{
|
{
|
||||||
BT_DBG("%s dst %s type 0x%x", hdev->name, batostr(dst), type);
|
BT_DBG("%s dst %pMR type 0x%x", hdev->name, dst, type);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case LE_LINK:
|
case LE_LINK:
|
||||||
@ -963,3 +989,35 @@ void hci_chan_list_flush(struct hci_conn *conn)
|
|||||||
list_for_each_entry_safe(chan, n, &conn->chan_list, list)
|
list_for_each_entry_safe(chan, n, &conn->chan_list, list)
|
||||||
hci_chan_del(chan);
|
hci_chan_del(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct hci_chan *__hci_chan_lookup_handle(struct hci_conn *hcon,
|
||||||
|
__u16 handle)
|
||||||
|
{
|
||||||
|
struct hci_chan *hchan;
|
||||||
|
|
||||||
|
list_for_each_entry(hchan, &hcon->chan_list, list) {
|
||||||
|
if (hchan->handle == handle)
|
||||||
|
return hchan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle)
|
||||||
|
{
|
||||||
|
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
struct hci_chan *hchan = NULL;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
list_for_each_entry_rcu(hcon, &h->list, list) {
|
||||||
|
hchan = __hci_chan_lookup_handle(hcon, handle);
|
||||||
|
if (hchan)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return hchan;
|
||||||
|
}
|
||||||
|
@ -405,7 +405,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev,
|
|||||||
struct discovery_state *cache = &hdev->discovery;
|
struct discovery_state *cache = &hdev->discovery;
|
||||||
struct inquiry_entry *e;
|
struct inquiry_entry *e;
|
||||||
|
|
||||||
BT_DBG("cache %p, %s", cache, batostr(bdaddr));
|
BT_DBG("cache %p, %pMR", cache, bdaddr);
|
||||||
|
|
||||||
list_for_each_entry(e, &cache->all, all) {
|
list_for_each_entry(e, &cache->all, all) {
|
||||||
if (!bacmp(&e->data.bdaddr, bdaddr))
|
if (!bacmp(&e->data.bdaddr, bdaddr))
|
||||||
@ -421,7 +421,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev,
|
|||||||
struct discovery_state *cache = &hdev->discovery;
|
struct discovery_state *cache = &hdev->discovery;
|
||||||
struct inquiry_entry *e;
|
struct inquiry_entry *e;
|
||||||
|
|
||||||
BT_DBG("cache %p, %s", cache, batostr(bdaddr));
|
BT_DBG("cache %p, %pMR", cache, bdaddr);
|
||||||
|
|
||||||
list_for_each_entry(e, &cache->unknown, list) {
|
list_for_each_entry(e, &cache->unknown, list) {
|
||||||
if (!bacmp(&e->data.bdaddr, bdaddr))
|
if (!bacmp(&e->data.bdaddr, bdaddr))
|
||||||
@ -438,7 +438,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev,
|
|||||||
struct discovery_state *cache = &hdev->discovery;
|
struct discovery_state *cache = &hdev->discovery;
|
||||||
struct inquiry_entry *e;
|
struct inquiry_entry *e;
|
||||||
|
|
||||||
BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state);
|
BT_DBG("cache %p bdaddr %pMR state %d", cache, bdaddr, state);
|
||||||
|
|
||||||
list_for_each_entry(e, &cache->resolve, list) {
|
list_for_each_entry(e, &cache->resolve, list) {
|
||||||
if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state)
|
if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state)
|
||||||
@ -475,7 +475,7 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
|
|||||||
struct discovery_state *cache = &hdev->discovery;
|
struct discovery_state *cache = &hdev->discovery;
|
||||||
struct inquiry_entry *ie;
|
struct inquiry_entry *ie;
|
||||||
|
|
||||||
BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr));
|
BT_DBG("cache %p, %pMR", cache, &data->bdaddr);
|
||||||
|
|
||||||
if (ssp)
|
if (ssp)
|
||||||
*ssp = data->ssp_mode;
|
*ssp = data->ssp_mode;
|
||||||
@ -1259,7 +1259,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
|
|||||||
list_add(&key->list, &hdev->link_keys);
|
list_add(&key->list, &hdev->link_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type);
|
BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
|
||||||
|
|
||||||
/* Some buggy controller combinations generate a changed
|
/* Some buggy controller combinations generate a changed
|
||||||
* combination key for legacy pairing even when there's no
|
* combination key for legacy pairing even when there's no
|
||||||
@ -1338,7 +1338,7 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||||||
if (!key)
|
if (!key)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
|
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
|
||||||
|
|
||||||
list_del(&key->list);
|
list_del(&key->list);
|
||||||
kfree(key);
|
kfree(key);
|
||||||
@ -1354,7 +1354,7 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||||||
if (bacmp(bdaddr, &k->bdaddr))
|
if (bacmp(bdaddr, &k->bdaddr))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
|
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
|
||||||
|
|
||||||
list_del(&k->list);
|
list_del(&k->list);
|
||||||
kfree(k);
|
kfree(k);
|
||||||
@ -1401,7 +1401,7 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||||||
if (!data)
|
if (!data)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
|
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
|
||||||
|
|
||||||
list_del(&data->list);
|
list_del(&data->list);
|
||||||
kfree(data);
|
kfree(data);
|
||||||
@ -1440,7 +1440,7 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
|
|||||||
memcpy(data->hash, hash, sizeof(data->hash));
|
memcpy(data->hash, hash, sizeof(data->hash));
|
||||||
memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
|
memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
|
||||||
|
|
||||||
BT_DBG("%s for %s", hdev->name, batostr(bdaddr));
|
BT_DBG("%s for %pMR", hdev->name, bdaddr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2153,9 +2153,10 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
|
|||||||
hdr->dlen = cpu_to_le16(len);
|
hdr->dlen = cpu_to_le16(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
|
static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
|
||||||
struct sk_buff *skb, __u16 flags)
|
struct sk_buff *skb, __u16 flags)
|
||||||
{
|
{
|
||||||
|
struct hci_conn *conn = chan->conn;
|
||||||
struct hci_dev *hdev = conn->hdev;
|
struct hci_dev *hdev = conn->hdev;
|
||||||
struct sk_buff *list;
|
struct sk_buff *list;
|
||||||
|
|
||||||
@ -2163,7 +2164,18 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
|
|||||||
skb->data_len = 0;
|
skb->data_len = 0;
|
||||||
|
|
||||||
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
|
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
|
||||||
hci_add_acl_hdr(skb, conn->handle, flags);
|
|
||||||
|
switch (hdev->dev_type) {
|
||||||
|
case HCI_BREDR:
|
||||||
|
hci_add_acl_hdr(skb, conn->handle, flags);
|
||||||
|
break;
|
||||||
|
case HCI_AMP:
|
||||||
|
hci_add_acl_hdr(skb, chan->handle, flags);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
list = skb_shinfo(skb)->frag_list;
|
list = skb_shinfo(skb)->frag_list;
|
||||||
if (!list) {
|
if (!list) {
|
||||||
@ -2202,14 +2214,13 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
|
|||||||
|
|
||||||
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
|
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
|
||||||
{
|
{
|
||||||
struct hci_conn *conn = chan->conn;
|
struct hci_dev *hdev = chan->conn->hdev;
|
||||||
struct hci_dev *hdev = conn->hdev;
|
|
||||||
|
|
||||||
BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
|
BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
|
||||||
|
|
||||||
skb->dev = (void *) hdev;
|
skb->dev = (void *) hdev;
|
||||||
|
|
||||||
hci_queue_acl(conn, &chan->data_q, skb, flags);
|
hci_queue_acl(chan, &chan->data_q, skb, flags);
|
||||||
|
|
||||||
queue_work(hdev->workqueue, &hdev->tx_work);
|
queue_work(hdev->workqueue, &hdev->tx_work);
|
||||||
}
|
}
|
||||||
@ -2311,8 +2322,8 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
|
|||||||
/* Kill stalled connections */
|
/* Kill stalled connections */
|
||||||
list_for_each_entry_rcu(c, &h->list, list) {
|
list_for_each_entry_rcu(c, &h->list, list) {
|
||||||
if (c->type == type && c->sent) {
|
if (c->type == type && c->sent) {
|
||||||
BT_ERR("%s killing stalled connection %s",
|
BT_ERR("%s killing stalled connection %pMR",
|
||||||
hdev->name, batostr(&c->dst));
|
hdev->name, &c->dst);
|
||||||
hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM);
|
hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2381,6 +2392,9 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
|
|||||||
case ACL_LINK:
|
case ACL_LINK:
|
||||||
cnt = hdev->acl_cnt;
|
cnt = hdev->acl_cnt;
|
||||||
break;
|
break;
|
||||||
|
case AMP_LINK:
|
||||||
|
cnt = hdev->block_cnt;
|
||||||
|
break;
|
||||||
case SCO_LINK:
|
case SCO_LINK:
|
||||||
case ESCO_LINK:
|
case ESCO_LINK:
|
||||||
cnt = hdev->sco_cnt;
|
cnt = hdev->sco_cnt;
|
||||||
@ -2510,11 +2524,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
|
|||||||
struct hci_chan *chan;
|
struct hci_chan *chan;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int quote;
|
int quote;
|
||||||
|
u8 type;
|
||||||
|
|
||||||
__check_timeout(hdev, cnt);
|
__check_timeout(hdev, cnt);
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
if (hdev->dev_type == HCI_AMP)
|
||||||
|
type = AMP_LINK;
|
||||||
|
else
|
||||||
|
type = ACL_LINK;
|
||||||
|
|
||||||
while (hdev->block_cnt > 0 &&
|
while (hdev->block_cnt > 0 &&
|
||||||
(chan = hci_chan_sent(hdev, ACL_LINK, "e))) {
|
(chan = hci_chan_sent(hdev, type, "e))) {
|
||||||
u32 priority = (skb_peek(&chan->data_q))->priority;
|
u32 priority = (skb_peek(&chan->data_q))->priority;
|
||||||
while (quote > 0 && (skb = skb_peek(&chan->data_q))) {
|
while (quote > 0 && (skb = skb_peek(&chan->data_q))) {
|
||||||
int blocks;
|
int blocks;
|
||||||
@ -2547,14 +2569,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cnt != hdev->block_cnt)
|
if (cnt != hdev->block_cnt)
|
||||||
hci_prio_recalculate(hdev, ACL_LINK);
|
hci_prio_recalculate(hdev, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_sched_acl(struct hci_dev *hdev)
|
static void hci_sched_acl(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!hci_conn_num(hdev, ACL_LINK))
|
/* No ACL link over BR/EDR controller */
|
||||||
|
if (!hci_conn_num(hdev, ACL_LINK) && hdev->dev_type == HCI_BREDR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* No AMP link over AMP controller */
|
||||||
|
if (!hci_conn_num(hdev, AMP_LINK) && hdev->dev_type == HCI_AMP)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (hdev->flow_ctl_mode) {
|
switch (hdev->flow_ctl_mode) {
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
#include <net/bluetooth/mgmt.h>
|
#include <net/bluetooth/mgmt.h>
|
||||||
|
#include <net/bluetooth/a2mp.h>
|
||||||
|
#include <net/bluetooth/amp.h>
|
||||||
|
|
||||||
/* Handle HCI Event packets */
|
/* Handle HCI Event packets */
|
||||||
|
|
||||||
@ -846,7 +848,7 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
|
|||||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||||
|
|
||||||
if (rp->status)
|
if (rp->status)
|
||||||
return;
|
goto a2mp_rsp;
|
||||||
|
|
||||||
hdev->amp_status = rp->amp_status;
|
hdev->amp_status = rp->amp_status;
|
||||||
hdev->amp_total_bw = __le32_to_cpu(rp->total_bw);
|
hdev->amp_total_bw = __le32_to_cpu(rp->total_bw);
|
||||||
@ -860,6 +862,46 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
|
|||||||
hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to);
|
hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to);
|
||||||
|
|
||||||
hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status);
|
hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status);
|
||||||
|
|
||||||
|
a2mp_rsp:
|
||||||
|
a2mp_send_getinfo_rsp(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_rp_read_local_amp_assoc *rp = (void *) skb->data;
|
||||||
|
struct amp_assoc *assoc = &hdev->loc_assoc;
|
||||||
|
size_t rem_len, frag_len;
|
||||||
|
|
||||||
|
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||||
|
|
||||||
|
if (rp->status)
|
||||||
|
goto a2mp_rsp;
|
||||||
|
|
||||||
|
frag_len = skb->len - sizeof(*rp);
|
||||||
|
rem_len = __le16_to_cpu(rp->rem_len);
|
||||||
|
|
||||||
|
if (rem_len > frag_len) {
|
||||||
|
BT_DBG("frag_len %zu rem_len %zu", frag_len, rem_len);
|
||||||
|
|
||||||
|
memcpy(assoc->data + assoc->offset, rp->frag, frag_len);
|
||||||
|
assoc->offset += frag_len;
|
||||||
|
|
||||||
|
/* Read other fragments */
|
||||||
|
amp_read_loc_assoc_frag(hdev, rp->phy_handle);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(assoc->data + assoc->offset, rp->frag, rem_len);
|
||||||
|
assoc->len = assoc->offset + rem_len;
|
||||||
|
assoc->offset = 0;
|
||||||
|
|
||||||
|
a2mp_rsp:
|
||||||
|
/* Send A2MP Rsp when all fragments are received */
|
||||||
|
a2mp_send_getampassoc_rsp(hdev, rp->status);
|
||||||
|
a2mp_send_create_phy_link_req(hdev, rp->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
|
static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
|
||||||
@ -1174,6 +1216,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
|
|||||||
hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status);
|
hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_rp_write_remote_amp_assoc *rp = (void *) skb->data;
|
||||||
|
|
||||||
|
BT_DBG("%s status 0x%2.2x phy_handle 0x%2.2x",
|
||||||
|
hdev->name, rp->status, rp->phy_handle);
|
||||||
|
|
||||||
|
if (rp->status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
amp_write_rem_assoc_continue(hdev, rp->phy_handle);
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||||
{
|
{
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||||
@ -1210,7 +1266,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
|
|||||||
|
|
||||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
||||||
|
|
||||||
BT_DBG("%s bdaddr %s hcon %p", hdev->name, batostr(&cp->bdaddr), conn);
|
BT_DBG("%s bdaddr %pMR hcon %p", hdev->name, &cp->bdaddr, conn);
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
if (conn && conn->state == BT_CONNECT) {
|
if (conn && conn->state == BT_CONNECT) {
|
||||||
@ -1639,8 +1695,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&conn->dst),
|
BT_DBG("%s bdaddr %pMR conn %p", hdev->name, &conn->dst, conn);
|
||||||
conn);
|
|
||||||
|
|
||||||
conn->state = BT_CLOSED;
|
conn->state = BT_CLOSED;
|
||||||
mgmt_connect_failed(hdev, &conn->dst, conn->type,
|
mgmt_connect_failed(hdev, &conn->dst, conn->type,
|
||||||
@ -1657,6 +1712,38 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
|
|||||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
struct hci_cp_create_phy_link *cp;
|
||||||
|
|
||||||
|
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK);
|
||||||
|
if (!cp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
amp_write_remote_assoc(hdev, cp->phy_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
struct hci_cp_accept_phy_link *cp;
|
||||||
|
|
||||||
|
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHY_LINK);
|
||||||
|
if (!cp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
amp_write_remote_assoc(hdev, cp->phy_handle);
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
__u8 status = *((__u8 *) skb->data);
|
__u8 status = *((__u8 *) skb->data);
|
||||||
@ -1822,7 +1909,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
struct hci_ev_conn_request *ev = (void *) skb->data;
|
struct hci_ev_conn_request *ev = (void *) skb->data;
|
||||||
int mask = hdev->link_mode;
|
int mask = hdev->link_mode;
|
||||||
|
|
||||||
BT_DBG("%s bdaddr %s type 0x%x", hdev->name, batostr(&ev->bdaddr),
|
BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
|
||||||
ev->link_type);
|
ev->link_type);
|
||||||
|
|
||||||
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
|
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
|
||||||
@ -2314,6 +2401,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_cc_read_local_amp_info(hdev, skb);
|
hci_cc_read_local_amp_info(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_OP_READ_LOCAL_AMP_ASSOC:
|
||||||
|
hci_cc_read_local_amp_assoc(hdev, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
case HCI_OP_DELETE_STORED_LINK_KEY:
|
case HCI_OP_DELETE_STORED_LINK_KEY:
|
||||||
hci_cc_delete_stored_link_key(hdev, skb);
|
hci_cc_delete_stored_link_key(hdev, skb);
|
||||||
break;
|
break;
|
||||||
@ -2386,6 +2477,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_cc_write_le_host_supported(hdev, skb);
|
hci_cc_write_le_host_supported(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_OP_WRITE_REMOTE_AMP_ASSOC:
|
||||||
|
hci_cc_write_remote_amp_assoc(hdev, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
|
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
|
||||||
break;
|
break;
|
||||||
@ -2467,6 +2562,14 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_cs_le_start_enc(hdev, ev->status);
|
hci_cs_le_start_enc(hdev, ev->status);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_OP_CREATE_PHY_LINK:
|
||||||
|
hci_cs_create_phylink(hdev, ev->status);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HCI_OP_ACCEPT_PHY_LINK:
|
||||||
|
hci_cs_accept_phylink(hdev, ev->status);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
|
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
|
||||||
break;
|
break;
|
||||||
@ -2574,6 +2677,27 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
queue_work(hdev->workqueue, &hdev->tx_work);
|
queue_work(hdev->workqueue, &hdev->tx_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct hci_conn *__hci_conn_lookup_handle(struct hci_dev *hdev,
|
||||||
|
__u16 handle)
|
||||||
|
{
|
||||||
|
struct hci_chan *chan;
|
||||||
|
|
||||||
|
switch (hdev->dev_type) {
|
||||||
|
case HCI_BREDR:
|
||||||
|
return hci_conn_hash_lookup_handle(hdev, handle);
|
||||||
|
case HCI_AMP:
|
||||||
|
chan = hci_chan_lookup_handle(hdev, handle);
|
||||||
|
if (chan)
|
||||||
|
return chan->conn;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_ev_num_comp_blocks *ev = (void *) skb->data;
|
struct hci_ev_num_comp_blocks *ev = (void *) skb->data;
|
||||||
@ -2595,13 +2719,13 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
for (i = 0; i < ev->num_hndl; i++) {
|
for (i = 0; i < ev->num_hndl; i++) {
|
||||||
struct hci_comp_blocks_info *info = &ev->handles[i];
|
struct hci_comp_blocks_info *info = &ev->handles[i];
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn = NULL;
|
||||||
__u16 handle, block_count;
|
__u16 handle, block_count;
|
||||||
|
|
||||||
handle = __le16_to_cpu(info->handle);
|
handle = __le16_to_cpu(info->handle);
|
||||||
block_count = __le16_to_cpu(info->blocks);
|
block_count = __le16_to_cpu(info->blocks);
|
||||||
|
|
||||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
conn = __hci_conn_lookup_handle(hdev, handle);
|
||||||
if (!conn)
|
if (!conn)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -2609,6 +2733,7 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
switch (conn->type) {
|
switch (conn->type) {
|
||||||
case ACL_LINK:
|
case ACL_LINK:
|
||||||
|
case AMP_LINK:
|
||||||
hdev->block_cnt += block_count;
|
hdev->block_cnt += block_count;
|
||||||
if (hdev->block_cnt > hdev->num_blocks)
|
if (hdev->block_cnt > hdev->num_blocks)
|
||||||
hdev->block_cnt = hdev->num_blocks;
|
hdev->block_cnt = hdev->num_blocks;
|
||||||
@ -2705,13 +2830,13 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
key = hci_find_link_key(hdev, &ev->bdaddr);
|
key = hci_find_link_key(hdev, &ev->bdaddr);
|
||||||
if (!key) {
|
if (!key) {
|
||||||
BT_DBG("%s link key not found for %s", hdev->name,
|
BT_DBG("%s link key not found for %pMR", hdev->name,
|
||||||
batostr(&ev->bdaddr));
|
&ev->bdaddr);
|
||||||
goto not_found;
|
goto not_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("%s found key type %u for %s", hdev->name, key->type,
|
BT_DBG("%s found key type %u for %pMR", hdev->name, key->type,
|
||||||
batostr(&ev->bdaddr));
|
&ev->bdaddr);
|
||||||
|
|
||||||
if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) &&
|
if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) &&
|
||||||
key->type == HCI_LK_DEBUG_COMBINATION) {
|
key->type == HCI_LK_DEBUG_COMBINATION) {
|
||||||
@ -3558,6 +3683,22 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_ev_channel_selected *ev = (void *) skb->data;
|
||||||
|
struct hci_conn *hcon;
|
||||||
|
|
||||||
|
BT_DBG("%s handle 0x%2.2x", hdev->name, ev->phy_handle);
|
||||||
|
|
||||||
|
skb_pull(skb, sizeof(*ev));
|
||||||
|
|
||||||
|
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
||||||
|
if (!hcon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
amp_read_loc_assoc_final_data(hdev, hcon);
|
||||||
|
}
|
||||||
|
|
||||||
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_event_hdr *hdr = (void *) skb->data;
|
struct hci_event_hdr *hdr = (void *) skb->data;
|
||||||
@ -3722,6 +3863,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_le_meta_evt(hdev, skb);
|
hci_le_meta_evt(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_EV_CHANNEL_SELECTED:
|
||||||
|
hci_chan_selected_evt(hdev, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
case HCI_EV_REMOTE_OOB_DATA_REQUEST:
|
case HCI_EV_REMOTE_OOB_DATA_REQUEST:
|
||||||
hci_remote_oob_data_request_evt(hdev, skb);
|
hci_remote_oob_data_request_evt(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
@ -38,7 +38,7 @@ static ssize_t show_link_address(struct device *dev,
|
|||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct hci_conn *conn = to_hci_conn(dev);
|
struct hci_conn *conn = to_hci_conn(dev);
|
||||||
return sprintf(buf, "%s\n", batostr(&conn->dst));
|
return sprintf(buf, "%pMR\n", &conn->dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t show_link_features(struct device *dev,
|
static ssize_t show_link_features(struct device *dev,
|
||||||
@ -224,7 +224,7 @@ static ssize_t show_address(struct device *dev,
|
|||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = to_hci_dev(dev);
|
struct hci_dev *hdev = to_hci_dev(dev);
|
||||||
return sprintf(buf, "%s\n", batostr(&hdev->bdaddr));
|
return sprintf(buf, "%pMR\n", &hdev->bdaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t show_features(struct device *dev,
|
static ssize_t show_features(struct device *dev,
|
||||||
@ -406,8 +406,8 @@ static int inquiry_cache_show(struct seq_file *f, void *p)
|
|||||||
|
|
||||||
list_for_each_entry(e, &cache->all, all) {
|
list_for_each_entry(e, &cache->all, all) {
|
||||||
struct inquiry_data *data = &e->data;
|
struct inquiry_data *data = &e->data;
|
||||||
seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
|
seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
|
||||||
batostr(&data->bdaddr),
|
&data->bdaddr,
|
||||||
data->pscan_rep_mode, data->pscan_period_mode,
|
data->pscan_rep_mode, data->pscan_period_mode,
|
||||||
data->pscan_mode, data->dev_class[2],
|
data->pscan_mode, data->dev_class[2],
|
||||||
data->dev_class[1], data->dev_class[0],
|
data->dev_class[1], data->dev_class[0],
|
||||||
@ -440,7 +440,7 @@ static int blacklist_show(struct seq_file *f, void *p)
|
|||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
list_for_each_entry(b, &hdev->blacklist, list)
|
list_for_each_entry(b, &hdev->blacklist, list)
|
||||||
seq_printf(f, "%s\n", batostr(&b->bdaddr));
|
seq_printf(f, "%pMR\n", &b->bdaddr);
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
|
@ -932,8 +932,12 @@ static int hidp_setup_hid(struct hidp_session *session,
|
|||||||
hid->country = req->country;
|
hid->country = req->country;
|
||||||
|
|
||||||
strncpy(hid->name, req->name, 128);
|
strncpy(hid->name, req->name, 128);
|
||||||
strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
|
|
||||||
strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
|
snprintf(hid->phys, sizeof(hid->phys), "%pMR",
|
||||||
|
&bt_sk(session->ctrl_sock->sk)->src);
|
||||||
|
|
||||||
|
snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
|
||||||
|
&bt_sk(session->ctrl_sock->sk)->dst);
|
||||||
|
|
||||||
hid->dev.parent = &session->conn->dev;
|
hid->dev.parent = &session->conn->dev;
|
||||||
hid->ll_driver = &hidp_hid_driver;
|
hid->ll_driver = &hidp_hid_driver;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -40,7 +40,8 @@ static struct bt_sock_list l2cap_sk_list = {
|
|||||||
|
|
||||||
static const struct proto_ops l2cap_sock_ops;
|
static const struct proto_ops l2cap_sock_ops;
|
||||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio);
|
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||||
|
int proto, gfp_t prio);
|
||||||
|
|
||||||
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||||
{
|
{
|
||||||
@ -106,7 +107,8 @@ done:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
|
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
|
||||||
|
int alen, int flags)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||||
@ -134,7 +136,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
|
|||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
err = bt_sock_wait_state(sk, BT_CONNECTED,
|
err = bt_sock_wait_state(sk, BT_CONNECTED,
|
||||||
sock_sndtimeo(sk, flags & O_NONBLOCK));
|
sock_sndtimeo(sk, flags & O_NONBLOCK));
|
||||||
|
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
|
|
||||||
@ -185,7 +187,8 @@ done:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
|
static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
|
||||||
|
int flags)
|
||||||
{
|
{
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
struct sock *sk = sock->sk, *nsk;
|
struct sock *sk = sock->sk, *nsk;
|
||||||
@ -241,7 +244,8 @@ done:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
|
static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
|
||||||
|
int *len, int peer)
|
||||||
{
|
{
|
||||||
struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
|
struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
@ -266,7 +270,8 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
||||||
|
char __user *optval, int __user *optlen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||||
@ -309,7 +314,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
|
|||||||
break;
|
break;
|
||||||
case BT_SECURITY_HIGH:
|
case BT_SECURITY_HIGH:
|
||||||
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
|
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
|
||||||
L2CAP_LM_SECURE;
|
L2CAP_LM_SECURE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
opt = 0;
|
opt = 0;
|
||||||
@ -353,7 +358,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||||
|
char __user *optval, int __user *optlen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||||
@ -377,19 +383,20 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
switch (optname) {
|
switch (optname) {
|
||||||
case BT_SECURITY:
|
case BT_SECURITY:
|
||||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
||||||
chan->chan_type != L2CAP_CHAN_RAW) {
|
chan->chan_type != L2CAP_CHAN_RAW) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&sec, 0, sizeof(sec));
|
memset(&sec, 0, sizeof(sec));
|
||||||
if (chan->conn)
|
if (chan->conn) {
|
||||||
sec.level = chan->conn->hcon->sec_level;
|
sec.level = chan->conn->hcon->sec_level;
|
||||||
else
|
|
||||||
sec.level = chan->sec_level;
|
|
||||||
|
|
||||||
if (sk->sk_state == BT_CONNECTED)
|
if (sk->sk_state == BT_CONNECTED)
|
||||||
sec.key_size = chan->conn->hcon->enc_key_size;
|
sec.key_size = chan->conn->hcon->enc_key_size;
|
||||||
|
} else {
|
||||||
|
sec.level = chan->sec_level;
|
||||||
|
}
|
||||||
|
|
||||||
len = min_t(unsigned int, len, sizeof(sec));
|
len = min_t(unsigned int, len, sizeof(sec));
|
||||||
if (copy_to_user(optval, (char *) &sec, len))
|
if (copy_to_user(optval, (char *) &sec, len))
|
||||||
@ -411,14 +418,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
|
|
||||||
case BT_FLUSHABLE:
|
case BT_FLUSHABLE:
|
||||||
if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
|
if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
|
||||||
(u32 __user *) optval))
|
(u32 __user *) optval))
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_POWER:
|
case BT_POWER:
|
||||||
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
|
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
|
||||||
&& sk->sk_type != SOCK_RAW) {
|
&& sk->sk_type != SOCK_RAW) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -466,7 +473,8 @@ static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen)
|
static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
||||||
|
char __user *optval, unsigned int optlen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||||
@ -529,6 +537,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
|||||||
chan->fcs = opts.fcs;
|
chan->fcs = opts.fcs;
|
||||||
chan->max_tx = opts.max_tx;
|
chan->max_tx = opts.max_tx;
|
||||||
chan->tx_win = opts.txwin_size;
|
chan->tx_win = opts.txwin_size;
|
||||||
|
chan->flush_to = opts.flush_to;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_LM:
|
case L2CAP_LM:
|
||||||
@ -564,7 +573,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
|
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||||
|
char __user *optval, unsigned int optlen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||||
@ -587,7 +597,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
switch (optname) {
|
switch (optname) {
|
||||||
case BT_SECURITY:
|
case BT_SECURITY:
|
||||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
||||||
chan->chan_type != L2CAP_CHAN_RAW) {
|
chan->chan_type != L2CAP_CHAN_RAW) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -601,7 +611,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sec.level < BT_SECURITY_LOW ||
|
if (sec.level < BT_SECURITY_LOW ||
|
||||||
sec.level > BT_SECURITY_HIGH) {
|
sec.level > BT_SECURITY_HIGH) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -627,7 +637,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
|
|
||||||
/* or for ACL link */
|
/* or for ACL link */
|
||||||
} else if ((sk->sk_state == BT_CONNECT2 &&
|
} else if ((sk->sk_state == BT_CONNECT2 &&
|
||||||
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) ||
|
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) ||
|
||||||
sk->sk_state == BT_CONNECTED) {
|
sk->sk_state == BT_CONNECTED) {
|
||||||
if (!l2cap_chan_check_security(chan))
|
if (!l2cap_chan_check_security(chan))
|
||||||
set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
|
set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
|
||||||
@ -684,7 +694,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
|
|
||||||
case BT_POWER:
|
case BT_POWER:
|
||||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
|
||||||
chan->chan_type != L2CAP_CHAN_RAW) {
|
chan->chan_type != L2CAP_CHAN_RAW) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -720,7 +730,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (chan->mode != L2CAP_MODE_ERTM &&
|
if (chan->mode != L2CAP_MODE_ERTM &&
|
||||||
chan->mode != L2CAP_MODE_STREAMING) {
|
chan->mode != L2CAP_MODE_STREAMING) {
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -737,7 +747,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
|
static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
|
struct msghdr *msg, size_t len)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||||
@ -762,7 +773,8 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags)
|
static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
|
struct msghdr *msg, size_t len, int flags)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||||
@ -866,7 +878,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
|||||||
|
|
||||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
|
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
|
||||||
err = bt_sock_wait_state(sk, BT_CLOSED,
|
err = bt_sock_wait_state(sk, BT_CLOSED,
|
||||||
sk->sk_lingertime);
|
sk->sk_lingertime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!err && sk->sk_err)
|
if (!err && sk->sk_err)
|
||||||
@ -930,7 +942,7 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
if (!sk)
|
if (!sk)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -938,6 +950,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
|||||||
|
|
||||||
l2cap_sock_init(sk, parent);
|
l2cap_sock_init(sk, parent);
|
||||||
|
|
||||||
|
bt_accept_enqueue(parent, sk);
|
||||||
|
|
||||||
return l2cap_pi(sk)->chan;
|
return l2cap_pi(sk)->chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1068,6 +1082,15 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
|
|||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void l2cap_sock_defer_cb(struct l2cap_chan *chan)
|
||||||
|
{
|
||||||
|
struct sock *sk = chan->data;
|
||||||
|
struct sock *parent = bt_sk(sk)->parent;
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
parent->sk_data_ready(parent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static struct l2cap_ops l2cap_chan_ops = {
|
static struct l2cap_ops l2cap_chan_ops = {
|
||||||
.name = "L2CAP Socket Interface",
|
.name = "L2CAP Socket Interface",
|
||||||
.new_connection = l2cap_sock_new_connection_cb,
|
.new_connection = l2cap_sock_new_connection_cb,
|
||||||
@ -1076,6 +1099,7 @@ static struct l2cap_ops l2cap_chan_ops = {
|
|||||||
.teardown = l2cap_sock_teardown_cb,
|
.teardown = l2cap_sock_teardown_cb,
|
||||||
.state_change = l2cap_sock_state_change_cb,
|
.state_change = l2cap_sock_state_change_cb,
|
||||||
.ready = l2cap_sock_ready_cb,
|
.ready = l2cap_sock_ready_cb,
|
||||||
|
.defer = l2cap_sock_defer_cb,
|
||||||
.alloc_skb = l2cap_sock_alloc_skb_cb,
|
.alloc_skb = l2cap_sock_alloc_skb_cb,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1083,7 +1107,8 @@ static void l2cap_sock_destruct(struct sock *sk)
|
|||||||
{
|
{
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
|
|
||||||
l2cap_chan_put(l2cap_pi(sk)->chan);
|
if (l2cap_pi(sk)->chan)
|
||||||
|
l2cap_chan_put(l2cap_pi(sk)->chan);
|
||||||
if (l2cap_pi(sk)->rx_busy_skb) {
|
if (l2cap_pi(sk)->rx_busy_skb) {
|
||||||
kfree_skb(l2cap_pi(sk)->rx_busy_skb);
|
kfree_skb(l2cap_pi(sk)->rx_busy_skb);
|
||||||
l2cap_pi(sk)->rx_busy_skb = NULL;
|
l2cap_pi(sk)->rx_busy_skb = NULL;
|
||||||
@ -1159,7 +1184,8 @@ static struct proto l2cap_proto = {
|
|||||||
.obj_size = sizeof(struct l2cap_pinfo)
|
.obj_size = sizeof(struct l2cap_pinfo)
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||||
|
int proto, gfp_t prio)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
@ -1204,7 +1230,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||||||
sock->state = SS_UNCONNECTED;
|
sock->state = SS_UNCONNECTED;
|
||||||
|
|
||||||
if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM &&
|
if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM &&
|
||||||
sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
|
sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
|
||||||
return -ESOCKTNOSUPPORT;
|
return -ESOCKTNOSUPPORT;
|
||||||
|
|
||||||
if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
|
if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
|
||||||
@ -1261,7 +1287,8 @@ int __init l2cap_init_sockets(void)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list, NULL);
|
err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list,
|
||||||
|
NULL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
BT_ERR("Failed to create L2CAP proc file");
|
BT_ERR("Failed to create L2CAP proc file");
|
||||||
bt_sock_unregister(BTPROTO_L2CAP);
|
bt_sock_unregister(BTPROTO_L2CAP);
|
||||||
|
@ -41,20 +41,6 @@ void baswap(bdaddr_t *dst, bdaddr_t *src)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(baswap);
|
EXPORT_SYMBOL(baswap);
|
||||||
|
|
||||||
char *batostr(bdaddr_t *ba)
|
|
||||||
{
|
|
||||||
static char str[2][18];
|
|
||||||
static int i = 1;
|
|
||||||
|
|
||||||
i ^= 1;
|
|
||||||
sprintf(str[i], "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
|
|
||||||
ba->b[5], ba->b[4], ba->b[3],
|
|
||||||
ba->b[2], ba->b[1], ba->b[0]);
|
|
||||||
|
|
||||||
return str[i];
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(batostr);
|
|
||||||
|
|
||||||
/* Bluetooth error codes to Unix errno mapping */
|
/* Bluetooth error codes to Unix errno mapping */
|
||||||
int bt_to_errno(__u16 code)
|
int bt_to_errno(__u16 code)
|
||||||
{
|
{
|
||||||
|
@ -3125,6 +3125,9 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
|
||||||
|
hdev);
|
||||||
|
|
||||||
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
|
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
|
||||||
if (!cmd)
|
if (!cmd)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
@ -3137,8 +3140,6 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||||||
|
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
|
|
||||||
hdev);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,8 +377,8 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
|
|||||||
int err = 0;
|
int err = 0;
|
||||||
u8 dlci;
|
u8 dlci;
|
||||||
|
|
||||||
BT_DBG("dlc %p state %ld %s %s channel %d",
|
BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d",
|
||||||
d, d->state, batostr(src), batostr(dst), channel);
|
d, d->state, src, dst, channel);
|
||||||
|
|
||||||
if (channel < 1 || channel > 30)
|
if (channel < 1 || channel > 30)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -676,7 +676,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
|
|||||||
struct socket *sock;
|
struct socket *sock;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
|
|
||||||
BT_DBG("%s %s", batostr(src), batostr(dst));
|
BT_DBG("%pMR -> %pMR", src, dst);
|
||||||
|
|
||||||
*err = rfcomm_l2sock_create(&sock);
|
*err = rfcomm_l2sock_create(&sock);
|
||||||
if (*err < 0)
|
if (*err < 0)
|
||||||
@ -709,7 +709,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
|
|||||||
|
|
||||||
bacpy(&addr.l2_bdaddr, dst);
|
bacpy(&addr.l2_bdaddr, dst);
|
||||||
addr.l2_family = AF_BLUETOOTH;
|
addr.l2_family = AF_BLUETOOTH;
|
||||||
addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
|
addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM);
|
||||||
addr.l2_cid = 0;
|
addr.l2_cid = 0;
|
||||||
*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
|
*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
|
||||||
if (*err == 0 || *err == -EINPROGRESS)
|
if (*err == 0 || *err == -EINPROGRESS)
|
||||||
@ -1987,7 +1987,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
|
|||||||
/* Bind socket */
|
/* Bind socket */
|
||||||
bacpy(&addr.l2_bdaddr, ba);
|
bacpy(&addr.l2_bdaddr, ba);
|
||||||
addr.l2_family = AF_BLUETOOTH;
|
addr.l2_family = AF_BLUETOOTH;
|
||||||
addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
|
addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM);
|
||||||
addr.l2_cid = 0;
|
addr.l2_cid = 0;
|
||||||
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
@ -2125,11 +2125,10 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
|
|||||||
list_for_each_entry(d, &s->dlcs, list) {
|
list_for_each_entry(d, &s->dlcs, list) {
|
||||||
struct sock *sk = s->sock->sk;
|
struct sock *sk = s->sock->sk;
|
||||||
|
|
||||||
seq_printf(f, "%s %s %ld %d %d %d %d\n",
|
seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n",
|
||||||
batostr(&bt_sk(sk)->src),
|
&bt_sk(sk)->src, &bt_sk(sk)->dst,
|
||||||
batostr(&bt_sk(sk)->dst),
|
d->state, d->dlci, d->mtu,
|
||||||
d->state, d->dlci, d->mtu,
|
d->rx_credits, d->tx_credits);
|
||||||
d->rx_credits, d->tx_credits);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +334,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
|
|||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr));
|
BT_DBG("sk %p %pMR", sk, &sa->rc_bdaddr);
|
||||||
|
|
||||||
if (!addr || addr->sa_family != AF_BLUETOOTH)
|
if (!addr || addr->sa_family != AF_BLUETOOTH)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -975,10 +975,9 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p)
|
|||||||
read_lock(&rfcomm_sk_list.lock);
|
read_lock(&rfcomm_sk_list.lock);
|
||||||
|
|
||||||
sk_for_each(sk, node, &rfcomm_sk_list.head) {
|
sk_for_each(sk, node, &rfcomm_sk_list.head) {
|
||||||
seq_printf(f, "%s %s %d %d\n",
|
seq_printf(f, "%pMR %pMR %d %d\n",
|
||||||
batostr(&bt_sk(sk)->src),
|
&bt_sk(sk)->src, &bt_sk(sk)->dst,
|
||||||
batostr(&bt_sk(sk)->dst),
|
sk->sk_state, rfcomm_pi(sk)->channel);
|
||||||
sk->sk_state, rfcomm_pi(sk)->channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
read_unlock(&rfcomm_sk_list.lock);
|
read_unlock(&rfcomm_sk_list.lock);
|
||||||
|
@ -166,7 +166,7 @@ static struct device *rfcomm_get_device(struct rfcomm_dev *dev)
|
|||||||
static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf)
|
static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
|
struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
|
||||||
return sprintf(buf, "%s\n", batostr(&dev->dst));
|
return sprintf(buf, "%pMR\n", &dev->dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf)
|
static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf)
|
||||||
@ -663,8 +663,8 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
|
|||||||
if (!dev)
|
if (!dev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst),
|
BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
|
||||||
dev->channel, dev->port.count);
|
dev->channel, dev->port.count);
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->port.lock, flags);
|
spin_lock_irqsave(&dev->port.lock, flags);
|
||||||
if (++dev->port.count > 1) {
|
if (++dev->port.count > 1) {
|
||||||
|
@ -172,7 +172,7 @@ static int sco_connect(struct sock *sk)
|
|||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
int err, type;
|
int err, type;
|
||||||
|
|
||||||
BT_DBG("%s -> %s", batostr(src), batostr(dst));
|
BT_DBG("%pMR -> %pMR", src, dst);
|
||||||
|
|
||||||
hdev = hci_get_route(dst, src);
|
hdev = hci_get_route(dst, src);
|
||||||
if (!hdev)
|
if (!hdev)
|
||||||
@ -460,7 +460,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
|
|||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr));
|
BT_DBG("sk %p %pMR", sk, &sa->sco_bdaddr);
|
||||||
|
|
||||||
if (!addr || addr->sa_family != AF_BLUETOOTH)
|
if (!addr || addr->sa_family != AF_BLUETOOTH)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -893,7 +893,7 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||||||
struct hlist_node *node;
|
struct hlist_node *node;
|
||||||
int lm = 0;
|
int lm = 0;
|
||||||
|
|
||||||
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
|
BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
|
||||||
|
|
||||||
/* Find listening sockets */
|
/* Find listening sockets */
|
||||||
read_lock(&sco_sk_list.lock);
|
read_lock(&sco_sk_list.lock);
|
||||||
@ -914,7 +914,7 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||||||
|
|
||||||
void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||||
{
|
{
|
||||||
BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
|
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
|
||||||
if (!status) {
|
if (!status) {
|
||||||
struct sco_conn *conn;
|
struct sco_conn *conn;
|
||||||
|
|
||||||
@ -959,8 +959,8 @@ static int sco_debugfs_show(struct seq_file *f, void *p)
|
|||||||
read_lock(&sco_sk_list.lock);
|
read_lock(&sco_sk_list.lock);
|
||||||
|
|
||||||
sk_for_each(sk, node, &sco_sk_list.head) {
|
sk_for_each(sk, node, &sco_sk_list.head) {
|
||||||
seq_printf(f, "%s %s %d\n", batostr(&bt_sk(sk)->src),
|
seq_printf(f, "%pMR %pMR %d\n", &bt_sk(sk)->src,
|
||||||
batostr(&bt_sk(sk)->dst), sk->sk_state);
|
&bt_sk(sk)->dst, sk->sk_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
read_unlock(&sco_sk_list.lock);
|
read_unlock(&sco_sk_list.lock);
|
||||||
|
@ -165,7 +165,7 @@ static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
|
|||||||
|
|
||||||
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
|
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
|
||||||
lh->len = cpu_to_le16(sizeof(code) + dlen);
|
lh->len = cpu_to_le16(sizeof(code) + dlen);
|
||||||
lh->cid = cpu_to_le16(L2CAP_CID_SMP);
|
lh->cid = __constant_cpu_to_le16(L2CAP_CID_SMP);
|
||||||
|
|
||||||
memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
|
memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user