Bluetooth: Add LE signaling commands handling
Claudio Takahasi [Fri, 11 Feb 2011 21:28:54 +0000 (19:28 -0200)]
This patch splits the L2CAP command handling function in order to
have a clear separation between the commands related to BR/EDR and
LE. Commands and responses in the LE signaling channel are not being
handled yet, command reject is sent to all received requests. Bluetooth
Core Specification, Volume 3, Part A, section 4 defines the signaling
packets formats and allowed commands/responses over the LE signaling
channel.

Signed-off-by: Claudio Takahasi <claudio.takahasi@openbossa.org>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>

include/net/bluetooth/l2cap.h
net/bluetooth/l2cap_core.c

index 41b3bc5..06f245d 100644 (file)
@@ -89,6 +89,8 @@ struct l2cap_conninfo {
 #define L2CAP_ECHO_RSP         0x09
 #define L2CAP_INFO_REQ         0x0a
 #define L2CAP_INFO_RSP         0x0b
+#define L2CAP_CONN_PARAM_UPDATE_REQ    0x12
+#define L2CAP_CONN_PARAM_UPDATE_RSP    0x13
 
 /* L2CAP feature mask */
 #define L2CAP_FEAT_FLOWCTL     0x00000001
index 3079175..ce781a4 100644 (file)
@@ -1428,7 +1428,11 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
 
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
-       lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING);
+
+       if (conn->hcon->type == LE_LINK)
+               lh->cid = cpu_to_le16(L2CAP_CID_LE_SIGNALING);
+       else
+               lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING);
 
        cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
        cmd->code  = code;
@@ -2497,12 +2501,90 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
        return 0;
 }
 
-static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
+                       struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
+{
+       int err = 0;
+
+       switch (cmd->code) {
+       case L2CAP_COMMAND_REJ:
+               l2cap_command_rej(conn, cmd, data);
+               break;
+
+       case L2CAP_CONN_REQ:
+               err = l2cap_connect_req(conn, cmd, data);
+               break;
+
+       case L2CAP_CONN_RSP:
+               err = l2cap_connect_rsp(conn, cmd, data);
+               break;
+
+       case L2CAP_CONF_REQ:
+               err = l2cap_config_req(conn, cmd, cmd_len, data);
+               break;
+
+       case L2CAP_CONF_RSP:
+               err = l2cap_config_rsp(conn, cmd, data);
+               break;
+
+       case L2CAP_DISCONN_REQ:
+               err = l2cap_disconnect_req(conn, cmd, data);
+               break;
+
+       case L2CAP_DISCONN_RSP:
+               err = l2cap_disconnect_rsp(conn, cmd, data);
+               break;
+
+       case L2CAP_ECHO_REQ:
+               l2cap_send_cmd(conn, cmd->ident, L2CAP_ECHO_RSP, cmd_len, data);
+               break;
+
+       case L2CAP_ECHO_RSP:
+               break;
+
+       case L2CAP_INFO_REQ:
+               err = l2cap_information_req(conn, cmd, data);
+               break;
+
+       case L2CAP_INFO_RSP:
+               err = l2cap_information_rsp(conn, cmd, data);
+               break;
+
+       default:
+               BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code);
+               err = -EINVAL;
+               break;
+       }
+
+       return err;
+}
+
+static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
+                                       struct l2cap_cmd_hdr *cmd, u8 *data)
+{
+       switch (cmd->code) {
+       case L2CAP_COMMAND_REJ:
+               return 0;
+
+       case L2CAP_CONN_PARAM_UPDATE_REQ:
+               return -EINVAL;
+
+       case L2CAP_CONN_PARAM_UPDATE_RSP:
+               return 0;
+
+       default:
+               BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
+               return -EINVAL;
+       }
+}
+
+static inline void l2cap_sig_channel(struct l2cap_conn *conn,
+                                                       struct sk_buff *skb)
 {
        u8 *data = skb->data;
        int len = skb->len;
        struct l2cap_cmd_hdr cmd;
-       int err = 0;
+       int err;
 
        l2cap_raw_recv(conn, skb);
 
@@ -2521,55 +2603,10 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk
                        break;
                }
 
-               switch (cmd.code) {
-               case L2CAP_COMMAND_REJ:
-                       l2cap_command_rej(conn, &cmd, data);
-                       break;
-
-               case L2CAP_CONN_REQ:
-                       err = l2cap_connect_req(conn, &cmd, data);
-                       break;
-
-               case L2CAP_CONN_RSP:
-                       err = l2cap_connect_rsp(conn, &cmd, data);
-                       break;
-
-               case L2CAP_CONF_REQ:
-                       err = l2cap_config_req(conn, &cmd, cmd_len, data);
-                       break;
-
-               case L2CAP_CONF_RSP:
-                       err = l2cap_config_rsp(conn, &cmd, data);
-                       break;
-
-               case L2CAP_DISCONN_REQ:
-                       err = l2cap_disconnect_req(conn, &cmd, data);
-                       break;
-
-               case L2CAP_DISCONN_RSP:
-                       err = l2cap_disconnect_rsp(conn, &cmd, data);
-                       break;
-
-               case L2CAP_ECHO_REQ:
-                       l2cap_send_cmd(conn, cmd.ident, L2CAP_ECHO_RSP, cmd_len, data);
-                       break;
-
-               case L2CAP_ECHO_RSP:
-                       break;
-
-               case L2CAP_INFO_REQ:
-                       err = l2cap_information_req(conn, &cmd, data);
-                       break;
-
-               case L2CAP_INFO_RSP:
-                       err = l2cap_information_rsp(conn, &cmd, data);
-                       break;
-
-               default:
-                       BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
-                       err = -EINVAL;
-                       break;
-               }
+               if (conn->hcon->type == LE_LINK)
+                       err = l2cap_le_sig_cmd(conn, &cmd, data);
+               else
+                       err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
 
                if (err) {
                        struct l2cap_cmd_rej rej;
@@ -3566,6 +3603,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("len %d, cid 0x%4.4x", len, cid);
 
        switch (cid) {
+       case L2CAP_CID_LE_SIGNALING:
        case L2CAP_CID_SIGNALING:
                l2cap_sig_channel(conn, skb);
                break;