Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says: ==================== pull request: bluetooth-next 2015-04-04 Here's what's probably the last bluetooth-next pull request for 4.1: - Fixes for LE advertising data & advertising parameters - Fix for race condition with HCI_RESET flag - New BNEPGETSUPPFEAT ioctl, needed for certification - New HCI request callback type to get the resulting skb - Cleanups to use BIT() macro wherever possible - Consolidate Broadcom device entries in the btusb HCI driver - Check for valid flags in CMTP, HIDP & BNEP - Disallow local privacy & OOB data combo to prevent a potential race - Expose SMP & ECDH selftest results through debugfs - Expose current Device ID info through debugfs Please let me know if there are any issues pulling. Thanks. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
7abccdba25
|
@ -111,13 +111,7 @@ static const struct usb_device_id btusb_table[] = {
|
||||||
{ USB_DEVICE(0x0c10, 0x0000) },
|
{ USB_DEVICE(0x0c10, 0x0000) },
|
||||||
|
|
||||||
/* Broadcom BCM20702A0 */
|
/* Broadcom BCM20702A0 */
|
||||||
{ USB_DEVICE(0x0489, 0xe042) },
|
|
||||||
{ USB_DEVICE(0x04ca, 0x2003) },
|
|
||||||
{ USB_DEVICE(0x0b05, 0x17b5) },
|
|
||||||
{ USB_DEVICE(0x0b05, 0x17cb) },
|
|
||||||
{ USB_DEVICE(0x413c, 0x8197) },
|
{ USB_DEVICE(0x413c, 0x8197) },
|
||||||
{ USB_DEVICE(0x13d3, 0x3404),
|
|
||||||
.driver_info = BTUSB_BCM_PATCHRAM },
|
|
||||||
|
|
||||||
/* Broadcom BCM20702B0 (Dynex/Insignia) */
|
/* Broadcom BCM20702B0 (Dynex/Insignia) */
|
||||||
{ USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
|
{ USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
|
||||||
|
@ -139,10 +133,12 @@ static const struct usb_device_id btusb_table[] = {
|
||||||
.driver_info = BTUSB_BCM_PATCHRAM },
|
.driver_info = BTUSB_BCM_PATCHRAM },
|
||||||
|
|
||||||
/* Belkin F8065bf - Broadcom based */
|
/* Belkin F8065bf - Broadcom based */
|
||||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01),
|
||||||
|
.driver_info = BTUSB_BCM_PATCHRAM },
|
||||||
|
|
||||||
/* IMC Networks - Broadcom based */
|
/* IMC Networks - Broadcom based */
|
||||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01),
|
||||||
|
.driver_info = BTUSB_BCM_PATCHRAM },
|
||||||
|
|
||||||
/* Intel Bluetooth USB Bootloader (RAM module) */
|
/* Intel Bluetooth USB Bootloader (RAM module) */
|
||||||
{ USB_DEVICE(0x8087, 0x0a5a),
|
{ USB_DEVICE(0x8087, 0x0a5a),
|
||||||
|
|
|
@ -499,7 +499,7 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
|
||||||
BIT(HCI_UART_INIT_PENDING) |
|
BIT(HCI_UART_INIT_PENDING) |
|
||||||
BIT(HCI_UART_EXT_CONFIG);
|
BIT(HCI_UART_EXT_CONFIG);
|
||||||
|
|
||||||
if ((flags & ~valid_flags))
|
if (flags & ~valid_flags)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
hu->hdev_flags = flags;
|
hu->hdev_flags = flags;
|
||||||
|
|
|
@ -570,6 +570,7 @@ static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, void __user *argp)
|
||||||
#define BNEPCONNDEL _IOW('B', 201, int)
|
#define BNEPCONNDEL _IOW('B', 201, int)
|
||||||
#define BNEPGETCONNLIST _IOR('B', 210, int)
|
#define BNEPGETCONNLIST _IOR('B', 210, int)
|
||||||
#define BNEPGETCONNINFO _IOR('B', 211, int)
|
#define BNEPGETCONNINFO _IOR('B', 211, int)
|
||||||
|
#define BNEPGETSUPPFEAT _IOR('B', 212, int)
|
||||||
|
|
||||||
#define CMTPCONNADD _IOW('C', 200, int)
|
#define CMTPCONNADD _IOW('C', 200, int)
|
||||||
#define CMTPCONNDEL _IOW('C', 201, int)
|
#define CMTPCONNDEL _IOW('C', 201, int)
|
||||||
|
@ -1247,6 +1248,7 @@ COMPATIBLE_IOCTL(BNEPCONNADD)
|
||||||
COMPATIBLE_IOCTL(BNEPCONNDEL)
|
COMPATIBLE_IOCTL(BNEPCONNDEL)
|
||||||
COMPATIBLE_IOCTL(BNEPGETCONNLIST)
|
COMPATIBLE_IOCTL(BNEPGETCONNLIST)
|
||||||
COMPATIBLE_IOCTL(BNEPGETCONNINFO)
|
COMPATIBLE_IOCTL(BNEPGETCONNINFO)
|
||||||
|
COMPATIBLE_IOCTL(BNEPGETSUPPFEAT)
|
||||||
COMPATIBLE_IOCTL(CMTPCONNADD)
|
COMPATIBLE_IOCTL(CMTPCONNADD)
|
||||||
COMPATIBLE_IOCTL(CMTPCONNDEL)
|
COMPATIBLE_IOCTL(CMTPCONNDEL)
|
||||||
COMPATIBLE_IOCTL(CMTPGETCONNLIST)
|
COMPATIBLE_IOCTL(CMTPGETCONNLIST)
|
||||||
|
|
|
@ -269,11 +269,23 @@ struct l2cap_ctrl {
|
||||||
__u16 reqseq;
|
__u16 reqseq;
|
||||||
__u16 txseq;
|
__u16 txseq;
|
||||||
__u8 retries;
|
__u8 retries;
|
||||||
|
__le16 psm;
|
||||||
|
bdaddr_t bdaddr;
|
||||||
|
struct l2cap_chan *chan;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hci_dev;
|
struct hci_dev;
|
||||||
|
|
||||||
typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
|
typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
|
||||||
|
typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status,
|
||||||
|
u16 opcode, struct sk_buff *skb);
|
||||||
|
|
||||||
|
struct req_ctrl {
|
||||||
|
bool start;
|
||||||
|
u8 event;
|
||||||
|
hci_req_complete_t complete;
|
||||||
|
hci_req_complete_skb_t complete_skb;
|
||||||
|
};
|
||||||
|
|
||||||
struct bt_skb_cb {
|
struct bt_skb_cb {
|
||||||
__u8 pkt_type;
|
__u8 pkt_type;
|
||||||
|
@ -281,13 +293,10 @@ struct bt_skb_cb {
|
||||||
__u16 opcode;
|
__u16 opcode;
|
||||||
__u16 expect;
|
__u16 expect;
|
||||||
__u8 incoming:1;
|
__u8 incoming:1;
|
||||||
__u8 req_start:1;
|
union {
|
||||||
u8 req_event;
|
struct l2cap_ctrl l2cap;
|
||||||
hci_req_complete_t req_complete;
|
struct req_ctrl req;
|
||||||
struct l2cap_chan *chan;
|
};
|
||||||
struct l2cap_ctrl control;
|
|
||||||
bdaddr_t bdaddr;
|
|
||||||
__le16 psm;
|
|
||||||
};
|
};
|
||||||
#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
|
#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
|
||||||
|
|
||||||
|
|
|
@ -326,7 +326,6 @@ struct hci_dev {
|
||||||
struct sk_buff_head raw_q;
|
struct sk_buff_head raw_q;
|
||||||
struct sk_buff_head cmd_q;
|
struct sk_buff_head cmd_q;
|
||||||
|
|
||||||
struct sk_buff *recv_evt;
|
|
||||||
struct sk_buff *sent_cmd;
|
struct sk_buff *sent_cmd;
|
||||||
struct sk_buff *reassembly[NUM_REASSEMBLY];
|
struct sk_buff *reassembly[NUM_REASSEMBLY];
|
||||||
|
|
||||||
|
@ -334,6 +333,7 @@ struct hci_dev {
|
||||||
wait_queue_head_t req_wait_q;
|
wait_queue_head_t req_wait_q;
|
||||||
__u32 req_status;
|
__u32 req_status;
|
||||||
__u32 req_result;
|
__u32 req_result;
|
||||||
|
struct sk_buff *req_skb;
|
||||||
|
|
||||||
void *smp_data;
|
void *smp_data;
|
||||||
void *smp_bredr_data;
|
void *smp_bredr_data;
|
||||||
|
@ -1284,8 +1284,6 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
|
||||||
int hci_register_cb(struct hci_cb *hcb);
|
int hci_register_cb(struct hci_cb *hcb);
|
||||||
int hci_unregister_cb(struct hci_cb *hcb);
|
int hci_unregister_cb(struct hci_cb *hcb);
|
||||||
|
|
||||||
bool hci_req_pending(struct hci_dev *hdev);
|
|
||||||
|
|
||||||
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
const void *param, u32 timeout);
|
const void *param, u32 timeout);
|
||||||
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
|
@ -1393,9 +1391,6 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
|
||||||
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
|
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
|
||||||
u8 status);
|
u8 status);
|
||||||
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
|
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
|
||||||
void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
|
|
||||||
u8 *rand192, u8 *hash256, u8 *rand256,
|
|
||||||
u8 status);
|
|
||||||
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||||
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
|
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
|
||||||
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
|
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
|
||||||
|
|
|
@ -111,6 +111,10 @@ struct bnep_ext_hdr {
|
||||||
#define BNEPCONNDEL _IOW('B', 201, int)
|
#define BNEPCONNDEL _IOW('B', 201, int)
|
||||||
#define BNEPGETCONNLIST _IOR('B', 210, int)
|
#define BNEPGETCONNLIST _IOR('B', 210, int)
|
||||||
#define BNEPGETCONNINFO _IOR('B', 211, int)
|
#define BNEPGETCONNINFO _IOR('B', 211, int)
|
||||||
|
#define BNEPGETSUPPFEAT _IOR('B', 212, int)
|
||||||
|
|
||||||
|
#define BNEP_SETUP_RESPONSE 0
|
||||||
|
#define BNEP_SETUP_RSP_SENT 10
|
||||||
|
|
||||||
struct bnep_connadd_req {
|
struct bnep_connadd_req {
|
||||||
int sock; /* Connected socket */
|
int sock; /* Connected socket */
|
||||||
|
|
|
@ -231,7 +231,14 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BNEP_SETUP_CONN_REQ:
|
case BNEP_SETUP_CONN_REQ:
|
||||||
err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
|
/* Successful response should be sent only once */
|
||||||
|
if (test_bit(BNEP_SETUP_RESPONSE, &s->flags) &&
|
||||||
|
!test_and_set_bit(BNEP_SETUP_RSP_SENT, &s->flags))
|
||||||
|
err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
|
||||||
|
BNEP_SUCCESS);
|
||||||
|
else
|
||||||
|
err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
|
||||||
|
BNEP_CONN_NOT_ALLOWED);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
@ -239,7 +246,7 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
|
||||||
pkt[0] = BNEP_CONTROL;
|
pkt[0] = BNEP_CONTROL;
|
||||||
pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
|
pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
|
||||||
pkt[2] = cmd;
|
pkt[2] = cmd;
|
||||||
bnep_send(s, pkt, sizeof(pkt));
|
err = bnep_send(s, pkt, sizeof(pkt));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -292,30 +299,56 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct net_device *dev = s->dev;
|
struct net_device *dev = s->dev;
|
||||||
struct sk_buff *nskb;
|
struct sk_buff *nskb;
|
||||||
u8 type;
|
u8 type, ctrl_type;
|
||||||
|
|
||||||
dev->stats.rx_bytes += skb->len;
|
dev->stats.rx_bytes += skb->len;
|
||||||
|
|
||||||
type = *(u8 *) skb->data;
|
type = *(u8 *) skb->data;
|
||||||
skb_pull(skb, 1);
|
skb_pull(skb, 1);
|
||||||
|
ctrl_type = *(u8 *)skb->data;
|
||||||
|
|
||||||
if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
|
if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
|
||||||
goto badframe;
|
goto badframe;
|
||||||
|
|
||||||
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
|
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
|
||||||
bnep_rx_control(s, skb->data, skb->len);
|
if (bnep_rx_control(s, skb->data, skb->len) < 0) {
|
||||||
kfree_skb(skb);
|
dev->stats.tx_errors++;
|
||||||
return 0;
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(type & BNEP_EXT_HEADER)) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify and pull ctrl message since it's already processed */
|
||||||
|
switch (ctrl_type) {
|
||||||
|
case BNEP_SETUP_CONN_REQ:
|
||||||
|
/* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
|
||||||
|
if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
|
||||||
|
goto badframe;
|
||||||
|
break;
|
||||||
|
case BNEP_FILTER_MULTI_ADDR_SET:
|
||||||
|
case BNEP_FILTER_NET_TYPE_SET:
|
||||||
|
/* Pull: ctrl type (1 b), len (2 b), data (len bytes) */
|
||||||
|
if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2))
|
||||||
|
goto badframe;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
skb_reset_mac_header(skb);
|
||||||
|
|
||||||
|
/* Verify and pull out header */
|
||||||
|
if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
|
||||||
|
goto badframe;
|
||||||
|
|
||||||
|
s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
skb_reset_mac_header(skb);
|
|
||||||
|
|
||||||
/* Verify and pull out header */
|
|
||||||
if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
|
|
||||||
goto badframe;
|
|
||||||
|
|
||||||
s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
|
|
||||||
|
|
||||||
if (type & BNEP_EXT_HEADER) {
|
if (type & BNEP_EXT_HEADER) {
|
||||||
if (bnep_rx_extension(s, skb) < 0)
|
if (bnep_rx_extension(s, skb) < 0)
|
||||||
goto badframe;
|
goto badframe;
|
||||||
|
@ -525,6 +558,7 @@ static struct device_type bnep_type = {
|
||||||
|
|
||||||
int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
struct bnep_session *s, *ss;
|
struct bnep_session *s, *ss;
|
||||||
u8 dst[ETH_ALEN], src[ETH_ALEN];
|
u8 dst[ETH_ALEN], src[ETH_ALEN];
|
||||||
|
@ -535,6 +569,9 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
||||||
if (!l2cap_is_socket(sock))
|
if (!l2cap_is_socket(sock))
|
||||||
return -EBADFD;
|
return -EBADFD;
|
||||||
|
|
||||||
|
if (req->flags & ~valid_flags)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
|
baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
|
||||||
baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
|
baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
|
||||||
|
|
||||||
|
@ -566,6 +603,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
||||||
s->sock = sock;
|
s->sock = sock;
|
||||||
s->role = req->role;
|
s->role = req->role;
|
||||||
s->state = BT_CONNECTED;
|
s->state = BT_CONNECTED;
|
||||||
|
s->flags = req->flags;
|
||||||
|
|
||||||
s->msg.msg_flags = MSG_NOSIGNAL;
|
s->msg.msg_flags = MSG_NOSIGNAL;
|
||||||
|
|
||||||
|
@ -611,11 +649,15 @@ failed:
|
||||||
|
|
||||||
int bnep_del_connection(struct bnep_conndel_req *req)
|
int bnep_del_connection(struct bnep_conndel_req *req)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = 0;
|
||||||
struct bnep_session *s;
|
struct bnep_session *s;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("");
|
BT_DBG("");
|
||||||
|
|
||||||
|
if (req->flags & ~valid_flags)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
down_read(&bnep_session_sem);
|
down_read(&bnep_session_sem);
|
||||||
|
|
||||||
s = __bnep_get_session(req->dst);
|
s = __bnep_get_session(req->dst);
|
||||||
|
@ -631,10 +673,12 @@ int bnep_del_connection(struct bnep_conndel_req *req)
|
||||||
|
|
||||||
static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
|
static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
|
||||||
|
|
||||||
memset(ci, 0, sizeof(*ci));
|
memset(ci, 0, sizeof(*ci));
|
||||||
memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
|
memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
|
||||||
strcpy(ci->device, s->dev->name);
|
strcpy(ci->device, s->dev->name);
|
||||||
ci->flags = s->flags;
|
ci->flags = s->flags & valid_flags;
|
||||||
ci->state = s->state;
|
ci->state = s->state;
|
||||||
ci->role = s->role;
|
ci->role = s->role;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
|
||||||
struct bnep_conninfo ci;
|
struct bnep_conninfo ci;
|
||||||
struct socket *nsock;
|
struct socket *nsock;
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
|
__u32 supp_feat = BIT(BNEP_SETUP_RESPONSE);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("cmd %x arg %lx", cmd, arg);
|
BT_DBG("cmd %x arg %lx", cmd, arg);
|
||||||
|
@ -120,6 +121,12 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
case BNEPGETSUPPFEAT:
|
||||||
|
if (copy_to_user(argp, &supp_feat, sizeof(supp_feat)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -333,7 +333,7 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session->flags & (1 << CMTP_LOOPBACK)) {
|
if (session->flags & BIT(CMTP_LOOPBACK)) {
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,10 +75,11 @@ static void __cmtp_unlink_session(struct cmtp_session *session)
|
||||||
|
|
||||||
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
|
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = BIT(CMTP_LOOPBACK);
|
||||||
memset(ci, 0, sizeof(*ci));
|
memset(ci, 0, sizeof(*ci));
|
||||||
bacpy(&ci->bdaddr, &session->bdaddr);
|
bacpy(&ci->bdaddr, &session->bdaddr);
|
||||||
|
|
||||||
ci->flags = session->flags;
|
ci->flags = session->flags & valid_flags;
|
||||||
ci->state = session->state;
|
ci->state = session->state;
|
||||||
|
|
||||||
ci->num = session->num;
|
ci->num = session->num;
|
||||||
|
@ -313,7 +314,7 @@ static int cmtp_session(void *arg)
|
||||||
|
|
||||||
down_write(&cmtp_session_sem);
|
down_write(&cmtp_session_sem);
|
||||||
|
|
||||||
if (!(session->flags & (1 << CMTP_LOOPBACK)))
|
if (!(session->flags & BIT(CMTP_LOOPBACK)))
|
||||||
cmtp_detach_device(session);
|
cmtp_detach_device(session);
|
||||||
|
|
||||||
fput(session->sock->file);
|
fput(session->sock->file);
|
||||||
|
@ -329,6 +330,7 @@ static int cmtp_session(void *arg)
|
||||||
|
|
||||||
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
|
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = BIT(CMTP_LOOPBACK);
|
||||||
struct cmtp_session *session, *s;
|
struct cmtp_session *session, *s;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
|
@ -337,6 +339,9 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
|
||||||
if (!l2cap_is_socket(sock))
|
if (!l2cap_is_socket(sock))
|
||||||
return -EBADFD;
|
return -EBADFD;
|
||||||
|
|
||||||
|
if (req->flags & ~valid_flags)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
|
session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
|
||||||
if (!session)
|
if (!session)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -385,7 +390,7 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
|
||||||
goto unlink;
|
goto unlink;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(session->flags & (1 << CMTP_LOOPBACK))) {
|
if (!(session->flags & BIT(CMTP_LOOPBACK))) {
|
||||||
err = cmtp_attach_device(session);
|
err = cmtp_attach_device(session);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
atomic_inc(&session->terminate);
|
atomic_inc(&session->terminate);
|
||||||
|
@ -409,11 +414,15 @@ failed:
|
||||||
|
|
||||||
int cmtp_del_connection(struct cmtp_conndel_req *req)
|
int cmtp_del_connection(struct cmtp_conndel_req *req)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = 0;
|
||||||
struct cmtp_session *session;
|
struct cmtp_session *session;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("");
|
BT_DBG("");
|
||||||
|
|
||||||
|
if (req->flags & ~valid_flags)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
down_read(&cmtp_session_sem);
|
down_read(&cmtp_session_sem);
|
||||||
|
|
||||||
session = __cmtp_get_session(&req->bdaddr);
|
session = __cmtp_get_session(&req->bdaddr);
|
||||||
|
|
|
@ -141,13 +141,16 @@ static const struct file_operations dut_mode_fops = {
|
||||||
|
|
||||||
/* ---- HCI requests ---- */
|
/* ---- HCI requests ---- */
|
||||||
|
|
||||||
static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode)
|
static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
|
||||||
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
BT_DBG("%s result 0x%2.2x", hdev->name, result);
|
BT_DBG("%s result 0x%2.2x", hdev->name, result);
|
||||||
|
|
||||||
if (hdev->req_status == HCI_REQ_PEND) {
|
if (hdev->req_status == HCI_REQ_PEND) {
|
||||||
hdev->req_result = result;
|
hdev->req_result = result;
|
||||||
hdev->req_status = HCI_REQ_DONE;
|
hdev->req_status = HCI_REQ_DONE;
|
||||||
|
if (skb)
|
||||||
|
hdev->req_skb = skb_get(skb);
|
||||||
wake_up_interruptible(&hdev->req_wait_q);
|
wake_up_interruptible(&hdev->req_wait_q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,66 +166,12 @@ static void hci_req_cancel(struct hci_dev *hdev, int err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sk_buff *hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
|
|
||||||
u8 event)
|
|
||||||
{
|
|
||||||
struct hci_ev_cmd_complete *ev;
|
|
||||||
struct hci_event_hdr *hdr;
|
|
||||||
struct sk_buff *skb;
|
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
|
|
||||||
skb = hdev->recv_evt;
|
|
||||||
hdev->recv_evt = NULL;
|
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
|
|
||||||
if (!skb)
|
|
||||||
return ERR_PTR(-ENODATA);
|
|
||||||
|
|
||||||
if (skb->len < sizeof(*hdr)) {
|
|
||||||
BT_ERR("Too short HCI event");
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
hdr = (void *) skb->data;
|
|
||||||
skb_pull(skb, HCI_EVENT_HDR_SIZE);
|
|
||||||
|
|
||||||
if (event) {
|
|
||||||
if (hdr->evt != event)
|
|
||||||
goto failed;
|
|
||||||
return skb;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hdr->evt != HCI_EV_CMD_COMPLETE) {
|
|
||||||
BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skb->len < sizeof(*ev)) {
|
|
||||||
BT_ERR("Too short cmd_complete event");
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
ev = (void *) skb->data;
|
|
||||||
skb_pull(skb, sizeof(*ev));
|
|
||||||
|
|
||||||
if (opcode == __le16_to_cpu(ev->opcode))
|
|
||||||
return skb;
|
|
||||||
|
|
||||||
BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
|
|
||||||
__le16_to_cpu(ev->opcode));
|
|
||||||
|
|
||||||
failed:
|
|
||||||
kfree_skb(skb);
|
|
||||||
return ERR_PTR(-ENODATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
const void *param, u8 event, u32 timeout)
|
const void *param, u8 event, u32 timeout)
|
||||||
{
|
{
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
struct hci_request req;
|
struct hci_request req;
|
||||||
|
struct sk_buff *skb;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
@ -236,7 +185,7 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
add_wait_queue(&hdev->req_wait_q, &wait);
|
add_wait_queue(&hdev->req_wait_q, &wait);
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
|
||||||
err = hci_req_run(&req, hci_req_sync_complete);
|
err = hci_req_run_skb(&req, hci_req_sync_complete);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
remove_wait_queue(&hdev->req_wait_q, &wait);
|
remove_wait_queue(&hdev->req_wait_q, &wait);
|
||||||
set_current_state(TASK_RUNNING);
|
set_current_state(TASK_RUNNING);
|
||||||
|
@ -265,13 +214,20 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
}
|
}
|
||||||
|
|
||||||
hdev->req_status = hdev->req_result = 0;
|
hdev->req_status = hdev->req_result = 0;
|
||||||
|
skb = hdev->req_skb;
|
||||||
|
hdev->req_skb = NULL;
|
||||||
|
|
||||||
BT_DBG("%s end: err %d", hdev->name, err);
|
BT_DBG("%s end: err %d", hdev->name, err);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0) {
|
||||||
|
kfree_skb(skb);
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
|
||||||
return hci_get_cmd_complete(hdev, opcode, event);
|
if (!skb)
|
||||||
|
return ERR_PTR(-ENODATA);
|
||||||
|
|
||||||
|
return skb;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__hci_cmd_sync_ev);
|
EXPORT_SYMBOL(__hci_cmd_sync_ev);
|
||||||
|
|
||||||
|
@ -303,7 +259,7 @@ static int __hci_req_sync(struct hci_dev *hdev,
|
||||||
add_wait_queue(&hdev->req_wait_q, &wait);
|
add_wait_queue(&hdev->req_wait_q, &wait);
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
|
||||||
err = hci_req_run(&req, hci_req_sync_complete);
|
err = hci_req_run_skb(&req, hci_req_sync_complete);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
hdev->req_status = 0;
|
hdev->req_status = 0;
|
||||||
|
|
||||||
|
@ -1690,9 +1646,6 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
||||||
hdev->sent_cmd = NULL;
|
hdev->sent_cmd = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree_skb(hdev->recv_evt);
|
|
||||||
hdev->recv_evt = NULL;
|
|
||||||
|
|
||||||
/* After this point our queues are empty
|
/* After this point our queues are empty
|
||||||
* and no tasks are scheduled. */
|
* and no tasks are scheduled. */
|
||||||
hdev->close(hdev);
|
hdev->close(hdev);
|
||||||
|
@ -3563,11 +3516,6 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hci_req_pending(struct hci_dev *hdev)
|
|
||||||
{
|
|
||||||
return (hdev->req_status == HCI_REQ_PEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send HCI command */
|
/* Send HCI command */
|
||||||
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
||||||
const void *param)
|
const void *param)
|
||||||
|
@ -3585,7 +3533,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
||||||
/* Stand-alone HCI commands must be flagged as
|
/* Stand-alone HCI commands must be flagged as
|
||||||
* single-command requests.
|
* single-command requests.
|
||||||
*/
|
*/
|
||||||
bt_cb(skb)->req_start = 1;
|
bt_cb(skb)->req.start = true;
|
||||||
|
|
||||||
skb_queue_tail(&hdev->cmd_q, skb);
|
skb_queue_tail(&hdev->cmd_q, skb);
|
||||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||||
|
@ -4263,7 +4211,7 @@ static bool hci_req_is_complete(struct hci_dev *hdev)
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return bt_cb(skb)->req_start;
|
return bt_cb(skb)->req.start;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_resend_last(struct hci_dev *hdev)
|
static void hci_resend_last(struct hci_dev *hdev)
|
||||||
|
@ -4288,9 +4236,10 @@ static void hci_resend_last(struct hci_dev *hdev)
|
||||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status)
|
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
|
||||||
|
hci_req_complete_t *req_complete,
|
||||||
|
hci_req_complete_skb_t *req_complete_skb)
|
||||||
{
|
{
|
||||||
hci_req_complete_t req_complete = NULL;
|
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
@ -4322,36 +4271,29 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status)
|
||||||
* callback would be found in hdev->sent_cmd instead of the
|
* callback would be found in hdev->sent_cmd instead of the
|
||||||
* command queue (hdev->cmd_q).
|
* command queue (hdev->cmd_q).
|
||||||
*/
|
*/
|
||||||
if (hdev->sent_cmd) {
|
if (bt_cb(hdev->sent_cmd)->req.complete) {
|
||||||
req_complete = bt_cb(hdev->sent_cmd)->req_complete;
|
*req_complete = bt_cb(hdev->sent_cmd)->req.complete;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (req_complete) {
|
if (bt_cb(hdev->sent_cmd)->req.complete_skb) {
|
||||||
/* We must set the complete callback to NULL to
|
*req_complete_skb = bt_cb(hdev->sent_cmd)->req.complete_skb;
|
||||||
* avoid calling the callback more than once if
|
return;
|
||||||
* this function gets called again.
|
|
||||||
*/
|
|
||||||
bt_cb(hdev->sent_cmd)->req_complete = NULL;
|
|
||||||
|
|
||||||
goto call_complete;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove all pending commands belonging to this request */
|
/* Remove all pending commands belonging to this request */
|
||||||
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
|
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
|
||||||
while ((skb = __skb_dequeue(&hdev->cmd_q))) {
|
while ((skb = __skb_dequeue(&hdev->cmd_q))) {
|
||||||
if (bt_cb(skb)->req_start) {
|
if (bt_cb(skb)->req.start) {
|
||||||
__skb_queue_head(&hdev->cmd_q, skb);
|
__skb_queue_head(&hdev->cmd_q, skb);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
req_complete = bt_cb(skb)->req_complete;
|
*req_complete = bt_cb(skb)->req.complete;
|
||||||
|
*req_complete_skb = bt_cb(skb)->req.complete_skb;
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
|
spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
|
||||||
|
|
||||||
call_complete:
|
|
||||||
if (req_complete)
|
|
||||||
req_complete(hdev, status, status ? opcode : HCI_OP_NOP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_rx_work(struct work_struct *work)
|
static void hci_rx_work(struct work_struct *work)
|
||||||
|
|
|
@ -114,6 +114,30 @@ static const struct file_operations features_fops = {
|
||||||
.release = single_release,
|
.release = single_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int device_id_show(struct seq_file *f, void *ptr)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = f->private;
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
seq_printf(f, "%4.4x:%4.4x:%4.4x:%4.4x\n", hdev->devid_source,
|
||||||
|
hdev->devid_vendor, hdev->devid_product, hdev->devid_version);
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device_id_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return single_open(file, device_id_show, inode->i_private);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations device_id_fops = {
|
||||||
|
.open = device_id_open,
|
||||||
|
.read = seq_read,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
|
|
||||||
static int device_list_show(struct seq_file *f, void *ptr)
|
static int device_list_show(struct seq_file *f, void *ptr)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = f->private;
|
struct hci_dev *hdev = f->private;
|
||||||
|
@ -335,6 +359,8 @@ void hci_debugfs_create_common(struct hci_dev *hdev)
|
||||||
debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
|
debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
|
||||||
debugfs_create_u8("hardware_error", 0444, hdev->debugfs,
|
debugfs_create_u8("hardware_error", 0444, hdev->debugfs,
|
||||||
&hdev->hw_error_code);
|
&hdev->hw_error_code);
|
||||||
|
debugfs_create_file("device_id", 0444, hdev->debugfs, hdev,
|
||||||
|
&device_id_fops);
|
||||||
|
|
||||||
debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
|
debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
|
||||||
&device_list_fops);
|
&device_list_fops);
|
||||||
|
|
|
@ -1045,11 +1045,6 @@ static void hci_cc_read_local_oob_data(struct hci_dev *hdev,
|
||||||
struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
|
struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
|
||||||
|
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
|
|
||||||
rp->status);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
|
static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
|
||||||
|
@ -1058,15 +1053,8 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
|
||||||
struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
|
struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
|
||||||
|
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
|
|
||||||
rp->hash256, rp->rand256,
|
|
||||||
rp->status);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
__u8 status = *((__u8 *) skb->data);
|
__u8 status = *((__u8 *) skb->data);
|
||||||
|
@ -2732,17 +2720,19 @@ unlock:
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
||||||
|
u16 *opcode, u8 *status,
|
||||||
|
hci_req_complete_t *req_complete,
|
||||||
|
hci_req_complete_skb_t *req_complete_skb)
|
||||||
{
|
{
|
||||||
struct hci_ev_cmd_complete *ev = (void *) skb->data;
|
struct hci_ev_cmd_complete *ev = (void *) skb->data;
|
||||||
u8 status = skb->data[sizeof(*ev)];
|
|
||||||
__u16 opcode;
|
*opcode = __le16_to_cpu(ev->opcode);
|
||||||
|
*status = skb->data[sizeof(*ev)];
|
||||||
|
|
||||||
skb_pull(skb, sizeof(*ev));
|
skb_pull(skb, sizeof(*ev));
|
||||||
|
|
||||||
opcode = __le16_to_cpu(ev->opcode);
|
switch (*opcode) {
|
||||||
|
|
||||||
switch (opcode) {
|
|
||||||
case HCI_OP_INQUIRY_CANCEL:
|
case HCI_OP_INQUIRY_CANCEL:
|
||||||
hci_cc_inquiry_cancel(hdev, skb);
|
hci_cc_inquiry_cancel(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
@ -3020,32 +3010,36 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
|
BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opcode != HCI_OP_NOP)
|
if (*opcode != HCI_OP_NOP)
|
||||||
cancel_delayed_work(&hdev->cmd_timer);
|
cancel_delayed_work(&hdev->cmd_timer);
|
||||||
|
|
||||||
hci_req_cmd_complete(hdev, opcode, status);
|
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
|
||||||
|
|
||||||
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
|
|
||||||
atomic_set(&hdev->cmd_cnt, 1);
|
atomic_set(&hdev->cmd_cnt, 1);
|
||||||
if (!skb_queue_empty(&hdev->cmd_q))
|
|
||||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
hci_req_cmd_complete(hdev, *opcode, *status, req_complete,
|
||||||
}
|
req_complete_skb);
|
||||||
|
|
||||||
|
if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
|
||||||
|
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
||||||
|
u16 *opcode, u8 *status,
|
||||||
|
hci_req_complete_t *req_complete,
|
||||||
|
hci_req_complete_skb_t *req_complete_skb)
|
||||||
{
|
{
|
||||||
struct hci_ev_cmd_status *ev = (void *) skb->data;
|
struct hci_ev_cmd_status *ev = (void *) skb->data;
|
||||||
__u16 opcode;
|
|
||||||
|
|
||||||
skb_pull(skb, sizeof(*ev));
|
skb_pull(skb, sizeof(*ev));
|
||||||
|
|
||||||
opcode = __le16_to_cpu(ev->opcode);
|
*opcode = __le16_to_cpu(ev->opcode);
|
||||||
|
*status = ev->status;
|
||||||
|
|
||||||
switch (opcode) {
|
switch (*opcode) {
|
||||||
case HCI_OP_INQUIRY:
|
case HCI_OP_INQUIRY:
|
||||||
hci_cs_inquiry(hdev, ev->status);
|
hci_cs_inquiry(hdev, ev->status);
|
||||||
break;
|
break;
|
||||||
|
@ -3115,22 +3109,29 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
|
BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opcode != HCI_OP_NOP)
|
if (*opcode != HCI_OP_NOP)
|
||||||
cancel_delayed_work(&hdev->cmd_timer);
|
cancel_delayed_work(&hdev->cmd_timer);
|
||||||
|
|
||||||
if (ev->status ||
|
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
|
||||||
(hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req_event))
|
|
||||||
hci_req_cmd_complete(hdev, opcode, ev->status);
|
|
||||||
|
|
||||||
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
|
|
||||||
atomic_set(&hdev->cmd_cnt, 1);
|
atomic_set(&hdev->cmd_cnt, 1);
|
||||||
if (!skb_queue_empty(&hdev->cmd_q))
|
|
||||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
/* Indicate request completion if the command failed. Also, if
|
||||||
}
|
* we're not waiting for a special event and we get a success
|
||||||
|
* command status we should try to flag the request as completed
|
||||||
|
* (since for this kind of commands there will not be a command
|
||||||
|
* complete event).
|
||||||
|
*/
|
||||||
|
if (ev->status ||
|
||||||
|
(hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event))
|
||||||
|
hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
|
||||||
|
req_complete_skb);
|
||||||
|
|
||||||
|
if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
|
||||||
|
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
@ -5031,32 +5032,79 @@ static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
amp_read_loc_assoc_final_data(hdev, hcon);
|
amp_read_loc_assoc_final_data(hdev, hcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
|
||||||
|
u8 event, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_ev_cmd_complete *ev;
|
||||||
|
struct hci_event_hdr *hdr;
|
||||||
|
|
||||||
|
if (!skb)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (skb->len < sizeof(*hdr)) {
|
||||||
|
BT_ERR("Too short HCI event");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr = (void *) skb->data;
|
||||||
|
skb_pull(skb, HCI_EVENT_HDR_SIZE);
|
||||||
|
|
||||||
|
if (event) {
|
||||||
|
if (hdr->evt != event)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->evt != HCI_EV_CMD_COMPLETE) {
|
||||||
|
BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb->len < sizeof(*ev)) {
|
||||||
|
BT_ERR("Too short cmd_complete event");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev = (void *) skb->data;
|
||||||
|
skb_pull(skb, sizeof(*ev));
|
||||||
|
|
||||||
|
if (opcode != __le16_to_cpu(ev->opcode)) {
|
||||||
|
BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
|
||||||
|
__le16_to_cpu(ev->opcode));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_event_hdr *hdr = (void *) skb->data;
|
struct hci_event_hdr *hdr = (void *) skb->data;
|
||||||
__u8 event = hdr->evt;
|
hci_req_complete_t req_complete = NULL;
|
||||||
|
hci_req_complete_skb_t req_complete_skb = NULL;
|
||||||
|
struct sk_buff *orig_skb = NULL;
|
||||||
|
u8 status = 0, event = hdr->evt, req_evt = 0;
|
||||||
|
u16 opcode = HCI_OP_NOP;
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
|
||||||
|
struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
|
||||||
/* Received events are (currently) only needed when a request is
|
opcode = __le16_to_cpu(cmd_hdr->opcode);
|
||||||
* ongoing so avoid unnecessary memory allocation.
|
hci_req_cmd_complete(hdev, opcode, status, &req_complete,
|
||||||
*/
|
&req_complete_skb);
|
||||||
if (hci_req_pending(hdev)) {
|
req_evt = event;
|
||||||
kfree_skb(hdev->recv_evt);
|
|
||||||
hdev->recv_evt = skb_clone(skb, GFP_KERNEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
/* If it looks like we might end up having to call
|
||||||
|
* req_complete_skb, store a pristine copy of the skb since the
|
||||||
|
* various handlers may modify the original one through
|
||||||
|
* skb_pull() calls, etc.
|
||||||
|
*/
|
||||||
|
if (req_complete_skb || event == HCI_EV_CMD_STATUS ||
|
||||||
|
event == HCI_EV_CMD_COMPLETE)
|
||||||
|
orig_skb = skb_clone(skb, GFP_KERNEL);
|
||||||
|
|
||||||
skb_pull(skb, HCI_EVENT_HDR_SIZE);
|
skb_pull(skb, HCI_EVENT_HDR_SIZE);
|
||||||
|
|
||||||
if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req_event == event) {
|
|
||||||
struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
|
|
||||||
u16 opcode = __le16_to_cpu(cmd_hdr->opcode);
|
|
||||||
|
|
||||||
hci_req_cmd_complete(hdev, opcode, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case HCI_EV_INQUIRY_COMPLETE:
|
case HCI_EV_INQUIRY_COMPLETE:
|
||||||
hci_inquiry_complete_evt(hdev, skb);
|
hci_inquiry_complete_evt(hdev, skb);
|
||||||
|
@ -5099,11 +5147,13 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_EV_CMD_COMPLETE:
|
case HCI_EV_CMD_COMPLETE:
|
||||||
hci_cmd_complete_evt(hdev, skb);
|
hci_cmd_complete_evt(hdev, skb, &opcode, &status,
|
||||||
|
&req_complete, &req_complete_skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_EV_CMD_STATUS:
|
case HCI_EV_CMD_STATUS:
|
||||||
hci_cmd_status_evt(hdev, skb);
|
hci_cmd_status_evt(hdev, skb, &opcode, &status, &req_complete,
|
||||||
|
&req_complete_skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_EV_HARDWARE_ERROR:
|
case HCI_EV_HARDWARE_ERROR:
|
||||||
|
@ -5235,6 +5285,17 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req_complete) {
|
||||||
|
req_complete(hdev, status, opcode);
|
||||||
|
} else if (req_complete_skb) {
|
||||||
|
if (!hci_get_cmd_complete(hdev, opcode, req_evt, orig_skb)) {
|
||||||
|
kfree_skb(orig_skb);
|
||||||
|
orig_skb = NULL;
|
||||||
|
}
|
||||||
|
req_complete_skb(hdev, status, opcode, orig_skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree_skb(orig_skb);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
hdev->stat.evt_rx++;
|
hdev->stat.evt_rx++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
|
||||||
req->err = 0;
|
req->err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
|
static int req_run(struct hci_request *req, hci_req_complete_t complete,
|
||||||
|
hci_req_complete_skb_t complete_skb)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = req->hdev;
|
struct hci_dev *hdev = req->hdev;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
@ -55,7 +56,8 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
|
|
||||||
skb = skb_peek_tail(&req->cmd_q);
|
skb = skb_peek_tail(&req->cmd_q);
|
||||||
bt_cb(skb)->req_complete = complete;
|
bt_cb(skb)->req.complete = complete;
|
||||||
|
bt_cb(skb)->req.complete_skb = complete_skb;
|
||||||
|
|
||||||
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
|
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
|
||||||
skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
|
skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
|
||||||
|
@ -66,6 +68,16 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
|
||||||
|
{
|
||||||
|
return req_run(req, complete, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete)
|
||||||
|
{
|
||||||
|
return req_run(req, NULL, complete);
|
||||||
|
}
|
||||||
|
|
||||||
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
const void *param)
|
const void *param)
|
||||||
{
|
{
|
||||||
|
@ -116,9 +128,9 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skb_queue_empty(&req->cmd_q))
|
if (skb_queue_empty(&req->cmd_q))
|
||||||
bt_cb(skb)->req_start = 1;
|
bt_cb(skb)->req.start = true;
|
||||||
|
|
||||||
bt_cb(skb)->req_event = event;
|
bt_cb(skb)->req.event = event;
|
||||||
|
|
||||||
skb_queue_tail(&req->cmd_q, skb);
|
skb_queue_tail(&req->cmd_q, skb);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,14 @@ struct hci_request {
|
||||||
|
|
||||||
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
|
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
|
||||||
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
|
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
|
||||||
|
int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete);
|
||||||
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
|
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
|
||||||
const void *param);
|
const void *param);
|
||||||
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
||||||
const void *param, u8 event);
|
const void *param, u8 event);
|
||||||
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
|
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
|
||||||
|
hci_req_complete_t *req_complete,
|
||||||
|
hci_req_complete_skb_t *req_complete_skb);
|
||||||
|
|
||||||
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
const void *param);
|
const void *param);
|
||||||
|
|
|
@ -1164,7 +1164,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||||
/* Stand-alone HCI commands must be flagged as
|
/* Stand-alone HCI commands must be flagged as
|
||||||
* single-command requests.
|
* single-command requests.
|
||||||
*/
|
*/
|
||||||
bt_cb(skb)->req_start = 1;
|
bt_cb(skb)->req.start = true;
|
||||||
|
|
||||||
skb_queue_tail(&hdev->cmd_q, skb);
|
skb_queue_tail(&hdev->cmd_q, skb);
|
||||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||||
|
|
|
@ -70,10 +70,11 @@ static void hidp_session_terminate(struct hidp_session *s);
|
||||||
|
|
||||||
static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
|
static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = 0;
|
||||||
memset(ci, 0, sizeof(*ci));
|
memset(ci, 0, sizeof(*ci));
|
||||||
bacpy(&ci->bdaddr, &session->bdaddr);
|
bacpy(&ci->bdaddr, &session->bdaddr);
|
||||||
|
|
||||||
ci->flags = session->flags;
|
ci->flags = session->flags & valid_flags;
|
||||||
ci->state = BT_CONNECTED;
|
ci->state = BT_CONNECTED;
|
||||||
|
|
||||||
if (session->input) {
|
if (session->input) {
|
||||||
|
@ -907,7 +908,7 @@ static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr,
|
||||||
kref_init(&session->ref);
|
kref_init(&session->ref);
|
||||||
atomic_set(&session->state, HIDP_SESSION_IDLING);
|
atomic_set(&session->state, HIDP_SESSION_IDLING);
|
||||||
init_waitqueue_head(&session->state_queue);
|
init_waitqueue_head(&session->state_queue);
|
||||||
session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
|
session->flags = req->flags & BIT(HIDP_BLUETOOTH_VENDOR_ID);
|
||||||
|
|
||||||
/* connection management */
|
/* connection management */
|
||||||
bacpy(&session->bdaddr, bdaddr);
|
bacpy(&session->bdaddr, bdaddr);
|
||||||
|
@ -1312,6 +1313,7 @@ int hidp_connection_add(struct hidp_connadd_req *req,
|
||||||
struct socket *ctrl_sock,
|
struct socket *ctrl_sock,
|
||||||
struct socket *intr_sock)
|
struct socket *intr_sock)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = 0;
|
||||||
struct hidp_session *session;
|
struct hidp_session *session;
|
||||||
struct l2cap_conn *conn;
|
struct l2cap_conn *conn;
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
|
@ -1321,6 +1323,9 @@ int hidp_connection_add(struct hidp_connadd_req *req,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (req->flags & ~valid_flags)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
chan = l2cap_pi(ctrl_sock->sk)->chan;
|
chan = l2cap_pi(ctrl_sock->sk)->chan;
|
||||||
conn = NULL;
|
conn = NULL;
|
||||||
l2cap_chan_lock(chan);
|
l2cap_chan_lock(chan);
|
||||||
|
@ -1351,13 +1356,17 @@ out_conn:
|
||||||
|
|
||||||
int hidp_connection_del(struct hidp_conndel_req *req)
|
int hidp_connection_del(struct hidp_conndel_req *req)
|
||||||
{
|
{
|
||||||
|
u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG);
|
||||||
struct hidp_session *session;
|
struct hidp_session *session;
|
||||||
|
|
||||||
|
if (req->flags & ~valid_flags)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
session = hidp_session_find(&req->bdaddr);
|
session = hidp_session_find(&req->bdaddr);
|
||||||
if (!session)
|
if (!session)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG))
|
if (req->flags & BIT(HIDP_VIRTUAL_CABLE_UNPLUG))
|
||||||
hidp_send_ctrl_message(session,
|
hidp_send_ctrl_message(session,
|
||||||
HIDP_TRANS_HID_CONTROL |
|
HIDP_TRANS_HID_CONTROL |
|
||||||
HIDP_CTRL_VIRTUAL_CABLE_UNPLUG,
|
HIDP_CTRL_VIRTUAL_CABLE_UNPLUG,
|
||||||
|
|
|
@ -292,7 +292,7 @@ static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head,
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
|
||||||
skb_queue_walk(head, skb) {
|
skb_queue_walk(head, skb) {
|
||||||
if (bt_cb(skb)->control.txseq == seq)
|
if (bt_cb(skb)->l2cap.txseq == seq)
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -954,11 +954,11 @@ static inline void __unpack_control(struct l2cap_chan *chan,
|
||||||
{
|
{
|
||||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
|
||||||
__unpack_extended_control(get_unaligned_le32(skb->data),
|
__unpack_extended_control(get_unaligned_le32(skb->data),
|
||||||
&bt_cb(skb)->control);
|
&bt_cb(skb)->l2cap);
|
||||||
skb_pull(skb, L2CAP_EXT_CTRL_SIZE);
|
skb_pull(skb, L2CAP_EXT_CTRL_SIZE);
|
||||||
} else {
|
} else {
|
||||||
__unpack_enhanced_control(get_unaligned_le16(skb->data),
|
__unpack_enhanced_control(get_unaligned_le16(skb->data),
|
||||||
&bt_cb(skb)->control);
|
&bt_cb(skb)->l2cap);
|
||||||
skb_pull(skb, L2CAP_ENH_CTRL_SIZE);
|
skb_pull(skb, L2CAP_ENH_CTRL_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1200,8 +1200,8 @@ static void l2cap_move_setup(struct l2cap_chan *chan)
|
||||||
|
|
||||||
chan->retry_count = 0;
|
chan->retry_count = 0;
|
||||||
skb_queue_walk(&chan->tx_q, skb) {
|
skb_queue_walk(&chan->tx_q, skb) {
|
||||||
if (bt_cb(skb)->control.retries)
|
if (bt_cb(skb)->l2cap.retries)
|
||||||
bt_cb(skb)->control.retries = 1;
|
bt_cb(skb)->l2cap.retries = 1;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1846,8 +1846,8 @@ static void l2cap_streaming_send(struct l2cap_chan *chan,
|
||||||
|
|
||||||
skb = skb_dequeue(&chan->tx_q);
|
skb = skb_dequeue(&chan->tx_q);
|
||||||
|
|
||||||
bt_cb(skb)->control.retries = 1;
|
bt_cb(skb)->l2cap.retries = 1;
|
||||||
control = &bt_cb(skb)->control;
|
control = &bt_cb(skb)->l2cap;
|
||||||
|
|
||||||
control->reqseq = 0;
|
control->reqseq = 0;
|
||||||
control->txseq = chan->next_tx_seq;
|
control->txseq = chan->next_tx_seq;
|
||||||
|
@ -1891,8 +1891,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
|
||||||
|
|
||||||
skb = chan->tx_send_head;
|
skb = chan->tx_send_head;
|
||||||
|
|
||||||
bt_cb(skb)->control.retries = 1;
|
bt_cb(skb)->l2cap.retries = 1;
|
||||||
control = &bt_cb(skb)->control;
|
control = &bt_cb(skb)->l2cap;
|
||||||
|
|
||||||
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
|
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
|
||||||
control->final = 1;
|
control->final = 1;
|
||||||
|
@ -1963,11 +1963,11 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_cb(skb)->control.retries++;
|
bt_cb(skb)->l2cap.retries++;
|
||||||
control = bt_cb(skb)->control;
|
control = bt_cb(skb)->l2cap;
|
||||||
|
|
||||||
if (chan->max_tx != 0 &&
|
if (chan->max_tx != 0 &&
|
||||||
bt_cb(skb)->control.retries > chan->max_tx) {
|
bt_cb(skb)->l2cap.retries > chan->max_tx) {
|
||||||
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
||||||
l2cap_send_disconn_req(chan, ECONNRESET);
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
||||||
l2cap_seq_list_clear(&chan->retrans_list);
|
l2cap_seq_list_clear(&chan->retrans_list);
|
||||||
|
@ -2045,7 +2045,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan,
|
||||||
|
|
||||||
if (chan->unacked_frames) {
|
if (chan->unacked_frames) {
|
||||||
skb_queue_walk(&chan->tx_q, skb) {
|
skb_queue_walk(&chan->tx_q, skb) {
|
||||||
if (bt_cb(skb)->control.txseq == control->reqseq ||
|
if (bt_cb(skb)->l2cap.txseq == control->reqseq ||
|
||||||
skb == chan->tx_send_head)
|
skb == chan->tx_send_head)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2055,7 +2055,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
l2cap_seq_list_append(&chan->retrans_list,
|
l2cap_seq_list_append(&chan->retrans_list,
|
||||||
bt_cb(skb)->control.txseq);
|
bt_cb(skb)->l2cap.txseq);
|
||||||
}
|
}
|
||||||
|
|
||||||
l2cap_ertm_resend(chan);
|
l2cap_ertm_resend(chan);
|
||||||
|
@ -2267,8 +2267,8 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_cb(skb)->control.fcs = chan->fcs;
|
bt_cb(skb)->l2cap.fcs = chan->fcs;
|
||||||
bt_cb(skb)->control.retries = 0;
|
bt_cb(skb)->l2cap.retries = 0;
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2321,7 +2321,7 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
|
||||||
return PTR_ERR(skb);
|
return PTR_ERR(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_cb(skb)->control.sar = sar;
|
bt_cb(skb)->l2cap.sar = sar;
|
||||||
__skb_queue_tail(seg_queue, skb);
|
__skb_queue_tail(seg_queue, skb);
|
||||||
|
|
||||||
len -= pdu_len;
|
len -= pdu_len;
|
||||||
|
@ -2856,7 +2856,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Don't send frame to the channel it came from */
|
/* Don't send frame to the channel it came from */
|
||||||
if (bt_cb(skb)->chan == chan)
|
if (bt_cb(skb)->l2cap.chan == chan)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
nskb = skb_clone(skb, GFP_KERNEL);
|
nskb = skb_clone(skb, GFP_KERNEL);
|
||||||
|
@ -5918,7 +5918,7 @@ static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
|
||||||
|
|
||||||
skb_unlink(skb, &chan->srej_q);
|
skb_unlink(skb, &chan->srej_q);
|
||||||
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
|
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
|
||||||
err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->control);
|
err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->l2cap);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -5952,7 +5952,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) {
|
if (chan->max_tx != 0 && bt_cb(skb)->l2cap.retries >= chan->max_tx) {
|
||||||
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
||||||
l2cap_send_disconn_req(chan, ECONNRESET);
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
||||||
return;
|
return;
|
||||||
|
@ -6005,7 +6005,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan,
|
||||||
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
|
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
|
||||||
|
|
||||||
if (chan->max_tx && skb &&
|
if (chan->max_tx && skb &&
|
||||||
bt_cb(skb)->control.retries >= chan->max_tx) {
|
bt_cb(skb)->l2cap.retries >= chan->max_tx) {
|
||||||
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
||||||
l2cap_send_disconn_req(chan, ECONNRESET);
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
||||||
return;
|
return;
|
||||||
|
@ -6565,7 +6565,7 @@ static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
|
||||||
|
|
||||||
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
|
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct l2cap_ctrl *control = &bt_cb(skb)->control;
|
struct l2cap_ctrl *control = &bt_cb(skb)->l2cap;
|
||||||
u16 len;
|
u16 len;
|
||||||
u8 event;
|
u8 event;
|
||||||
|
|
||||||
|
@ -6864,8 +6864,8 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
/* Store remote BD_ADDR and PSM for msg_name */
|
/* Store remote BD_ADDR and PSM for msg_name */
|
||||||
bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
|
bacpy(&bt_cb(skb)->l2cap.bdaddr, &hcon->dst);
|
||||||
bt_cb(skb)->psm = psm;
|
bt_cb(skb)->l2cap.psm = psm;
|
||||||
|
|
||||||
if (!chan->ops->recv(chan, skb)) {
|
if (!chan->ops->recv(chan, skb)) {
|
||||||
l2cap_chan_put(chan);
|
l2cap_chan_put(chan);
|
||||||
|
|
|
@ -1330,7 +1330,7 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
|
||||||
|
|
||||||
skb->priority = sk->sk_priority;
|
skb->priority = sk->sk_priority;
|
||||||
|
|
||||||
bt_cb(skb)->chan = chan;
|
bt_cb(skb)->l2cap.chan = chan;
|
||||||
|
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
|
@ -1444,8 +1444,8 @@ static void l2cap_skb_msg_name(struct sk_buff *skb, void *msg_name,
|
||||||
|
|
||||||
memset(la, 0, sizeof(struct sockaddr_l2));
|
memset(la, 0, sizeof(struct sockaddr_l2));
|
||||||
la->l2_family = AF_BLUETOOTH;
|
la->l2_family = AF_BLUETOOTH;
|
||||||
la->l2_psm = bt_cb(skb)->psm;
|
la->l2_psm = bt_cb(skb)->l2cap.psm;
|
||||||
bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr);
|
bacpy(&la->l2_bdaddr, &bt_cb(skb)->l2cap.bdaddr);
|
||||||
|
|
||||||
*msg_namelen = sizeof(struct sockaddr_l2);
|
*msg_namelen = sizeof(struct sockaddr_l2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -985,14 +985,27 @@ static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
|
||||||
/* Instance 0 always manages the "Tx Power" and "Flags" fields */
|
/* Instance 0 always manages the "Tx Power" and "Flags" fields */
|
||||||
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
|
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
|
||||||
|
|
||||||
/* For instance 0, assemble the flags from global settings */
|
/* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting corresponds
|
||||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE) ||
|
* to the "connectable" instance flag.
|
||||||
get_connectable(hdev))
|
*/
|
||||||
|
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
|
||||||
flags |= MGMT_ADV_FLAG_CONNECTABLE;
|
flags |= MGMT_ADV_FLAG_CONNECTABLE;
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
|
||||||
|
{
|
||||||
|
/* Ignore instance 0 and other unsupported instances */
|
||||||
|
if (instance != 0x01)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* TODO: Take into account the "appearance" and "local-name" flags here.
|
||||||
|
* These are currently being ignored as they are not supported.
|
||||||
|
*/
|
||||||
|
return hdev->adv_instance.scan_rsp_len;
|
||||||
|
}
|
||||||
|
|
||||||
static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||||
{
|
{
|
||||||
u8 ad_len = 0, flags = 0;
|
u8 ad_len = 0, flags = 0;
|
||||||
|
@ -1030,6 +1043,14 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (instance) {
|
||||||
|
memcpy(ptr, hdev->adv_instance.adv_data,
|
||||||
|
hdev->adv_instance.adv_data_len);
|
||||||
|
|
||||||
|
ad_len += hdev->adv_instance.adv_data_len;
|
||||||
|
ptr += hdev->adv_instance.adv_data_len;
|
||||||
|
}
|
||||||
|
|
||||||
/* Provide Tx Power only if we can provide a valid value for it */
|
/* Provide Tx Power only if we can provide a valid value for it */
|
||||||
if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
|
if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
|
||||||
(instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
|
(instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
|
||||||
|
@ -1041,12 +1062,6 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||||
ptr += 3;
|
ptr += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance) {
|
|
||||||
memcpy(ptr, hdev->adv_instance.adv_data,
|
|
||||||
hdev->adv_instance.adv_data_len);
|
|
||||||
ad_len += hdev->adv_instance.adv_data_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ad_len;
|
return ad_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1242,7 +1257,12 @@ static void enable_advertising(struct hci_request *req)
|
||||||
|
|
||||||
instance = get_current_adv_instance(hdev);
|
instance = get_current_adv_instance(hdev);
|
||||||
flags = get_adv_instance_flags(hdev, instance);
|
flags = get_adv_instance_flags(hdev, instance);
|
||||||
connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE);
|
|
||||||
|
/* If the "connectable" instance flag was not set, then choose between
|
||||||
|
* ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
|
||||||
|
*/
|
||||||
|
connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
|
||||||
|
get_connectable(hdev);
|
||||||
|
|
||||||
/* Set require_privacy to true only when non-connectable
|
/* Set require_privacy to true only when non-connectable
|
||||||
* advertising is used. In that case it is fine to use a
|
* advertising is used. In that case it is fine to use a
|
||||||
|
@ -1254,7 +1274,14 @@ static void enable_advertising(struct hci_request *req)
|
||||||
memset(&cp, 0, sizeof(cp));
|
memset(&cp, 0, sizeof(cp));
|
||||||
cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
|
cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
|
||||||
cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
|
cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
|
||||||
cp.type = connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND;
|
|
||||||
|
if (connectable)
|
||||||
|
cp.type = LE_ADV_IND;
|
||||||
|
else if (get_adv_instance_scan_rsp_len(hdev, instance))
|
||||||
|
cp.type = LE_ADV_SCAN_IND;
|
||||||
|
else
|
||||||
|
cp.type = LE_ADV_NONCONN_IND;
|
||||||
|
|
||||||
cp.own_address_type = own_addr_type;
|
cp.own_address_type = own_addr_type;
|
||||||
cp.channel_map = hdev->le_adv_channel_map;
|
cp.channel_map = hdev->le_adv_channel_map;
|
||||||
|
|
||||||
|
@ -2088,7 +2115,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||||
|
|
||||||
no_scan_update:
|
no_scan_update:
|
||||||
/* Update the advertising parameters if necessary */
|
/* Update the advertising parameters if necessary */
|
||||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||||
|
hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||||
enable_advertising(&req);
|
enable_advertising(&req);
|
||||||
|
|
||||||
err = hci_req_run(&req, set_connectable_complete);
|
err = hci_req_run(&req, set_connectable_complete);
|
||||||
|
@ -3757,10 +3785,70 @@ failed:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
|
||||||
|
u16 opcode, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct mgmt_rp_read_local_oob_data mgmt_rp;
|
||||||
|
size_t rp_size = sizeof(mgmt_rp);
|
||||||
|
struct mgmt_pending_cmd *cmd;
|
||||||
|
|
||||||
|
BT_DBG("%s status %u", hdev->name, status);
|
||||||
|
|
||||||
|
cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
|
||||||
|
if (!cmd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (status || !skb) {
|
||||||
|
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||||
|
status ? mgmt_status(status) : MGMT_STATUS_FAILED);
|
||||||
|
goto remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&mgmt_rp, 0, sizeof(mgmt_rp));
|
||||||
|
|
||||||
|
if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) {
|
||||||
|
struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
|
||||||
|
|
||||||
|
if (skb->len < sizeof(*rp)) {
|
||||||
|
mgmt_cmd_status(cmd->sk, hdev->id,
|
||||||
|
MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||||
|
MGMT_STATUS_FAILED);
|
||||||
|
goto remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(mgmt_rp.hash192, rp->hash, sizeof(rp->hash));
|
||||||
|
memcpy(mgmt_rp.rand192, rp->rand, sizeof(rp->rand));
|
||||||
|
|
||||||
|
rp_size -= sizeof(mgmt_rp.hash256) + sizeof(mgmt_rp.rand256);
|
||||||
|
} else {
|
||||||
|
struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
|
||||||
|
|
||||||
|
if (skb->len < sizeof(*rp)) {
|
||||||
|
mgmt_cmd_status(cmd->sk, hdev->id,
|
||||||
|
MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||||
|
MGMT_STATUS_FAILED);
|
||||||
|
goto remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(mgmt_rp.hash192, rp->hash192, sizeof(rp->hash192));
|
||||||
|
memcpy(mgmt_rp.rand192, rp->rand192, sizeof(rp->rand192));
|
||||||
|
|
||||||
|
memcpy(mgmt_rp.hash256, rp->hash256, sizeof(rp->hash256));
|
||||||
|
memcpy(mgmt_rp.rand256, rp->rand256, sizeof(rp->rand256));
|
||||||
|
}
|
||||||
|
|
||||||
|
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||||
|
MGMT_STATUS_SUCCESS, &mgmt_rp, rp_size);
|
||||||
|
|
||||||
|
remove:
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
|
static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
|
||||||
void *data, u16 data_len)
|
void *data, u16 data_len)
|
||||||
{
|
{
|
||||||
struct mgmt_pending_cmd *cmd;
|
struct mgmt_pending_cmd *cmd;
|
||||||
|
struct hci_request req;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
@ -3791,12 +3879,14 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bredr_sc_enabled(hdev))
|
hci_req_init(&req, hdev);
|
||||||
err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
|
|
||||||
0, NULL);
|
|
||||||
else
|
|
||||||
err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
|
|
||||||
|
|
||||||
|
if (bredr_sc_enabled(hdev))
|
||||||
|
hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL);
|
||||||
|
else
|
||||||
|
hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
|
||||||
|
|
||||||
|
err = hci_req_run_skb(&req, read_local_oob_data_complete);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
|
@ -6388,46 +6478,41 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!hdev_is_powered(hdev))
|
if (hdev_is_powered(hdev)) {
|
||||||
return mgmt_cmd_complete(sk, hdev->id,
|
switch (cp->type) {
|
||||||
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
|
case BIT(BDADDR_BREDR):
|
||||||
MGMT_STATUS_NOT_POWERED,
|
status = mgmt_bredr_support(hdev);
|
||||||
&cp->type, sizeof(cp->type));
|
if (status)
|
||||||
|
eir_len = 0;
|
||||||
switch (cp->type) {
|
else
|
||||||
case BIT(BDADDR_BREDR):
|
eir_len = 5;
|
||||||
status = mgmt_bredr_support(hdev);
|
break;
|
||||||
if (status)
|
case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
|
||||||
return mgmt_cmd_complete(sk, hdev->id,
|
status = mgmt_le_support(hdev);
|
||||||
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
|
if (status)
|
||||||
status, &cp->type,
|
eir_len = 0;
|
||||||
sizeof(cp->type));
|
else
|
||||||
eir_len = 5;
|
eir_len = 9 + 3 + 18 + 18 + 3;
|
||||||
break;
|
break;
|
||||||
case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
|
default:
|
||||||
status = mgmt_le_support(hdev);
|
status = MGMT_STATUS_INVALID_PARAMS;
|
||||||
if (status)
|
eir_len = 0;
|
||||||
return mgmt_cmd_complete(sk, hdev->id,
|
break;
|
||||||
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
|
}
|
||||||
status, &cp->type,
|
} else {
|
||||||
sizeof(cp->type));
|
status = MGMT_STATUS_NOT_POWERED;
|
||||||
eir_len = 9 + 3 + 18 + 18 + 3;
|
eir_len = 0;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return mgmt_cmd_complete(sk, hdev->id,
|
|
||||||
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
|
|
||||||
MGMT_STATUS_INVALID_PARAMS,
|
|
||||||
&cp->type, sizeof(cp->type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
|
|
||||||
rp_len = sizeof(*rp) + eir_len;
|
rp_len = sizeof(*rp) + eir_len;
|
||||||
rp = kmalloc(rp_len, GFP_ATOMIC);
|
rp = kmalloc(rp_len, GFP_ATOMIC);
|
||||||
if (!rp) {
|
if (!rp)
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
if (status)
|
||||||
|
goto complete;
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
eir_len = 0;
|
eir_len = 0;
|
||||||
switch (cp->type) {
|
switch (cp->type) {
|
||||||
|
@ -6439,20 +6524,30 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
|
||||||
if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
|
if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
|
||||||
smp_generate_oob(hdev, hash, rand) < 0) {
|
smp_generate_oob(hdev, hash, rand) < 0) {
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
err = mgmt_cmd_complete(sk, hdev->id,
|
status = MGMT_STATUS_FAILED;
|
||||||
MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
|
goto complete;
|
||||||
MGMT_STATUS_FAILED,
|
|
||||||
&cp->type, sizeof(cp->type));
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This should return the active RPA, but since the RPA
|
||||||
|
* is only programmed on demand, it is really hard to fill
|
||||||
|
* this in at the moment. For now disallow retrieving
|
||||||
|
* local out-of-band data when privacy is in use.
|
||||||
|
*
|
||||||
|
* Returning the identity address will not help here since
|
||||||
|
* pairing happens before the identity resolving key is
|
||||||
|
* known and thus the connection establishment happens
|
||||||
|
* based on the RPA and not the identity address.
|
||||||
|
*/
|
||||||
if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
|
if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
|
||||||
memcpy(addr, &hdev->rpa, 6);
|
hci_dev_unlock(hdev);
|
||||||
addr[6] = 0x01;
|
status = MGMT_STATUS_REJECTED;
|
||||||
} else if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
|
goto complete;
|
||||||
!bacmp(&hdev->bdaddr, BDADDR_ANY) ||
|
}
|
||||||
(!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
|
|
||||||
bacmp(&hdev->static_addr, BDADDR_ANY))) {
|
if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
|
||||||
|
!bacmp(&hdev->bdaddr, BDADDR_ANY) ||
|
||||||
|
(!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
|
||||||
|
bacmp(&hdev->static_addr, BDADDR_ANY))) {
|
||||||
memcpy(addr, &hdev->static_addr, 6);
|
memcpy(addr, &hdev->static_addr, 6);
|
||||||
addr[6] = 0x01;
|
addr[6] = 0x01;
|
||||||
} else {
|
} else {
|
||||||
|
@ -6491,16 +6586,19 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
rp->type = cp->type;
|
|
||||||
rp->eir_len = cpu_to_le16(eir_len);
|
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS);
|
hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS);
|
||||||
|
|
||||||
|
status = MGMT_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
complete:
|
||||||
|
rp->type = cp->type;
|
||||||
|
rp->eir_len = cpu_to_le16(eir_len);
|
||||||
|
|
||||||
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
|
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
|
||||||
MGMT_STATUS_SUCCESS, rp, sizeof(*rp) + eir_len);
|
status, rp, sizeof(*rp) + eir_len);
|
||||||
if (err < 0)
|
if (err < 0 || status)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
|
err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
|
||||||
|
@ -7899,43 +7997,6 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
|
||||||
cmd ? cmd->sk : NULL);
|
cmd ? cmd->sk : NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
|
|
||||||
u8 *rand192, u8 *hash256, u8 *rand256,
|
|
||||||
u8 status)
|
|
||||||
{
|
|
||||||
struct mgmt_pending_cmd *cmd;
|
|
||||||
|
|
||||||
BT_DBG("%s status %u", hdev->name, status);
|
|
||||||
|
|
||||||
cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
|
|
||||||
if (!cmd)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
|
|
||||||
mgmt_status(status));
|
|
||||||
} else {
|
|
||||||
struct mgmt_rp_read_local_oob_data rp;
|
|
||||||
size_t rp_size = sizeof(rp);
|
|
||||||
|
|
||||||
memcpy(rp.hash192, hash192, sizeof(rp.hash192));
|
|
||||||
memcpy(rp.rand192, rand192, sizeof(rp.rand192));
|
|
||||||
|
|
||||||
if (bredr_sc_enabled(hdev) && hash256 && rand256) {
|
|
||||||
memcpy(rp.hash256, hash256, sizeof(rp.hash256));
|
|
||||||
memcpy(rp.rand256, rand256, sizeof(rp.rand256));
|
|
||||||
} else {
|
|
||||||
rp_size -= sizeof(rp.hash256) + sizeof(rp.rand256);
|
|
||||||
}
|
|
||||||
|
|
||||||
mgmt_cmd_complete(cmd->sk, hdev->id,
|
|
||||||
MGMT_OP_READ_LOCAL_OOB_DATA, 0,
|
|
||||||
&rp, rp_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
mgmt_pending_remove(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
|
static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
SOFTWARE IS DISCLAIMED.
|
SOFTWARE IS DISCLAIMED.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
|
||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
|
|
||||||
|
@ -154,6 +156,21 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32],
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char test_ecdh_buffer[32];
|
||||||
|
|
||||||
|
static ssize_t test_ecdh_read(struct file *file, char __user *user_buf,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
|
{
|
||||||
|
return simple_read_from_buffer(user_buf, count, ppos, test_ecdh_buffer,
|
||||||
|
strlen(test_ecdh_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations test_ecdh_fops = {
|
||||||
|
.open = simple_open,
|
||||||
|
.read = test_ecdh_read,
|
||||||
|
.llseek = default_llseek,
|
||||||
|
};
|
||||||
|
|
||||||
static int __init test_ecdh(void)
|
static int __init test_ecdh(void)
|
||||||
{
|
{
|
||||||
ktime_t calltime, delta, rettime;
|
ktime_t calltime, delta, rettime;
|
||||||
|
@ -165,19 +182,19 @@ static int __init test_ecdh(void)
|
||||||
err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1);
|
err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("ECDH sample 1 failed");
|
BT_ERR("ECDH sample 1 failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2);
|
err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("ECDH sample 2 failed");
|
BT_ERR("ECDH sample 2 failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3);
|
err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("ECDH sample 3 failed");
|
BT_ERR("ECDH sample 3 failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
rettime = ktime_get();
|
rettime = ktime_get();
|
||||||
|
@ -186,7 +203,17 @@ static int __init test_ecdh(void)
|
||||||
|
|
||||||
BT_INFO("ECDH test passed in %llu usecs", duration);
|
BT_INFO("ECDH test passed in %llu usecs", duration);
|
||||||
|
|
||||||
return 0;
|
done:
|
||||||
|
if (!err)
|
||||||
|
snprintf(test_ecdh_buffer, sizeof(test_ecdh_buffer),
|
||||||
|
"PASS (%llu usecs)\n", duration);
|
||||||
|
else
|
||||||
|
snprintf(test_ecdh_buffer, sizeof(test_ecdh_buffer), "FAIL\n");
|
||||||
|
|
||||||
|
debugfs_create_file("selftest_ecdh", 0444, bt_debugfs, NULL,
|
||||||
|
&test_ecdh_fops);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -3017,7 +3017,7 @@ static struct sk_buff *smp_alloc_skb_cb(struct l2cap_chan *chan,
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
skb->priority = HCI_PRIO_MAX;
|
skb->priority = HCI_PRIO_MAX;
|
||||||
bt_cb(skb)->chan = chan;
|
bt_cb(skb)->l2cap.chan = chan;
|
||||||
|
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
|
@ -3549,6 +3549,21 @@ static int __init test_h6(struct crypto_hash *tfm_cmac)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char test_smp_buffer[32];
|
||||||
|
|
||||||
|
static ssize_t test_smp_read(struct file *file, char __user *user_buf,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
|
{
|
||||||
|
return simple_read_from_buffer(user_buf, count, ppos, test_smp_buffer,
|
||||||
|
strlen(test_smp_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations test_smp_fops = {
|
||||||
|
.open = simple_open,
|
||||||
|
.read = test_smp_read,
|
||||||
|
.llseek = default_llseek,
|
||||||
|
};
|
||||||
|
|
||||||
static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
|
static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
|
||||||
struct crypto_hash *tfm_cmac)
|
struct crypto_hash *tfm_cmac)
|
||||||
{
|
{
|
||||||
|
@ -3561,49 +3576,49 @@ static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
|
||||||
err = test_ah(tfm_aes);
|
err = test_ah(tfm_aes);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("smp_ah test failed");
|
BT_ERR("smp_ah test failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_c1(tfm_aes);
|
err = test_c1(tfm_aes);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("smp_c1 test failed");
|
BT_ERR("smp_c1 test failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_s1(tfm_aes);
|
err = test_s1(tfm_aes);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("smp_s1 test failed");
|
BT_ERR("smp_s1 test failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_f4(tfm_cmac);
|
err = test_f4(tfm_cmac);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("smp_f4 test failed");
|
BT_ERR("smp_f4 test failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_f5(tfm_cmac);
|
err = test_f5(tfm_cmac);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("smp_f5 test failed");
|
BT_ERR("smp_f5 test failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_f6(tfm_cmac);
|
err = test_f6(tfm_cmac);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("smp_f6 test failed");
|
BT_ERR("smp_f6 test failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_g2(tfm_cmac);
|
err = test_g2(tfm_cmac);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("smp_g2 test failed");
|
BT_ERR("smp_g2 test failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test_h6(tfm_cmac);
|
err = test_h6(tfm_cmac);
|
||||||
if (err) {
|
if (err) {
|
||||||
BT_ERR("smp_h6 test failed");
|
BT_ERR("smp_h6 test failed");
|
||||||
return err;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
rettime = ktime_get();
|
rettime = ktime_get();
|
||||||
|
@ -3612,7 +3627,17 @@ static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
|
||||||
|
|
||||||
BT_INFO("SMP test passed in %llu usecs", duration);
|
BT_INFO("SMP test passed in %llu usecs", duration);
|
||||||
|
|
||||||
return 0;
|
done:
|
||||||
|
if (!err)
|
||||||
|
snprintf(test_smp_buffer, sizeof(test_smp_buffer),
|
||||||
|
"PASS (%llu usecs)\n", duration);
|
||||||
|
else
|
||||||
|
snprintf(test_smp_buffer, sizeof(test_smp_buffer), "FAIL\n");
|
||||||
|
|
||||||
|
debugfs_create_file("selftest_smp", 0444, bt_debugfs, NULL,
|
||||||
|
&test_smp_fops);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __init bt_selftest_smp(void)
|
int __init bt_selftest_smp(void)
|
||||||
|
|
Loading…
Reference in New Issue