dect
/
linux-2.6
Archived
13
0
Fork 0

Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

This commit is contained in:
John W. Linville 2012-12-03 13:46:03 -05:00
commit 06ef5c4bbb
8 changed files with 213 additions and 81 deletions

View File

@ -376,7 +376,7 @@ extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt);
extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb,
u16 flags);
extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status);
extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
@ -577,6 +577,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
int hci_conn_del(struct hci_conn *conn);
void hci_conn_hash_flush(struct hci_dev *hdev);
void hci_conn_check_pending(struct hci_dev *hdev);
void hci_conn_accept(struct hci_conn *conn, int mask);
struct hci_chan *hci_chan_create(struct hci_conn *conn);
void hci_chan_del(struct hci_chan *chan);
@ -766,7 +767,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#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_le_br_capable(dev) !!((dev)->features[6] & LMP_SIMUL_LE_BR)
#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_lsto_capable(dev) ((dev)->features[7] & LMP_LSTO)
@ -775,12 +776,30 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
/* ----- 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_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR)
#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)
/* returns true if at least one AMP active */
static inline bool hci_amp_capable(void)
{
struct hci_dev *hdev;
bool ret = false;
read_lock(&hci_dev_list_lock);
list_for_each_entry(hdev, &hci_dev_list, list)
if (hdev->amp_type == HCI_AMP &&
test_bit(HCI_UP, &hdev->flags))
ret = true;
read_unlock(&hci_dev_list_lock);
return ret;
}
/* ----- HCI protocols ----- */
#define HCI_PROTO_DEFER 0x01
static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
__u8 type)
__u8 type, __u8 *flags)
{
switch (type) {
case ACL_LINK:
@ -788,7 +807,7 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
case SCO_LINK:
case ESCO_LINK:
return sco_connect_ind(hdev, bdaddr);
return sco_connect_ind(hdev, bdaddr, flags);
default:
BT_ERR("unknown link type %d", type);

View File

@ -611,7 +611,7 @@ enum {
CONF_MTU_DONE,
CONF_MODE_DONE,
CONF_CONNECT_PEND,
CONF_NO_FCS_RECV,
CONF_RECV_NO_FCS,
CONF_STATE2_DEVICE,
CONF_EWS_RECV,
CONF_LOC_CONF_PEND,

View File

@ -861,6 +861,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
/* Clear flags */
hdev->flags = 0;
/* Controller radio is available but is currently powered down */
hdev->amp_status = 0;
memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
@ -1854,6 +1857,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
for (i = 0; i < NUM_REASSEMBLY; i++)
kfree_skb(hdev->reassembly[i]);
cancel_work_sync(&hdev->power_on);
if (!test_bit(HCI_INIT, &hdev->flags) &&
!test_bit(HCI_SETUP, &hdev->dev_flags)) {
hci_dev_lock(hdev);

View File

@ -794,10 +794,10 @@ static void hci_set_le_support(struct hci_dev *hdev)
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
cp.le = 1;
cp.simul = !!lmp_le_br_capable(hdev);
cp.simul = lmp_le_br_capable(hdev);
}
if (cp.le != !!lmp_host_le_capable(hdev))
if (cp.le != lmp_host_le_capable(hdev))
hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp),
&cp);
}
@ -2047,15 +2047,53 @@ unlock:
hci_conn_check_pending(hdev);
}
void hci_conn_accept(struct hci_conn *conn, int mask)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p", conn);
conn->state = BT_CONFIG;
if (!lmp_esco_capable(hdev)) {
struct hci_cp_accept_conn_req cp;
bacpy(&cp.bdaddr, &conn->dst);
if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
cp.role = 0x00; /* Become master */
else
cp.role = 0x01; /* Remain slave */
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp);
} else /* lmp_esco_capable(hdev)) */ {
struct hci_cp_accept_sync_conn_req cp;
bacpy(&cp.bdaddr, &conn->dst);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
cp.max_latency = __constant_cpu_to_le16(0xffff);
cp.content_format = cpu_to_le16(hdev->voice_setting);
cp.retrans_effort = 0xff;
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
sizeof(cp), &cp);
}
}
static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_conn_request *ev = (void *) skb->data;
int mask = hdev->link_mode;
__u8 flags = 0;
BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
ev->link_type);
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type,
&flags);
if ((mask & HCI_LM_ACCEPT) &&
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
@ -2081,12 +2119,13 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
memcpy(conn->dev_class, ev->dev_class, 3);
conn->state = BT_CONNECT;
hci_dev_unlock(hdev);
if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
if (ev->link_type == ACL_LINK ||
(!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) {
struct hci_cp_accept_conn_req cp;
conn->state = BT_CONNECT;
bacpy(&cp.bdaddr, &ev->bdaddr);
@ -2097,8 +2136,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
&cp);
} else {
} else if (!(flags & HCI_PROTO_DEFER)) {
struct hci_cp_accept_sync_conn_req cp;
conn->state = BT_CONNECT;
bacpy(&cp.bdaddr, &ev->bdaddr);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
@ -2111,6 +2151,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
sizeof(cp), &cp);
} else {
conn->state = BT_CONNECT2;
hci_proto_connect_cfm(conn, 0);
hci_conn_put(conn);
}
} else {
/* Connection rejected */

View File

@ -53,8 +53,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
void *data);
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
static void l2cap_send_disconn_req(struct l2cap_conn *conn,
struct l2cap_chan *chan, int err);
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff_head *skbs, u8 event);
@ -632,7 +631,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
conn->hcon->type == ACL_LINK) {
__set_chan_timer(chan, sk->sk_sndtimeo);
l2cap_send_disconn_req(conn, chan, reason);
l2cap_send_disconn_req(chan, reason);
} else
l2cap_chan_del(chan, reason);
break;
@ -1014,6 +1013,7 @@ static bool __amp_capable(struct l2cap_chan *chan)
struct l2cap_conn *conn = chan->conn;
if (enable_hs &&
hci_amp_capable() &&
chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED &&
conn->fixed_chan_mask & L2CAP_FC_A2MP)
return true;
@ -1180,10 +1180,10 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
}
}
static void l2cap_send_disconn_req(struct l2cap_conn *conn,
struct l2cap_chan *chan, int err)
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct l2cap_disconn_req req;
if (!conn)
@ -1960,7 +1960,7 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
if (chan->max_tx != 0 &&
bt_cb(skb)->control.retries > chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
l2cap_seq_list_clear(&chan->retrans_list);
break;
}
@ -2666,7 +2666,7 @@ static void l2cap_tx_state_wait_f(struct l2cap_chan *chan,
__set_monitor_timer(chan);
chan->retry_count++;
} else {
l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
l2cap_send_disconn_req(chan, ECONNABORTED);
}
break;
default:
@ -3106,18 +3106,17 @@ done:
if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
l2cap_add_opt_efs(&ptr, chan);
if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
break;
if (chan->fcs == L2CAP_FCS_NONE ||
test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
chan->tx_win);
if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
if (chan->fcs == L2CAP_FCS_NONE ||
test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
chan->fcs);
}
break;
case L2CAP_MODE_STREAMING:
@ -3139,14 +3138,13 @@ done:
if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
l2cap_add_opt_efs(&ptr, chan);
if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
break;
if (chan->fcs == L2CAP_FCS_NONE ||
test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
if (chan->fcs == L2CAP_FCS_NONE ||
test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
chan->fcs);
}
break;
}
@ -3198,7 +3196,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
case L2CAP_CONF_FCS:
if (val == L2CAP_FCS_NONE)
set_bit(CONF_NO_FCS_RECV, &chan->conf_state);
set_bit(CONF_RECV_NO_FCS, &chan->conf_state);
break;
case L2CAP_CONF_EFS:
@ -3433,6 +3431,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
(unsigned long) &efs);
break;
case L2CAP_CONF_FCS:
if (*result == L2CAP_CONF_PENDING)
if (val == L2CAP_FCS_NONE)
set_bit(CONF_RECV_NO_FCS,
&chan->conf_state);
break;
}
}
@ -3802,7 +3807,7 @@ static inline void set_default_fcs(struct l2cap_chan *chan)
*/
if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING)
chan->fcs = L2CAP_FCS_NONE;
else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state))
else if (!test_bit(CONF_RECV_NO_FCS, &chan->conf_state))
chan->fcs = L2CAP_FCS_CRC16;
}
@ -3877,7 +3882,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
/* Complete config. */
len = l2cap_parse_conf_req(chan, rsp);
if (len < 0) {
l2cap_send_disconn_req(conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
goto unlock;
}
@ -3899,7 +3904,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
err = l2cap_ertm_init(chan);
if (err < 0)
l2cap_send_disconn_req(chan->conn, chan, -err);
l2cap_send_disconn_req(chan, -err);
else
l2cap_chan_ready(chan);
@ -3967,7 +3972,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
buf, &result);
if (len < 0) {
l2cap_send_disconn_req(conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
@ -3988,7 +3993,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
char req[64];
if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) {
l2cap_send_disconn_req(conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
@ -3997,7 +4002,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
req, &result);
if (len < 0) {
l2cap_send_disconn_req(conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
@ -4013,7 +4018,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
l2cap_chan_set_err(chan, ECONNRESET);
__set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
l2cap_send_disconn_req(conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
@ -4030,7 +4035,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
err = l2cap_ertm_init(chan);
if (err < 0)
l2cap_send_disconn_req(chan->conn, chan, -err);
l2cap_send_disconn_req(chan, -err);
else
l2cap_chan_ready(chan);
}
@ -4392,7 +4397,7 @@ static void l2cap_logical_fail(struct l2cap_chan *chan)
/* Logical link setup failed */
if (chan->state != BT_CONNECTED) {
/* Create channel failure, disconnect */
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@ -4435,7 +4440,7 @@ static void l2cap_logical_finish_create(struct l2cap_chan *chan,
err = l2cap_ertm_init(chan);
if (err < 0)
l2cap_send_disconn_req(chan->conn, chan, -err);
l2cap_send_disconn_req(chan, -err);
else
l2cap_chan_ready(chan);
}
@ -5400,7 +5405,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan,
if (control->reqseq == chan->next_tx_seq) {
BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@ -5414,7 +5419,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan,
if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@ -5458,7 +5463,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan,
if (control->reqseq == chan->next_tx_seq) {
BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@ -5467,7 +5472,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan,
if (chan->max_tx && skb &&
bt_cb(skb)->control.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@ -5651,8 +5656,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
break;
case L2CAP_TXSEQ_INVALID:
default:
l2cap_send_disconn_req(chan->conn, chan,
ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
break;
}
break;
@ -5785,8 +5789,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
break;
case L2CAP_TXSEQ_INVALID:
default:
l2cap_send_disconn_req(chan->conn, chan,
ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
break;
}
break;
@ -5981,7 +5984,7 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
BT_DBG("Invalid reqseq %d (next_tx_seq %d, expected_ack_seq %d",
control->reqseq, chan->next_tx_seq,
chan->expected_ack_seq);
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
}
return err;
@ -6050,7 +6053,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
len -= L2CAP_FCS_SIZE;
if (len > chan->mps) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
goto drop;
}
@ -6075,8 +6078,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
}
if (err)
l2cap_send_disconn_req(chan->conn, chan,
ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
} else {
const u8 rx_func_to_event[4] = {
L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ,
@ -6093,7 +6095,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
if (len != 0) {
BT_ERR("Trailing bytes: %d in sframe", len);
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
goto drop;
}
@ -6104,7 +6106,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
event = rx_func_to_event[control->super];
if (l2cap_rx(chan, control, skb, event))
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
l2cap_send_disconn_req(chan, ECONNRESET);
}
return 0;

View File

@ -1226,7 +1226,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
}
val = !!cp->val;
enabled = !!lmp_host_le_capable(hdev);
enabled = lmp_host_le_capable(hdev);
if (!hdev_is_powered(hdev) || val == enabled) {
bool changed = false;
@ -1262,7 +1262,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
if (val) {
hci_cp.le = val;
hci_cp.simul = !!lmp_le_br_capable(hdev);
hci_cp.simul = lmp_le_br_capable(hdev);
}
err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
@ -2926,13 +2926,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
struct hci_cp_write_le_host_supported cp;
cp.le = 1;
cp.simul = !!lmp_le_br_capable(hdev);
cp.simul = lmp_le_br_capable(hdev);
/* 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))
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);

View File

@ -467,7 +467,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f
long timeo;
int err = 0;
lock_sock(sk);
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
if (sk->sk_type != SOCK_STREAM) {
err = -EINVAL;
@ -504,7 +504,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);

View File

@ -131,15 +131,6 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
sco_sock_clear_timer(sk);
sco_chan_del(sk, err);
bh_unlock_sock(sk);
sco_conn_lock(conn);
conn->sk = NULL;
sco_pi(sk)->conn = NULL;
sco_conn_unlock(conn);
if (conn->hcon)
hci_conn_put(conn->hcon);
sco_sock_kill(sk);
}
@ -397,6 +388,7 @@ static void sco_sock_init(struct sock *sk, struct sock *parent)
if (parent) {
sk->sk_type = parent->sk_type;
bt_sk(sk)->flags = bt_sk(parent)->flags;
security_sk_clone(parent, sk);
}
}
@ -662,16 +654,57 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
return err;
}
static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len, int flags)
{
struct sock *sk = sock->sk;
struct sco_pinfo *pi = sco_pi(sk);
lock_sock(sk);
if (sk->sk_state == BT_CONNECT2 &&
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
hci_conn_accept(pi->conn->hcon, 0);
sk->sk_state = BT_CONFIG;
release_sock(sk);
return 0;
}
release_sock(sk);
return bt_sock_recvmsg(iocb, sock, msg, len, flags);
}
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err = 0;
u32 opt;
BT_DBG("sk %p", sk);
lock_sock(sk);
switch (optname) {
case BT_DEFER_SETUP:
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
err = -EINVAL;
break;
}
if (get_user(opt, (u32 __user *) optval)) {
err = -EFAULT;
break;
}
if (opt)
set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
else
clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
break;
default:
err = -ENOPROTOOPT;
break;
@ -753,6 +786,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);
switch (optname) {
case BT_DEFER_SETUP:
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
err = -EINVAL;
break;
}
if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
(u32 __user *) optval))
err = -EFAULT;
break;
default:
err = -ENOPROTOOPT;
break;
@ -830,6 +876,16 @@ static void sco_chan_del(struct sock *sk, int err)
BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
if (conn) {
sco_conn_lock(conn);
conn->sk = NULL;
sco_pi(sk)->conn = NULL;
sco_conn_unlock(conn);
if (conn->hcon)
hci_conn_put(conn->hcon);
}
sk->sk_state = BT_CLOSED;
sk->sk_err = err;
sk->sk_state_change(sk);
@ -874,7 +930,10 @@ static void sco_conn_ready(struct sco_conn *conn)
hci_conn_hold(conn->hcon);
__sco_chan_add(conn, sk, parent);
sk->sk_state = BT_CONNECTED;
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
sk->sk_state = BT_CONNECT2;
else
sk->sk_state = BT_CONNECTED;
/* Wake up parent */
parent->sk_data_ready(parent, 1);
@ -887,7 +946,7 @@ done:
}
/* ----- SCO interface with lower layer (HCI) ----- */
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
{
struct sock *sk;
struct hlist_node *node;
@ -904,6 +963,9 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
lm |= HCI_LM_ACCEPT;
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
*flags |= HCI_PROTO_DEFER;
break;
}
}
@ -992,7 +1054,7 @@ static const struct proto_ops sco_sock_ops = {
.accept = sco_sock_accept,
.getname = sco_sock_getname,
.sendmsg = sco_sock_sendmsg,
.recvmsg = bt_sock_recvmsg,
.recvmsg = sco_sock_recvmsg,
.poll = bt_sock_poll,
.ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,