Re: [PATCH] Bluetooth: add support to configure ERTM and Streaming mode

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Marcel,

On Sat, Jul 4, 2009 at 4:41 AM, Gustavo F.
Padovan<gustavo@xxxxxxxxxxxxxxxxx> wrote:
> Add code to config_req and config_rsp to configure ERTM and Streaming
> mode. If the remote devices specifies ERTM or Streaming Mode, we propose
> the same mode received. Otherwise we propose ERTM or Basic Mode.
> And if we are a state 2 device the remote should propose the same mode
> that we sent, if not, the channel is disconnected.
>
> Signed-off-by: Gustavo F. Padovan <gustavo@xxxxxxxxxxxxxxxxx>
> ---
>  include/net/bluetooth/l2cap.h |   28 +++-
>  net/bluetooth/l2cap.c         |  284 +++++++++++++++++++++++++++++++++++------
>  2 files changed, 268 insertions(+), 44 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 06b072f..6fc7698 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -27,8 +27,9 @@
>
>  /* L2CAP defaults */
>  #define L2CAP_DEFAULT_MTU              672
> +#define L2CAP_DEFAULT_MIN_MTU          48
>  #define L2CAP_DEFAULT_FLUSH_TO         0xffff
> -#define L2CAP_DEFAULT_RX_WINDOW                1
> +#define L2CAP_DEFAULT_TX_WINDOW                1
>  #define L2CAP_DEFAULT_MAX_RECEIVE      1
>  #define L2CAP_DEFAULT_RETRANS_TO       300    /* 300 milliseconds */
>  #define L2CAP_DEFAULT_MONITOR_TO       1000   /* 1 second */
> @@ -272,6 +273,9 @@ struct l2cap_pinfo {
>        __u16           omtu;
>        __u16           flush_to;
>        __u8            mode;
> +       __u8            num_conf_req;
> +       __u8            num_conf_rsp;
> +
>        __u8            fcs;
>        __u8            sec_level;
>        __u8            role_switch;
> @@ -280,10 +284,15 @@ struct l2cap_pinfo {
>        __u8            conf_req[64];
>        __u8            conf_len;
>        __u8            conf_state;
> -       __u8            conf_retry;
>
>        __u8            ident;
>
> +       __u8            remote_tx_win;
> +       __u8            remote_max_tx;
> +       __u16           retrans_timeout;
> +       __u16           monitor_timeout;
> +       __u16           max_pdu_size;
> +
>        __le16          sport;
>
>        struct l2cap_conn       *conn;
> @@ -291,12 +300,17 @@ struct l2cap_pinfo {
>        struct sock             *prev_c;
>  };
>
> -#define L2CAP_CONF_REQ_SENT    0x01
> -#define L2CAP_CONF_INPUT_DONE  0x02
> -#define L2CAP_CONF_OUTPUT_DONE 0x04
> -#define L2CAP_CONF_CONNECT_PEND        0x80
> +#define L2CAP_CONF_REQ_SENT       0x01
> +#define L2CAP_CONF_INPUT_DONE     0x02
> +#define L2CAP_CONF_OUTPUT_DONE    0x04
> +#define L2CAP_CONF_MTU_DONE       0x08
> +#define L2CAP_CONF_MODE_DONE      0x10
> +#define L2CAP_CONF_CONNECT_PEND   0x20
> +#define L2CAP_CONF_STATE2_DEVICE  0x80
> +
> +#define L2CAP_CONF_MAX_CONF_REQ 2
> +#define L2CAP_CONF_MAX_CONF_RSP 2
>
> -#define L2CAP_CONF_MAX_RETRIES 2
>
>  void l2cap_load(void);
>
> diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
> index 7ce1a24..a78277a 100644
> --- a/net/bluetooth/l2cap.c
> +++ b/net/bluetooth/l2cap.c
> @@ -966,6 +966,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
>        case L2CAP_MODE_BASIC:
>                break;
>        case L2CAP_MODE_ERTM:
> +       case L2CAP_MODE_STREAMING:
>                if (enable_ertm)
>                        break;
>                /* fall through */
> @@ -1029,6 +1030,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
>        case L2CAP_MODE_BASIC:
>                break;
>        case L2CAP_MODE_ERTM:
> +       case L2CAP_MODE_STREAMING:
>                if (enable_ertm)
>                        break;
>                /* fall through */
> @@ -1739,15 +1741,65 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
>        *ptr += L2CAP_CONF_OPT_SIZE + len;
>  }
>
> -static int l2cap_build_conf_req(struct sock *sk, void *data)
> +static int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
> +{
> +       u32 local_feat_mask = l2cap_feat_mask;
> +       if (enable_ertm)
> +               local_feat_mask |= L2CAP_FEAT_ERTM;
> +
> +       switch (mode) {
> +       case L2CAP_MODE_ERTM:
> +               return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask;
> +       case L2CAP_MODE_STREAMING:
> +               return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask;
> +       default:
> +               return 0x00;
> +       }
> +}
> +
> +static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
> +{
> +       switch (mode) {
> +       case L2CAP_MODE_STREAMING:
> +       case L2CAP_MODE_ERTM:
> +               if (l2cap_mode_supported(mode, remote_feat_mask))
> +                       return mode;
> +               /* fall through */
> +       default:
> +               return L2CAP_MODE_BASIC;
> +       }
> +}
> +
> +static int l2cap_build_conf_req(struct sock *sk, struct l2cap_conn *conn, void *data)
>  {
>        struct l2cap_pinfo *pi = l2cap_pi(sk);
>        struct l2cap_conf_req *req = data;
> -       struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
> +       struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_ERTM };
>        void *ptr = req->data;
>
>        BT_DBG("sk %p", sk);
>
> +       if (pi->num_conf_req || pi->num_conf_rsp)
> +               goto done;
> +
> +       switch (pi->mode) {
> +       case L2CAP_MODE_STREAMING:
> +       case L2CAP_MODE_ERTM:
> +               pi->conf_state |= L2CAP_CONF_STATE2_DEVICE;
> +               if (!l2cap_mode_supported(pi->mode, conn->feat_mask)) {
> +                       struct l2cap_disconn_req req;
> +                       req.dcid = cpu_to_le16(pi->dcid);
> +                       req.scid = cpu_to_le16(pi->scid);
> +                       l2cap_send_cmd(conn, l2cap_get_ident(conn),
> +                               L2CAP_DISCONN_REQ, sizeof(req), &req);
> +               }
> +               break;
> +       default:
> +               pi->mode = l2cap_select_mode(rfc.mode, conn->feat_mask);
> +               break;
> +       }
> +
> +done:
>        switch (pi->mode) {
>        case L2CAP_MODE_BASIC:
>                if (pi->imtu != L2CAP_DEFAULT_MTU)
> @@ -1756,14 +1808,26 @@ static int l2cap_build_conf_req(struct sock *sk, void *data)
>
>        case L2CAP_MODE_ERTM:
>                rfc.mode            = L2CAP_MODE_ERTM;
> -               rfc.txwin_size      = L2CAP_DEFAULT_RX_WINDOW;
> +               rfc.txwin_size      = L2CAP_DEFAULT_TX_WINDOW;
>                rfc.max_transmit    = L2CAP_DEFAULT_MAX_RECEIVE;
> -               rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
> -               rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
> +               rfc.retrans_timeout = 0;
> +               rfc.monitor_timeout = 0;
>                rfc.max_pdu_size    = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU);
>
> -               l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
> -                                       sizeof(rfc), (unsigned long) &rfc);
> +               l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
> +                               (unsigned long) &rfc);
> +               break;
> +
> +       case L2CAP_MODE_STREAMING:
> +               rfc.mode            = L2CAP_MODE_STREAMING;
> +               rfc.txwin_size      = 0;
> +               rfc.max_transmit    = 0;
> +               rfc.retrans_timeout = 0;
> +               rfc.monitor_timeout = 0;
> +               rfc.max_pdu_size    = cpu_to_le16(L2CAP_DEFAULT_MAX_RX_APDU);
> +
> +               l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
> +                               (unsigned long) &rfc);
>                break;
>        }
>
> @@ -1777,7 +1841,7 @@ static int l2cap_build_conf_req(struct sock *sk, void *data)
>        return ptr - data;
>  }
>
> -static int l2cap_parse_conf_req(struct sock *sk, void *data)
> +static int l2cap_parse_conf_req(struct sock *sk, struct l2cap_conn *conn, void *data, int *disconnect)


I noted that l2cap_parse_conf_req and l2cap_parse_conf_rsp never
return negative values ( ptr - data is always positive), so I think we
can add a return -Eerror(ECONNREFUSED?) to raise disconnect request.
That way we don't need the extra parameter disconnect.


>  {
>        struct l2cap_pinfo *pi = l2cap_pi(sk);
>        struct l2cap_conf_rsp *rsp = data;
> @@ -1825,30 +1889,83 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
>                }
>        }
>
> +       if (pi->num_conf_rsp || pi->num_conf_req)
> +               goto done;
> +
> +       switch (pi->mode) {
> +       case L2CAP_MODE_STREAMING:
> +       case L2CAP_MODE_ERTM:
> +               pi->conf_state |= L2CAP_CONF_STATE2_DEVICE;
> +               if (!l2cap_mode_supported(pi->mode, conn->feat_mask))
> +                       *disconnect = 1;
> +               break;
> +       default:
> +               pi->mode = l2cap_select_mode(rfc.mode, conn->feat_mask);
> +               break;
> +       }
> +
> +done:
> +       if (pi->mode != rfc.mode) {
> +               result = L2CAP_CONF_UNACCEPT;
> +               rfc.mode = pi->mode;
> +
> +               if (pi->num_conf_rsp == 1)
> +                       *disconnect = 1;
> +               else
> +                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
> +                               sizeof(rfc), (unsigned long) &rfc);
> +       }
> +
> +
>        if (result == L2CAP_CONF_SUCCESS) {
>                /* Configure output options and let the other side know
>                 * which ones we don't like. */
>
> -               if (rfc.mode == L2CAP_MODE_BASIC) {
> -                       if (mtu < pi->omtu)
> -                               result = L2CAP_CONF_UNACCEPT;
> -                       else {
> -                               pi->omtu = mtu;
> -                               pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;
> -                       }
> +               if (mtu < L2CAP_DEFAULT_MIN_MTU)
> +                       result = L2CAP_CONF_UNACCEPT;
> +               else {
> +                       pi->omtu = mtu;
> +                       pi->conf_state |= L2CAP_CONF_MTU_DONE;
> +               }
> +               l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
>
> -                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
> -               } else {
> +               switch (rfc.mode) {
> +               case L2CAP_MODE_BASIC:
> +                       pi->fcs = L2CAP_FCS_NONE;
> +                       pi->conf_state |= L2CAP_CONF_MODE_DONE;
> +                       break;
> +
> +               case L2CAP_MODE_ERTM:
> +                       pi->remote_tx_win = rfc.txwin_size;
> +                       pi->remote_max_tx = rfc.max_transmit;
> +                       pi->max_pdu_size = rfc.max_pdu_size;
> +
> +                       rfc.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
> +                       rfc.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
> +
> +                       pi->conf_state |= L2CAP_CONF_MODE_DONE;
> +                       break;
> +
> +               case L2CAP_MODE_STREAMING:
> +                       pi->remote_tx_win = rfc.txwin_size;
> +                       pi->max_pdu_size = rfc.max_pdu_size;
> +
> +                       pi->conf_state |= L2CAP_CONF_MODE_DONE;
> +                       break;
> +
> +               default:
>                        result = L2CAP_CONF_UNACCEPT;
>
>                        memset(&rfc, 0, sizeof(rfc));
> -                       rfc.mode = L2CAP_MODE_BASIC;
> -
> -                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
> -                                       sizeof(rfc), (unsigned long) &rfc);
> +                       rfc.mode = pi->mode;
>                }
> -       }
>
> +               l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
> +                               sizeof(rfc), (unsigned long) &rfc);
> +
> +               if (result == L2CAP_CONF_SUCCESS)
> +                       pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;
> +       }
>        rsp->scid   = cpu_to_le16(pi->dcid);
>        rsp->result = cpu_to_le16(result);
>        rsp->flags  = cpu_to_le16(0x0000);
> @@ -1856,6 +1973,73 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
>        return ptr - data;
>  }
>
> +static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, u16 *result, int *disconnect)
> +{
> +       struct l2cap_pinfo *pi = l2cap_pi(sk);
> +       struct l2cap_conf_req *req = data;
> +       void *ptr = req->data;
> +       int type, olen;
> +       unsigned long val;
> +       struct l2cap_conf_rfc rfc;
> +
> +       BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data);
> +
> +       while (len >= L2CAP_CONF_OPT_SIZE) {
> +               len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
> +
> +               switch (type) {
> +               case L2CAP_CONF_MTU:
> +                       if (val < L2CAP_DEFAULT_MIN_MTU) {
> +                               *result = L2CAP_CONF_UNACCEPT;
> +                               pi->omtu = L2CAP_DEFAULT_MIN_MTU;
> +                       } else
> +                               pi->omtu = val;
> +                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
> +                       break;
> +
> +               case L2CAP_CONF_FLUSH_TO:
> +                       pi->flush_to = val;
> +                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2,
> +                                               pi->flush_to);
> +                       break;
> +
> +               case L2CAP_CONF_RFC:
> +                       if (olen == sizeof(rfc))
> +                               memcpy(&rfc, (void *)val, olen);
> +
> +                       if (rfc.mode != pi->mode &&
> +                               (pi->conf_state & L2CAP_CONF_STATE2_DEVICE))
> +                               *disconnect = 1;
> +
> +                       pi->mode = rfc.mode;
> +                       pi->fcs = 0;
> +
> +                       l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
> +                               sizeof(rfc), (unsigned long) &rfc);
> +                       break;
> +               }
> +       }
> +
> +       if (*result == L2CAP_CONF_SUCCESS) {
> +               switch (rfc.mode) {
> +               case L2CAP_MODE_ERTM:
> +                       pi->remote_tx_win   = rfc.txwin_size;
> +                       pi->retrans_timeout = rfc.retrans_timeout;
> +                       pi->monitor_timeout = rfc.monitor_timeout;
> +                       pi->max_pdu_size    = le16_to_cpu(rfc.max_pdu_size);
> +                       break;
> +               case L2CAP_MODE_STREAMING:
> +                       pi->max_pdu_size    = le16_to_cpu(rfc.max_pdu_size);
> +                       break;
> +               }
> +       }
> +
> +       req->dcid   = cpu_to_le16(pi->dcid);
> +       req->flags  = cpu_to_le16(0x0000);
> +
> +       return ptr - data;
> +}
> +
>  static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags)
>  {
>        struct l2cap_conf_rsp *rsp = data;
> @@ -2041,7 +2225,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
>                l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
>
>                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
> -                                       l2cap_build_conf_req(sk, req), req);
> +                                       l2cap_build_conf_req(sk, conn, req), req);
> +               l2cap_pi(sk)->num_conf_req++;
>                break;
>
>        case L2CAP_CR_PEND:
> @@ -2063,7 +2248,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
>        u16 dcid, flags;
>        u8 rsp[64];
>        struct sock *sk;
> -       int len;
> +       int len, disconn = 0;
>
>        dcid  = __le16_to_cpu(req->dcid);
>        flags = __le16_to_cpu(req->flags);
> @@ -2099,11 +2284,21 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
>        }
>
>        /* Complete config. */
> -       len = l2cap_parse_conf_req(sk, rsp);
> +       len = l2cap_parse_conf_req(sk, conn, rsp, &disconn);
>        if (len < 0)
>                goto unlock;
>
> -       l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
> +       if (disconn) {
> +               struct l2cap_disconn_req req;
> +               req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
> +               req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
> +               l2cap_send_cmd(conn, l2cap_get_ident(conn),
> +                               L2CAP_DISCONN_REQ, sizeof(req), &req);
> +               goto unlock;
> +       } else {
> +               l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
> +               l2cap_pi(sk)->num_conf_rsp++;
> +       }
>
>        /* Reset config buffer. */
>        l2cap_pi(sk)->conf_len = 0;
> @@ -2120,7 +2315,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
>        if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
>                u8 buf[64];
>                l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
> -                                       l2cap_build_conf_req(sk, buf), buf);
> +                                       l2cap_build_conf_req(sk, conn, buf), buf);
> +               l2cap_pi(sk)->num_conf_req++;
>        }
>
>  unlock:
> @@ -2133,6 +2329,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
>        struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
>        u16 scid, flags, result;
>        struct sock *sk;
> +       int disconn = 0;
>
>        scid   = __le16_to_cpu(rsp->scid);
>        flags  = __le16_to_cpu(rsp->flags);
> @@ -2150,16 +2347,29 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
>                break;
>
>        case L2CAP_CONF_UNACCEPT:
> -               if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {
> -                       char req[128];
> -                       /* It does not make sense to adjust L2CAP parameters
> -                        * that are currently defined in the spec. We simply
> -                        * resend config request that we sent earlier. It is
> -                        * stupid, but it helps qualification testing which
> -                        * expects at least some response from us. */
> -                       l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
> -                                               l2cap_build_conf_req(sk, req), req);
> -                       goto done;
> +               if (l2cap_pi(sk)->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
> +                       int len = cmd->len - sizeof(*rsp);
> +                       char req[64];
> +
> +                       /* throw out any old conf requests we stored */
> +                       result = L2CAP_CONF_SUCCESS;
> +                       len = l2cap_parse_conf_rsp(sk, rsp->data, len, req,
> +                                       &result, &disconn);
> +                       if (disconn) {
> +                               struct l2cap_disconn_req req;
> +                               req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
> +                               req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
> +                               l2cap_send_cmd(conn, l2cap_get_ident(conn),
> +                                       L2CAP_DISCONN_REQ, sizeof(req), &req);
> +                               goto done;
> +                       }
> +
> +                       l2cap_send_cmd(conn, l2cap_get_ident(conn),
> +                                               L2CAP_CONF_REQ, len, req);
> +                       l2cap_pi(sk)->num_conf_req++;
> +                       if (result != L2CAP_CONF_SUCCESS)
> +                               goto done;
> +                       break;
>                }
>
>        default:
> --
> 1.6.0.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



-- 
Gustavo F. Padovan
http://padovan.org
--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux