Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-next-2.6
This commit is contained in:
commit
15cb309614
@ -62,6 +62,7 @@ static struct usb_device_id ath3k_table[] = {
|
|||||||
|
|
||||||
/* Atheros AR3011 with sflash firmware*/
|
/* Atheros AR3011 with sflash firmware*/
|
||||||
{ USB_DEVICE(0x0CF3, 0x3002) },
|
{ USB_DEVICE(0x0CF3, 0x3002) },
|
||||||
|
{ USB_DEVICE(0x13d3, 0x3304) },
|
||||||
|
|
||||||
/* Atheros AR9285 Malbec with sflash firmware */
|
/* Atheros AR9285 Malbec with sflash firmware */
|
||||||
{ USB_DEVICE(0x03F0, 0x311D) },
|
{ USB_DEVICE(0x03F0, 0x311D) },
|
||||||
|
@ -104,6 +104,7 @@ static struct usb_device_id blacklist_table[] = {
|
|||||||
|
|
||||||
/* Atheros 3011 with sflash firmware */
|
/* Atheros 3011 with sflash firmware */
|
||||||
{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
|
{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
|
||||||
|
{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
|
||||||
|
|
||||||
/* Atheros AR9285 Malbec with sflash firmware */
|
/* Atheros AR9285 Malbec with sflash firmware */
|
||||||
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
|
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
|
||||||
|
@ -422,6 +422,7 @@ void hci_conn_check_pending(struct hci_dev *hdev);
|
|||||||
|
|
||||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type);
|
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type);
|
||||||
int hci_conn_check_link_mode(struct hci_conn *conn);
|
int hci_conn_check_link_mode(struct hci_conn *conn);
|
||||||
|
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
|
||||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
|
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
|
||||||
int hci_conn_change_link_key(struct hci_conn *conn);
|
int hci_conn_change_link_key(struct hci_conn *conn);
|
||||||
int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
|
int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
|
||||||
|
@ -350,6 +350,7 @@ struct l2cap_chan {
|
|||||||
struct list_head srej_l;
|
struct list_head srej_l;
|
||||||
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
struct list_head global_l;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct l2cap_conn {
|
struct l2cap_conn {
|
||||||
@ -441,7 +442,6 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
|
|||||||
#define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START)
|
#define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START)
|
||||||
|
|
||||||
extern int disable_ertm;
|
extern int disable_ertm;
|
||||||
extern struct bt_sock_list l2cap_sk_list;
|
|
||||||
|
|
||||||
int l2cap_init_sockets(void);
|
int l2cap_init_sockets(void);
|
||||||
void l2cap_cleanup_sockets(void);
|
void l2cap_cleanup_sockets(void);
|
||||||
@ -458,6 +458,9 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb);
|
|||||||
void l2cap_streaming_send(struct l2cap_chan *chan);
|
void l2cap_streaming_send(struct l2cap_chan *chan);
|
||||||
int l2cap_ertm_send(struct l2cap_chan *chan);
|
int l2cap_ertm_send(struct l2cap_chan *chan);
|
||||||
|
|
||||||
|
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
|
||||||
|
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid);
|
||||||
|
|
||||||
void l2cap_sock_set_timer(struct sock *sk, long timeout);
|
void l2cap_sock_set_timer(struct sock *sk, long timeout);
|
||||||
void l2cap_sock_clear_timer(struct sock *sk);
|
void l2cap_sock_clear_timer(struct sock *sk);
|
||||||
void __l2cap_sock_close(struct sock *sk, int reason);
|
void __l2cap_sock_close(struct sock *sk, int reason);
|
||||||
@ -466,9 +469,9 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
|||||||
struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||||
int proto, gfp_t prio);
|
int proto, gfp_t prio);
|
||||||
void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err);
|
void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err);
|
||||||
struct l2cap_chan *l2cap_chan_alloc(struct sock *sk);
|
struct l2cap_chan *l2cap_chan_create(struct sock *sk);
|
||||||
void l2cap_chan_del(struct l2cap_chan *chan, int err);
|
void l2cap_chan_del(struct l2cap_chan *chan, int err);
|
||||||
void l2cap_chan_free(struct l2cap_chan *chan);
|
void l2cap_chan_destroy(struct l2cap_chan *chan);
|
||||||
int l2cap_chan_connect(struct l2cap_chan *chan);
|
int l2cap_chan_connect(struct l2cap_chan *chan);
|
||||||
|
|
||||||
#endif /* __L2CAP_H */
|
#endif /* __L2CAP_H */
|
||||||
|
@ -623,6 +623,23 @@ encrypt:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(hci_conn_security);
|
EXPORT_SYMBOL(hci_conn_security);
|
||||||
|
|
||||||
|
/* Check secure link requirement */
|
||||||
|
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level)
|
||||||
|
{
|
||||||
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
|
if (sec_level != BT_SECURITY_HIGH)
|
||||||
|
return 1; /* Accept if non-secure is required */
|
||||||
|
|
||||||
|
if (conn->key_type == HCI_LK_AUTH_COMBINATION ||
|
||||||
|
(conn->key_type == HCI_LK_COMBINATION &&
|
||||||
|
conn->pin_length == 16))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0; /* Reject not secure link */
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(hci_conn_check_secure);
|
||||||
|
|
||||||
/* Change link key */
|
/* Change link key */
|
||||||
int hci_conn_change_link_key(struct hci_conn *conn)
|
int hci_conn_change_link_key(struct hci_conn *conn)
|
||||||
{
|
{
|
||||||
|
@ -1440,7 +1440,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
|||||||
|
|
||||||
conn->state = BT_CLOSED;
|
conn->state = BT_CLOSED;
|
||||||
|
|
||||||
if (conn->type == ACL_LINK)
|
if (conn->type == ACL_LINK || conn->type == LE_LINK)
|
||||||
mgmt_disconnected(hdev->id, &conn->dst);
|
mgmt_disconnected(hdev->id, &conn->dst);
|
||||||
|
|
||||||
hci_proto_disconn_cfm(conn, ev->reason);
|
hci_proto_disconn_cfm(conn, ev->reason);
|
||||||
@ -2659,12 +2659,15 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ev->status) {
|
if (ev->status) {
|
||||||
|
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
|
||||||
hci_proto_connect_cfm(conn, ev->status);
|
hci_proto_connect_cfm(conn, ev->status);
|
||||||
conn->state = BT_CLOSED;
|
conn->state = BT_CLOSED;
|
||||||
hci_conn_del(conn);
|
hci_conn_del(conn);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mgmt_connected(hdev->id, &ev->bdaddr);
|
||||||
|
|
||||||
conn->handle = __le16_to_cpu(ev->handle);
|
conn->handle = __le16_to_cpu(ev->handle);
|
||||||
conn->state = BT_CONNECTED;
|
conn->state = BT_CONNECTED;
|
||||||
|
|
||||||
|
@ -62,9 +62,8 @@ static u8 l2cap_fixed_chan[8] = { 0x02, };
|
|||||||
|
|
||||||
static struct workqueue_struct *_busy_wq;
|
static struct workqueue_struct *_busy_wq;
|
||||||
|
|
||||||
struct bt_sock_list l2cap_sk_list = {
|
LIST_HEAD(chan_list);
|
||||||
.lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
|
DEFINE_RWLOCK(chan_list_lock);
|
||||||
};
|
|
||||||
|
|
||||||
static void l2cap_busy_work(struct work_struct *work);
|
static void l2cap_busy_work(struct work_struct *work);
|
||||||
|
|
||||||
@ -135,6 +134,64 @@ static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
|
||||||
|
{
|
||||||
|
struct l2cap_chan *c;
|
||||||
|
|
||||||
|
list_for_each_entry(c, &chan_list, global_l) {
|
||||||
|
if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = NULL;
|
||||||
|
found:
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
write_lock_bh(&chan_list_lock);
|
||||||
|
|
||||||
|
if (psm && __l2cap_global_chan_by_addr(psm, src)) {
|
||||||
|
err = -EADDRINUSE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (psm) {
|
||||||
|
chan->psm = psm;
|
||||||
|
chan->sport = psm;
|
||||||
|
err = 0;
|
||||||
|
} else {
|
||||||
|
u16 p;
|
||||||
|
|
||||||
|
err = -EINVAL;
|
||||||
|
for (p = 0x1001; p < 0x1100; p += 2)
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
write_unlock_bh(&chan_list_lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
|
||||||
|
{
|
||||||
|
write_lock_bh(&chan_list_lock);
|
||||||
|
|
||||||
|
chan->scid = scid;
|
||||||
|
|
||||||
|
write_unlock_bh(&chan_list_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
|
static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
|
||||||
{
|
{
|
||||||
u16 cid = L2CAP_CID_DYN_START;
|
u16 cid = L2CAP_CID_DYN_START;
|
||||||
@ -147,7 +204,7 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
|
|||||||
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;
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
@ -157,11 +214,19 @@ struct l2cap_chan *l2cap_chan_alloc(struct sock *sk)
|
|||||||
|
|
||||||
chan->sk = sk;
|
chan->sk = sk;
|
||||||
|
|
||||||
|
write_lock_bh(&chan_list_lock);
|
||||||
|
list_add(&chan->global_l, &chan_list);
|
||||||
|
write_unlock_bh(&chan_list_lock);
|
||||||
|
|
||||||
return chan;
|
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);
|
kfree(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,48 +656,51 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||||||
/* Find socket with cid and source bdaddr.
|
/* Find socket with cid and source bdaddr.
|
||||||
* Returns closest match, locked.
|
* 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 l2cap_chan *c, *c1 = NULL;
|
||||||
struct hlist_node *node;
|
|
||||||
|
|
||||||
read_lock(&l2cap_sk_list.lock);
|
read_lock(&chan_list_lock);
|
||||||
|
|
||||||
sk_for_each(sk, node, &l2cap_sk_list.head) {
|
list_for_each_entry(c, &chan_list, global_l) {
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct sock *sk = c->sk;
|
||||||
|
|
||||||
if (state && sk->sk_state != state)
|
if (state && sk->sk_state != state)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (chan->scid == cid) {
|
if (c->scid == cid) {
|
||||||
/* Exact match. */
|
/* Exact match. */
|
||||||
if (!bacmp(&bt_sk(sk)->src, src))
|
if (!bacmp(&bt_sk(sk)->src, src)) {
|
||||||
break;
|
read_unlock(&chan_list_lock);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
/* Closest match */
|
/* Closest match */
|
||||||
if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
|
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)
|
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
||||||
{
|
{
|
||||||
struct sock *parent, *sk;
|
struct sock *parent, *sk;
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan, *pchan;
|
||||||
|
|
||||||
BT_DBG("");
|
BT_DBG("");
|
||||||
|
|
||||||
/* Check if we have socket listening on cid */
|
/* 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);
|
conn->src);
|
||||||
if (!parent)
|
if (!pchan)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
parent = pchan->sk;
|
||||||
|
|
||||||
bh_lock_sock(parent);
|
bh_lock_sock(parent);
|
||||||
|
|
||||||
/* Check for backlog size */
|
/* Check for backlog size */
|
||||||
@ -645,7 +713,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
|||||||
if (!sk)
|
if (!sk)
|
||||||
goto clean;
|
goto clean;
|
||||||
|
|
||||||
chan = l2cap_chan_alloc(sk);
|
chan = l2cap_chan_create(sk);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
l2cap_sock_kill(sk);
|
l2cap_sock_kill(sk);
|
||||||
goto clean;
|
goto clean;
|
||||||
@ -823,33 +891,34 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *ch
|
|||||||
/* Find socket with psm and source bdaddr.
|
/* Find socket with psm and source bdaddr.
|
||||||
* Returns closest match.
|
* 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 l2cap_chan *c, *c1 = NULL;
|
||||||
struct hlist_node *node;
|
|
||||||
|
|
||||||
read_lock(&l2cap_sk_list.lock);
|
read_lock(&chan_list_lock);
|
||||||
|
|
||||||
sk_for_each(sk, node, &l2cap_sk_list.head) {
|
list_for_each_entry(c, &chan_list, global_l) {
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct sock *sk = c->sk;
|
||||||
|
|
||||||
if (state && sk->sk_state != state)
|
if (state && sk->sk_state != state)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (chan->psm == psm) {
|
if (c->psm == psm) {
|
||||||
/* Exact match. */
|
/* Exact match. */
|
||||||
if (!bacmp(&bt_sk(sk)->src, src))
|
if (!bacmp(&bt_sk(sk)->src, src)) {
|
||||||
break;
|
read_unlock_bh(&chan_list_lock);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
/* Closest match */
|
/* Closest match */
|
||||||
if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
|
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)
|
int l2cap_chan_connect(struct l2cap_chan *chan)
|
||||||
@ -2019,7 +2088,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||||||
{
|
{
|
||||||
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
|
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
|
||||||
struct l2cap_conn_rsp rsp;
|
struct l2cap_conn_rsp rsp;
|
||||||
struct l2cap_chan *chan = NULL;
|
struct l2cap_chan *chan = NULL, *pchan;
|
||||||
struct sock *parent, *sk = NULL;
|
struct sock *parent, *sk = NULL;
|
||||||
int result, status = L2CAP_CS_NO_INFO;
|
int result, status = L2CAP_CS_NO_INFO;
|
||||||
|
|
||||||
@ -2029,12 +2098,14 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||||||
BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
|
BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
|
||||||
|
|
||||||
/* Check if we have socket listening on psm */
|
/* Check if we have socket listening on psm */
|
||||||
parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
|
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src);
|
||||||
if (!parent) {
|
if (!pchan) {
|
||||||
result = L2CAP_CR_BAD_PSM;
|
result = L2CAP_CR_BAD_PSM;
|
||||||
goto sendresp;
|
goto sendresp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent = pchan->sk;
|
||||||
|
|
||||||
bh_lock_sock(parent);
|
bh_lock_sock(parent);
|
||||||
|
|
||||||
/* Check if the ACL is secure enough (if not SDP) */
|
/* Check if the ACL is secure enough (if not SDP) */
|
||||||
@ -2057,7 +2128,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||||||
if (!sk)
|
if (!sk)
|
||||||
goto response;
|
goto response;
|
||||||
|
|
||||||
chan = l2cap_chan_alloc(sk);
|
chan = l2cap_chan_create(sk);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
l2cap_sock_kill(sk);
|
l2cap_sock_kill(sk);
|
||||||
goto response;
|
goto response;
|
||||||
@ -3685,11 +3756,14 @@ done:
|
|||||||
static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb)
|
static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
sk = l2cap_get_sock_by_psm(0, psm, conn->src);
|
chan = l2cap_global_chan_by_psm(0, psm, conn->src);
|
||||||
if (!sk)
|
if (!chan)
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
|
sk = chan->sk;
|
||||||
|
|
||||||
bh_lock_sock(sk);
|
bh_lock_sock(sk);
|
||||||
|
|
||||||
BT_DBG("sk %p, len %d", sk, skb->len);
|
BT_DBG("sk %p, len %d", sk, skb->len);
|
||||||
@ -3715,11 +3789,14 @@ done:
|
|||||||
static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb)
|
static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
sk = l2cap_get_sock_by_scid(0, cid, conn->src);
|
chan = l2cap_global_chan_by_scid(0, cid, conn->src);
|
||||||
if (!sk)
|
if (!chan)
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
|
sk = chan->sk;
|
||||||
|
|
||||||
bh_lock_sock(sk);
|
bh_lock_sock(sk);
|
||||||
|
|
||||||
BT_DBG("sk %p, len %d", sk, skb->len);
|
BT_DBG("sk %p, len %d", sk, skb->len);
|
||||||
@ -3786,8 +3863,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
||||||
{
|
{
|
||||||
int exact = 0, lm1 = 0, lm2 = 0;
|
int exact = 0, lm1 = 0, lm2 = 0;
|
||||||
register struct sock *sk;
|
struct l2cap_chan *c;
|
||||||
struct hlist_node *node;
|
|
||||||
|
|
||||||
if (type != ACL_LINK)
|
if (type != ACL_LINK)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -3795,25 +3871,25 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
|||||||
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
|
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
|
||||||
|
|
||||||
/* Find listening sockets and check their link_mode */
|
/* Find listening sockets and check their link_mode */
|
||||||
read_lock(&l2cap_sk_list.lock);
|
read_lock(&chan_list_lock);
|
||||||
sk_for_each(sk, node, &l2cap_sk_list.head) {
|
list_for_each_entry(c, &chan_list, global_l) {
|
||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct sock *sk = c->sk;
|
||||||
|
|
||||||
if (sk->sk_state != BT_LISTEN)
|
if (sk->sk_state != BT_LISTEN)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
|
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
|
||||||
lm1 |= HCI_LM_ACCEPT;
|
lm1 |= HCI_LM_ACCEPT;
|
||||||
if (chan->role_switch)
|
if (c->role_switch)
|
||||||
lm1 |= HCI_LM_MASTER;
|
lm1 |= HCI_LM_MASTER;
|
||||||
exact++;
|
exact++;
|
||||||
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
|
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
|
||||||
lm2 |= HCI_LM_ACCEPT;
|
lm2 |= HCI_LM_ACCEPT;
|
||||||
if (chan->role_switch)
|
if (c->role_switch)
|
||||||
lm2 |= HCI_LM_MASTER;
|
lm2 |= HCI_LM_MASTER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_unlock(&l2cap_sk_list.lock);
|
read_unlock(&chan_list_lock);
|
||||||
|
|
||||||
return exact ? lm1 : lm2;
|
return exact ? lm1 : lm2;
|
||||||
}
|
}
|
||||||
@ -4066,25 +4142,22 @@ drop:
|
|||||||
|
|
||||||
static int l2cap_debugfs_show(struct seq_file *f, void *p)
|
static int l2cap_debugfs_show(struct seq_file *f, void *p)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct l2cap_chan *c;
|
||||||
struct hlist_node *node;
|
|
||||||
|
|
||||||
read_lock_bh(&l2cap_sk_list.lock);
|
read_lock_bh(&chan_list_lock);
|
||||||
|
|
||||||
sk_for_each(sk, node, &l2cap_sk_list.head) {
|
list_for_each_entry(c, &chan_list, global_l) {
|
||||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
struct sock *sk = c->sk;
|
||||||
struct l2cap_chan *chan = pi->chan;
|
|
||||||
|
|
||||||
seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
|
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)->src),
|
||||||
batostr(&bt_sk(sk)->dst),
|
batostr(&bt_sk(sk)->dst),
|
||||||
sk->sk_state, __le16_to_cpu(chan->psm),
|
sk->sk_state, __le16_to_cpu(c->psm),
|
||||||
chan->scid, chan->dcid,
|
c->scid, c->dcid, c->imtu, c->omtu,
|
||||||
chan->imtu, chan->omtu, chan->sec_level,
|
c->sec_level, c->mode);
|
||||||
chan->mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
read_unlock_bh(&l2cap_sk_list.lock);
|
read_unlock_bh(&chan_list_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -78,22 +78,6 @@ void l2cap_sock_clear_timer(struct sock *sk)
|
|||||||
sk_stop_timer(sk, &sk->sk_timer);
|
sk_stop_timer(sk, &sk->sk_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sock *__l2cap_get_sock_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;
|
|
||||||
|
|
||||||
if (chan->sport == psm && !bacmp(&bt_sk(sk)->src, src))
|
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
|
|
||||||
sk = NULL;
|
|
||||||
found:
|
|
||||||
return sk;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
@ -136,26 +120,20 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write_lock_bh(&l2cap_sk_list.lock);
|
if (la.l2_cid)
|
||||||
|
err = l2cap_add_scid(chan, la.l2_cid);
|
||||||
|
else
|
||||||
|
err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm);
|
||||||
|
|
||||||
if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) {
|
if (err < 0)
|
||||||
err = -EADDRINUSE;
|
goto done;
|
||||||
} else {
|
|
||||||
/* Save source address */
|
|
||||||
bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
|
|
||||||
chan->psm = la.l2_psm;
|
|
||||||
chan->sport = la.l2_psm;
|
|
||||||
sk->sk_state = BT_BOUND;
|
|
||||||
|
|
||||||
if (__le16_to_cpu(la.l2_psm) == 0x0001 ||
|
if (__le16_to_cpu(la.l2_psm) == 0x0001 ||
|
||||||
__le16_to_cpu(la.l2_psm) == 0x0003)
|
__le16_to_cpu(la.l2_psm) == 0x0003)
|
||||||
chan->sec_level = BT_SECURITY_SDP;
|
chan->sec_level = BT_SECURITY_SDP;
|
||||||
}
|
|
||||||
|
|
||||||
if (la.l2_cid)
|
bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
|
||||||
chan->scid = la.l2_cid;
|
sk->sk_state = BT_BOUND;
|
||||||
|
|
||||||
write_unlock_bh(&l2cap_sk_list.lock);
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
@ -278,28 +256,6 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chan->psm && !chan->scid) {
|
|
||||||
bdaddr_t *src = &bt_sk(sk)->src;
|
|
||||||
u16 psm;
|
|
||||||
|
|
||||||
err = -EINVAL;
|
|
||||||
|
|
||||||
write_lock_bh(&l2cap_sk_list.lock);
|
|
||||||
|
|
||||||
for (psm = 0x1001; psm < 0x1100; psm += 2)
|
|
||||||
if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) {
|
|
||||||
chan->psm = cpu_to_le16(psm);
|
|
||||||
chan->sport = cpu_to_le16(psm);
|
|
||||||
err = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_unlock_bh(&l2cap_sk_list.lock);
|
|
||||||
|
|
||||||
if (err < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
sk->sk_max_ack_backlog = backlog;
|
sk->sk_max_ack_backlog = backlog;
|
||||||
sk->sk_ack_backlog = 0;
|
sk->sk_ack_backlog = 0;
|
||||||
sk->sk_state = BT_LISTEN;
|
sk->sk_state = BT_LISTEN;
|
||||||
@ -852,8 +808,7 @@ void l2cap_sock_kill(struct sock *sk)
|
|||||||
|
|
||||||
/* Kill poor orphan */
|
/* Kill poor orphan */
|
||||||
|
|
||||||
l2cap_chan_free(l2cap_pi(sk)->chan);
|
l2cap_chan_destroy(l2cap_pi(sk)->chan);
|
||||||
bt_sock_unlink(&l2cap_sk_list, sk);
|
|
||||||
sock_set_flag(sk, SOCK_DEAD);
|
sock_set_flag(sk, SOCK_DEAD);
|
||||||
sock_put(sk);
|
sock_put(sk);
|
||||||
}
|
}
|
||||||
@ -1069,7 +1024,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g
|
|||||||
|
|
||||||
setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);
|
setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk);
|
||||||
|
|
||||||
bt_sock_link(&l2cap_sk_list, sk);
|
|
||||||
return sk;
|
return sk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1096,7 +1050,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||||||
if (!sk)
|
if (!sk)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
chan = l2cap_chan_alloc(sk);
|
chan = l2cap_chan_create(sk);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
l2cap_sock_kill(sk);
|
l2cap_sock_kill(sk);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1033,6 +1033,9 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
||||||
|
if (!conn)
|
||||||
|
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
|
||||||
|
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN);
|
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN);
|
||||||
goto failed;
|
goto failed;
|
||||||
|
@ -2096,7 +2096,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
|||||||
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
|
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!status)
|
if (!status && hci_conn_check_secure(conn, d->sec_level))
|
||||||
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
|
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
|
||||||
else
|
else
|
||||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||||
|
Loading…
Reference in New Issue
Block a user