static struct workqueue_struct *_busy_wq;
-struct bt_sock_list l2cap_sk_list = {
- .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
-};
+LIST_HEAD(chan_list);
+DEFINE_RWLOCK(chan_list_lock);
static void l2cap_busy_work(struct work_struct *work);
return c;
}
-static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src)
+static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
{
- struct sock *sk;
- struct hlist_node *node;
- sk_for_each(sk, node, &l2cap_sk_list.head) {
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct l2cap_chan *c;
- if (chan->sport == psm && !bacmp(&bt_sk(sk)->src, src))
+ list_for_each_entry(c, &chan_list, global_l) {
+ if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
goto found;
}
- sk = NULL;
+ c = NULL;
found:
- return sk;
+ return c;
}
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
{
int err;
- write_lock_bh(&l2cap_sk_list.lock);
+ write_lock_bh(&chan_list_lock);
- if (psm && __l2cap_get_sock_by_addr(psm, src)) {
+ if (psm && __l2cap_global_chan_by_addr(psm, src)) {
err = -EADDRINUSE;
goto done;
}
err = -EINVAL;
for (p = 0x1001; p < 0x1100; p += 2)
- if (!__l2cap_get_sock_by_addr(cpu_to_le16(p), src)) {
+ if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) {
chan->psm = cpu_to_le16(p);
chan->sport = cpu_to_le16(p);
err = 0;
}
done:
- write_unlock_bh(&l2cap_sk_list.lock);
+ write_unlock_bh(&chan_list_lock);
return err;
}
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
{
- write_lock_bh(&l2cap_sk_list.lock);
+ write_lock_bh(&chan_list_lock);
chan->scid = scid;
- write_unlock_bh(&l2cap_sk_list.lock);
+ write_unlock_bh(&chan_list_lock);
return 0;
}
return 0;
}
-struct l2cap_chan *l2cap_chan_alloc(struct sock *sk)
+struct l2cap_chan *l2cap_chan_create(struct sock *sk)
{
struct l2cap_chan *chan;
chan->sk = sk;
+ write_lock_bh(&chan_list_lock);
+ list_add(&chan->global_l, &chan_list);
+ write_unlock_bh(&chan_list_lock);
+
return chan;
}
-void l2cap_chan_free(struct l2cap_chan *chan)
+void l2cap_chan_destroy(struct l2cap_chan *chan)
{
+ write_lock_bh(&chan_list_lock);
+ list_del(&chan->global_l);
+ write_unlock_bh(&chan_list_lock);
+
kfree(chan);
}
/* Find socket with cid and source bdaddr.
* Returns closest match, locked.
*/
-static struct sock *l2cap_get_sock_by_scid(int state, __le16 cid, bdaddr_t *src)
+static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdaddr_t *src)
{
- struct sock *sk = NULL, *sk1 = NULL;
- struct hlist_node *node;
+ struct l2cap_chan *c, *c1 = NULL;
- read_lock(&l2cap_sk_list.lock);
+ read_lock(&chan_list_lock);
- sk_for_each(sk, node, &l2cap_sk_list.head) {
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ list_for_each_entry(c, &chan_list, global_l) {
+ struct sock *sk = c->sk;
if (state && sk->sk_state != state)
continue;
- if (chan->scid == cid) {
+ if (c->scid == cid) {
/* Exact match. */
- if (!bacmp(&bt_sk(sk)->src, src))
- break;
+ if (!bacmp(&bt_sk(sk)->src, src)) {
+ read_unlock(&chan_list_lock);
+ return c;
+ }
/* Closest match */
if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
- sk1 = sk;
+ c1 = c;
}
}
- read_unlock(&l2cap_sk_list.lock);
+ read_unlock(&chan_list_lock);
- return node ? sk : sk1;
+ return c1;
}
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
{
struct sock *parent, *sk;
- struct l2cap_chan *chan;
+ struct l2cap_chan *chan, *pchan;
BT_DBG("");
/* Check if we have socket listening on cid */
- parent = l2cap_get_sock_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
+ pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
conn->src);
- if (!parent)
+ if (!pchan)
return;
+ parent = pchan->sk;
+
bh_lock_sock(parent);
/* Check for backlog size */
if (!sk)
goto clean;
- chan = l2cap_chan_alloc(sk);
+ chan = l2cap_chan_create(sk);
if (!chan) {
l2cap_sock_kill(sk);
goto clean;
/* Find socket with psm and source bdaddr.
* Returns closest match.
*/
-static struct sock *l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t *src)
+static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *src)
{
- struct sock *sk = NULL, *sk1 = NULL;
- struct hlist_node *node;
+ struct l2cap_chan *c, *c1 = NULL;
- read_lock(&l2cap_sk_list.lock);
+ read_lock(&chan_list_lock);
- sk_for_each(sk, node, &l2cap_sk_list.head) {
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ list_for_each_entry(c, &chan_list, global_l) {
+ struct sock *sk = c->sk;
if (state && sk->sk_state != state)
continue;
- if (chan->psm == psm) {
+ if (c->psm == psm) {
/* Exact match. */
- if (!bacmp(&bt_sk(sk)->src, src))
- break;
+ if (!bacmp(&bt_sk(sk)->src, src)) {
+ read_unlock_bh(&chan_list_lock);
+ return c;
+ }
/* Closest match */
if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
- sk1 = sk;
+ c1 = c;
}
}
- read_unlock(&l2cap_sk_list.lock);
+ read_unlock(&chan_list_lock);
- return node ? sk : sk1;
+ return c1;
}
int l2cap_chan_connect(struct l2cap_chan *chan)
{
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
- struct l2cap_chan *chan = NULL;
+ struct l2cap_chan *chan = NULL, *pchan;
struct sock *parent, *sk = NULL;
int result, status = L2CAP_CS_NO_INFO;
u16 dcid = 0, scid = __le16_to_cpu(req->scid);
__le16 psm = req->psm;
- BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
+ BT_ERR("psm 0x%2.2x scid 0x%4.4x", psm, scid);
/* Check if we have socket listening on psm */
- parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
- if (!parent) {
+ pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src);
+ if (!pchan) {
result = L2CAP_CR_BAD_PSM;
goto sendresp;
}
+ BT_ERR("%p 0x%2.2x", pchan, pchan->psm);
+
+ parent = pchan->sk;
+
bh_lock_sock(parent);
/* Check if the ACL is secure enough (if not SDP) */
if (!sk)
goto response;
- chan = l2cap_chan_alloc(sk);
+ chan = l2cap_chan_create(sk);
if (!chan) {
l2cap_sock_kill(sk);
goto response;
static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb)
{
struct sock *sk;
+ struct l2cap_chan *chan;
- sk = l2cap_get_sock_by_psm(0, psm, conn->src);
- if (!sk)
+ chan = l2cap_global_chan_by_psm(0, psm, conn->src);
+ if (!chan)
goto drop;
+ sk = chan->sk;
+
bh_lock_sock(sk);
BT_DBG("sk %p, len %d", sk, skb->len);
static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb)
{
struct sock *sk;
+ struct l2cap_chan *chan;
- sk = l2cap_get_sock_by_scid(0, cid, conn->src);
- if (!sk)
+ chan = l2cap_global_chan_by_scid(0, cid, conn->src);
+ if (!chan)
goto drop;
+ sk = chan->sk;
+
bh_lock_sock(sk);
BT_DBG("sk %p, len %d", sk, skb->len);
static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
int exact = 0, lm1 = 0, lm2 = 0;
- register struct sock *sk;
- struct hlist_node *node;
+ struct l2cap_chan *c;
if (type != ACL_LINK)
return -EINVAL;
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
/* Find listening sockets and check their link_mode */
- read_lock(&l2cap_sk_list.lock);
- sk_for_each(sk, node, &l2cap_sk_list.head) {
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ read_lock(&chan_list_lock);
+ list_for_each_entry(c, &chan_list, global_l) {
+ struct sock *sk = c->sk;
if (sk->sk_state != BT_LISTEN)
continue;
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
lm1 |= HCI_LM_ACCEPT;
- if (chan->role_switch)
+ if (c->role_switch)
lm1 |= HCI_LM_MASTER;
exact++;
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
lm2 |= HCI_LM_ACCEPT;
- if (chan->role_switch)
+ if (c->role_switch)
lm2 |= HCI_LM_MASTER;
}
}
- read_unlock(&l2cap_sk_list.lock);
+ read_unlock(&chan_list_lock);
return exact ? lm1 : lm2;
}
static int l2cap_debugfs_show(struct seq_file *f, void *p)
{
- struct sock *sk;
- struct hlist_node *node;
+ struct l2cap_chan *c;
- read_lock_bh(&l2cap_sk_list.lock);
+ read_lock_bh(&chan_list_lock);
- sk_for_each(sk, node, &l2cap_sk_list.head) {
- struct l2cap_pinfo *pi = l2cap_pi(sk);
- struct l2cap_chan *chan = pi->chan;
+ list_for_each_entry(c, &chan_list, global_l) {
+ struct sock *sk = c->sk;
seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
batostr(&bt_sk(sk)->src),
batostr(&bt_sk(sk)->dst),
- sk->sk_state, __le16_to_cpu(chan->psm),
- chan->scid, chan->dcid,
- chan->imtu, chan->omtu, chan->sec_level,
- chan->mode);
+ sk->sk_state, __le16_to_cpu(c->psm),
+ c->scid, c->dcid, c->imtu, c->omtu,
+ c->sec_level, c->mode);
}
- read_unlock_bh(&l2cap_sk_list.lock);
+ read_unlock_bh(&chan_list_lock);
return 0;
}