Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/holtmann/bluetooth-next-2.6
This commit is contained in:
commit
63748aa8cd
19 changed files with 983 additions and 377 deletions
|
@ -257,8 +257,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
|
|||
|
||||
if (hdr & 0x10) {
|
||||
BT_ERR("%s error in block", data->hdev->name);
|
||||
if (data->reassembly)
|
||||
kfree_skb(data->reassembly);
|
||||
kfree_skb(data->reassembly);
|
||||
data->reassembly = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
|
|
@ -359,9 +359,9 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
|
|||
BT_ERR("Very strange (stat=0x%04x)", stat);
|
||||
} else if ((stat & 0xff) != 0xff) {
|
||||
if (stat & 0x0020) {
|
||||
int stat = bt3c_read(iobase, 0x7002) & 0x10;
|
||||
int status = bt3c_read(iobase, 0x7002) & 0x10;
|
||||
BT_INFO("%s: Antenna %s", info->hdev->name,
|
||||
stat ? "out" : "in");
|
||||
status ? "out" : "in");
|
||||
}
|
||||
if (stat & 0x0001)
|
||||
bt3c_receive(info);
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#define VERSION "0.4"
|
||||
#define VERSION "0.5"
|
||||
|
||||
static int ignore_dga;
|
||||
static int ignore_csr;
|
||||
|
@ -171,6 +171,7 @@ struct btusb_data {
|
|||
|
||||
__u8 cmdreq_type;
|
||||
|
||||
unsigned int sco_num;
|
||||
int isoc_altsetting;
|
||||
int suspend_count;
|
||||
};
|
||||
|
@ -496,11 +497,23 @@ static int btusb_open(struct hci_dev *hdev)
|
|||
return 0;
|
||||
|
||||
err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
|
||||
if (err < 0)
|
||||
goto failed;
|
||||
|
||||
err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
|
||||
if (err < 0) {
|
||||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
||||
usb_kill_anchored_urbs(&data->intr_anchor);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
set_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||
btusb_submit_bulk_urb(hdev, GFP_KERNEL);
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -655,19 +668,10 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
|
|||
|
||||
BT_DBG("%s evt %d", hdev->name, evt);
|
||||
|
||||
if (hdev->conn_hash.acl_num > 0) {
|
||||
if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
|
||||
if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
|
||||
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||
else
|
||||
btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
|
||||
}
|
||||
} else {
|
||||
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||
usb_unlink_anchored_urbs(&data->bulk_anchor);
|
||||
if (hdev->conn_hash.sco_num != data->sco_num) {
|
||||
data->sco_num = hdev->conn_hash.sco_num;
|
||||
schedule_work(&data->work);
|
||||
}
|
||||
|
||||
schedule_work(&data->work);
|
||||
}
|
||||
|
||||
static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
|
||||
|
@ -982,9 +986,11 @@ static int btusb_resume(struct usb_interface *intf)
|
|||
}
|
||||
|
||||
if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
|
||||
if (btusb_submit_bulk_urb(hdev, GFP_NOIO) < 0)
|
||||
err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
|
||||
if (err < 0) {
|
||||
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||
else
|
||||
return err;
|
||||
} else
|
||||
btusb_submit_bulk_urb(hdev, GFP_NOIO);
|
||||
}
|
||||
|
||||
|
|
|
@ -102,8 +102,7 @@ static int h4_close(struct hci_uart *hu)
|
|||
|
||||
skb_queue_purge(&h4->txq);
|
||||
|
||||
if (h4->rx_skb)
|
||||
kfree_skb(h4->rx_skb);
|
||||
kfree_skb(h4->rx_skb);
|
||||
|
||||
hu->priv = NULL;
|
||||
kfree(h4);
|
||||
|
|
|
@ -163,8 +163,7 @@ static int ll_close(struct hci_uart *hu)
|
|||
skb_queue_purge(&ll->tx_wait_q);
|
||||
skb_queue_purge(&ll->txq);
|
||||
|
||||
if (ll->rx_skb)
|
||||
kfree_skb(ll->rx_skb);
|
||||
kfree_skb(ll->rx_skb);
|
||||
|
||||
hu->priv = NULL;
|
||||
|
||||
|
|
|
@ -53,6 +53,17 @@
|
|||
#define SOL_SCO 17
|
||||
#define SOL_RFCOMM 18
|
||||
|
||||
#define BT_SECURITY 4
|
||||
struct bt_security {
|
||||
__u8 level;
|
||||
};
|
||||
#define BT_SECURITY_SDP 0
|
||||
#define BT_SECURITY_LOW 1
|
||||
#define BT_SECURITY_MEDIUM 2
|
||||
#define BT_SECURITY_HIGH 3
|
||||
|
||||
#define BT_DEFER_SETUP 7
|
||||
|
||||
#define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg)
|
||||
#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
|
||||
#define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)
|
||||
|
@ -108,6 +119,7 @@ struct bt_sock {
|
|||
bdaddr_t dst;
|
||||
struct list_head accept_q;
|
||||
struct sock *parent;
|
||||
u32 defer_setup;
|
||||
};
|
||||
|
||||
struct bt_sock_list {
|
||||
|
|
|
@ -133,8 +133,13 @@ enum {
|
|||
#define ESCO_EV3 0x0008
|
||||
#define ESCO_EV4 0x0010
|
||||
#define ESCO_EV5 0x0020
|
||||
#define ESCO_2EV3 0x0040
|
||||
#define ESCO_3EV3 0x0080
|
||||
#define ESCO_2EV5 0x0100
|
||||
#define ESCO_3EV5 0x0200
|
||||
|
||||
#define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3)
|
||||
#define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5)
|
||||
|
||||
/* ACL flags */
|
||||
#define ACL_CONT 0x01
|
||||
|
@ -176,6 +181,9 @@ enum {
|
|||
#define LMP_EV5 0x02
|
||||
|
||||
#define LMP_SNIFF_SUBR 0x02
|
||||
#define LMP_EDR_ESCO_2M 0x20
|
||||
#define LMP_EDR_ESCO_3M 0x40
|
||||
#define LMP_EDR_3S_ESCO 0x80
|
||||
|
||||
#define LMP_SIMPLE_PAIR 0x08
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@ struct hci_conn {
|
|||
__u16 link_policy;
|
||||
__u32 link_mode;
|
||||
__u8 auth_type;
|
||||
__u8 sec_level;
|
||||
__u8 power_save;
|
||||
unsigned long pend;
|
||||
|
||||
|
@ -325,12 +326,11 @@ int hci_conn_del(struct hci_conn *conn);
|
|||
void hci_conn_hash_flush(struct hci_dev *hdev);
|
||||
void hci_conn_check_pending(struct hci_dev *hdev);
|
||||
|
||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __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_auth(struct hci_conn *conn);
|
||||
int hci_conn_encrypt(struct hci_conn *conn);
|
||||
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_switch_role(struct hci_conn *conn, uint8_t role);
|
||||
int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
|
||||
|
||||
void hci_conn_enter_active_mode(struct hci_conn *conn);
|
||||
void hci_conn_enter_sniff_mode(struct hci_conn *conn);
|
||||
|
@ -470,26 +470,26 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
|||
|
||||
/* ----- HCI protocols ----- */
|
||||
struct hci_proto {
|
||||
char *name;
|
||||
char *name;
|
||||
unsigned int id;
|
||||
unsigned long flags;
|
||||
|
||||
void *priv;
|
||||
|
||||
int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
|
||||
int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
|
||||
int (*connect_cfm) (struct hci_conn *conn, __u8 status);
|
||||
int (*disconn_ind) (struct hci_conn *conn, __u8 reason);
|
||||
int (*disconn_ind) (struct hci_conn *conn);
|
||||
int (*disconn_cfm) (struct hci_conn *conn, __u8 reason);
|
||||
int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
|
||||
int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb);
|
||||
int (*auth_cfm) (struct hci_conn *conn, __u8 status);
|
||||
int (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
|
||||
int (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
|
||||
};
|
||||
|
||||
static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
int mask = 0;
|
||||
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->connect_ind)
|
||||
mask |= hp->connect_ind(hdev, bdaddr, type);
|
||||
|
@ -514,30 +514,52 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
|
|||
hp->connect_cfm(conn, status);
|
||||
}
|
||||
|
||||
static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason)
|
||||
static inline int hci_proto_disconn_ind(struct hci_conn *conn)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
int reason = 0x13;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->disconn_ind)
|
||||
reason = hp->disconn_ind(conn);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->disconn_ind)
|
||||
reason = hp->disconn_ind(conn);
|
||||
|
||||
return reason;
|
||||
}
|
||||
|
||||
static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->disconn_ind)
|
||||
hp->disconn_ind(conn, reason);
|
||||
if (hp && hp->disconn_cfm)
|
||||
hp->disconn_cfm(conn, reason);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->disconn_ind)
|
||||
hp->disconn_ind(conn, reason);
|
||||
if (hp && hp->disconn_cfm)
|
||||
hp->disconn_cfm(conn, reason);
|
||||
}
|
||||
|
||||
static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
__u8 encrypt;
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
return;
|
||||
|
||||
encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->auth_cfm)
|
||||
hp->auth_cfm(conn, status);
|
||||
if (hp && hp->security_cfm)
|
||||
hp->security_cfm(conn, status, encrypt);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->auth_cfm)
|
||||
hp->auth_cfm(conn, status);
|
||||
if (hp && hp->security_cfm)
|
||||
hp->security_cfm(conn, status, encrypt);
|
||||
}
|
||||
|
||||
static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt)
|
||||
|
@ -545,12 +567,12 @@ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u
|
|||
register struct hci_proto *hp;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->encrypt_cfm)
|
||||
hp->encrypt_cfm(conn, status, encrypt);
|
||||
if (hp && hp->security_cfm)
|
||||
hp->security_cfm(conn, status, encrypt);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->encrypt_cfm)
|
||||
hp->encrypt_cfm(conn, status, encrypt);
|
||||
if (hp && hp->security_cfm)
|
||||
hp->security_cfm(conn, status, encrypt);
|
||||
}
|
||||
|
||||
int hci_register_proto(struct hci_proto *hproto);
|
||||
|
@ -562,8 +584,7 @@ struct hci_cb {
|
|||
|
||||
char *name;
|
||||
|
||||
void (*auth_cfm) (struct hci_conn *conn, __u8 status);
|
||||
void (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
|
||||
void (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
|
||||
void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
|
||||
void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
|
||||
};
|
||||
|
@ -571,14 +592,20 @@ struct hci_cb {
|
|||
static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
struct list_head *p;
|
||||
__u8 encrypt;
|
||||
|
||||
hci_proto_auth_cfm(conn, status);
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
return;
|
||||
|
||||
encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
|
||||
|
||||
read_lock_bh(&hci_cb_list_lock);
|
||||
list_for_each(p, &hci_cb_list) {
|
||||
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
|
||||
if (cb->auth_cfm)
|
||||
cb->auth_cfm(conn, status);
|
||||
if (cb->security_cfm)
|
||||
cb->security_cfm(conn, status, encrypt);
|
||||
}
|
||||
read_unlock_bh(&hci_cb_list_lock);
|
||||
}
|
||||
|
@ -587,13 +614,16 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encr
|
|||
{
|
||||
struct list_head *p;
|
||||
|
||||
if (conn->sec_level == BT_SECURITY_SDP)
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
|
||||
hci_proto_encrypt_cfm(conn, status, encrypt);
|
||||
|
||||
read_lock_bh(&hci_cb_list_lock);
|
||||
list_for_each(p, &hci_cb_list) {
|
||||
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
|
||||
if (cb->encrypt_cfm)
|
||||
cb->encrypt_cfm(conn, status, encrypt);
|
||||
if (cb->security_cfm)
|
||||
cb->security_cfm(conn, status, encrypt);
|
||||
}
|
||||
read_unlock_bh(&hci_cb_list_lock);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ struct sockaddr_l2 {
|
|||
sa_family_t l2_family;
|
||||
__le16 l2_psm;
|
||||
bdaddr_t l2_bdaddr;
|
||||
__le16 l2_cid;
|
||||
};
|
||||
|
||||
/* L2CAP socket options */
|
||||
|
@ -185,6 +186,7 @@ struct l2cap_info_rsp {
|
|||
/* info type */
|
||||
#define L2CAP_IT_CL_MTU 0x0001
|
||||
#define L2CAP_IT_FEAT_MASK 0x0002
|
||||
#define L2CAP_IT_FIXED_CHAN 0x0003
|
||||
|
||||
/* info result */
|
||||
#define L2CAP_IR_SUCCESS 0x0000
|
||||
|
@ -219,11 +221,14 @@ struct l2cap_conn {
|
|||
__u8 rx_ident;
|
||||
__u8 tx_ident;
|
||||
|
||||
__u8 disc_reason;
|
||||
|
||||
struct l2cap_chan_list chan_list;
|
||||
};
|
||||
|
||||
#define L2CAP_INFO_CL_MTU_REQ_SENT 0x01
|
||||
#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x02
|
||||
#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04
|
||||
#define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08
|
||||
|
||||
/* ----- L2CAP channel and socket info ----- */
|
||||
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
|
||||
|
@ -237,8 +242,9 @@ struct l2cap_pinfo {
|
|||
__u16 imtu;
|
||||
__u16 omtu;
|
||||
__u16 flush_to;
|
||||
|
||||
__u32 link_mode;
|
||||
__u8 sec_level;
|
||||
__u8 role_switch;
|
||||
__u8 force_reliable;
|
||||
|
||||
__u8 conf_req[64];
|
||||
__u8 conf_len;
|
||||
|
@ -257,6 +263,7 @@ struct l2cap_pinfo {
|
|||
#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_MAX_RETRIES 2
|
||||
|
||||
|
|
|
@ -183,8 +183,9 @@ struct rfcomm_dlc {
|
|||
u8 remote_v24_sig;
|
||||
u8 mscex;
|
||||
u8 out;
|
||||
|
||||
u32 link_mode;
|
||||
u8 sec_level;
|
||||
u8 role_switch;
|
||||
u32 defer_setup;
|
||||
|
||||
uint mtu;
|
||||
uint cfc;
|
||||
|
@ -202,10 +203,12 @@ struct rfcomm_dlc {
|
|||
#define RFCOMM_RX_THROTTLED 0
|
||||
#define RFCOMM_TX_THROTTLED 1
|
||||
#define RFCOMM_TIMED_OUT 2
|
||||
#define RFCOMM_MSC_PENDING 3
|
||||
#define RFCOMM_AUTH_PENDING 4
|
||||
#define RFCOMM_AUTH_ACCEPT 5
|
||||
#define RFCOMM_AUTH_REJECT 6
|
||||
#define RFCOMM_MSC_PENDING 3
|
||||
#define RFCOMM_SEC_PENDING 4
|
||||
#define RFCOMM_AUTH_PENDING 5
|
||||
#define RFCOMM_AUTH_ACCEPT 6
|
||||
#define RFCOMM_AUTH_REJECT 7
|
||||
#define RFCOMM_DEFER_SETUP 8
|
||||
|
||||
/* Scheduling flags and events */
|
||||
#define RFCOMM_SCHED_STATE 0
|
||||
|
@ -239,6 +242,7 @@ int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
|
|||
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
|
||||
int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
|
||||
int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
|
||||
void rfcomm_dlc_accept(struct rfcomm_dlc *d);
|
||||
|
||||
#define rfcomm_dlc_lock(d) spin_lock(&d->lock)
|
||||
#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock)
|
||||
|
@ -304,7 +308,8 @@ struct rfcomm_pinfo {
|
|||
struct bt_sock bt;
|
||||
struct rfcomm_dlc *dlc;
|
||||
u8 channel;
|
||||
u32 link_mode;
|
||||
u8 sec_level;
|
||||
u8 role_switch;
|
||||
};
|
||||
|
||||
int rfcomm_init_sockets(void);
|
||||
|
@ -333,7 +338,6 @@ struct rfcomm_dev_req {
|
|||
bdaddr_t src;
|
||||
bdaddr_t dst;
|
||||
u8 channel;
|
||||
|
||||
};
|
||||
|
||||
struct rfcomm_dev_info {
|
||||
|
|
|
@ -41,14 +41,13 @@
|
|||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
#define VERSION "2.14"
|
||||
#define VERSION "2.15"
|
||||
|
||||
/* Bluetooth sockets */
|
||||
#define BT_MAX_PROTO 8
|
||||
static struct net_proto_family *bt_proto[BT_MAX_PROTO];
|
||||
static DEFINE_RWLOCK(bt_proto_lock);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
static struct lock_class_key bt_lock_key[BT_MAX_PROTO];
|
||||
static const char *bt_key_strings[BT_MAX_PROTO] = {
|
||||
"sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP",
|
||||
|
@ -86,11 +85,6 @@ static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
|
|||
bt_slock_key_strings[proto], &bt_slock_key[proto],
|
||||
bt_key_strings[proto], &bt_lock_key[proto]);
|
||||
}
|
||||
#else
|
||||
static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int bt_sock_register(int proto, struct net_proto_family *ops)
|
||||
{
|
||||
|
@ -217,7 +211,8 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (sk->sk_state == BT_CONNECTED || !newsock) {
|
||||
if (sk->sk_state == BT_CONNECTED || !newsock ||
|
||||
bt_sk(parent)->defer_setup) {
|
||||
bt_accept_unlink(sk);
|
||||
if (newsock)
|
||||
sock_graft(sk, newsock);
|
||||
|
@ -232,7 +227,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
|||
EXPORT_SYMBOL(bt_accept_dequeue);
|
||||
|
||||
int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t len, int flags)
|
||||
struct msghdr *msg, size_t len, int flags)
|
||||
{
|
||||
int noblock = flags & MSG_DONTWAIT;
|
||||
struct sock *sk = sock->sk;
|
||||
|
@ -277,7 +272,9 @@ static inline unsigned int bt_accept_poll(struct sock *parent)
|
|||
|
||||
list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
|
||||
sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
|
||||
if (sk->sk_state == BT_CONNECTED)
|
||||
if (sk->sk_state == BT_CONNECTED ||
|
||||
(bt_sk(parent)->defer_setup &&
|
||||
sk->sk_state == BT_CONNECT2))
|
||||
return POLLIN | POLLRDNORM;
|
||||
}
|
||||
|
||||
|
|
|
@ -126,8 +126,7 @@ static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const
|
|||
|
||||
session->reassembly[id] = nskb;
|
||||
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
|
||||
|
|
|
@ -123,6 +123,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle)
|
|||
conn->state = BT_CONNECT;
|
||||
conn->out = 1;
|
||||
|
||||
conn->attempt++;
|
||||
|
||||
cp.handle = cpu_to_le16(handle);
|
||||
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
||||
|
||||
|
@ -139,6 +141,8 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
|||
conn->state = BT_CONNECT;
|
||||
conn->out = 1;
|
||||
|
||||
conn->attempt++;
|
||||
|
||||
cp.handle = cpu_to_le16(handle);
|
||||
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
||||
|
||||
|
@ -155,6 +159,7 @@ static void hci_conn_timeout(unsigned long arg)
|
|||
{
|
||||
struct hci_conn *conn = (void *) arg;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
__u8 reason;
|
||||
|
||||
BT_DBG("conn %p state %d", conn, conn->state);
|
||||
|
||||
|
@ -173,7 +178,8 @@ static void hci_conn_timeout(unsigned long arg)
|
|||
break;
|
||||
case BT_CONFIG:
|
||||
case BT_CONNECTED:
|
||||
hci_acl_disconn(conn, 0x13);
|
||||
reason = hci_proto_disconn_ind(conn);
|
||||
hci_acl_disconn(conn, reason);
|
||||
break;
|
||||
default:
|
||||
conn->state = BT_CLOSED;
|
||||
|
@ -216,12 +222,13 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
|||
break;
|
||||
case SCO_LINK:
|
||||
if (lmp_esco_capable(hdev))
|
||||
conn->pkt_type = hdev->esco_type & SCO_ESCO_MASK;
|
||||
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
|
||||
(hdev->esco_type & EDR_ESCO_MASK);
|
||||
else
|
||||
conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
|
||||
break;
|
||||
case ESCO_LINK:
|
||||
conn->pkt_type = hdev->esco_type;
|
||||
conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -280,6 +287,8 @@ int hci_conn_del(struct hci_conn *conn)
|
|||
|
||||
skb_queue_purge(&conn->data_q);
|
||||
|
||||
hci_conn_del_sysfs(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -325,7 +334,7 @@ EXPORT_SYMBOL(hci_get_route);
|
|||
|
||||
/* Create SCO or ACL connection.
|
||||
* Device _must_ be locked */
|
||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 auth_type)
|
||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
|
||||
{
|
||||
struct hci_conn *acl;
|
||||
struct hci_conn *sco;
|
||||
|
@ -340,6 +349,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
|
|||
hci_conn_hold(acl);
|
||||
|
||||
if (acl->state == BT_OPEN || acl->state == BT_CLOSED) {
|
||||
acl->sec_level = sec_level;
|
||||
acl->auth_type = auth_type;
|
||||
hci_acl_connect(acl);
|
||||
}
|
||||
|
@ -385,51 +395,59 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
|
|||
EXPORT_SYMBOL(hci_conn_check_link_mode);
|
||||
|
||||
/* Authenticate remote device */
|
||||
int hci_conn_auth(struct hci_conn *conn)
|
||||
static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
||||
{
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) {
|
||||
if (!(conn->auth_type & 0x01)) {
|
||||
conn->auth_type |= 0x01;
|
||||
conn->link_mode &= ~HCI_LM_AUTH;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->link_mode & HCI_LM_AUTH)
|
||||
if (sec_level > conn->sec_level)
|
||||
conn->sec_level = sec_level;
|
||||
else if (conn->link_mode & HCI_LM_AUTH)
|
||||
return 1;
|
||||
|
||||
conn->auth_type = auth_type;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
|
||||
struct hci_cp_auth_requested cp;
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_conn_auth);
|
||||
|
||||
/* Enable encryption */
|
||||
int hci_conn_encrypt(struct hci_conn *conn)
|
||||
/* Enable security */
|
||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
||||
{
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (sec_level == BT_SECURITY_SDP)
|
||||
return 1;
|
||||
|
||||
if (sec_level == BT_SECURITY_LOW) {
|
||||
if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0)
|
||||
return hci_conn_auth(conn, sec_level, auth_type);
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (conn->link_mode & HCI_LM_ENCRYPT)
|
||||
return hci_conn_auth(conn);
|
||||
return hci_conn_auth(conn, sec_level, auth_type);
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
return 0;
|
||||
|
||||
if (hci_conn_auth(conn)) {
|
||||
if (hci_conn_auth(conn, sec_level, auth_type)) {
|
||||
struct hci_cp_set_conn_encrypt cp;
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.encrypt = 1;
|
||||
hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_conn_encrypt);
|
||||
EXPORT_SYMBOL(hci_conn_security);
|
||||
|
||||
/* Change link key */
|
||||
int hci_conn_change_link_key(struct hci_conn *conn)
|
||||
|
@ -442,12 +460,13 @@ int hci_conn_change_link_key(struct hci_conn *conn)
|
|||
hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_conn_change_link_key);
|
||||
|
||||
/* Switch role */
|
||||
int hci_conn_switch_role(struct hci_conn *conn, uint8_t role)
|
||||
int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
|
||||
{
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
|
@ -460,6 +479,7 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role)
|
|||
cp.role = role;
|
||||
hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_conn_switch_role);
|
||||
|
@ -542,9 +562,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
|
|||
|
||||
c->state = BT_CLOSED;
|
||||
|
||||
hci_conn_del_sysfs(c);
|
||||
|
||||
hci_proto_disconn_ind(c, 0x16);
|
||||
hci_proto_disconn_cfm(c, 0x16);
|
||||
hci_conn_del(c);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1565,8 +1565,7 @@ static void hci_cmd_task(unsigned long arg)
|
|||
|
||||
/* Send queued commands */
|
||||
if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
|
||||
if (hdev->sent_cmd)
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
|
||||
if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) {
|
||||
atomic_dec(&hdev->cmd_cnt);
|
||||
|
|
|
@ -484,6 +484,15 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb
|
|||
if (hdev->features[4] & LMP_EV5)
|
||||
hdev->esco_type |= (ESCO_EV5);
|
||||
|
||||
if (hdev->features[5] & LMP_EDR_ESCO_2M)
|
||||
hdev->esco_type |= (ESCO_2EV3);
|
||||
|
||||
if (hdev->features[5] & LMP_EDR_ESCO_3M)
|
||||
hdev->esco_type |= (ESCO_3EV3);
|
||||
|
||||
if (hdev->features[5] & LMP_EDR_3S_ESCO)
|
||||
hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5);
|
||||
|
||||
BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name,
|
||||
hdev->features[0], hdev->features[1],
|
||||
hdev->features[2], hdev->features[3],
|
||||
|
@ -914,7 +923,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
|
|||
if (ev->status) {
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
} else if (ev->link_type != ACL_LINK)
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -1009,9 +1019,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
|||
if (conn) {
|
||||
conn->state = BT_CLOSED;
|
||||
|
||||
hci_conn_del_sysfs(conn);
|
||||
|
||||
hci_proto_disconn_ind(conn, ev->reason);
|
||||
hci_proto_disconn_cfm(conn, ev->reason);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
|
||||
|
@ -1600,7 +1608,8 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
|
|||
|
||||
if (conn->state == BT_CONFIG) {
|
||||
if (!ev->status && hdev->ssp_mode > 0 &&
|
||||
conn->ssp_mode > 0 && conn->out) {
|
||||
conn->ssp_mode > 0 && conn->out &&
|
||||
conn->sec_level != BT_SECURITY_SDP) {
|
||||
struct hci_cp_auth_requested cp;
|
||||
cp.handle = ev->handle;
|
||||
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
|
||||
|
@ -1637,6 +1646,13 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
|
|||
conn->type = SCO_LINK;
|
||||
}
|
||||
|
||||
if (conn->out && ev->status == 0x1c && conn->attempt < 2) {
|
||||
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
|
||||
(hdev->esco_type & EDR_ESCO_MASK);
|
||||
hci_setup_sync(conn, conn->link->handle);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!ev->status) {
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
conn->state = BT_CONNECTED;
|
||||
|
|
|
@ -50,9 +50,10 @@
|
|||
#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 = 0x0080;
|
||||
static u8 l2cap_fixed_chan[8] = { 0x02, };
|
||||
|
||||
static const struct proto_ops l2cap_sock_ops;
|
||||
|
||||
|
@ -77,9 +78,10 @@ static void l2cap_sock_timeout(unsigned long arg)
|
|||
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if (sk->sk_state == BT_CONNECT &&
|
||||
(l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH |
|
||||
L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)))
|
||||
if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
|
||||
reason = ECONNREFUSED;
|
||||
else if (sk->sk_state == BT_CONNECT &&
|
||||
l2cap_pi(sk)->sec_level != BT_SECURITY_SDP)
|
||||
reason = ECONNREFUSED;
|
||||
else
|
||||
reason = ETIMEDOUT;
|
||||
|
@ -204,6 +206,8 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
|
|||
|
||||
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;
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
|
@ -259,18 +263,35 @@ static void l2cap_chan_del(struct sock *sk, int err)
|
|||
}
|
||||
|
||||
/* Service level security */
|
||||
static inline int l2cap_check_link_mode(struct sock *sk)
|
||||
static inline int l2cap_check_security(struct sock *sk)
|
||||
{
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
__u8 auth_type;
|
||||
|
||||
if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
|
||||
(l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE))
|
||||
return hci_conn_encrypt(conn->hcon);
|
||||
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;
|
||||
|
||||
if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH)
|
||||
return hci_conn_auth(conn->hcon);
|
||||
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 1;
|
||||
return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level,
|
||||
auth_type);
|
||||
}
|
||||
|
||||
static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
|
||||
|
@ -312,7 +333,10 @@ static void l2cap_do_start(struct sock *sk)
|
|||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
|
||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
|
||||
if (l2cap_check_link_mode(sk)) {
|
||||
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);
|
||||
req.psm = l2cap_pi(sk)->psm;
|
||||
|
@ -356,7 +380,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||
}
|
||||
|
||||
if (sk->sk_state == BT_CONNECT) {
|
||||
if (l2cap_check_link_mode(sk)) {
|
||||
if (l2cap_check_security(sk)) {
|
||||
struct l2cap_conn_req req;
|
||||
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
req.psm = l2cap_pi(sk)->psm;
|
||||
|
@ -371,10 +395,18 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
|
||||
if (l2cap_check_link_mode(sk)) {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
if (l2cap_check_security(sk)) {
|
||||
if (bt_sk(sk)->defer_setup) {
|
||||
struct sock *parent = bt_sk(sk)->parent;
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
|
||||
parent->sk_data_ready(parent, 0);
|
||||
|
||||
} else {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
}
|
||||
} else {
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
|
||||
|
@ -426,7 +458,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
|
|||
read_lock(&l->lock);
|
||||
|
||||
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
|
||||
if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
|
||||
if (l2cap_pi(sk)->force_reliable)
|
||||
sk->sk_err = err;
|
||||
}
|
||||
|
||||
|
@ -437,6 +469,7 @@ static void l2cap_info_timeout(unsigned long arg)
|
|||
{
|
||||
struct l2cap_conn *conn = (void *) arg;
|
||||
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
conn->info_ident = 0;
|
||||
|
||||
l2cap_conn_start(conn);
|
||||
|
@ -470,6 +503,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
|||
spin_lock_init(&conn->lock);
|
||||
rwlock_init(&conn->chan_list.lock);
|
||||
|
||||
conn->disc_reason = 0x13;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
@ -483,8 +518,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|||
|
||||
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)) {
|
||||
|
@ -608,7 +642,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
|
|||
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
case BT_CONNECT2:
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
struct l2cap_disconn_req req;
|
||||
|
@ -624,6 +657,27 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
|
|||
l2cap_chan_del(sk, reason);
|
||||
break;
|
||||
|
||||
case BT_CONNECT2:
|
||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 result;
|
||||
|
||||
if (bt_sk(sk)->defer_setup)
|
||||
result = L2CAP_CR_SEC_BLOCK;
|
||||
else
|
||||
result = L2CAP_CR_BAD_PSM;
|
||||
|
||||
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
rsp.result = cpu_to_le16(result);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
|
||||
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||
} else
|
||||
l2cap_chan_del(sk, reason);
|
||||
break;
|
||||
|
||||
case BT_CONNECT:
|
||||
case BT_DISCONN:
|
||||
l2cap_chan_del(sk, reason);
|
||||
|
@ -653,13 +707,19 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||
|
||||
if (parent) {
|
||||
sk->sk_type = parent->sk_type;
|
||||
bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup;
|
||||
|
||||
pi->imtu = l2cap_pi(parent)->imtu;
|
||||
pi->omtu = l2cap_pi(parent)->omtu;
|
||||
pi->link_mode = l2cap_pi(parent)->link_mode;
|
||||
pi->sec_level = l2cap_pi(parent)->sec_level;
|
||||
pi->role_switch = l2cap_pi(parent)->role_switch;
|
||||
pi->force_reliable = l2cap_pi(parent)->force_reliable;
|
||||
} else {
|
||||
pi->imtu = L2CAP_DEFAULT_MTU;
|
||||
pi->omtu = 0;
|
||||
pi->link_mode = 0;
|
||||
pi->sec_level = BT_SECURITY_LOW;
|
||||
pi->role_switch = 0;
|
||||
pi->force_reliable = 0;
|
||||
}
|
||||
|
||||
/* Default config options */
|
||||
|
@ -723,17 +783,24 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol)
|
|||
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) {
|
||||
|
@ -741,7 +808,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
|
|||
goto done;
|
||||
}
|
||||
|
||||
if (la->l2_psm && btohs(la->l2_psm) < 0x1001 &&
|
||||
if (la.l2_psm && btohs(la.l2_psm) < 0x1001 &&
|
||||
!capable(CAP_NET_BIND_SERVICE)) {
|
||||
err = -EACCES;
|
||||
goto done;
|
||||
|
@ -749,14 +816,17 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
|
|||
|
||||
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 || btohs(la.l2_psm) == 0x0003)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
|
||||
}
|
||||
|
||||
write_unlock_bh(&l2cap_sk_list.lock);
|
||||
|
@ -776,7 +846,8 @@ static int l2cap_do_connect(struct sock *sk)
|
|||
__u8 auth_type;
|
||||
int err = 0;
|
||||
|
||||
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)))
|
||||
return -EHOSTUNREACH;
|
||||
|
@ -785,21 +856,42 @@ static int l2cap_do_connect(struct sock *sk)
|
|||
|
||||
err = -ENOMEM;
|
||||
|
||||
if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH ||
|
||||
l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT ||
|
||||
l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) {
|
||||
if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001))
|
||||
if (sk->sk_type == SOCK_RAW) {
|
||||
switch (l2cap_pi(sk)->sec_level) {
|
||||
case BT_SECURITY_HIGH:
|
||||
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
auth_type = HCI_AT_DEDICATED_BONDING;
|
||||
break;
|
||||
default:
|
||||
auth_type = HCI_AT_NO_BONDING;
|
||||
break;
|
||||
}
|
||||
} else 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_GENERAL_BONDING_MITM;
|
||||
} else {
|
||||
if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001))
|
||||
auth_type = HCI_AT_NO_BONDING;
|
||||
else
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
hcon = hci_connect(hdev, ACL_LINK, dst, auth_type);
|
||||
hcon = hci_connect(hdev, ACL_LINK, dst,
|
||||
l2cap_pi(sk)->sec_level, auth_type);
|
||||
if (!hcon)
|
||||
goto done;
|
||||
|
||||
|
@ -835,20 +927,25 @@ static int l2cap_do_connect(struct sock *sk)
|
|||
|
||||
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;
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) {
|
||||
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) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
@ -875,8 +972,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
|
|||
}
|
||||
|
||||
/* 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)))
|
||||
goto done;
|
||||
|
@ -1000,12 +1097,16 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
|
|||
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 = htobs(l2cap_pi(sk)->dcid);
|
||||
} else {
|
||||
la->l2_psm = l2cap_pi(sk)->sport;
|
||||
bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
|
||||
la->l2_cid = htobs(l2cap_pi(sk)->scid);
|
||||
}
|
||||
|
||||
la->l2_psm = l2cap_pi(sk)->psm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1106,11 +1207,38 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
|||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
|
||||
static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
|
||||
sk->sk_state = BT_CONFIG;
|
||||
|
||||
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident,
|
||||
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||
|
||||
release_sock(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
return bt_sock_recvmsg(iocb, sock, msg, len, flags);
|
||||
}
|
||||
|
||||
static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_options opts;
|
||||
int err = 0, len;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
@ -1140,7 +1268,15 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||
break;
|
||||
}
|
||||
|
||||
l2cap_pi(sk)->link_mode = opt;
|
||||
if (opt & L2CAP_LM_AUTH)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_LOW;
|
||||
if (opt & L2CAP_LM_ENCRYPT)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM;
|
||||
if (opt & L2CAP_LM_SECURE)
|
||||
l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH;
|
||||
|
||||
l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER);
|
||||
l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1152,12 +1288,77 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct bt_security sec;
|
||||
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 (copy_from_user((char *) &sec, optval, len)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sec.level < BT_SECURITY_LOW ||
|
||||
sec.level > BT_SECURITY_HIGH) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_pi(sk)->sec_level = sec.level;
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_user(opt, (u32 __user *) optval)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
bt_sk(sk)->defer_setup = opt;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_options opts;
|
||||
struct l2cap_conninfo cinfo;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
|
@ -1180,12 +1381,36 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||
break;
|
||||
|
||||
case L2CAP_LM:
|
||||
if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *) optval))
|
||||
switch (l2cap_pi(sk)->sec_level) {
|
||||
case BT_SECURITY_LOW:
|
||||
opt = L2CAP_LM_AUTH;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT;
|
||||
break;
|
||||
case BT_SECURITY_HIGH:
|
||||
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
|
||||
L2CAP_LM_SECURE;
|
||||
break;
|
||||
default:
|
||||
opt = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (l2cap_pi(sk)->role_switch)
|
||||
opt |= L2CAP_LM_MASTER;
|
||||
|
||||
if (l2cap_pi(sk)->force_reliable)
|
||||
opt |= L2CAP_LM_RELIABLE;
|
||||
|
||||
if (put_user(opt, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case L2CAP_CONNINFO:
|
||||
if (sk->sk_state != BT_CONNECTED) {
|
||||
if (sk->sk_state != BT_CONNECTED &&
|
||||
!(sk->sk_state == BT_CONNECT2 &&
|
||||
bt_sk(sk)->defer_setup)) {
|
||||
err = -ENOTCONN;
|
||||
break;
|
||||
}
|
||||
|
@ -1208,6 +1433,60 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct bt_security sec;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
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;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
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));
|
||||
if (copy_to_user(optval, (char *) &sec, len))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_sock_shutdown(struct socket *sock, int how)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
@ -1270,11 +1549,6 @@ static void l2cap_chan_ready(struct sock *sk)
|
|||
*/
|
||||
parent->sk_data_ready(parent, 0);
|
||||
}
|
||||
|
||||
if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) {
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
hci_conn_change_link_key(conn->hcon);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy frame to all raw sockets on that connection */
|
||||
|
@ -1549,8 +1823,11 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1580,6 +1857,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||
/* 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;
|
||||
}
|
||||
|
@ -1621,11 +1899,18 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||
|
||||
l2cap_pi(sk)->ident = cmd->ident;
|
||||
|
||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
|
||||
if (l2cap_check_link_mode(sk)) {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
result = L2CAP_CR_SUCCESS;
|
||||
status = L2CAP_CS_NO_INFO;
|
||||
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;
|
||||
result = L2CAP_CR_PEND;
|
||||
status = L2CAP_CS_AUTHOR_PEND;
|
||||
parent->sk_data_ready(parent, 0);
|
||||
} else {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
result = L2CAP_CR_SUCCESS;
|
||||
status = L2CAP_CS_NO_INFO;
|
||||
}
|
||||
} else {
|
||||
sk->sk_state = BT_CONNECT2;
|
||||
result = L2CAP_CR_PEND;
|
||||
|
@ -1695,11 +1980,14 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||
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:
|
||||
|
@ -1908,6 +2196,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
|
|||
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);
|
||||
|
@ -1929,14 +2225,31 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
|
|||
|
||||
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 & 0x0080) {
|
||||
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;
|
||||
}
|
||||
|
@ -2143,10 +2456,15 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
|||
continue;
|
||||
|
||||
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
|
||||
lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
|
||||
lm1 |= HCI_LM_ACCEPT;
|
||||
if (l2cap_pi(sk)->role_switch)
|
||||
lm1 |= HCI_LM_MASTER;
|
||||
exact++;
|
||||
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
|
||||
lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
|
||||
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
|
||||
lm2 |= HCI_LM_ACCEPT;
|
||||
if (l2cap_pi(sk)->role_switch)
|
||||
lm2 |= HCI_LM_MASTER;
|
||||
}
|
||||
}
|
||||
read_unlock(&l2cap_sk_list.lock);
|
||||
|
||||
|
@ -2172,7 +2490,19 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
|||
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);
|
||||
|
||||
|
@ -2184,7 +2514,24 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
|
||||
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);
|
||||
l2cap_sock_set_timer(sk, HZ * 5);
|
||||
} else if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
|
||||
__l2cap_sock_close(sk, ECONNREFUSED);
|
||||
} else {
|
||||
if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM)
|
||||
l2cap_sock_clear_timer(sk);
|
||||
}
|
||||
}
|
||||
|
||||
static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
{
|
||||
struct l2cap_chan_list *l;
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
|
@ -2200,85 +2547,16 @@ static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
|
|||
read_lock(&l->lock);
|
||||
|
||||
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
|
||||
!(hcon->link_mode & HCI_LM_ENCRYPT) &&
|
||||
!status) {
|
||||
if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) {
|
||||
bh_unlock_sock(sk);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sk->sk_state == BT_CONNECT) {
|
||||
if (!status) {
|
||||
struct l2cap_conn_req req;
|
||||
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
req.psm = l2cap_pi(sk)->psm;
|
||||
|
||||
l2cap_pi(sk)->ident = l2cap_get_ident(conn);
|
||||
|
||||
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
|
||||
L2CAP_CONN_REQ, sizeof(req), &req);
|
||||
} else {
|
||||
l2cap_sock_clear_timer(sk);
|
||||
l2cap_sock_set_timer(sk, HZ / 10);
|
||||
}
|
||||
} else if (sk->sk_state == BT_CONNECT2) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 result;
|
||||
|
||||
if (!status) {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
result = L2CAP_CR_SUCCESS;
|
||||
} else {
|
||||
sk->sk_state = BT_DISCONN;
|
||||
l2cap_sock_set_timer(sk, HZ / 10);
|
||||
result = L2CAP_CR_SEC_BLOCK;
|
||||
}
|
||||
|
||||
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||
rsp.result = cpu_to_le16(result);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
|
||||
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||
}
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
}
|
||||
|
||||
read_unlock(&l->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
{
|
||||
struct l2cap_chan_list *l;
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct sock *sk;
|
||||
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
l = &conn->chan_list;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
read_lock(&l->lock);
|
||||
|
||||
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
|
||||
(sk->sk_state == BT_CONNECTED ||
|
||||
sk->sk_state == BT_CONFIG) &&
|
||||
!status && encrypt == 0x00) {
|
||||
__l2cap_sock_close(sk, ECONNREFUSED);
|
||||
if (!status && (sk->sk_state == BT_CONNECTED ||
|
||||
sk->sk_state == BT_CONFIG)) {
|
||||
l2cap_check_encryption(sk, encrypt);
|
||||
bh_unlock_sock(sk);
|
||||
continue;
|
||||
}
|
||||
|
@ -2376,7 +2654,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
|
|||
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);
|
||||
|
@ -2398,7 +2676,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -2424,10 +2702,10 @@ static ssize_t l2cap_sysfs_show(struct class *dev, char *buf)
|
|||
sk_for_each(sk, node, &l2cap_sk_list.head) {
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
|
||||
str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
|
||||
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->link_mode);
|
||||
pi->imtu, pi->omtu, pi->sec_level);
|
||||
}
|
||||
|
||||
read_unlock_bh(&l2cap_sk_list.lock);
|
||||
|
@ -2447,7 +2725,7 @@ static const struct proto_ops l2cap_sock_ops = {
|
|||
.accept = l2cap_sock_accept,
|
||||
.getname = l2cap_sock_getname,
|
||||
.sendmsg = l2cap_sock_sendmsg,
|
||||
.recvmsg = bt_sock_recvmsg,
|
||||
.recvmsg = l2cap_sock_recvmsg,
|
||||
.poll = bt_sock_poll,
|
||||
.ioctl = bt_sock_ioctl,
|
||||
.mmap = sock_no_mmap,
|
||||
|
@ -2469,8 +2747,8 @@ static struct hci_proto l2cap_hci_proto = {
|
|||
.connect_ind = l2cap_connect_ind,
|
||||
.connect_cfm = l2cap_connect_cfm,
|
||||
.disconn_ind = l2cap_disconn_ind,
|
||||
.auth_cfm = l2cap_auth_cfm,
|
||||
.encrypt_cfm = l2cap_encrypt_cfm,
|
||||
.disconn_cfm = l2cap_disconn_cfm,
|
||||
.security_cfm = l2cap_security_cfm,
|
||||
.recv_acldata = l2cap_recv_acldata
|
||||
};
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/rfcomm.h>
|
||||
|
||||
#define VERSION "1.10"
|
||||
#define VERSION "1.11"
|
||||
|
||||
static int disable_cfc = 0;
|
||||
static int channel_mtu = -1;
|
||||
|
@ -223,19 +223,25 @@ static int rfcomm_l2sock_create(struct socket **sock)
|
|||
return err;
|
||||
}
|
||||
|
||||
static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
|
||||
static inline int rfcomm_check_security(struct rfcomm_dlc *d)
|
||||
{
|
||||
struct sock *sk = d->session->sock->sk;
|
||||
__u8 auth_type;
|
||||
|
||||
if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
|
||||
if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
|
||||
return 1;
|
||||
} else if (d->link_mode & RFCOMM_LM_AUTH) {
|
||||
if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
|
||||
return 1;
|
||||
switch (d->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 0;
|
||||
return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level,
|
||||
auth_type);
|
||||
}
|
||||
|
||||
/* ---- RFCOMM DLCs ---- */
|
||||
|
@ -388,10 +394,10 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
|
|||
d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
|
||||
|
||||
if (s->state == BT_CONNECTED) {
|
||||
if (rfcomm_check_link_mode(d))
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
else
|
||||
if (rfcomm_check_security(d))
|
||||
rfcomm_send_pn(s, 1, d);
|
||||
else
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
}
|
||||
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
|
||||
|
@ -421,9 +427,16 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
|
|||
d, d->state, d->dlci, err, s);
|
||||
|
||||
switch (d->state) {
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
case BT_CONNECT:
|
||||
case BT_CONFIG:
|
||||
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
|
||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||
rfcomm_schedule(RFCOMM_SCHED_AUTH);
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
|
||||
case BT_CONNECTED:
|
||||
d->state = BT_DISCONN;
|
||||
if (skb_queue_empty(&d->tx_queue)) {
|
||||
rfcomm_send_disc(s, d->dlci);
|
||||
|
@ -434,6 +447,15 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
|
|||
}
|
||||
break;
|
||||
|
||||
case BT_OPEN:
|
||||
case BT_CONNECT2:
|
||||
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
|
||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||
rfcomm_schedule(RFCOMM_SCHED_AUTH);
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
|
||||
default:
|
||||
rfcomm_dlc_clear_timer(d);
|
||||
|
||||
|
@ -636,6 +658,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
|
|||
bacpy(&addr.l2_bdaddr, src);
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_psm = 0;
|
||||
addr.l2_cid = 0;
|
||||
*err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (*err < 0)
|
||||
goto failed;
|
||||
|
@ -657,6 +680,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
|
|||
bacpy(&addr.l2_bdaddr, dst);
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_psm = htobs(RFCOMM_PSM);
|
||||
addr.l2_cid = 0;
|
||||
*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
|
||||
if (*err == 0 || *err == -EINPROGRESS)
|
||||
return s;
|
||||
|
@ -1162,7 +1186,7 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
||||
{
|
||||
struct sock *sk = d->session->sock->sk;
|
||||
|
||||
|
@ -1175,12 +1199,31 @@ static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
|
|||
d->state_change(d, 0);
|
||||
rfcomm_dlc_unlock(d);
|
||||
|
||||
if (d->link_mode & RFCOMM_LM_MASTER)
|
||||
if (d->role_switch)
|
||||
hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00);
|
||||
|
||||
rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
|
||||
}
|
||||
|
||||
static void rfcomm_check_accept(struct rfcomm_dlc *d)
|
||||
{
|
||||
if (rfcomm_check_security(d)) {
|
||||
if (d->defer_setup) {
|
||||
set_bit(RFCOMM_DEFER_SETUP, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
|
||||
rfcomm_dlc_lock(d);
|
||||
d->state = BT_CONNECT2;
|
||||
d->state_change(d, 0);
|
||||
rfcomm_dlc_unlock(d);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
} else {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
|
||||
{
|
||||
struct rfcomm_dlc *d;
|
||||
|
@ -1203,11 +1246,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
|
|||
if (d) {
|
||||
if (d->state == BT_OPEN) {
|
||||
/* DLC was previously opened by PN request */
|
||||
if (rfcomm_check_link_mode(d)) {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
rfcomm_check_accept(d);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1219,11 +1258,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
|
|||
d->addr = __addr(s->initiator, dlci);
|
||||
rfcomm_dlc_link(s, d);
|
||||
|
||||
if (rfcomm_check_link_mode(d)) {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
rfcomm_check_accept(d);
|
||||
} else {
|
||||
rfcomm_send_dm(s, dlci);
|
||||
}
|
||||
|
@ -1637,11 +1672,12 @@ static void rfcomm_process_connect(struct rfcomm_session *s)
|
|||
d = list_entry(p, struct rfcomm_dlc, list);
|
||||
if (d->state == BT_CONFIG) {
|
||||
d->mtu = s->mtu;
|
||||
if (rfcomm_check_link_mode(d)) {
|
||||
if (rfcomm_check_security(d)) {
|
||||
rfcomm_send_pn(s, 1, d);
|
||||
} else {
|
||||
set_bit(RFCOMM_AUTH_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
} else
|
||||
rfcomm_send_pn(s, 1, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1717,11 +1753,17 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
|
|||
if (d->out) {
|
||||
rfcomm_send_pn(s, 1, d);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
if (d->link_mode & RFCOMM_LM_SECURE) {
|
||||
struct sock *sk = s->sock->sk;
|
||||
hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
|
||||
} else {
|
||||
if (d->defer_setup) {
|
||||
set_bit(RFCOMM_DEFER_SETUP, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
|
||||
rfcomm_dlc_lock(d);
|
||||
d->state = BT_CONNECT2;
|
||||
d->state_change(d, 0);
|
||||
rfcomm_dlc_unlock(d);
|
||||
} else
|
||||
rfcomm_dlc_accept(d);
|
||||
}
|
||||
continue;
|
||||
} else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
|
||||
|
@ -1734,6 +1776,9 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (test_bit(RFCOMM_SEC_PENDING, &d->flags))
|
||||
continue;
|
||||
|
||||
if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
|
||||
continue;
|
||||
|
||||
|
@ -1876,6 +1921,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
|
|||
bacpy(&addr.l2_bdaddr, ba);
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_psm = htobs(RFCOMM_PSM);
|
||||
addr.l2_cid = 0;
|
||||
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (err < 0) {
|
||||
BT_ERR("Bind failed %d", err);
|
||||
|
@ -1947,42 +1993,7 @@ static int rfcomm_run(void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
|
||||
{
|
||||
struct rfcomm_session *s;
|
||||
struct rfcomm_dlc *d;
|
||||
struct list_head *p, *n;
|
||||
|
||||
BT_DBG("conn %p status 0x%02x", conn, status);
|
||||
|
||||
s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
rfcomm_session_hold(s);
|
||||
|
||||
list_for_each_safe(p, n, &s->dlcs) {
|
||||
d = list_entry(p, struct rfcomm_dlc, list);
|
||||
|
||||
if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
|
||||
!(conn->link_mode & HCI_LM_ENCRYPT) && !status)
|
||||
continue;
|
||||
|
||||
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
|
||||
continue;
|
||||
|
||||
if (!status)
|
||||
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
|
||||
else
|
||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||
}
|
||||
|
||||
rfcomm_session_put(s);
|
||||
|
||||
rfcomm_schedule(RFCOMM_SCHED_AUTH);
|
||||
}
|
||||
|
||||
static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
||||
static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
||||
{
|
||||
struct rfcomm_session *s;
|
||||
struct rfcomm_dlc *d;
|
||||
|
@ -1999,18 +2010,29 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
|||
list_for_each_safe(p, n, &s->dlcs) {
|
||||
d = list_entry(p, struct rfcomm_dlc, list);
|
||||
|
||||
if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
|
||||
(d->state == BT_CONNECTED ||
|
||||
d->state == BT_CONFIG) &&
|
||||
!status && encrypt == 0x00) {
|
||||
__rfcomm_dlc_close(d, ECONNREFUSED);
|
||||
continue;
|
||||
if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
|
||||
rfcomm_dlc_clear_timer(d);
|
||||
if (status || encrypt == 0x00) {
|
||||
__rfcomm_dlc_close(d, ECONNREFUSED);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (d->state == BT_CONNECTED && !status && encrypt == 0x00) {
|
||||
if (d->sec_level == BT_SECURITY_MEDIUM) {
|
||||
set_bit(RFCOMM_SEC_PENDING, &d->flags);
|
||||
rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
|
||||
continue;
|
||||
} else if (d->sec_level == BT_SECURITY_HIGH) {
|
||||
__rfcomm_dlc_close(d, ECONNREFUSED);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
|
||||
continue;
|
||||
|
||||
if (!status && encrypt)
|
||||
if (!status)
|
||||
set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
|
||||
else
|
||||
set_bit(RFCOMM_AUTH_REJECT, &d->flags);
|
||||
|
@ -2023,8 +2045,7 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
|
|||
|
||||
static struct hci_cb rfcomm_cb = {
|
||||
.name = "RFCOMM",
|
||||
.auth_cfm = rfcomm_auth_cfm,
|
||||
.encrypt_cfm = rfcomm_encrypt_cfm
|
||||
.security_cfm = rfcomm_security_cfm
|
||||
};
|
||||
|
||||
static ssize_t rfcomm_dlc_sysfs_show(struct class *dev, char *buf)
|
||||
|
|
|
@ -261,12 +261,19 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
|
|||
|
||||
if (parent) {
|
||||
sk->sk_type = parent->sk_type;
|
||||
pi->link_mode = rfcomm_pi(parent)->link_mode;
|
||||
pi->dlc->defer_setup = bt_sk(parent)->defer_setup;
|
||||
|
||||
pi->sec_level = rfcomm_pi(parent)->sec_level;
|
||||
pi->role_switch = rfcomm_pi(parent)->role_switch;
|
||||
} else {
|
||||
pi->link_mode = 0;
|
||||
pi->dlc->defer_setup = 0;
|
||||
|
||||
pi->sec_level = BT_SECURITY_LOW;
|
||||
pi->role_switch = 0;
|
||||
}
|
||||
|
||||
pi->dlc->link_mode = pi->link_mode;
|
||||
pi->dlc->sec_level = pi->sec_level;
|
||||
pi->dlc->role_switch = pi->role_switch;
|
||||
}
|
||||
|
||||
static struct proto rfcomm_proto = {
|
||||
|
@ -406,7 +413,8 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
|
|||
bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr);
|
||||
rfcomm_pi(sk)->channel = sa->rc_channel;
|
||||
|
||||
d->link_mode = rfcomm_pi(sk)->link_mode;
|
||||
d->sec_level = rfcomm_pi(sk)->sec_level;
|
||||
d->role_switch = rfcomm_pi(sk)->role_switch;
|
||||
|
||||
err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
|
||||
if (!err)
|
||||
|
@ -554,6 +562,9 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||
struct sk_buff *skb;
|
||||
int sent = 0;
|
||||
|
||||
if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
|
||||
return -ENOTCONN;
|
||||
|
||||
if (msg->msg_flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
@ -570,8 +581,11 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||
|
||||
skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
|
||||
msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
if (!skb)
|
||||
if (!skb) {
|
||||
if (sent == 0)
|
||||
sent = err;
|
||||
break;
|
||||
}
|
||||
skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
|
||||
|
||||
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
|
||||
|
@ -630,10 +644,16 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|||
struct msghdr *msg, size_t size, int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
|
||||
int err = 0;
|
||||
size_t target, copied = 0;
|
||||
long timeo;
|
||||
|
||||
if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
|
||||
rfcomm_dlc_accept(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flags & MSG_OOB)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
@ -710,7 +730,7 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|||
return copied ? : err;
|
||||
}
|
||||
|
||||
static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
|
||||
static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int err = 0;
|
||||
|
@ -727,7 +747,144 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
|
|||
break;
|
||||
}
|
||||
|
||||
rfcomm_pi(sk)->link_mode = opt;
|
||||
if (opt & RFCOMM_LM_AUTH)
|
||||
rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW;
|
||||
if (opt & RFCOMM_LM_ENCRYPT)
|
||||
rfcomm_pi(sk)->sec_level = BT_SECURITY_MEDIUM;
|
||||
if (opt & RFCOMM_LM_SECURE)
|
||||
rfcomm_pi(sk)->sec_level = BT_SECURITY_HIGH;
|
||||
|
||||
rfcomm_pi(sk)->role_switch = (opt & RFCOMM_LM_MASTER);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct bt_security sec;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_RFCOMM)
|
||||
return rfcomm_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_STREAM) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
sec.level = BT_SECURITY_LOW;
|
||||
|
||||
len = min_t(unsigned int, sizeof(sec), optlen);
|
||||
if (copy_from_user((char *) &sec, optval, len)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sec.level > BT_SECURITY_HIGH) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
rfcomm_pi(sk)->sec_level = sec.level;
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_user(opt, (u32 __user *) optval)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
bt_sk(sk)->defer_setup = opt;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sock *l2cap_sk;
|
||||
struct rfcomm_conninfo cinfo;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
case RFCOMM_LM:
|
||||
switch (rfcomm_pi(sk)->sec_level) {
|
||||
case BT_SECURITY_LOW:
|
||||
opt = RFCOMM_LM_AUTH;
|
||||
break;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
|
||||
break;
|
||||
case BT_SECURITY_HIGH:
|
||||
opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT |
|
||||
RFCOMM_LM_SECURE;
|
||||
break;
|
||||
default:
|
||||
opt = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rfcomm_pi(sk)->role_switch)
|
||||
opt |= RFCOMM_LM_MASTER;
|
||||
|
||||
if (put_user(opt, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case RFCOMM_CONNINFO:
|
||||
if (sk->sk_state != BT_CONNECTED &&
|
||||
!rfcomm_pi(sk)->dlc->defer_setup) {
|
||||
err = -ENOTCONN;
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk;
|
||||
|
||||
cinfo.hci_handle = l2cap_pi(l2cap_sk)->conn->hcon->handle;
|
||||
memcpy(cinfo.dev_class, l2cap_pi(l2cap_sk)->conn->hcon->dev_class, 3);
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(cinfo));
|
||||
if (copy_to_user(optval, (char *) &cinfo, len))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -742,36 +899,44 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
|
|||
static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sock *l2cap_sk;
|
||||
struct rfcomm_conninfo cinfo;
|
||||
struct bt_security sec;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_RFCOMM)
|
||||
return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen);
|
||||
|
||||
if (level != SOL_BLUETOOTH)
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
case RFCOMM_LM:
|
||||
if (put_user(rfcomm_pi(sk)->link_mode, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case RFCOMM_CONNINFO:
|
||||
if (sk->sk_state != BT_CONNECTED) {
|
||||
err = -ENOTCONN;
|
||||
case BT_SECURITY:
|
||||
if (sk->sk_type != SOCK_STREAM) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk;
|
||||
sec.level = rfcomm_pi(sk)->sec_level;
|
||||
|
||||
cinfo.hci_handle = l2cap_pi(l2cap_sk)->conn->hcon->handle;
|
||||
memcpy(cinfo.dev_class, l2cap_pi(l2cap_sk)->conn->hcon->dev_class, 3);
|
||||
len = min_t(unsigned int, len, sizeof(sec));
|
||||
if (copy_to_user(optval, (char *) &sec, len))
|
||||
err = -EFAULT;
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(cinfo));
|
||||
if (copy_to_user(optval, (char *) &cinfo, len))
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
@ -888,6 +1053,10 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
|
|||
|
||||
done:
|
||||
bh_unlock_sock(parent);
|
||||
|
||||
if (bt_sk(parent)->defer_setup)
|
||||
parent->sk_state_change(parent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ static int sco_connect(struct sock *sk)
|
|||
else
|
||||
type = SCO_LINK;
|
||||
|
||||
hcon = hci_connect(hdev, type, dst, HCI_AT_NO_BONDING);
|
||||
hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
|
||||
if (!hcon)
|
||||
goto done;
|
||||
|
||||
|
@ -668,7 +668,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
|
|||
return err;
|
||||
}
|
||||
|
||||
static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sco_options opts;
|
||||
|
@ -723,6 +723,31 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
|
|||
return err;
|
||||
}
|
||||
|
||||
static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int len, err = 0;
|
||||
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (level == SOL_SCO)
|
||||
return sco_sock_getsockopt_old(sock, optname, optval, optlen);
|
||||
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sco_sock_release(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
@ -832,10 +857,30 @@ static void sco_conn_ready(struct sco_conn *conn)
|
|||
/* ----- SCO interface with lower layer (HCI) ----- */
|
||||
static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
|
||||
{
|
||||
register struct sock *sk;
|
||||
struct hlist_node *node;
|
||||
int lm = 0;
|
||||
|
||||
if (type != SCO_LINK && type != ESCO_LINK)
|
||||
return 0;
|
||||
|
||||
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
|
||||
|
||||
/* Always accept connection */
|
||||
return HCI_LM_ACCEPT;
|
||||
/* Find listening sockets */
|
||||
read_lock(&sco_sk_list.lock);
|
||||
sk_for_each(sk, node, &sco_sk_list.head) {
|
||||
if (sk->sk_state != BT_LISTEN)
|
||||
continue;
|
||||
|
||||
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
|
||||
!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
|
||||
lm |= HCI_LM_ACCEPT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock(&sco_sk_list.lock);
|
||||
|
||||
return lm;
|
||||
}
|
||||
|
||||
static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
|
@ -857,7 +902,7 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
|
||||
static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
{
|
||||
BT_DBG("hcon %p reason %d", hcon, reason);
|
||||
|
||||
|
@ -940,7 +985,7 @@ static struct hci_proto sco_hci_proto = {
|
|||
.id = HCI_PROTO_SCO,
|
||||
.connect_ind = sco_connect_ind,
|
||||
.connect_cfm = sco_connect_cfm,
|
||||
.disconn_ind = sco_disconn_ind,
|
||||
.disconn_cfm = sco_disconn_cfm,
|
||||
.recv_scodata = sco_recv_scodata
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue