Commit b7fd76d1 authored by John W. Linville's avatar John W. Linville

Merge branch 'for-upstream' of...

Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
parents 38141fcf 0c0afedf
...@@ -46,5 +46,9 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, ...@@ -46,5 +46,9 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
struct hci_conn *hcon); struct hci_conn *hcon);
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
void amp_create_logical_link(struct l2cap_chan *chan);
void amp_disconnect_logical_link(struct hci_chan *hchan);
void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason);
#endif /* __AMP_H */ #endif /* __AMP_H */
...@@ -115,6 +115,7 @@ enum { ...@@ -115,6 +115,7 @@ enum {
HCI_SSP_ENABLED, HCI_SSP_ENABLED,
HCI_HS_ENABLED, HCI_HS_ENABLED,
HCI_LE_ENABLED, HCI_LE_ENABLED,
HCI_LE_PERIPHERAL,
HCI_CONNECTABLE, HCI_CONNECTABLE,
HCI_DISCOVERABLE, HCI_DISCOVERABLE,
HCI_LINK_SECURITY, HCI_LINK_SECURITY,
...@@ -932,6 +933,12 @@ struct hci_rp_le_read_buffer_size { ...@@ -932,6 +933,12 @@ struct hci_rp_le_read_buffer_size {
__u8 le_max_pkt; __u8 le_max_pkt;
} __packed; } __packed;
#define HCI_OP_LE_READ_ADV_TX_POWER 0x2007
struct hci_rp_le_read_adv_tx_power {
__u8 status;
__s8 tx_power;
} __packed;
#define HCI_OP_LE_SET_SCAN_PARAM 0x200b #define HCI_OP_LE_SET_SCAN_PARAM 0x200b
struct hci_cp_le_set_scan_param { struct hci_cp_le_set_scan_param {
__u8 type; __u8 type;
......
...@@ -278,6 +278,8 @@ struct hci_dev { ...@@ -278,6 +278,8 @@ struct hci_dev {
struct work_struct le_scan; struct work_struct le_scan;
struct le_scan_params le_scan_params; struct le_scan_params le_scan_params;
__s8 adv_tx_power;
int (*open)(struct hci_dev *hdev); int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev);
...@@ -355,6 +357,7 @@ struct hci_chan { ...@@ -355,6 +357,7 @@ struct hci_chan {
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;
__u8 state;
}; };
extern struct list_head hci_dev_list; extern struct list_head hci_dev_list;
...@@ -682,7 +685,7 @@ static inline uint8_t __hci_num_ctrl(void) ...@@ -682,7 +685,7 @@ static inline uint8_t __hci_num_ctrl(void)
} }
struct hci_dev *hci_dev_get(int index); struct hci_dev *hci_dev_get(int index);
struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src);
struct hci_dev *hci_alloc_dev(void); struct hci_dev *hci_alloc_dev(void);
void hci_free_dev(struct hci_dev *hdev); void hci_free_dev(struct hci_dev *hdev);
...@@ -747,18 +750,29 @@ void hci_conn_del_sysfs(struct hci_conn *conn); ...@@ -747,18 +750,29 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev)) #define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev))
/* ----- LMP capabilities ----- */ /* ----- LMP capabilities ----- */
#define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH)
#define lmp_encrypt_capable(dev) ((dev)->features[0] & LMP_ENCRYPT) #define lmp_encrypt_capable(dev) ((dev)->features[0] & LMP_ENCRYPT)
#define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH)
#define lmp_hold_capable(dev) ((dev)->features[0] & LMP_HOLD)
#define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF) #define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF)
#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) #define lmp_park_capable(dev) ((dev)->features[1] & LMP_PARK)
#define lmp_inq_rssi_capable(dev) ((dev)->features[3] & LMP_RSSI_INQ)
#define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO) #define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO)
#define lmp_bredr_capable(dev) (!((dev)->features[4] & LMP_NO_BREDR))
#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE)
#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
#define lmp_pause_enc_capable(dev) ((dev)->features[5] & LMP_PAUSE_ENC)
#define lmp_ext_inq_capable(dev) ((dev)->features[6] & LMP_EXT_INQ)
#define lmp_le_br_capable(dev) ((dev)->features[6] & LMP_SIMUL_LE_BR)
#define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR)
#define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) #define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH)
#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE) #define lmp_lsto_capable(dev) ((dev)->features[7] & LMP_LSTO)
#define lmp_bredr_capable(dev) (!((dev)->features[4] & LMP_NO_BREDR)) #define lmp_inq_tx_pwr_capable(dev) ((dev)->features[7] & LMP_INQ_TX_PWR)
#define lmp_ext_feat_capable(dev) ((dev)->features[7] & LMP_EXTFEATURES)
/* ----- Extended LMP capabilities ----- */ /* ----- Extended LMP capabilities ----- */
#define lmp_host_ssp_capable(dev) ((dev)->host_features[0] & LMP_HOST_SSP)
#define lmp_host_le_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE) #define lmp_host_le_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE)
#define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR)
/* ----- HCI protocols ----- */ /* ----- HCI protocols ----- */
static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
...@@ -877,7 +891,7 @@ struct hci_cb { ...@@ -877,7 +891,7 @@ struct hci_cb {
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
{ {
struct list_head *p; struct hci_cb *cb;
__u8 encrypt; __u8 encrypt;
hci_proto_auth_cfm(conn, status); hci_proto_auth_cfm(conn, status);
...@@ -888,8 +902,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) ...@@ -888,8 +902,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
read_lock(&hci_cb_list_lock); read_lock(&hci_cb_list_lock);
list_for_each(p, &hci_cb_list) { list_for_each_entry(cb, &hci_cb_list, list) {
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
if (cb->security_cfm) if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt); cb->security_cfm(conn, status, encrypt);
} }
...@@ -899,7 +912,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) ...@@ -899,7 +912,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
__u8 encrypt) __u8 encrypt)
{ {
struct list_head *p; struct hci_cb *cb;
if (conn->sec_level == BT_SECURITY_SDP) if (conn->sec_level == BT_SECURITY_SDP)
conn->sec_level = BT_SECURITY_LOW; conn->sec_level = BT_SECURITY_LOW;
...@@ -910,8 +923,7 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, ...@@ -910,8 +923,7 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
hci_proto_encrypt_cfm(conn, status, encrypt); hci_proto_encrypt_cfm(conn, status, encrypt);
read_lock(&hci_cb_list_lock); read_lock(&hci_cb_list_lock);
list_for_each(p, &hci_cb_list) { list_for_each_entry(cb, &hci_cb_list, list) {
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
if (cb->security_cfm) if (cb->security_cfm)
cb->security_cfm(conn, status, encrypt); cb->security_cfm(conn, status, encrypt);
} }
...@@ -920,11 +932,10 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, ...@@ -920,11 +932,10 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
{ {
struct list_head *p; struct hci_cb *cb;
read_lock(&hci_cb_list_lock); read_lock(&hci_cb_list_lock);
list_for_each(p, &hci_cb_list) { list_for_each_entry(cb, &hci_cb_list, list) {
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
if (cb->key_change_cfm) if (cb->key_change_cfm)
cb->key_change_cfm(conn, status); cb->key_change_cfm(conn, status);
} }
...@@ -934,11 +945,10 @@ static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) ...@@ -934,11 +945,10 @@ static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
__u8 role) __u8 role)
{ {
struct list_head *p; struct hci_cb *cb;
read_lock(&hci_cb_list_lock); read_lock(&hci_cb_list_lock);
list_for_each(p, &hci_cb_list) { list_for_each_entry(cb, &hci_cb_list, list) {
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
if (cb->role_switch_cfm) if (cb->role_switch_cfm)
cb->role_switch_cfm(conn, status, role); cb->role_switch_cfm(conn, status, role);
} }
......
...@@ -52,6 +52,8 @@ ...@@ -52,6 +52,8 @@
#define L2CAP_ENC_TIMEOUT msecs_to_jiffies(5000) #define L2CAP_ENC_TIMEOUT msecs_to_jiffies(5000)
#define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000) #define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000)
#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000) #define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)
#define L2CAP_MOVE_TIMEOUT msecs_to_jiffies(4000)
#define L2CAP_MOVE_ERTX_TIMEOUT msecs_to_jiffies(60000)
#define L2CAP_A2MP_DEFAULT_MTU 670 #define L2CAP_A2MP_DEFAULT_MTU 670
...@@ -434,6 +436,8 @@ struct l2cap_chan { ...@@ -434,6 +436,8 @@ struct l2cap_chan {
struct sock *sk; struct sock *sk;
struct l2cap_conn *conn; struct l2cap_conn *conn;
struct hci_conn *hs_hcon;
struct hci_chan *hs_hchan;
struct kref kref; struct kref kref;
__u8 state; __u8 state;
...@@ -477,6 +481,12 @@ struct l2cap_chan { ...@@ -477,6 +481,12 @@ struct l2cap_chan {
unsigned long conn_state; unsigned long conn_state;
unsigned long flags; unsigned long flags;
__u8 remote_amp_id;
__u8 local_amp_id;
__u8 move_id;
__u8 move_state;
__u8 move_role;
__u16 next_tx_seq; __u16 next_tx_seq;
__u16 expected_ack_seq; __u16 expected_ack_seq;
__u16 expected_tx_seq; __u16 expected_tx_seq;
...@@ -509,8 +519,6 @@ struct l2cap_chan { ...@@ -509,8 +519,6 @@ 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;
...@@ -644,6 +652,9 @@ enum { ...@@ -644,6 +652,9 @@ enum {
enum { enum {
L2CAP_RX_STATE_RECV, L2CAP_RX_STATE_RECV,
L2CAP_RX_STATE_SREJ_SENT, L2CAP_RX_STATE_SREJ_SENT,
L2CAP_RX_STATE_MOVE,
L2CAP_RX_STATE_WAIT_P,
L2CAP_RX_STATE_WAIT_F,
}; };
enum { enum {
...@@ -674,6 +685,25 @@ enum { ...@@ -674,6 +685,25 @@ enum {
L2CAP_EV_RECV_FRAME, L2CAP_EV_RECV_FRAME,
}; };
enum {
L2CAP_MOVE_ROLE_NONE,
L2CAP_MOVE_ROLE_INITIATOR,
L2CAP_MOVE_ROLE_RESPONDER,
};
enum {
L2CAP_MOVE_STABLE,
L2CAP_MOVE_WAIT_REQ,
L2CAP_MOVE_WAIT_RSP,
L2CAP_MOVE_WAIT_RSP_SUCCESS,
L2CAP_MOVE_WAIT_CONFIRM,
L2CAP_MOVE_WAIT_CONFIRM_RSP,
L2CAP_MOVE_WAIT_LOGICAL_COMP,
L2CAP_MOVE_WAIT_LOGICAL_CFM,
L2CAP_MOVE_WAIT_LOCAL_BUSY,
L2CAP_MOVE_WAIT_PREPARE,
};
void l2cap_chan_hold(struct l2cap_chan *c); void l2cap_chan_hold(struct l2cap_chan *c);
void l2cap_chan_put(struct l2cap_chan *c); void l2cap_chan_put(struct l2cap_chan *c);
...@@ -778,5 +808,9 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); ...@@ -778,5 +808,9 @@ 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); void l2cap_send_conn_req(struct l2cap_chan *chan);
void l2cap_move_start(struct l2cap_chan *chan);
void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
u8 status);
void l2cap_physical_cfm(struct l2cap_chan *chan, int result);
#endif /* __L2CAP_H */ #endif /* __L2CAP_H */
...@@ -48,4 +48,3 @@ source "net/bluetooth/cmtp/Kconfig" ...@@ -48,4 +48,3 @@ source "net/bluetooth/cmtp/Kconfig"
source "net/bluetooth/hidp/Kconfig" source "net/bluetooth/hidp/Kconfig"
source "drivers/bluetooth/Kconfig" source "drivers/bluetooth/Kconfig"
...@@ -423,7 +423,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, ...@@ -423,7 +423,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id);
mgr->bredr_chan->ctrl_id = rsp->id; mgr->bredr_chan->remote_amp_id = rsp->id;
amp_create_phylink(hdev, mgr, hcon); amp_create_phylink(hdev, mgr, hcon);
...@@ -939,7 +939,7 @@ void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status) ...@@ -939,7 +939,7 @@ void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status)
goto clean; goto clean;
req->local_id = hdev->id; req->local_id = hdev->id;
req->remote_id = bredr_chan->ctrl_id; req->remote_id = bredr_chan->remote_amp_id;
memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len); memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len);
a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req); a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req);
......
...@@ -372,3 +372,96 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, ...@@ -372,3 +372,96 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp); hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
} }
void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
{
struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
struct amp_mgr *mgr = hs_hcon->amp_mgr;
struct l2cap_chan *bredr_chan;
BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr);
if (!bredr_hdev || !mgr || !mgr->bredr_chan)
return;
bredr_chan = mgr->bredr_chan;
set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags);
bredr_chan->remote_amp_id = hs_hcon->remote_id;
bredr_chan->hs_hcon = hs_hcon;
bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu;
bredr_chan->fcs = L2CAP_FCS_NONE;
l2cap_physical_cfm(bredr_chan, 0);
hci_dev_put(bredr_hdev);
}
void amp_create_logical_link(struct l2cap_chan *chan)
{
struct hci_cp_create_accept_logical_link cp;
struct hci_conn *hcon;
struct hci_dev *hdev;
BT_DBG("chan %p", chan);
if (!chan->hs_hcon)
return;
hdev = hci_dev_hold(chan->hs_hcon->hdev);
if (!hdev)
return;
BT_DBG("chan %p dst %pMR", chan, chan->conn->dst);
hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst);
if (!hcon)
goto done;
cp.phy_handle = hcon->handle;
cp.tx_flow_spec.id = chan->local_id;
cp.tx_flow_spec.stype = chan->local_stype;
cp.tx_flow_spec.msdu = cpu_to_le16(chan->local_msdu);
cp.tx_flow_spec.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
cp.tx_flow_spec.acc_lat = cpu_to_le32(chan->local_acc_lat);
cp.tx_flow_spec.flush_to = cpu_to_le32(chan->local_flush_to);
cp.rx_flow_spec.id = chan->remote_id;
cp.rx_flow_spec.stype = chan->remote_stype;
cp.rx_flow_spec.msdu = cpu_to_le16(chan->remote_msdu);
cp.rx_flow_spec.sdu_itime = cpu_to_le32(chan->remote_sdu_itime);
cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat);
cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to);
if (hcon->out)
hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
&cp);
else
hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
&cp);
done:
hci_dev_put(hdev);
}
void amp_disconnect_logical_link(struct hci_chan *hchan)
{
struct hci_conn *hcon = hchan->conn;
struct hci_cp_disconn_logical_link cp;
if (hcon->state != BT_CONNECTED) {
BT_DBG("hchan %p not connected", hchan);
return;
}
cp.log_handle = cpu_to_le16(hchan->handle);
hci_send_cmd(hcon->hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp);
}
void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason)
{
BT_DBG("hchan %p", hchan);
hci_chan_del(hchan);
}
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
SOFTWARE IS DISCLAIMED. SOFTWARE IS DISCLAIMED.
*/ */
#include <linux/export.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
SOFTWARE IS DISCLAIMED. SOFTWARE IS DISCLAIMED.
*/ */
#include <linux/module.h> #include <linux/export.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/types.h> #include <linux/types.h>
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
SOFTWARE IS DISCLAIMED. SOFTWARE IS DISCLAIMED.
*/ */
#include <linux/module.h> #include <linux/export.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/capability.h> #include <linux/capability.h>
......
...@@ -502,6 +502,9 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -502,6 +502,9 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
{ {
struct hci_conn *le; struct hci_conn *le;
if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags))
return ERR_PTR(-ENOTSUPP);
le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
if (!le) { if (!le) {
le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
...@@ -959,6 +962,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn) ...@@ -959,6 +962,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
chan->conn = conn; chan->conn = conn;
skb_queue_head_init(&chan->data_q); skb_queue_head_init(&chan->data_q);
chan->state = BT_CONNECTED;
list_add_rcu(&chan->list, &conn->chan_list); list_add_rcu(&chan->list, &conn->chan_list);
...@@ -976,6 +980,8 @@ void hci_chan_del(struct hci_chan *chan) ...@@ -976,6 +980,8 @@ void hci_chan_del(struct hci_chan *chan)
synchronize_rcu(); synchronize_rcu();
hci_conn_put(conn);
skb_queue_purge(&chan->data_q); skb_queue_purge(&chan->data_q);
kfree(chan); kfree(chan);
} }
......
...@@ -178,48 +178,13 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) ...@@ -178,48 +178,13 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
static void bredr_init(struct hci_dev *hdev) static void bredr_init(struct hci_dev *hdev)
{ {
struct hci_cp_delete_stored_link_key cp;
__le16 param;
__u8 flt_type;
hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED;
/* Mandatory initialization */
/* Read Local Supported Features */ /* Read Local Supported Features */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
/* Read Local Version */ /* Read Local Version */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
/* Read Buffer Size (ACL mtu, max pkt, etc.) */
hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
/* Read BD Address */
hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
/* Read Class of Device */
hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
/* Read Local Name */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);
/* Read Voice Setting */
hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
/* Optional initialization */
/* Clear Event Filters */
flt_type = HCI_FLT_CLEAR_ALL;
hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
/* Connection accept timeout ~20 secs */
param = __constant_cpu_to_le16(0x7d00);
hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
bacpy(&cp.bdaddr, BDADDR_ANY);
cp.delete_all = 1;
hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
} }
static void amp_init(struct hci_dev *hdev) static void amp_init(struct hci_dev *hdev)
...@@ -273,14 +238,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) ...@@ -273,14 +238,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
} }
} }
static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
{
BT_DBG("%s", hdev->name);
/* Read LE buffer size */
hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
}
static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
{ {
__u8 scan = opt; __u8 scan = opt;
...@@ -687,10 +644,6 @@ int hci_dev_open(__u16 dev) ...@@ -687,10 +644,6 @@ int hci_dev_open(__u16 dev)
ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
if (lmp_host_le_capable(hdev))
ret = __hci_request(hdev, hci_le_init_req, 0,
HCI_INIT_TIMEOUT);
clear_bit(HCI_INIT, &hdev->flags); clear_bit(HCI_INIT, &hdev->flags);
} }
...@@ -1039,10 +992,17 @@ int hci_get_dev_info(void __user *arg) ...@@ -1039,10 +992,17 @@ int hci_get_dev_info(void __user *arg)
di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4);
di.flags = hdev->flags; di.flags = hdev->flags;
di.pkt_type = hdev->pkt_type; di.pkt_type = hdev->pkt_type;
if (lmp_bredr_capable(hdev)) {
di.acl_mtu = hdev->acl_mtu; di.acl_mtu = hdev->acl_mtu;
di.acl_pkts = hdev->acl_pkts; di.acl_pkts = hdev->acl_pkts;
di.sco_mtu = hdev->sco_mtu; di.sco_mtu = hdev->sco_mtu;
di.sco_pkts = hdev->sco_pkts; di.sco_pkts = hdev->sco_pkts;
} else {
di.acl_mtu = hdev->le_mtu;
di.acl_pkts = hdev->le_pkts;
di.sco_mtu = 0;
di.sco_pkts = 0;
}
di.link_policy = hdev->link_policy; di.link_policy = hdev->link_policy;
di.link_mode = hdev->link_mode; di.link_mode = hdev->link_mode;
...@@ -1617,6 +1577,9 @@ int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, ...@@ -1617,6 +1577,9 @@ int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
return -ENOTSUPP;
if (work_busy(&hdev->le_scan)) if (work_busy(&hdev->le_scan))
return -EINPROGRESS; return -EINPROGRESS;
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
/* Bluetooth HCI event handling. */ /* Bluetooth HCI event handling. */
#include <linux/export.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
...@@ -440,7 +439,7 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -440,7 +439,7 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
{ {
__u8 status = *((__u8 *) skb->data); __u8 status = *((__u8 *) skb->data);
void *sent; struct hci_cp_write_ssp_mode *sent;
BT_DBG("%s status 0x%2.2x", hdev->name, status); BT_DBG("%s status 0x%2.2x", hdev->name, status);
...@@ -448,10 +447,17 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -448,10 +447,17 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
if (!sent) if (!sent)
return; return;
if (!status) {
if (sent->mode)
hdev->host_features[0] |= LMP_HOST_SSP;
else
hdev->host_features[0] &= ~LMP_HOST_SSP;
}
if (test_bit(HCI_MGMT, &hdev->dev_flags)) if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status); mgmt_ssp_enable_complete(hdev, sent->mode, status);
else if (!status) { else if (!status) {
if (*((u8 *) sent)) if (sent->mode)
set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
else else
clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
...@@ -460,10 +466,10 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -460,10 +466,10 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
static u8 hci_get_inquiry_mode(struct hci_dev *hdev) static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
{ {
if (hdev->features[6] & LMP_EXT_INQ) if (lmp_ext_inq_capable(hdev))
return 2; return 2;
if (hdev->features[3] & LMP_RSSI_INQ) if (lmp_inq_rssi_capable(hdev))
return 1; return 1;
if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 &&
...@@ -507,28 +513,30 @@ static void hci_setup_event_mask(struct hci_dev *hdev) ...@@ -507,28 +513,30 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
if (hdev->hci_ver < BLUETOOTH_VER_1_2) if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return; return;
if (lmp_bredr_capable(hdev)) {
events[4] |= 0x01; /* Flow Specification Complete */ events[4] |= 0x01; /* Flow Specification Complete */
events[4] |= 0x02; /* Inquiry Result with RSSI */ events[4] |= 0x02; /* Inquiry Result with RSSI */
events[4] |= 0x04; /* Read Remote Extended Features Complete */ events[4] |= 0x04; /* Read Remote Extended Features Complete */
events[5] |= 0x08; /* Synchronous Connection Complete */ events[5] |= 0x08; /* Synchronous Connection Complete */
events[5] |= 0x10; /* Synchronous Connection Changed */ events[5] |= 0x10; /* Synchronous Connection Changed */
}
if (hdev->features[3] & LMP_RSSI_INQ) if (lmp_inq_rssi_capable(hdev))
events[4] |= 0x02; /* Inquiry Result with RSSI */ events[4] |= 0x02; /* Inquiry Result with RSSI */
if (lmp_sniffsubr_capable(hdev)) if (lmp_sniffsubr_capable(hdev))
events[5] |= 0x20; /* Sniff Subrating */ events[5] |= 0x20; /* Sniff Subrating */
if (hdev->features[5] & LMP_PAUSE_ENC) if (lmp_pause_enc_capable(hdev))
events[5] |= 0x80; /* Encryption Key Refresh Complete */ events[5] |= 0x80; /* Encryption Key Refresh Complete */
if (hdev->features[6] & LMP_EXT_INQ) if (lmp_ext_inq_capable(hdev))
events[5] |= 0x40; /* Extended Inquiry Result */ events[5] |= 0x40; /* Extended Inquiry Result */
if (lmp_no_flush_capable(hdev)) if (lmp_no_flush_capable(hdev))
events[7] |= 0x01; /* Enhanced Flush Complete */ events[7] |= 0x01; /* Enhanced Flush Complete */
if (hdev->features[7] & LMP_LSTO) if (lmp_lsto_capable(hdev))
events[6] |= 0x80; /* Link Supervision Timeout Changed */ events[6] |= 0x80; /* Link Supervision Timeout Changed */
if (lmp_ssp_capable(hdev)) { if (lmp_ssp_capable(hdev)) {
...@@ -548,6 +556,53 @@ static void hci_setup_event_mask(struct hci_dev *hdev) ...@@ -548,6 +556,53 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
events[7] |= 0x20; /* LE Meta-Event */ events[7] |= 0x20; /* LE Meta-Event */
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
if (lmp_le_capable(hdev)) {
memset(events, 0, sizeof(events));
events[0] = 0x1f;
hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK,
sizeof(events), events);
}
}
static void bredr_setup(struct hci_dev *hdev)
{
struct hci_cp_delete_stored_link_key cp;
__le16 param;
__u8 flt_type;
/* Read Buffer Size (ACL mtu, max pkt, etc.) */
hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
/* Read Class of Device */
hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
/* Read Local Name */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);
/* Read Voice Setting */
hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
/* Clear Event Filters */
flt_type = HCI_FLT_CLEAR_ALL;
hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
/* Connection accept timeout ~20 secs */
param = __constant_cpu_to_le16(0x7d00);
hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
bacpy(&cp.bdaddr, BDADDR_ANY);
cp.delete_all = 1;
hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
}
static void le_setup(struct hci_dev *hdev)
{
/* Read LE Buffer Size */
hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
/* Read LE Advertising Channel TX Power */
hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
} }
static void hci_setup(struct hci_dev *hdev) static void hci_setup(struct hci_dev *hdev)
...@@ -555,6 +610,15 @@ static void hci_setup(struct hci_dev *hdev) ...@@ -555,6 +610,15 @@ static void hci_setup(struct hci_dev *hdev)
if (hdev->dev_type != HCI_BREDR) if (hdev->dev_type != HCI_BREDR)
return; return;
/* Read BD Address */
hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
if (lmp_bredr_capable(hdev))
bredr_setup(hdev);
if (lmp_le_capable(hdev))
le_setup(hdev);
hci_setup_event_mask(hdev); hci_setup_event_mask(hdev);
if (hdev->hci_ver > BLUETOOTH_VER_1_1) if (hdev->hci_ver > BLUETOOTH_VER_1_1)
...@@ -575,13 +639,13 @@ static void hci_setup(struct hci_dev *hdev) ...@@ -575,13 +639,13 @@ static void hci_setup(struct hci_dev *hdev)
} }
} }
if (hdev->features[3] & LMP_RSSI_INQ) if (lmp_inq_rssi_capable(hdev))
hci_setup_inquiry_mode(hdev); hci_setup_inquiry_mode(hdev);
if (hdev->features[7] & LMP_INQ_TX_PWR) if (lmp_inq_tx_pwr_capable(hdev))
hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
if (hdev->features[7] & LMP_EXTFEATURES) { if (lmp_ext_feat_capable(hdev)) {
struct hci_cp_read_local_ext_features cp; struct hci_cp_read_local_ext_features cp;
cp.page = 0x01; cp.page = 0x01;
...@@ -628,11 +692,11 @@ static void hci_setup_link_policy(struct hci_dev *hdev) ...@@ -628,11 +692,11 @@ static void hci_setup_link_policy(struct hci_dev *hdev)
if (lmp_rswitch_capable(hdev)) if (lmp_rswitch_capable(hdev))
link_policy |= HCI_LP_RSWITCH; link_policy |= HCI_LP_RSWITCH;
if (hdev->features[0] & LMP_HOLD) if (lmp_hold_capable(hdev))
link_policy |= HCI_LP_HOLD; link_policy |= HCI_LP_HOLD;
if (lmp_sniff_capable(hdev)) if (lmp_sniff_capable(hdev))
link_policy |= HCI_LP_SNIFF; link_policy |= HCI_LP_SNIFF;
if (hdev->features[1] & LMP_PARK) if (lmp_park_capable(hdev))
link_policy |= HCI_LP_PARK; link_policy |= HCI_LP_PARK;
cp.policy = cpu_to_le16(link_policy); cp.policy = cpu_to_le16(link_policy);
...@@ -722,10 +786,10 @@ static void hci_set_le_support(struct hci_dev *hdev) ...@@ -722,10 +786,10 @@ static void hci_set_le_support(struct hci_dev *hdev)
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
cp.le = 1; cp.le = 1;
cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); cp.simul = !!lmp_le_br_capable(hdev);
} }
if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE)) if (cp.le != !!lmp_host_le_capable(hdev))
hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp),
&cp); &cp);
} }
...@@ -1018,6 +1082,28 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, ...@@ -1018,6 +1082,28 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
} }
static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_rp_le_read_adv_tx_power *rp = (void *) skb->data;
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
if (!rp->status)
hdev->adv_tx_power = rp->tx_power;
hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status);
}
static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%2.2x", hdev->name, status);
hci_req_complete(hdev, HCI_OP_LE_SET_EVENT_MASK, status);
}
static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
{ {
struct hci_rp_user_confirm_reply *rp = (void *) skb->data; struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
...@@ -1207,6 +1293,11 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, ...@@ -1207,6 +1293,11 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
hdev->host_features[0] |= LMP_HOST_LE; hdev->host_features[0] |= LMP_HOST_LE;
else else
hdev->host_features[0] &= ~LMP_HOST_LE; hdev->host_features[0] &= ~LMP_HOST_LE;
if (sent->simul)
hdev->host_features[0] |= LMP_HOST_LE_BREDR;
else
hdev->host_features[0] &= ~LMP_HOST_LE_BREDR;
} }
if (test_bit(HCI_MGMT, &hdev->dev_flags) && if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
...@@ -1718,14 +1809,23 @@ static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) ...@@ -1718,14 +1809,23 @@ static void hci_cs_create_phylink(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);
if (status)
return;
cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK); cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK);
if (!cp) if (!cp)
return; return;
hci_dev_lock(hdev);
if (status) {
struct hci_conn *hcon;
hcon = hci_conn_hash_lookup_handle(hdev, cp->phy_handle);
if (hcon)
hci_conn_del(hcon);
} else {
amp_write_remote_assoc(hdev, cp->phy_handle); amp_write_remote_assoc(hdev, cp->phy_handle);
}
hci_dev_unlock(hdev);
} }
static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
...@@ -1744,6 +1844,11 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) ...@@ -1744,6 +1844,11 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
amp_write_remote_assoc(hdev, cp->phy_handle); amp_write_remote_assoc(hdev, cp->phy_handle);
} }
static void hci_cs_create_logical_link(struct hci_dev *hdev, u8 status)
{
BT_DBG("%s status 0x%2.2x", hdev->name, status);
}
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);
...@@ -2441,6 +2546,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -2441,6 +2546,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_read_buffer_size(hdev, skb); hci_cc_le_read_buffer_size(hdev, skb);
break; break;
case HCI_OP_LE_READ_ADV_TX_POWER:
hci_cc_le_read_adv_tx_power(hdev, skb);
break;
case HCI_OP_LE_SET_EVENT_MASK:
hci_cc_le_set_event_mask(hdev, skb);
break;
case HCI_OP_USER_CONFIRM_REPLY: case HCI_OP_USER_CONFIRM_REPLY:
hci_cc_user_confirm_reply(hdev, skb); hci_cc_user_confirm_reply(hdev, skb);
break; break;
...@@ -2570,6 +2683,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -2570,6 +2683,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_accept_phylink(hdev, ev->status); hci_cs_accept_phylink(hdev, ev->status);
break; break;
case HCI_OP_CREATE_LOGICAL_LINK:
hci_cs_create_logical_link(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;
...@@ -3544,6 +3661,130 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, ...@@ -3544,6 +3661,130 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
} }
static void hci_phy_link_complete_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_ev_phy_link_complete *ev = (void *) skb->data;
struct hci_conn *hcon, *bredr_hcon;
BT_DBG("%s handle 0x%2.2x status 0x%2.2x", hdev->name, ev->phy_handle,
ev->status);
hci_dev_lock(hdev);
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
if (!hcon) {
hci_dev_unlock(hdev);
return;
}
if (ev->status) {
hci_conn_del(hcon);
hci_dev_unlock(hdev);
return;
}
bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon;
hcon->state = BT_CONNECTED;
bacpy(&hcon->dst, &bredr_hcon->dst);
hci_conn_hold(hcon);
hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
hci_conn_put(hcon);
hci_conn_hold_device(hcon);
hci_conn_add_sysfs(hcon);
amp_physical_cfm(bredr_hcon, hcon);
hci_dev_unlock(hdev);
}
static void hci_loglink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_logical_link_complete *ev = (void *) skb->data;
struct hci_conn *hcon;
struct hci_chan *hchan;
struct amp_mgr *mgr;
BT_DBG("%s log_handle 0x%4.4x phy_handle 0x%2.2x status 0x%2.2x",
hdev->name, le16_to_cpu(ev->handle), ev->phy_handle,
ev->status);
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
if (!hcon)
return;
/* Create AMP hchan */
hchan = hci_chan_create(hcon);
if (!hchan)
return;
hchan->handle = le16_to_cpu(ev->handle);
BT_DBG("hcon %p mgr %p hchan %p", hcon, hcon->amp_mgr, hchan);
mgr = hcon->amp_mgr;
if (mgr && mgr->bredr_chan) {
struct l2cap_chan *bredr_chan = mgr->bredr_chan;
l2cap_chan_lock(bredr_chan);
bredr_chan->conn->mtu = hdev->block_mtu;
l2cap_logical_cfm(bredr_chan, hchan, 0);
hci_conn_hold(hcon);
l2cap_chan_unlock(bredr_chan);
}
}
static void hci_disconn_loglink_complete_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_ev_disconn_logical_link_complete *ev = (void *) skb->data;
struct hci_chan *hchan;
BT_DBG("%s log handle 0x%4.4x status 0x%2.2x", hdev->name,
le16_to_cpu(ev->handle), ev->status);
if (ev->status)
return;
hci_dev_lock(hdev);
hchan = hci_chan_lookup_handle(hdev, le16_to_cpu(ev->handle));
if (!hchan)
goto unlock;
amp_destroy_logical_link(hchan, ev->reason);
unlock:
hci_dev_unlock(hdev);
}
static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_ev_disconn_phy_link_complete *ev = (void *) skb->data;
struct hci_conn *hcon;
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
if (ev->status)
return;
hci_dev_lock(hdev);
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
if (hcon) {
hcon->state = BT_CLOSED;
hci_conn_del(hcon);
}
hci_dev_unlock(hdev);
}
static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{ {
struct hci_ev_le_conn_complete *ev = (void *) skb->data; struct hci_ev_le_conn_complete *ev = (void *) skb->data;
...@@ -3871,6 +4112,22 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -3871,6 +4112,22 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_remote_oob_data_request_evt(hdev, skb); hci_remote_oob_data_request_evt(hdev, skb);
break; break;
case HCI_EV_PHY_LINK_COMPLETE:
hci_phy_link_complete_evt(hdev, skb);
break;
case HCI_EV_LOGICAL_LINK_COMPLETE:
hci_loglink_complete_evt(hdev, skb);
break;
case HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE:
hci_disconn_loglink_complete_evt(hdev, skb);
break;
case HCI_EV_DISCONN_PHY_LINK_COMPLETE:
hci_disconn_phylink_complete_evt(hdev, skb);
break;
case HCI_EV_NUM_COMP_BLOCKS: case HCI_EV_NUM_COMP_BLOCKS:
hci_num_comp_blocks_evt(hdev, skb); hci_num_comp_blocks_evt(hdev, skb);
break; break;
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <net/bluetooth/l2cap.h> #include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h> #include <net/bluetooth/smp.h>
#include <net/bluetooth/a2mp.h> #include <net/bluetooth/a2mp.h>
#include <net/bluetooth/amp.h>
bool disable_ertm; bool disable_ertm;
...@@ -100,6 +101,23 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, ...@@ -100,6 +101,23 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
return c; return c;
} }
/* Find channel with given DCID.
* Returns locked channel.
*/
static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
u16 cid)
{
struct l2cap_chan *c;
mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_dcid(conn, cid);
if (c)
l2cap_chan_lock(c);
mutex_unlock(&conn->chan_lock);
return c;
}
static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
u8 ident) u8 ident)
{ {
...@@ -112,6 +130,20 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, ...@@ -112,6 +130,20 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
return NULL; return NULL;
} }
static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
u8 ident)
{
struct l2cap_chan *c;
mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_ident(conn, ident);
if (c)
l2cap_chan_lock(c);
mutex_unlock(&conn->chan_lock);
return c;
}
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
{ {
struct l2cap_chan *c; struct l2cap_chan *c;
...@@ -546,6 +578,13 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) ...@@ -546,6 +578,13 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
mgr->bredr_chan = NULL; mgr->bredr_chan = NULL;
} }
if (chan->hs_hchan) {
struct hci_chan *hs_hchan = chan->hs_hchan;
BT_DBG("chan %p disconnect hs_hchan %p", chan, hs_hchan);
amp_disconnect_logical_link(hs_hchan);
}
chan->ops->teardown(chan, err); chan->ops->teardown(chan, err);
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
...@@ -718,6 +757,12 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, ...@@ -718,6 +757,12 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
hci_send_acl(conn->hchan, skb, flags); hci_send_acl(conn->hchan, skb, flags);
} }
static bool __chan_is_moving(struct l2cap_chan *chan)
{
return chan->move_state != L2CAP_MOVE_STABLE &&
chan->move_state != L2CAP_MOVE_WAIT_PREPARE;
}
static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
{ {
struct hci_conn *hcon = chan->conn->hcon; struct hci_conn *hcon = chan->conn->hcon;
...@@ -726,6 +771,15 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) ...@@ -726,6 +771,15 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
skb->priority); skb->priority);
if (chan->hs_hcon && !__chan_is_moving(chan)) {
if (chan->hs_hchan)
hci_send_acl(chan->hs_hchan, skb, ACL_COMPLETE);
else
kfree_skb(skb);
return;
}
if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
lmp_no_flush_capable(hcon->hdev)) lmp_no_flush_capable(hcon->hdev))
flags = ACL_START_NO_FLUSH; flags = ACL_START_NO_FLUSH;
...@@ -901,6 +955,9 @@ static void l2cap_send_sframe(struct l2cap_chan *chan, ...@@ -901,6 +955,9 @@ static void l2cap_send_sframe(struct l2cap_chan *chan,
if (!control->sframe) if (!control->sframe)
return; return;
if (__chan_is_moving(chan))
return;
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) && if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) &&
!control->poll) !control->poll)
control->final = 1; control->final = 1;
...@@ -964,6 +1021,12 @@ static bool __amp_capable(struct l2cap_chan *chan) ...@@ -964,6 +1021,12 @@ static bool __amp_capable(struct l2cap_chan *chan)
return false; return false;
} }
static bool l2cap_check_efs(struct l2cap_chan *chan)
{
/* Check EFS parameters */
return true;
}
void l2cap_send_conn_req(struct l2cap_chan *chan) void l2cap_send_conn_req(struct l2cap_chan *chan)
{ {
struct l2cap_conn *conn = chan->conn; struct l2cap_conn *conn = chan->conn;
...@@ -979,6 +1042,76 @@ void l2cap_send_conn_req(struct l2cap_chan *chan) ...@@ -979,6 +1042,76 @@ void l2cap_send_conn_req(struct l2cap_chan *chan)
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
} }
static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id)
{
struct l2cap_create_chan_req req;
req.scid = cpu_to_le16(chan->scid);
req.psm = chan->psm;
req.amp_id = amp_id;
chan->ident = l2cap_get_ident(chan->conn);
l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ,
sizeof(req), &req);
}
static void l2cap_move_setup(struct l2cap_chan *chan)
{
struct sk_buff *skb;
BT_DBG("chan %p", chan);
if (chan->mode != L2CAP_MODE_ERTM)
return;
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
__clear_ack_timer(chan);
chan->retry_count = 0;
skb_queue_walk(&chan->tx_q, skb) {
if (bt_cb(skb)->control.retries)
bt_cb(skb)->control.retries = 1;
else
break;
}
chan->expected_tx_seq = chan->buffer_seq;
clear_bit(CONN_REJ_ACT, &chan->conn_state);
clear_bit(CONN_SREJ_ACT, &chan->conn_state);
l2cap_seq_list_clear(&chan->retrans_list);
l2cap_seq_list_clear(&chan->srej_list);
skb_queue_purge(&chan->srej_q);
chan->tx_state = L2CAP_TX_STATE_XMIT;
chan->rx_state = L2CAP_RX_STATE_MOVE;
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
}
static void l2cap_move_done(struct l2cap_chan *chan)
{
u8 move_role = chan->move_role;
BT_DBG("chan %p", chan);
chan->move_state = L2CAP_MOVE_STABLE;
chan->move_role = L2CAP_MOVE_ROLE_NONE;
if (chan->mode != L2CAP_MODE_ERTM)
return;
switch (move_role) {
case L2CAP_MOVE_ROLE_INITIATOR:
l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL);
chan->rx_state = L2CAP_RX_STATE_WAIT_F;
break;
case L2CAP_MOVE_ROLE_RESPONDER:
chan->rx_state = L2CAP_RX_STATE_WAIT_P;
break;
}
}
static void l2cap_chan_ready(struct l2cap_chan *chan) static void l2cap_chan_ready(struct l2cap_chan *chan)
{ {
/* This clears all conf flags, including CONF_NOT_COMPLETE */ /* This clears all conf flags, including CONF_NOT_COMPLETE */
...@@ -1695,6 +1828,9 @@ static void l2cap_streaming_send(struct l2cap_chan *chan, ...@@ -1695,6 +1828,9 @@ static void l2cap_streaming_send(struct l2cap_chan *chan,
BT_DBG("chan %p, skbs %p", chan, skbs); BT_DBG("chan %p, skbs %p", chan, skbs);
if (__chan_is_moving(chan))
return;
skb_queue_splice_tail_init(skbs, &chan->tx_q); skb_queue_splice_tail_init(skbs, &chan->tx_q);
while (!skb_queue_empty(&chan->tx_q)) { while (!skb_queue_empty(&chan->tx_q)) {
...@@ -1737,6 +1873,9 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) ...@@ -1737,6 +1873,9 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return 0; return 0;
if (__chan_is_moving(chan))
return 0;
while (chan->tx_send_head && while (chan->tx_send_head &&
chan->unacked_frames < chan->remote_tx_win && chan->unacked_frames < chan->remote_tx_win &&
chan->tx_state == L2CAP_TX_STATE_XMIT) { chan->tx_state == L2CAP_TX_STATE_XMIT) {
...@@ -1802,6 +1941,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) ...@@ -1802,6 +1941,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return; return;
if (__chan_is_moving(chan))
return;
while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) { while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) {
seq = l2cap_seq_list_pop(&chan->retrans_list); seq = l2cap_seq_list_pop(&chan->retrans_list);
...@@ -2144,6 +2286,8 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan, ...@@ -2144,6 +2286,8 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
/* PDU size is derived from the HCI MTU */ /* PDU size is derived from the HCI MTU */
pdu_len = chan->conn->mtu; pdu_len = chan->conn->mtu;
/* Constrain PDU size for BR/EDR connections */
if (!chan->hs_hcon)
pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD); pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
/* Adjust for largest possible L2CAP overhead. */ /* Adjust for largest possible L2CAP overhead. */
...@@ -2788,6 +2932,11 @@ int l2cap_ertm_init(struct l2cap_chan *chan) ...@@ -2788,6 +2932,11 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
skb_queue_head_init(&chan->tx_q); skb_queue_head_init(&chan->tx_q);
chan->local_amp_id = 0;
chan->move_id = 0;
chan->move_state = L2CAP_MOVE_STABLE;
chan->move_role = L2CAP_MOVE_ROLE_NONE;
if (chan->mode != L2CAP_MODE_ERTM) if (chan->mode != L2CAP_MODE_ERTM)
return 0; return 0;
...@@ -2834,6 +2983,44 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) ...@@ -2834,6 +2983,44 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
} }
static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
struct l2cap_conf_rfc *rfc)
{
if (chan->local_amp_id && chan->hs_hcon) {
u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
/* Class 1 devices have must have ERTM timeouts
* exceeding the Link Supervision Timeout. The
* default Link Supervision Timeout for AMP
* controllers is 10 seconds.
*
* Class 1 devices use 0xffffffff for their
* best-effort flush timeout, so the clamping logic
* will result in a timeout that meets the above
* requirement. ERTM timeouts are 16-bit values, so
* the maximum timeout is 65.535 seconds.
*/
/* Convert timeout to milliseconds and round */
ertm_to = DIV_ROUND_UP_ULL(ertm_to, 1000);
/* This is the recommended formula for class 2 devices
* that start ERTM timers when packets are sent to the
* controller.
*/
ertm_to = 3 * ertm_to + 500;
if (ertm_to > 0xffff)
ertm_to = 0xffff;
rfc->retrans_timeout = cpu_to_le16((u16) ertm_to);
rfc->monitor_timeout = rfc->retrans_timeout;
} else {
rfc->retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
rfc->monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
}
}
static inline void l2cap_txwin_setup(struct l2cap_chan *chan) static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
{ {
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
...@@ -2900,8 +3087,8 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) ...@@ -2900,8 +3087,8 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM; rfc.mode = L2CAP_MODE_ERTM;
rfc.max_transmit = chan->max_tx; rfc.max_transmit = chan->max_tx;
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0; __l2cap_set_ertm_timeouts(chan, &rfc);
size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
...@@ -3129,10 +3316,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) ...@@ -3129,10 +3316,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
rfc.max_pdu_size = cpu_to_le16(size); rfc.max_pdu_size = cpu_to_le16(size);
chan->remote_mps = size; chan->remote_mps = size;
rfc.retrans_timeout = __l2cap_set_ertm_timeouts(chan, &rfc);
__constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
rfc.monitor_timeout =
__constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
set_bit(CONF_MODE_DONE, &chan->conf_state); set_bit(CONF_MODE_DONE, &chan->conf_state);
...@@ -3308,12 +3492,21 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) ...@@ -3308,12 +3492,21 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
struct l2cap_conn_rsp rsp; struct l2cap_conn_rsp rsp;
struct l2cap_conn *conn = chan->conn; struct l2cap_conn *conn = chan->conn;
u8 buf[128]; u8 buf[128];
u8 rsp_code;
rsp.scid = cpu_to_le16(chan->dcid); rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid); rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
if (chan->hs_hcon)
rsp_code = L2CAP_CREATE_CHAN_RSP;
else
rsp_code = L2CAP_CONN_RSP;
BT_DBG("chan %p rsp_code %u", chan, rsp_code);
l2cap_send_cmd(conn, chan->ident, rsp_code, sizeof(rsp), &rsp);
if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
return; return;
...@@ -3395,7 +3588,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, ...@@ -3395,7 +3588,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
return 0; return 0;
} }
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u8 *data, u8 rsp_code, u8 amp_id) u8 *data, u8 rsp_code, u8 amp_id)
{ {
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
...@@ -3447,6 +3641,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, ...@@ -3447,6 +3641,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
bacpy(&bt_sk(sk)->dst, conn->dst); bacpy(&bt_sk(sk)->dst, conn->dst);
chan->psm = psm; chan->psm = psm;
chan->dcid = scid; chan->dcid = scid;
chan->local_amp_id = amp_id;
__l2cap_chan_add(conn, chan); __l2cap_chan_add(conn, chan);
...@@ -3463,9 +3658,18 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, ...@@ -3463,9 +3658,18 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
result = L2CAP_CR_PEND; result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND; status = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan); chan->ops->defer(chan);
} else {
/* Force pending result for AMP controllers.
* The connection will succeed after the
* physical link is up.
*/
if (amp_id) {
__l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
} else { } else {
__l2cap_state_change(chan, BT_CONFIG); __l2cap_state_change(chan, BT_CONFIG);
result = L2CAP_CR_SUCCESS; result = L2CAP_CR_SUCCESS;
}
status = L2CAP_CS_NO_INFO; status = L2CAP_CS_NO_INFO;
} }
} else { } else {
...@@ -3511,6 +3715,8 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, ...@@ -3511,6 +3715,8 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
l2cap_build_conf_req(chan, buf), buf); l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++; chan->num_conf_req++;
} }
return chan;
} }
static int l2cap_connect_req(struct l2cap_conn *conn, static int l2cap_connect_req(struct l2cap_conn *conn,
...@@ -3520,7 +3726,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn, ...@@ -3520,7 +3726,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
return 0; return 0;
} }
static inline int l2cap_connect_rsp(struct l2cap_conn *conn, static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u8 *data) struct l2cap_cmd_hdr *cmd, u8 *data)
{ {
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
...@@ -3675,6 +3881,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, ...@@ -3675,6 +3881,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
goto unlock; goto unlock;
} }
chan->ident = cmd->ident;
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
chan->num_conf_rsp++; chan->num_conf_rsp++;
...@@ -3714,7 +3921,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, ...@@ -3714,7 +3921,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
/* check compatibility */ /* check compatibility */
/* Send rsp for BR/EDR channel */ /* Send rsp for BR/EDR channel */
if (!chan->ctrl_id) if (!chan->hs_hcon)
l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags); l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags);
else else
chan->ident = cmd->ident; chan->ident = cmd->ident;
...@@ -3764,14 +3971,16 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, ...@@ -3764,14 +3971,16 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
goto done; goto done;
} }
/* check compatibility */ if (!chan->hs_hcon) {
if (!chan->ctrl_id)
l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, l2cap_send_efs_conf_rsp(chan, buf, cmd->ident,
0); 0);
else } else {
if (l2cap_check_efs(chan)) {
amp_create_logical_link(chan);
chan->ident = cmd->ident; chan->ident = cmd->ident;
} }
}
}
goto done; goto done;
case L2CAP_CONF_UNACCEPT: case L2CAP_CONF_UNACCEPT:
...@@ -4023,12 +4232,14 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, ...@@ -4023,12 +4232,14 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
return 0; return 0;
} }
static inline int l2cap_create_channel_req(struct l2cap_conn *conn, static int l2cap_create_channel_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data) u16 cmd_len, void *data)
{ {
struct l2cap_create_chan_req *req = data; struct l2cap_create_chan_req *req = data;
struct l2cap_create_chan_rsp rsp; struct l2cap_create_chan_rsp rsp;
struct l2cap_chan *chan;
struct hci_dev *hdev;
u16 psm, scid; u16 psm, scid;
if (cmd_len != sizeof(*req)) if (cmd_len != sizeof(*req))
...@@ -4042,57 +4253,119 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn, ...@@ -4042,57 +4253,119 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id); BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
/* Placeholder: Always reject */ /* For controller id 0 make BR/EDR connection */
if (req->amp_id == HCI_BREDR_ID) {
l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
req->amp_id);
return 0;
}
/* Validate AMP controller id */
hdev = hci_dev_get(req->amp_id);
if (!hdev)
goto error;
if (hdev->dev_type != HCI_AMP || !test_bit(HCI_UP, &hdev->flags)) {
hci_dev_put(hdev);
goto error;
}
chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
req->amp_id);
if (chan) {
struct amp_mgr *mgr = conn->hcon->amp_mgr;
struct hci_conn *hs_hcon;
hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
if (!hs_hcon) {
hci_dev_put(hdev);
return -EFAULT;
}
BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
chan->local_amp_id = req->amp_id;
mgr->bredr_chan = chan;
chan->hs_hcon = hs_hcon;
conn->mtu = hdev->block_mtu;
}
hci_dev_put(hdev);
return 0;
error:
rsp.dcid = 0; rsp.dcid = 0;
rsp.scid = cpu_to_le16(scid); rsp.scid = cpu_to_le16(scid);
rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM); rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
sizeof(rsp), &rsp); sizeof(rsp), &rsp);
return 0; return -EFAULT;
} }
static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
struct l2cap_cmd_hdr *cmd,
void *data)
{ {
BT_DBG("conn %p", conn); struct l2cap_move_chan_req req;
u8 ident;
BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id);
ident = l2cap_get_ident(chan->conn);
chan->ident = ident;
return l2cap_connect_rsp(conn, cmd, data); req.icid = cpu_to_le16(chan->scid);
req.dest_amp_id = dest_amp_id;
l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req),
&req);
__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
} }
static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result)
u16 icid, u16 result)
{ {
struct l2cap_move_chan_rsp rsp; struct l2cap_move_chan_rsp rsp;
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); BT_DBG("chan %p, result 0x%4.4x", chan, result);
rsp.icid = cpu_to_le16(icid); rsp.icid = cpu_to_le16(chan->dcid);
rsp.result = cpu_to_le16(result); rsp.result = cpu_to_le16(result);
l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_RSP,
sizeof(rsp), &rsp);
} }
static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn, static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result)
struct l2cap_chan *chan,
u16 icid, u16 result)
{ {
struct l2cap_move_chan_cfm cfm; struct l2cap_move_chan_cfm cfm;
u8 ident;
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); BT_DBG("chan %p, result 0x%4.4x", chan, result);
ident = l2cap_get_ident(conn); chan->ident = l2cap_get_ident(chan->conn);
if (chan)
chan->ident = ident;
cfm.icid = cpu_to_le16(icid); cfm.icid = cpu_to_le16(chan->scid);
cfm.result = cpu_to_le16(result); cfm.result = cpu_to_le16(result);
l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm); l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_CFM,
sizeof(cfm), &cfm);
__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
}
static void l2cap_send_move_chan_cfm_icid(struct l2cap_conn *conn, u16 icid)
{
struct l2cap_move_chan_cfm cfm;
BT_DBG("conn %p, icid 0x%4.4x", conn, icid);
cfm.icid = cpu_to_le16(icid);
cfm.result = __constant_cpu_to_le16(L2CAP_MC_UNCONFIRMED);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_MOVE_CHAN_CFM,
sizeof(cfm), &cfm);
} }
static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
...@@ -4106,76 +4379,564 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, ...@@ -4106,76 +4379,564 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp); l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
} }
static inline int l2cap_move_channel_req(struct l2cap_conn *conn, static void __release_logical_link(struct l2cap_chan *chan)
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
{ {
struct l2cap_move_chan_req *req = data; chan->hs_hchan = NULL;
u16 icid = 0; chan->hs_hcon = NULL;
u16 result = L2CAP_MR_NOT_ALLOWED;
if (cmd_len != sizeof(*req)) /* Placeholder - release the logical link */
return -EPROTO; }
icid = le16_to_cpu(req->icid);
BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
if (!enable_hs) static void l2cap_logical_fail(struct l2cap_chan *chan)
return -EINVAL; {
/* Logical link setup failed */
if (chan->state != BT_CONNECTED) {
/* Create channel failure, disconnect */
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
return;
}
/* Placeholder: Always refuse */ switch (chan->move_role) {
l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); case L2CAP_MOVE_ROLE_RESPONDER:
l2cap_move_done(chan);
l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_SUPP);
break;
case L2CAP_MOVE_ROLE_INITIATOR:
if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP ||
chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) {
/* Remote has only sent pending or
* success responses, clean up
*/
l2cap_move_done(chan);
}
return 0; /* Other amp move states imply that the move
* has already aborted
*/
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
break;
}
} }
static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, static void l2cap_logical_finish_create(struct l2cap_chan *chan,
struct l2cap_cmd_hdr *cmd, struct hci_chan *hchan)
u16 cmd_len, void *data)
{ {
struct l2cap_move_chan_rsp *rsp = data; struct l2cap_conf_rsp rsp;
u16 icid, result;
if (cmd_len != sizeof(*rsp)) chan->hs_hchan = hchan;
return -EPROTO; chan->hs_hcon->l2cap_data = chan->conn;
icid = le16_to_cpu(rsp->icid); l2cap_send_efs_conf_rsp(chan, &rsp, chan->ident, 0);
result = le16_to_cpu(rsp->result);
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
int err;
/* Placeholder: Always unconfirmed */ set_default_fcs(chan);
l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED);
return 0; err = l2cap_ertm_init(chan);
if (err < 0)
l2cap_send_disconn_req(chan->conn, chan, -err);
else
l2cap_chan_ready(chan);
}
} }
static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, static void l2cap_logical_finish_move(struct l2cap_chan *chan,
struct l2cap_cmd_hdr *cmd, struct hci_chan *hchan)
u16 cmd_len, void *data)
{ {
struct l2cap_move_chan_cfm *cfm = data; chan->hs_hcon = hchan->conn;
u16 icid, result; chan->hs_hcon->l2cap_data = chan->conn;
if (cmd_len != sizeof(*cfm))
return -EPROTO;
icid = le16_to_cpu(cfm->icid);
result = le16_to_cpu(cfm->result);
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); BT_DBG("move_state %d", chan->move_state);
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); switch (chan->move_state) {
case L2CAP_MOVE_WAIT_LOGICAL_COMP:
/* Move confirm will be sent after a success
* response is received
*/
chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
break;
case L2CAP_MOVE_WAIT_LOGICAL_CFM:
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
} else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
} else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS);
}
break;
default:
/* Move was not in expected state, free the channel */
__release_logical_link(chan);
return 0; chan->move_state = L2CAP_MOVE_STABLE;
}
} }
static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, /* Call with chan locked */
struct l2cap_cmd_hdr *cmd, void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
u16 cmd_len, void *data) u8 status)
{ {
struct l2cap_move_chan_cfm_rsp *rsp = data; BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status);
if (status) {
l2cap_logical_fail(chan);
__release_logical_link(chan);
return;
}
if (chan->state != BT_CONNECTED) {
/* Ignore logical link if channel is on BR/EDR */
if (chan->local_amp_id)
l2cap_logical_finish_create(chan, hchan);
} else {
l2cap_logical_finish_move(chan, hchan);
}
}
void l2cap_move_start(struct l2cap_chan *chan)
{
BT_DBG("chan %p", chan);
if (chan->local_amp_id == HCI_BREDR_ID) {
if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
return;
chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
/* Placeholder - start physical link setup */
} else {
chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
chan->move_id = 0;
l2cap_move_setup(chan);
l2cap_send_move_chan_req(chan, 0);
}
}
static void l2cap_do_create(struct l2cap_chan *chan, int result,
u8 local_amp_id, u8 remote_amp_id)
{
if (!test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
struct l2cap_conn_rsp rsp;
char buf[128];
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
/* Incoming channel on AMP */
if (result == L2CAP_CR_SUCCESS) {
/* Send successful response */
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
} else {
/* Send negative response */
rsp.result = cpu_to_le16(L2CAP_CR_NO_MEM);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP,
sizeof(rsp), &rsp);
if (result == L2CAP_CR_SUCCESS) {
__l2cap_state_change(chan, BT_CONFIG);
set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
} else {
/* Outgoing channel on AMP */
if (result == L2CAP_CR_SUCCESS) {
chan->local_amp_id = local_amp_id;
l2cap_send_create_chan_req(chan, remote_amp_id);
} else {
/* Revert to BR/EDR connect */
l2cap_send_conn_req(chan);
}
}
}
static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id,
u8 remote_amp_id)
{
l2cap_move_setup(chan);
chan->move_id = local_amp_id;
chan->move_state = L2CAP_MOVE_WAIT_RSP;
l2cap_send_move_chan_req(chan, remote_amp_id);
}
static void l2cap_do_move_respond(struct l2cap_chan *chan, int result)
{
struct hci_chan *hchan = NULL;
/* Placeholder - get hci_chan for logical link */
if (hchan) {
if (hchan->state == BT_CONNECTED) {
/* Logical link is ready to go */
chan->hs_hcon = hchan->conn;
chan->hs_hcon->l2cap_data = chan->conn;
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS);
l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
} else {
/* Wait for logical link to be ready */
chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
}
} else {
/* Logical link not available */
l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_ALLOWED);
}
}
static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result)
{
if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
u8 rsp_result;
if (result == -EINVAL)
rsp_result = L2CAP_MR_BAD_ID;
else
rsp_result = L2CAP_MR_NOT_ALLOWED;
l2cap_send_move_chan_rsp(chan, rsp_result);
}
chan->move_role = L2CAP_MOVE_ROLE_NONE;
chan->move_state = L2CAP_MOVE_STABLE;
/* Restart data transmission */
l2cap_ertm_send(chan);
}
void l2cap_physical_cfm(struct l2cap_chan *chan, int result)
{
u8 local_amp_id = chan->local_amp_id;
u8 remote_amp_id = chan->remote_amp_id;
BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d",
chan, result, local_amp_id, remote_amp_id);
l2cap_chan_lock(chan);
if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) {
l2cap_chan_unlock(chan);
return;
}
if (chan->state != BT_CONNECTED) {
l2cap_do_create(chan, result, local_amp_id, remote_amp_id);
} else if (result != L2CAP_MR_SUCCESS) {
l2cap_do_move_cancel(chan, result);
} else {
switch (chan->move_role) {
case L2CAP_MOVE_ROLE_INITIATOR:
l2cap_do_move_initiate(chan, local_amp_id,
remote_amp_id);
break;
case L2CAP_MOVE_ROLE_RESPONDER:
l2cap_do_move_respond(chan, result);
break;
default:
l2cap_do_move_cancel(chan, result);
break;
}
}
l2cap_chan_unlock(chan);
}
static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_req *req = data;
struct l2cap_move_chan_rsp rsp;
struct l2cap_chan *chan;
u16 icid = 0;
u16 result = L2CAP_MR_NOT_ALLOWED;
if (cmd_len != sizeof(*req))
return -EPROTO;
icid = le16_to_cpu(req->icid);
BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
if (!enable_hs)
return -EINVAL;
chan = l2cap_get_chan_by_dcid(conn, icid);
if (!chan) {
rsp.icid = cpu_to_le16(icid);
rsp.result = __constant_cpu_to_le16(L2CAP_MR_NOT_ALLOWED);
l2cap_send_cmd(conn, cmd->ident, L2CAP_MOVE_CHAN_RSP,
sizeof(rsp), &rsp);
return 0;
}
chan->ident = cmd->ident;
if (chan->scid < L2CAP_CID_DYN_START ||
chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY ||
(chan->mode != L2CAP_MODE_ERTM &&
chan->mode != L2CAP_MODE_STREAMING)) {
result = L2CAP_MR_NOT_ALLOWED;
goto send_move_response;
}
if (chan->local_amp_id == req->dest_amp_id) {
result = L2CAP_MR_SAME_ID;
goto send_move_response;
}
if (req->dest_amp_id) {
struct hci_dev *hdev;
hdev = hci_dev_get(req->dest_amp_id);
if (!hdev || hdev->dev_type != HCI_AMP ||
!test_bit(HCI_UP, &hdev->flags)) {
if (hdev)
hci_dev_put(hdev);
result = L2CAP_MR_BAD_ID;
goto send_move_response;
}
hci_dev_put(hdev);
}
/* Detect a move collision. Only send a collision response
* if this side has "lost", otherwise proceed with the move.
* The winner has the larger bd_addr.
*/
if ((__chan_is_moving(chan) ||
chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
bacmp(conn->src, conn->dst) > 0) {
result = L2CAP_MR_COLLISION;
goto send_move_response;
}
chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
l2cap_move_setup(chan);
chan->move_id = req->dest_amp_id;
icid = chan->dcid;
if (!req->dest_amp_id) {
/* Moving to BR/EDR */
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
result = L2CAP_MR_PEND;
} else {
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
result = L2CAP_MR_SUCCESS;
}
} else {
chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
/* Placeholder - uncomment when amp functions are available */
/*amp_accept_physical(chan, req->dest_amp_id);*/
result = L2CAP_MR_PEND;
}
send_move_response:
l2cap_send_move_chan_rsp(chan, result);
l2cap_chan_unlock(chan);
return 0;
}
static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result)
{
struct l2cap_chan *chan;
struct hci_chan *hchan = NULL;
chan = l2cap_get_chan_by_scid(conn, icid);
if (!chan) {
l2cap_send_move_chan_cfm_icid(conn, icid);
return;
}
__clear_chan_timer(chan);
if (result == L2CAP_MR_PEND)
__set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT);
switch (chan->move_state) {
case L2CAP_MOVE_WAIT_LOGICAL_COMP:
/* Move confirm will be sent when logical link
* is complete.
*/
chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
break;
case L2CAP_MOVE_WAIT_RSP_SUCCESS:
if (result == L2CAP_MR_PEND) {
break;
} else if (test_bit(CONN_LOCAL_BUSY,
&chan->conn_state)) {
chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
} else {
/* Logical link is up or moving to BR/EDR,
* proceed with move
*/
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
}
break;
case L2CAP_MOVE_WAIT_RSP:
/* Moving to AMP */
if (result == L2CAP_MR_SUCCESS) {
/* Remote is ready, send confirm immediately
* after logical link is ready
*/
chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
} else {
/* Both logical link and move success
* are required to confirm
*/
chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_COMP;
}
/* Placeholder - get hci_chan for logical link */
if (!hchan) {
/* Logical link not available */
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
break;
}
/* If the logical link is not yet connected, do not
* send confirmation.
*/
if (hchan->state != BT_CONNECTED)
break;
/* Logical link is already ready to go */
chan->hs_hcon = hchan->conn;
chan->hs_hcon->l2cap_data = chan->conn;
if (result == L2CAP_MR_SUCCESS) {
/* Can confirm now */
l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
} else {
/* Now only need move success
* to confirm
*/
chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
}
l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
break;
default:
/* Any other amp move state means the move failed. */
chan->move_id = chan->local_amp_id;
l2cap_move_done(chan);
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
}
l2cap_chan_unlock(chan);
}
static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
u16 result)
{
struct l2cap_chan *chan;
chan = l2cap_get_chan_by_ident(conn, ident);
if (!chan) {
/* Could not locate channel, icid is best guess */
l2cap_send_move_chan_cfm_icid(conn, icid);
return;
}
__clear_chan_timer(chan);
if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
if (result == L2CAP_MR_COLLISION) {
chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
} else {
/* Cleanup - cancel move */
chan->move_id = chan->local_amp_id;
l2cap_move_done(chan);
}
}
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
l2cap_chan_unlock(chan);
}
static int l2cap_move_channel_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_rsp *rsp = data;
u16 icid, result;
if (cmd_len != sizeof(*rsp))
return -EPROTO;
icid = le16_to_cpu(rsp->icid);
result = le16_to_cpu(rsp->result);
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
if (result == L2CAP_MR_SUCCESS || result == L2CAP_MR_PEND)
l2cap_move_continue(conn, icid, result);
else
l2cap_move_fail(conn, cmd->ident, icid, result);
return 0;
}
static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_cfm *cfm = data;
struct l2cap_chan *chan;
u16 icid, result;
if (cmd_len != sizeof(*cfm))
return -EPROTO;
icid = le16_to_cpu(cfm->icid);
result = le16_to_cpu(cfm->result);
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
chan = l2cap_get_chan_by_dcid(conn, icid);
if (!chan) {
/* Spec requires a response even if the icid was not found */
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
return 0;
}
if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
if (result == L2CAP_MC_CONFIRMED) {
chan->local_amp_id = chan->move_id;
if (!chan->local_amp_id)
__release_logical_link(chan);
} else {
chan->move_id = chan->local_amp_id;
}
l2cap_move_done(chan);
}
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
l2cap_chan_unlock(chan);
return 0;
}
static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_cfm_rsp *rsp = data;
struct l2cap_chan *chan;
u16 icid; u16 icid;
if (cmd_len != sizeof(*rsp)) if (cmd_len != sizeof(*rsp))
...@@ -4185,6 +4946,23 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, ...@@ -4185,6 +4946,23 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x", icid); BT_DBG("icid 0x%4.4x", icid);
chan = l2cap_get_chan_by_scid(conn, icid);
if (!chan)
return 0;
__clear_chan_timer(chan);
if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
chan->local_amp_id = chan->move_id;
if (!chan->local_amp_id && chan->hs_hchan)
__release_logical_link(chan);
l2cap_move_done(chan);
}
l2cap_chan_unlock(chan);
return 0; return 0;
} }
...@@ -4269,7 +5047,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, ...@@ -4269,7 +5047,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
case L2CAP_CONN_RSP: case L2CAP_CONN_RSP:
case L2CAP_CREATE_CHAN_RSP: case L2CAP_CREATE_CHAN_RSP:
err = l2cap_connect_rsp(conn, cmd, data); err = l2cap_connect_create_rsp(conn, cmd, data);
break; break;
case L2CAP_CONF_REQ: case L2CAP_CONF_REQ:
...@@ -4556,6 +5334,12 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, ...@@ -4556,6 +5334,12 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
return err; return err;
} }
static int l2cap_resegment(struct l2cap_chan *chan)
{
/* Placeholder */
return 0;
}
void l2cap_chan_busy(struct l2cap_chan *chan, int busy) void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
{ {
u8 event; u8 event;
...@@ -4871,8 +5655,8 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, ...@@ -4871,8 +5655,8 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
if (control->final) { if (control->final) {
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
if (!test_and_clear_bit(CONN_REJ_ACT, if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state) &&
&chan->conn_state)) { !__chan_is_moving(chan)) {
control->final = 0; control->final = 0;
l2cap_retransmit_all(chan, control); l2cap_retransmit_all(chan, control);
} }
...@@ -5061,6 +5845,96 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, ...@@ -5061,6 +5845,96 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
return err; return err;
} }
static int l2cap_finish_move(struct l2cap_chan *chan)
{
BT_DBG("chan %p", chan);
chan->rx_state = L2CAP_RX_STATE_RECV;
if (chan->hs_hcon)
chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
else
chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
return l2cap_resegment(chan);
}
static int l2cap_rx_state_wait_p(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event)
{
int err;
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
event);
if (!control->poll)
return -EPROTO;
l2cap_process_reqseq(chan, control->reqseq);
if (!skb_queue_empty(&chan->tx_q))
chan->tx_send_head = skb_peek(&chan->tx_q);
else
chan->tx_send_head = NULL;
/* Rewind next_tx_seq to the point expected
* by the receiver.
*/
chan->next_tx_seq = control->reqseq;
chan->unacked_frames = 0;
err = l2cap_finish_move(chan);
if (err)
return err;
set_bit(CONN_SEND_FBIT, &chan->conn_state);
l2cap_send_i_or_rr_or_rnr(chan);
if (event == L2CAP_EV_RECV_IFRAME)
return -EPROTO;
return l2cap_rx_state_recv(chan, control, NULL, event);
}
static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,
struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event)
{
int err;
if (!control->final)
return -EPROTO;
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->rx_state = L2CAP_RX_STATE_RECV;
l2cap_process_reqseq(chan, control->reqseq);
if (!skb_queue_empty(&chan->tx_q))
chan->tx_send_head = skb_peek(&chan->tx_q);
else
chan->tx_send_head = NULL;
/* Rewind next_tx_seq to the point expected
* by the receiver.
*/
chan->next_tx_seq = control->reqseq;
chan->unacked_frames = 0;
if (chan->hs_hcon)
chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
else
chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
err = l2cap_resegment(chan);
if (!err)
err = l2cap_rx_state_recv(chan, control, skb, event);
return err;
}
static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq) static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq)
{ {
/* Make sure reqseq is for a packet that has been sent but not acked */ /* Make sure reqseq is for a packet that has been sent but not acked */
...@@ -5087,6 +5961,12 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, ...@@ -5087,6 +5961,12 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
err = l2cap_rx_state_srej_sent(chan, control, skb, err = l2cap_rx_state_srej_sent(chan, control, skb,
event); event);
break; break;
case L2CAP_RX_STATE_WAIT_P:
err = l2cap_rx_state_wait_p(chan, control, skb, event);
break;
case L2CAP_RX_STATE_WAIT_F:
err = l2cap_rx_state_wait_f(chan, control, skb, event);
break;
default: default:
/* shut it down */ /* shut it down */
break; break;
...@@ -5422,9 +6302,9 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) ...@@ -5422,9 +6302,9 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
conn = l2cap_conn_add(hcon, status); conn = l2cap_conn_add(hcon, status);
if (conn) if (conn)
l2cap_conn_ready(conn); l2cap_conn_ready(conn);
} else } else {
l2cap_conn_del(hcon, bt_to_errno(status)); l2cap_conn_del(hcon, bt_to_errno(status));
}
} }
int l2cap_disconn_ind(struct hci_conn *hcon) int l2cap_disconn_ind(struct hci_conn *hcon)
......
...@@ -736,6 +736,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ...@@ -736,6 +736,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
} }
chan->chan_policy = (u8) opt; chan->chan_policy = (u8) opt;
if (sk->sk_state == BT_CONNECTED &&
chan->move_role == L2CAP_MOVE_ROLE_NONE)
l2cap_move_start(chan);
break; break;
default: default:
......
...@@ -222,7 +222,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) ...@@ -222,7 +222,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_STATUS);
hdr->index = cpu_to_le16(index); hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev)); hdr->len = cpu_to_le16(sizeof(*ev));
...@@ -253,7 +253,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, ...@@ -253,7 +253,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index); hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
...@@ -376,15 +376,15 @@ static u32 get_supported_settings(struct hci_dev *hdev) ...@@ -376,15 +376,15 @@ static u32 get_supported_settings(struct hci_dev *hdev)
u32 settings = 0; u32 settings = 0;
settings |= MGMT_SETTING_POWERED; settings |= MGMT_SETTING_POWERED;
settings |= MGMT_SETTING_CONNECTABLE;
settings |= MGMT_SETTING_FAST_CONNECTABLE;
settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_PAIRABLE; settings |= MGMT_SETTING_PAIRABLE;
if (lmp_ssp_capable(hdev)) if (lmp_ssp_capable(hdev))
settings |= MGMT_SETTING_SSP; settings |= MGMT_SETTING_SSP;
if (lmp_bredr_capable(hdev)) { if (lmp_bredr_capable(hdev)) {
settings |= MGMT_SETTING_CONNECTABLE;
settings |= MGMT_SETTING_FAST_CONNECTABLE;
settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_BREDR; settings |= MGMT_SETTING_BREDR;
settings |= MGMT_SETTING_LINK_SECURITY; settings |= MGMT_SETTING_LINK_SECURITY;
} }
...@@ -565,7 +565,7 @@ static int update_eir(struct hci_dev *hdev) ...@@ -565,7 +565,7 @@ static int update_eir(struct hci_dev *hdev)
if (!hdev_is_powered(hdev)) if (!hdev_is_powered(hdev))
return 0; return 0;
if (!(hdev->features[6] & LMP_EXT_INQ)) if (!lmp_ext_inq_capable(hdev))
return 0; return 0;
if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
...@@ -832,7 +832,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, ...@@ -832,7 +832,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
if (hdev) if (hdev)
hdr->index = cpu_to_le16(hdev->id); hdr->index = cpu_to_le16(hdev->id);
else else
hdr->index = cpu_to_le16(MGMT_INDEX_NONE); hdr->index = __constant_cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len); hdr->len = cpu_to_le16(data_len);
if (data) if (data)
...@@ -867,6 +867,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -867,6 +867,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name); BT_DBG("request for %s", hdev->name);
if (!lmp_bredr_capable(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
MGMT_STATUS_NOT_SUPPORTED);
timeout = __le16_to_cpu(cp->timeout); timeout = __le16_to_cpu(cp->timeout);
if (!cp->val && timeout > 0) if (!cp->val && timeout > 0)
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
...@@ -962,6 +966,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -962,6 +966,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name); BT_DBG("request for %s", hdev->name);
if (!lmp_bredr_capable(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
MGMT_STATUS_NOT_SUPPORTED);
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) { if (!hdev_is_powered(hdev)) {
...@@ -1060,6 +1068,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, ...@@ -1060,6 +1068,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name); BT_DBG("request for %s", hdev->name);
if (!lmp_bredr_capable(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
MGMT_STATUS_NOT_SUPPORTED);
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) { if (!hdev_is_powered(hdev)) {
...@@ -1213,7 +1225,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) ...@@ -1213,7 +1225,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
} }
val = !!cp->val; val = !!cp->val;
enabled = !!(hdev->host_features[0] & LMP_HOST_LE); enabled = !!lmp_host_le_capable(hdev);
if (!hdev_is_powered(hdev) || val == enabled) { if (!hdev_is_powered(hdev) || val == enabled) {
bool changed = false; bool changed = false;
...@@ -1249,7 +1261,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) ...@@ -1249,7 +1261,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
if (val) { if (val) {
hci_cp.le = val; hci_cp.le = val;
hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); hci_cp.simul = !!lmp_le_br_capable(hdev);
} }
err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
...@@ -2594,6 +2606,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, ...@@ -2594,6 +2606,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
if (!lmp_bredr_capable(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_NOT_SUPPORTED);
if (!hdev_is_powered(hdev)) if (!hdev_is_powered(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_NOT_POWERED); MGMT_STATUS_NOT_POWERED);
...@@ -2871,6 +2887,21 @@ static void settings_rsp(struct pending_cmd *cmd, void *data) ...@@ -2871,6 +2887,21 @@ static void settings_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_free(cmd); mgmt_pending_free(cmd);
} }
static int set_bredr_scan(struct hci_dev *hdev)
{
u8 scan = 0;
if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
scan |= SCAN_PAGE;
if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
scan |= SCAN_INQUIRY;
if (!scan)
return 0;
return hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
}
int mgmt_powered(struct hci_dev *hdev, u8 powered) int mgmt_powered(struct hci_dev *hdev, u8 powered)
{ {
struct cmd_lookup match = { NULL, hdev }; struct cmd_lookup match = { NULL, hdev };
...@@ -2882,17 +2913,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) ...@@ -2882,17 +2913,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
if (powered) { if (powered) {
u8 scan = 0; if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
!lmp_host_ssp_capable(hdev)) {
if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
scan |= SCAN_PAGE;
if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
scan |= SCAN_INQUIRY;
if (scan)
hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
u8 ssp = 1; u8 ssp = 1;
hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp); hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
...@@ -2902,15 +2924,24 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) ...@@ -2902,15 +2924,24 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
struct hci_cp_write_le_host_supported cp; struct hci_cp_write_le_host_supported cp;
cp.le = 1; cp.le = 1;
cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); cp.simul = !!lmp_le_br_capable(hdev);
hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, /* Check first if we already have the right
* host state (host features set)
*/
if (cp.le != !!lmp_host_le_capable(hdev) ||
cp.simul != !!lmp_host_le_br_capable(hdev))
hci_send_cmd(hdev,
HCI_OP_WRITE_LE_HOST_SUPPORTED,
sizeof(cp), &cp); sizeof(cp), &cp);
} }
if (lmp_bredr_capable(hdev)) {
set_bredr_scan(hdev);
update_class(hdev); update_class(hdev);
update_name(hdev, hdev->dev_name); update_name(hdev, hdev->dev_name);
update_eir(hdev); update_eir(hdev);
}
} else { } else {
u8 status = MGMT_STATUS_NOT_POWERED; u8 status = MGMT_STATUS_NOT_POWERED;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
...@@ -3359,7 +3390,7 @@ static int clear_eir(struct hci_dev *hdev) ...@@ -3359,7 +3390,7 @@ static int clear_eir(struct hci_dev *hdev)
{ {
struct hci_cp_write_eir cp; struct hci_cp_write_eir cp;
if (!(hdev->features[6] & LMP_EXT_INQ)) if (!lmp_ext_inq_capable(hdev))
return 0; return 0;
memset(hdev->eir, 0, sizeof(hdev->eir)); memset(hdev->eir, 0, sizeof(hdev->eir));
...@@ -3491,6 +3522,11 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) ...@@ -3491,6 +3522,11 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev,
sizeof(ev), cmd ? cmd->sk : NULL); sizeof(ev), cmd ? cmd->sk : NULL);
/* EIR is taken care of separately when powering on the
* adapter so only update them here if this is a name change
* unrelated to power on.
*/
if (!test_bit(HCI_INIT, &hdev->flags))
update_eir(hdev); update_eir(hdev);
failed: failed:
...@@ -3586,9 +3622,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ...@@ -3586,9 +3622,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->addr.type = link_to_bdaddr(link_type, addr_type);
ev->rssi = rssi; ev->rssi = rssi;
if (cfm_name) if (cfm_name)
ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME); ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
if (!ssp) if (!ssp)
ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING); ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
if (eir_len > 0) if (eir_len > 0)
memcpy(ev->eir, eir, eir_len); memcpy(ev->eir, eir, eir_len);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment