Commit 38319713 authored by Johan Hedberg's avatar Johan Hedberg Committed by Marcel Holtmann

Bluetooth: Add LE L2CAP flow control mode

The LE connection oriented channels have their own mode with its own
data transfer rules. In order to implement this properly we need to
distinguish L2CAP channels operating in this mode from other modes.
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent b5ecba64
...@@ -328,6 +328,12 @@ struct l2cap_conf_rfc { ...@@ -328,6 +328,12 @@ struct l2cap_conf_rfc {
#define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_ERTM 0x03
#define L2CAP_MODE_STREAMING 0x04 #define L2CAP_MODE_STREAMING 0x04
/* Unlike the above this one doesn't actually map to anything that would
* ever be sent over the air. Therefore, use a value that's unlikely to
* ever be used in the BR/EDR configuration phase.
*/
#define L2CAP_MODE_LE_FLOWCTL 0x80
struct l2cap_conf_efs { struct l2cap_conf_efs {
__u8 id; __u8 id;
__u8 stype; __u8 stype;
...@@ -861,6 +867,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, ...@@ -861,6 +867,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
void l2cap_chan_busy(struct l2cap_chan *chan, int busy); void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
int l2cap_chan_check_security(struct l2cap_chan *chan); int l2cap_chan_check_security(struct l2cap_chan *chan);
void l2cap_chan_set_defaults(struct l2cap_chan *chan); void l2cap_chan_set_defaults(struct l2cap_chan *chan);
void l2cap_le_flowctl_init(struct l2cap_chan *chan);
int l2cap_ertm_init(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan);
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
......
...@@ -490,6 +490,13 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) ...@@ -490,6 +490,13 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
set_bit(FLAG_FORCE_ACTIVE, &chan->flags); set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
} }
void l2cap_le_flowctl_init(struct l2cap_chan *chan)
{
chan->imtu = L2CAP_DEFAULT_MTU;
chan->omtu = L2CAP_LE_MIN_MTU;
chan->mode = L2CAP_MODE_LE_FLOWCTL;
}
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{ {
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
...@@ -597,6 +604,9 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) ...@@ -597,6 +604,9 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
case L2CAP_MODE_BASIC: case L2CAP_MODE_BASIC:
break; break;
case L2CAP_MODE_LE_FLOWCTL:
break;
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
__clear_retrans_timer(chan); __clear_retrans_timer(chan);
__clear_monitor_timer(chan); __clear_monitor_timer(chan);
...@@ -1849,6 +1859,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, ...@@ -1849,6 +1859,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
switch (chan->mode) { switch (chan->mode) {
case L2CAP_MODE_BASIC: case L2CAP_MODE_BASIC:
case L2CAP_MODE_LE_FLOWCTL:
break; break;
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING: case L2CAP_MODE_STREAMING:
...@@ -2530,6 +2541,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, ...@@ -2530,6 +2541,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
switch (chan->mode) { switch (chan->mode) {
case L2CAP_MODE_BASIC: case L2CAP_MODE_BASIC:
case L2CAP_MODE_LE_FLOWCTL:
/* Check outgoing MTU */ /* Check outgoing MTU */
if (len > chan->omtu) if (len > chan->omtu)
return -EMSGSIZE; return -EMSGSIZE;
...@@ -6621,6 +6633,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, ...@@ -6621,6 +6633,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
goto drop; goto drop;
switch (chan->mode) { switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
case L2CAP_MODE_BASIC: case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here /* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable. * which is *bad* because L2CAP has to be reliable.
......
...@@ -130,6 +130,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) ...@@ -130,6 +130,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
bacpy(&chan->src, &la.l2_bdaddr); bacpy(&chan->src, &la.l2_bdaddr);
chan->src_type = la.l2_bdaddr_type; chan->src_type = la.l2_bdaddr_type;
if (chan->psm && bdaddr_type_is_le(chan->src_type))
l2cap_le_flowctl_init(chan);
chan->state = BT_BOUND; chan->state = BT_BOUND;
sk->sk_state = BT_BOUND; sk->sk_state = BT_BOUND;
...@@ -200,6 +203,9 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, ...@@ -200,6 +203,9 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
return -EINVAL; return -EINVAL;
} }
if (chan->psm && bdaddr_type_is_le(chan->src_type))
l2cap_le_flowctl_init(chan);
err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
&la.l2_bdaddr, la.l2_bdaddr_type); &la.l2_bdaddr, la.l2_bdaddr_type);
if (err) if (err)
...@@ -237,6 +243,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) ...@@ -237,6 +243,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
switch (chan->mode) { switch (chan->mode) {
case L2CAP_MODE_BASIC: case L2CAP_MODE_BASIC:
case L2CAP_MODE_LE_FLOWCTL:
break; break;
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING: case L2CAP_MODE_STREAMING:
...@@ -588,6 +595,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, ...@@ -588,6 +595,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
chan->mode = opts.mode; chan->mode = opts.mode;
switch (chan->mode) { switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
break;
case L2CAP_MODE_BASIC: case L2CAP_MODE_BASIC:
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
break; break;
...@@ -862,10 +871,16 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -862,10 +871,16 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP,
&bt_sk(sk)->flags)) { &bt_sk(sk)->flags)) {
sk->sk_state = BT_CONFIG; if (bdaddr_type_is_le(pi->chan->src_type)) {
pi->chan->state = BT_CONFIG; sk->sk_state = BT_CONNECTED;
pi->chan->state = BT_CONNECTED;
__l2cap_le_connect_rsp_defer(pi->chan);
} else {
sk->sk_state = BT_CONFIG;
pi->chan->state = BT_CONFIG;
__l2cap_connect_rsp_defer(pi->chan);
}
__l2cap_connect_rsp_defer(pi->chan);
err = 0; err = 0;
goto done; goto done;
} }
......
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