#include <linux/skbuff.h>
#include <linux/list.h>
#include <linux/device.h>
+#include <linux/uaccess.h>
#include <net/sock.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
-#define VERSION "2.11"
+#define VERSION "2.13"
-static u32 l2cap_feat_mask = 0x0000;
+static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
+static u8 l2cap_fixed_chan[8] = { 0x02, };
static const struct proto_ops l2cap_sock_ops;
struct sock *s;
read_lock(&l->lock);
s = __l2cap_get_chan_by_scid(l, cid);
- if (s) bh_lock_sock(s);
+ if (s)
+ bh_lock_sock(s);
read_unlock(&l->lock);
return s;
}
struct sock *s;
read_lock(&l->lock);
s = __l2cap_get_chan_by_ident(l, ident);
- if (s) bh_lock_sock(s);
+ if (s)
+ bh_lock_sock(s);
read_unlock(&l->lock);
return s;
}
static u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
{
- u16 cid = 0x0040;
+ u16 cid = L2CAP_CID_DYN_START;
- for (; cid < 0xffff; cid++) {
- if(!__l2cap_get_chan_by_scid(l, cid))
+ for (; cid < L2CAP_CID_DYN_END; cid++) {
+ if (!__l2cap_get_chan_by_scid(l, cid))
return cid;
}
{
struct l2cap_chan_list *l = &conn->chan_list;
- BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+ BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
+ l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+
+ conn->disc_reason = 0x13;
l2cap_pi(sk)->conn = conn;
l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
} else if (sk->sk_type == SOCK_DGRAM) {
/* Connectionless socket */
- l2cap_pi(sk)->scid = 0x0002;
- l2cap_pi(sk)->dcid = 0x0002;
+ l2cap_pi(sk)->scid = L2CAP_CID_CONN_LESS;
+ l2cap_pi(sk)->dcid = L2CAP_CID_CONN_LESS;
l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
} else {
/* Raw socket can send/recv signalling messages only */
- l2cap_pi(sk)->scid = 0x0001;
- l2cap_pi(sk)->dcid = 0x0001;
+ l2cap_pi(sk)->scid = L2CAP_CID_SIGNALING;
+ l2cap_pi(sk)->dcid = L2CAP_CID_SIGNALING;
l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
}
static inline int l2cap_check_security(struct sock *sk)
{
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ __u8 auth_type;
+
+ if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) {
+ if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
+ auth_type = HCI_AT_NO_BONDING_MITM;
+ else
+ auth_type = HCI_AT_NO_BONDING;
- return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level);
+ if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
+ l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
+ } else {
+ switch (l2cap_pi(sk)->sec_level) {
+ case BT_SECURITY_HIGH:
+ auth_type = HCI_AT_GENERAL_BONDING_MITM;
+ break;
+ case BT_SECURITY_MEDIUM:
+ auth_type = HCI_AT_GENERAL_BONDING;
+ break;
+ default:
+ auth_type = HCI_AT_NO_BONDING;
+ break;
+ }
+ }
+
+ return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level,
+ auth_type);
}
static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+ if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
+ return;
+
if (l2cap_check_security(sk)) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
{
struct l2cap_conn *conn = (void *) arg;
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
l2cap_conn_start(conn);
spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock);
+ conn->disc_reason = 0x13;
+
return conn;
}
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
- if (conn->rx_skb)
- kfree_skb(conn->rx_skb);
+ kfree_skb(conn->rx_skb);
/* Kill channels */
while ((sk = conn->chan_list.head)) {
struct sock *s;
read_lock(&l2cap_sk_list.lock);
s = __l2cap_get_sock_by_psm(state, psm, src);
- if (s) bh_lock_sock(s);
+ if (s)
+ bh_lock_sock(s);
read_unlock(&l2cap_sk_list.lock);
return s;
}
return 0;
}
-static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
struct sock *sk = sock->sk;
- int err = 0;
+ struct sockaddr_l2 la;
+ int len, err = 0;
- BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
+ BT_DBG("sk %p", sk);
if (!addr || addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
+ memset(&la, 0, sizeof(la));
+ len = min_t(unsigned int, sizeof(la), alen);
+ memcpy(&la, addr, len);
+
+ if (la.l2_cid)
+ return -EINVAL;
+
lock_sock(sk);
if (sk->sk_state != BT_OPEN) {
goto done;
}
- if (la->l2_psm && btohs(la->l2_psm) < 0x1001 &&
+ if (la.l2_psm && __le16_to_cpu(la.l2_psm) < 0x1001 &&
!capable(CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto done;
write_lock_bh(&l2cap_sk_list.lock);
- if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
+ if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) {
err = -EADDRINUSE;
} else {
/* Save source address */
- bacpy(&bt_sk(sk)->src, &la->l2_bdaddr);
- l2cap_pi(sk)->psm = la->l2_psm;
- l2cap_pi(sk)->sport = la->l2_psm;
+ bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
+ l2cap_pi(sk)->psm = la.l2_psm;
+ l2cap_pi(sk)->sport = la.l2_psm;
sk->sk_state = BT_BOUND;
- if (btohs(la->l2_psm) == 0x0001)
+ if (__le16_to_cpu(la.l2_psm) == 0x0001 ||
+ __le16_to_cpu(la.l2_psm) == 0x0003)
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
}
struct hci_conn *hcon;
struct hci_dev *hdev;
__u8 auth_type;
- int err = 0;
+ int err;
- BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
+ BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst),
+ l2cap_pi(sk)->psm);
- if (!(hdev = hci_get_route(dst, src)))
+ hdev = hci_get_route(dst, src);
+ if (!hdev)
return -EHOSTUNREACH;
hci_dev_lock_bh(hdev);
auth_type = HCI_AT_NO_BONDING_MITM;
else
auth_type = HCI_AT_NO_BONDING;
+
+ if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
+ l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
} else {
switch (l2cap_pi(sk)->sec_level) {
case BT_SECURITY_HIGH:
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
{
- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
struct sock *sk = sock->sk;
- int err = 0;
-
- lock_sock(sk);
+ struct sockaddr_l2 la;
+ int len, err = 0;
BT_DBG("sk %p", sk);
- if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
- err = -EINVAL;
- goto done;
- }
+ if (!addr || addr->sa_family != AF_BLUETOOTH)
+ return -EINVAL;
+
+ memset(&la, 0, sizeof(la));
+ len = min_t(unsigned int, sizeof(la), alen);
+ memcpy(&la, addr, len);
+
+ if (la.l2_cid)
+ return -EINVAL;
+
+ lock_sock(sk);
- if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) {
+ if (sk->sk_type == SOCK_SEQPACKET && !la.l2_psm) {
err = -EINVAL;
goto done;
}
- switch(sk->sk_state) {
+ switch (sk->sk_state) {
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
}
/* Set destination address and psm */
- bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr);
- l2cap_pi(sk)->psm = la->l2_psm;
+ bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
+ l2cap_pi(sk)->psm = la.l2_psm;
- if ((err = l2cap_do_connect(sk)))
+ err = l2cap_do_connect(sk);
+ if (err)
goto done;
wait:
write_lock_bh(&l2cap_sk_list.lock);
for (psm = 0x1001; psm < 0x1100; psm += 2)
- if (!__l2cap_get_sock_by_addr(htobs(psm), src)) {
- l2cap_pi(sk)->psm = htobs(psm);
- l2cap_pi(sk)->sport = htobs(psm);
+ if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) {
+ l2cap_pi(sk)->psm = cpu_to_le16(psm);
+ l2cap_pi(sk)->sport = cpu_to_le16(psm);
err = 0;
break;
}
addr->sa_family = AF_BLUETOOTH;
*len = sizeof(struct sockaddr_l2);
- if (peer)
+ if (peer) {
+ la->l2_psm = l2cap_pi(sk)->psm;
bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
- else
+ la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ } else {
+ la->l2_psm = l2cap_pi(sk)->sport;
bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
+ la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid);
+ }
- la->l2_psm = l2cap_pi(sk)->psm;
return 0;
}
{
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
struct sk_buff *skb, **frag;
- int err, hlen, count, sent=0;
+ int err, hlen, count, sent = 0;
struct l2cap_hdr *lh;
BT_DBG("sk %p len %d", sk, len);
frag = &(*frag)->next;
}
-
- if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0)
+ err = hci_send_acl(conn->hcon, skb, 0);
+ if (err < 0)
goto fail;
return sent;
{
struct sock *sk = sock->sk;
struct l2cap_options opts;
- int err = 0, len;
+ int len, err = 0;
u32 opt;
BT_DBG("sk %p", sk);
if (level == SOL_L2CAP)
return l2cap_sock_setsockopt_old(sock, optname, optval, optlen);
+ if (level != SOL_BLUETOOTH)
+ return -ENOPROTOOPT;
+
lock_sock(sk);
switch (optname) {
case BT_SECURITY:
+ if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) {
+ err = -EINVAL;
+ break;
+ }
+
sec.level = BT_SECURITY_LOW;
len = min_t(unsigned int, sizeof(sec), optlen);
if (level == SOL_L2CAP)
return l2cap_sock_getsockopt_old(sock, optname, optval, optlen);
+ if (level != SOL_BLUETOOTH)
+ return -ENOPROTOOPT;
+
if (get_user(len, optlen))
return -EFAULT;
switch (optname) {
case BT_SECURITY:
+ if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) {
+ err = -EINVAL;
+ break;
+ }
+
sec.level = l2cap_pi(sk)->sec_level;
len = min_t(unsigned int, len, sizeof(sec));
{
struct l2cap_chan_list *l = &conn->chan_list;
struct sk_buff *nskb;
- struct sock * sk;
+ struct sock *sk;
BT_DBG("conn %p", conn);
/* Don't send frame to the socket it came from */
if (skb->sk == sk)
continue;
-
- if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
continue;
if (sock_queue_rcv_skb(sk, nskb))
struct l2cap_hdr *lh;
int len, count;
- BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen);
+ BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d",
+ conn, code, ident, dlen);
len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
count = min_t(unsigned int, conn->mtu, len);
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(0x0001);
+ lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING);
cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
cmd->code = code;
while (len >= L2CAP_CONF_OPT_SIZE) {
len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
- hint = type & 0x80;
- type &= 0x7f;
+ hint = type & L2CAP_CONF_HINT;
+ type &= L2CAP_CONF_MASK;
switch (type) {
case L2CAP_CONF_MTU:
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
cmd->ident == conn->info_ident) {
- conn->info_ident = 0;
del_timer(&conn->info_timer);
+
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
l2cap_conn_start(conn);
}
/* Check if the ACL is secure enough (if not SDP) */
if (psm != cpu_to_le16(0x0001) &&
!hci_conn_check_link_mode(conn->hcon)) {
+ conn->disc_reason = 0x05;
result = L2CAP_CR_SEC_BLOCK;
goto response;
}
l2cap_pi(sk)->ident = cmd->ident;
- if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_check_security(sk)) {
if (bt_sk(sk)->defer_setup) {
sk->sk_state = BT_CONNECT2;
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
if (scid) {
- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ sk = l2cap_get_chan_by_scid(&conn->chan_list, scid);
+ if (!sk)
return 0;
} else {
- if (!(sk = l2cap_get_chan_by_ident(&conn->chan_list, cmd->ident)))
+ sk = l2cap_get_chan_by_ident(&conn->chan_list, cmd->ident);
+ if (!sk)
return 0;
}
l2cap_pi(sk)->dcid = dcid;
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+ 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);
break;
case L2CAP_CR_PEND:
+ l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
break;
default:
BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+ sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid);
+ if (!sk)
return -ENOENT;
if (sk->sk_state == BT_DISCONN)
flags = __le16_to_cpu(rsp->flags);
result = __le16_to_cpu(rsp->result);
- BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
+ BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x",
+ scid, flags, result);
- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ sk = l2cap_get_chan_by_scid(&conn->chan_list, scid);
+ if (!sk)
return 0;
switch (result) {
BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+ sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid);
+ if (!sk)
return 0;
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ sk = l2cap_get_chan_by_scid(&conn->chan_list, scid);
+ if (!sk)
return 0;
l2cap_chan_del(sk, 0);
put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data);
l2cap_send_cmd(conn, cmd->ident,
L2CAP_INFO_RSP, sizeof(buf), buf);
+ } else if (type == L2CAP_IT_FIXED_CHAN) {
+ u8 buf[12];
+ struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
+ rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
+ rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
+ memcpy(buf + 4, l2cap_fixed_chan, 8);
+ l2cap_send_cmd(conn, cmd->ident,
+ L2CAP_INFO_RSP, sizeof(buf), buf);
} else {
struct l2cap_info_rsp rsp;
rsp.type = cpu_to_le16(type);
BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
- conn->info_ident = 0;
-
del_timer(&conn->info_timer);
- if (type == L2CAP_IT_FEAT_MASK)
+ if (type == L2CAP_IT_FEAT_MASK) {
conn->feat_mask = get_unaligned_le32(rsp->data);
- l2cap_conn_start(conn);
+ if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
+ struct l2cap_info_req req;
+ req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
+
+ conn->info_ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, conn->info_ident,
+ L2CAP_INFO_REQ, sizeof(req), &req);
+ } else {
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
+ l2cap_conn_start(conn);
+ }
+ } else if (type == L2CAP_IT_FIXED_CHAN) {
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
+ l2cap_conn_start(conn);
+ }
return 0;
}
kfree_skb(skb);
done:
- if (sk) bh_unlock_sock(sk);
+ if (sk)
+ bh_unlock_sock(sk);
return 0;
}
BT_DBG("len %d, cid 0x%4.4x", len, cid);
switch (cid) {
- case 0x0001:
+ case L2CAP_CID_SIGNALING:
l2cap_sig_channel(conn, skb);
break;
- case 0x0002:
+ case L2CAP_CID_CONN_LESS:
psm = get_unaligned((__le16 *) skb->data);
skb_pull(skb, 2);
l2cap_conless_channel(conn, psm, skb);
return 0;
}
-static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason)
+static int l2cap_disconn_ind(struct hci_conn *hcon)
+{
+ struct l2cap_conn *conn = hcon->l2cap_data;
+
+ BT_DBG("hcon %p", hcon);
+
+ if (hcon->type != ACL_LINK || !conn)
+ return 0x13;
+
+ return conn->disc_reason;
+}
+
+static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt)
{
+ if (sk->sk_type != SOCK_SEQPACKET)
+ return;
+
if (encrypt == 0x00) {
if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) {
l2cap_sock_clear_timer(sk);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
+ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) {
+ bh_unlock_sock(sk);
+ continue;
+ }
+
if (!status && (sk->sk_state == BT_CONNECTED ||
sk->sk_state == BT_CONFIG)) {
l2cap_check_encryption(sk, encrypt);
}
/* Allocate skb for the complete frame (with header) */
- if (!(conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC)))
+ conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!conn->rx_skb)
goto drop;
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
- skb->len);
+ skb->len);
conn->rx_len = len - skb->len;
} else {
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
}
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
- skb->len);
+ skb->len);
conn->rx_len -= skb->len;
if (!conn->rx_len) {
str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
- sk->sk_state, btohs(pi->psm), pi->scid, pi->dcid,
- pi->imtu, pi->omtu, pi->sec_level);
+ sk->sk_state, __le16_to_cpu(pi->psm), pi->scid,
+ pi->dcid, pi->imtu, pi->omtu, pi->sec_level);
}
read_unlock_bh(&l2cap_sk_list.lock);
- return (str - buf);
+ return str - buf;
}
static CLASS_ATTR(l2cap, S_IRUGO, l2cap_sysfs_show, NULL);
.connect_ind = l2cap_connect_ind,
.connect_cfm = l2cap_connect_cfm,
.disconn_ind = l2cap_disconn_ind,
+ .disconn_cfm = l2cap_disconn_cfm,
.security_cfm = l2cap_security_cfm,
.recv_acldata = l2cap_recv_acldata
};