Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-next-2.6
This commit is contained in:
commit
f3b3e36f4e
21 changed files with 971 additions and 281 deletions
|
@ -71,6 +71,9 @@ static struct usb_device_id btusb_table[] = {
|
|||
/* Apple MacBookAir3,1, MacBookAir3,2 */
|
||||
{ USB_DEVICE(0x05ac, 0x821b) },
|
||||
|
||||
/* Apple MacBookPro8,2 */
|
||||
{ USB_DEVICE(0x05ac, 0x821a) },
|
||||
|
||||
/* AVM BlueFRITZ! USB v2.0 */
|
||||
{ USB_DEVICE(0x057c, 0x3800) },
|
||||
|
||||
|
@ -690,7 +693,8 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
break;
|
||||
|
||||
case HCI_ACLDATA_PKT:
|
||||
if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
|
||||
if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 &&
|
||||
hdev->conn_hash.le_num < 1))
|
||||
return -ENODEV;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
|
|
|
@ -84,6 +84,8 @@ enum {
|
|||
HCI_SERVICE_CACHE,
|
||||
HCI_LINK_KEYS,
|
||||
HCI_DEBUG_KEYS,
|
||||
|
||||
HCI_RESET,
|
||||
};
|
||||
|
||||
/* HCI ioctl defines */
|
||||
|
@ -426,6 +428,18 @@ struct hci_rp_user_confirm_reply {
|
|||
|
||||
#define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d
|
||||
|
||||
#define HCI_OP_REMOTE_OOB_DATA_REPLY 0x0430
|
||||
struct hci_cp_remote_oob_data_reply {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 hash[16];
|
||||
__u8 randomizer[16];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_REMOTE_OOB_DATA_NEG_REPLY 0x0433
|
||||
struct hci_cp_remote_oob_data_neg_reply {
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434
|
||||
struct hci_cp_io_capability_neg_reply {
|
||||
bdaddr_t bdaddr;
|
||||
|
@ -535,15 +549,17 @@ struct hci_cp_delete_stored_link_key {
|
|||
__u8 delete_all;
|
||||
} __packed;
|
||||
|
||||
#define HCI_MAX_NAME_LENGTH 248
|
||||
|
||||
#define HCI_OP_WRITE_LOCAL_NAME 0x0c13
|
||||
struct hci_cp_write_local_name {
|
||||
__u8 name[248];
|
||||
__u8 name[HCI_MAX_NAME_LENGTH];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_LOCAL_NAME 0x0c14
|
||||
struct hci_rp_read_local_name {
|
||||
__u8 status;
|
||||
__u8 name[248];
|
||||
__u8 name[HCI_MAX_NAME_LENGTH];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_WRITE_CA_TIMEOUT 0x0c16
|
||||
|
@ -600,6 +616,14 @@ struct hci_cp_host_buffer_size {
|
|||
|
||||
#define HCI_OP_WRITE_INQUIRY_MODE 0x0c45
|
||||
|
||||
#define HCI_MAX_EIR_LENGTH 240
|
||||
|
||||
#define HCI_OP_WRITE_EIR 0x0c52
|
||||
struct hci_cp_write_eir {
|
||||
uint8_t fec;
|
||||
uint8_t data[HCI_MAX_EIR_LENGTH];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_SSP_MODE 0x0c55
|
||||
struct hci_rp_read_ssp_mode {
|
||||
__u8 status;
|
||||
|
@ -611,6 +635,13 @@ struct hci_cp_write_ssp_mode {
|
|||
__u8 mode;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_LOCAL_OOB_DATA 0x0c57
|
||||
struct hci_rp_read_local_oob_data {
|
||||
__u8 status;
|
||||
__u8 hash[16];
|
||||
__u8 randomizer[16];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58
|
||||
|
||||
#define HCI_OP_READ_LOCAL_VERSION 0x1001
|
||||
|
@ -745,7 +776,7 @@ struct hci_ev_auth_complete {
|
|||
struct hci_ev_remote_name {
|
||||
__u8 status;
|
||||
bdaddr_t bdaddr;
|
||||
__u8 name[248];
|
||||
__u8 name[HCI_MAX_NAME_LENGTH];
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_ENCRYPT_CHANGE 0x08
|
||||
|
@ -953,6 +984,11 @@ struct hci_ev_user_confirm_req {
|
|||
__le32 passkey;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35
|
||||
struct hci_ev_remote_oob_data_request {
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36
|
||||
struct hci_ev_simple_pair_complete {
|
||||
__u8 status;
|
||||
|
|
|
@ -82,6 +82,13 @@ struct link_key {
|
|||
u8 pin_len;
|
||||
};
|
||||
|
||||
struct oob_data {
|
||||
struct list_head list;
|
||||
bdaddr_t bdaddr;
|
||||
u8 hash[16];
|
||||
u8 randomizer[16];
|
||||
};
|
||||
|
||||
#define NUM_REASSEMBLY 4
|
||||
struct hci_dev {
|
||||
struct list_head list;
|
||||
|
@ -94,7 +101,8 @@ struct hci_dev {
|
|||
__u8 bus;
|
||||
__u8 dev_type;
|
||||
bdaddr_t bdaddr;
|
||||
__u8 dev_name[248];
|
||||
__u8 dev_name[HCI_MAX_NAME_LENGTH];
|
||||
__u8 eir[HCI_MAX_EIR_LENGTH];
|
||||
__u8 dev_class[3];
|
||||
__u8 major_class;
|
||||
__u8 minor_class;
|
||||
|
@ -169,6 +177,8 @@ struct hci_dev {
|
|||
|
||||
struct list_head link_keys;
|
||||
|
||||
struct list_head remote_oob_data;
|
||||
|
||||
struct hci_dev_stats stat;
|
||||
|
||||
struct sk_buff_head driver_init;
|
||||
|
@ -505,6 +515,13 @@ int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
|
|||
u8 *key, u8 type, u8 pin_len);
|
||||
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
|
||||
int hci_remote_oob_data_clear(struct hci_dev *hdev);
|
||||
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr);
|
||||
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
|
||||
u8 *randomizer);
|
||||
int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
|
||||
void hci_del_off_timer(struct hci_dev *hdev);
|
||||
|
||||
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
|
@ -767,6 +784,12 @@ int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
|
|||
int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr,
|
||||
u8 status);
|
||||
int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status);
|
||||
int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status);
|
||||
int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
|
||||
u8 status);
|
||||
int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
|
||||
u8 *eir);
|
||||
int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name);
|
||||
|
||||
/* HCI info for socket */
|
||||
#define hci_pi(sk) ((struct hci_pinfo *) sk)
|
||||
|
|
|
@ -280,7 +280,6 @@ struct l2cap_conn_param_update_rsp {
|
|||
struct l2cap_chan_list {
|
||||
struct sock *head;
|
||||
rwlock_t lock;
|
||||
long num;
|
||||
};
|
||||
|
||||
struct l2cap_conn {
|
||||
|
@ -302,7 +301,6 @@ struct l2cap_conn {
|
|||
|
||||
struct sk_buff *rx_skb;
|
||||
__u32 rx_len;
|
||||
__u8 rx_ident;
|
||||
__u8 tx_ident;
|
||||
|
||||
__u8 disc_reason;
|
||||
|
|
|
@ -41,6 +41,10 @@ struct mgmt_rp_read_index_list {
|
|||
__le16 index[0];
|
||||
} __packed;
|
||||
|
||||
/* Reserve one extra byte for names in management messages so that they
|
||||
* are always guaranteed to be nul-terminated */
|
||||
#define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1)
|
||||
|
||||
#define MGMT_OP_READ_INFO 0x0004
|
||||
struct mgmt_rp_read_info {
|
||||
__u8 type;
|
||||
|
@ -55,6 +59,7 @@ struct mgmt_rp_read_info {
|
|||
__u16 manufacturer;
|
||||
__u8 hci_ver;
|
||||
__u16 hci_rev;
|
||||
__u8 name[MGMT_MAX_NAME_LENGTH];
|
||||
} __packed;
|
||||
|
||||
struct mgmt_mode {
|
||||
|
@ -167,6 +172,29 @@ struct mgmt_rp_user_confirm_reply {
|
|||
|
||||
#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x0016
|
||||
|
||||
#define MGMT_OP_SET_LOCAL_NAME 0x0017
|
||||
struct mgmt_cp_set_local_name {
|
||||
__u8 name[MGMT_MAX_NAME_LENGTH];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0018
|
||||
struct mgmt_rp_read_local_oob_data {
|
||||
__u8 hash[16];
|
||||
__u8 randomizer[16];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0019
|
||||
struct mgmt_cp_add_remote_oob_data {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 hash[16];
|
||||
__u8 randomizer[16];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x001A
|
||||
struct mgmt_cp_remove_remote_oob_data {
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||
struct mgmt_ev_cmd_complete {
|
||||
__le16 opcode;
|
||||
|
@ -234,3 +262,22 @@ struct mgmt_ev_auth_failed {
|
|||
bdaddr_t bdaddr;
|
||||
__u8 status;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_LOCAL_NAME_CHANGED 0x0011
|
||||
struct mgmt_ev_local_name_changed {
|
||||
__u8 name[MGMT_MAX_NAME_LENGTH];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_DEVICE_FOUND 0x0012
|
||||
struct mgmt_ev_device_found {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 dev_class[3];
|
||||
__s8 rssi;
|
||||
__u8 eir[HCI_MAX_EIR_LENGTH];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_REMOTE_NAME 0x0013
|
||||
struct mgmt_ev_remote_name {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 name[MGMT_MAX_NAME_LENGTH];
|
||||
} __packed;
|
||||
|
|
|
@ -23,88 +23,88 @@
|
|||
#include <linux/crc32.h>
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
// Limits
|
||||
#define BNEP_MAX_PROTO_FILTERS 5
|
||||
#define BNEP_MAX_MULTICAST_FILTERS 20
|
||||
/* Limits */
|
||||
#define BNEP_MAX_PROTO_FILTERS 5
|
||||
#define BNEP_MAX_MULTICAST_FILTERS 20
|
||||
|
||||
// UUIDs
|
||||
#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
|
||||
#define BNEP_UUID16 0x02
|
||||
#define BNEP_UUID32 0x04
|
||||
#define BNEP_UUID128 0x16
|
||||
/* UUIDs */
|
||||
#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
|
||||
#define BNEP_UUID16 0x02
|
||||
#define BNEP_UUID32 0x04
|
||||
#define BNEP_UUID128 0x16
|
||||
|
||||
#define BNEP_SVC_PANU 0x1115
|
||||
#define BNEP_SVC_NAP 0x1116
|
||||
#define BNEP_SVC_GN 0x1117
|
||||
#define BNEP_SVC_PANU 0x1115
|
||||
#define BNEP_SVC_NAP 0x1116
|
||||
#define BNEP_SVC_GN 0x1117
|
||||
|
||||
// Packet types
|
||||
#define BNEP_GENERAL 0x00
|
||||
#define BNEP_CONTROL 0x01
|
||||
#define BNEP_COMPRESSED 0x02
|
||||
#define BNEP_COMPRESSED_SRC_ONLY 0x03
|
||||
#define BNEP_COMPRESSED_DST_ONLY 0x04
|
||||
/* Packet types */
|
||||
#define BNEP_GENERAL 0x00
|
||||
#define BNEP_CONTROL 0x01
|
||||
#define BNEP_COMPRESSED 0x02
|
||||
#define BNEP_COMPRESSED_SRC_ONLY 0x03
|
||||
#define BNEP_COMPRESSED_DST_ONLY 0x04
|
||||
|
||||
// Control types
|
||||
#define BNEP_CMD_NOT_UNDERSTOOD 0x00
|
||||
#define BNEP_SETUP_CONN_REQ 0x01
|
||||
#define BNEP_SETUP_CONN_RSP 0x02
|
||||
#define BNEP_FILTER_NET_TYPE_SET 0x03
|
||||
#define BNEP_FILTER_NET_TYPE_RSP 0x04
|
||||
#define BNEP_FILTER_MULTI_ADDR_SET 0x05
|
||||
#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
|
||||
/* Control types */
|
||||
#define BNEP_CMD_NOT_UNDERSTOOD 0x00
|
||||
#define BNEP_SETUP_CONN_REQ 0x01
|
||||
#define BNEP_SETUP_CONN_RSP 0x02
|
||||
#define BNEP_FILTER_NET_TYPE_SET 0x03
|
||||
#define BNEP_FILTER_NET_TYPE_RSP 0x04
|
||||
#define BNEP_FILTER_MULTI_ADDR_SET 0x05
|
||||
#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
|
||||
|
||||
// Extension types
|
||||
#define BNEP_EXT_CONTROL 0x00
|
||||
/* Extension types */
|
||||
#define BNEP_EXT_CONTROL 0x00
|
||||
|
||||
// Response messages
|
||||
#define BNEP_SUCCESS 0x00
|
||||
/* Response messages */
|
||||
#define BNEP_SUCCESS 0x00
|
||||
|
||||
#define BNEP_CONN_INVALID_DST 0x01
|
||||
#define BNEP_CONN_INVALID_SRC 0x02
|
||||
#define BNEP_CONN_INVALID_SVC 0x03
|
||||
#define BNEP_CONN_NOT_ALLOWED 0x04
|
||||
#define BNEP_CONN_INVALID_DST 0x01
|
||||
#define BNEP_CONN_INVALID_SRC 0x02
|
||||
#define BNEP_CONN_INVALID_SVC 0x03
|
||||
#define BNEP_CONN_NOT_ALLOWED 0x04
|
||||
|
||||
#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
|
||||
#define BNEP_FILTER_INVALID_RANGE 0x02
|
||||
#define BNEP_FILTER_INVALID_MCADDR 0x02
|
||||
#define BNEP_FILTER_LIMIT_REACHED 0x03
|
||||
#define BNEP_FILTER_DENIED_SECURITY 0x04
|
||||
#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
|
||||
#define BNEP_FILTER_INVALID_RANGE 0x02
|
||||
#define BNEP_FILTER_INVALID_MCADDR 0x02
|
||||
#define BNEP_FILTER_LIMIT_REACHED 0x03
|
||||
#define BNEP_FILTER_DENIED_SECURITY 0x04
|
||||
|
||||
// L2CAP settings
|
||||
#define BNEP_MTU 1691
|
||||
#define BNEP_PSM 0x0f
|
||||
#define BNEP_FLUSH_TO 0xffff
|
||||
#define BNEP_CONNECT_TO 15
|
||||
#define BNEP_FILTER_TO 15
|
||||
/* L2CAP settings */
|
||||
#define BNEP_MTU 1691
|
||||
#define BNEP_PSM 0x0f
|
||||
#define BNEP_FLUSH_TO 0xffff
|
||||
#define BNEP_CONNECT_TO 15
|
||||
#define BNEP_FILTER_TO 15
|
||||
|
||||
// Headers
|
||||
#define BNEP_TYPE_MASK 0x7f
|
||||
#define BNEP_EXT_HEADER 0x80
|
||||
/* Headers */
|
||||
#define BNEP_TYPE_MASK 0x7f
|
||||
#define BNEP_EXT_HEADER 0x80
|
||||
|
||||
struct bnep_setup_conn_req {
|
||||
__u8 type;
|
||||
__u8 ctrl;
|
||||
__u8 uuid_size;
|
||||
__u8 service[0];
|
||||
__u8 type;
|
||||
__u8 ctrl;
|
||||
__u8 uuid_size;
|
||||
__u8 service[0];
|
||||
} __packed;
|
||||
|
||||
struct bnep_set_filter_req {
|
||||
__u8 type;
|
||||
__u8 ctrl;
|
||||
__u8 type;
|
||||
__u8 ctrl;
|
||||
__be16 len;
|
||||
__u8 list[0];
|
||||
__u8 list[0];
|
||||
} __packed;
|
||||
|
||||
struct bnep_control_rsp {
|
||||
__u8 type;
|
||||
__u8 ctrl;
|
||||
__u8 type;
|
||||
__u8 ctrl;
|
||||
__be16 resp;
|
||||
} __packed;
|
||||
|
||||
struct bnep_ext_hdr {
|
||||
__u8 type;
|
||||
__u8 len;
|
||||
__u8 data[0];
|
||||
__u8 type;
|
||||
__u8 len;
|
||||
__u8 data[0];
|
||||
} __packed;
|
||||
|
||||
/* BNEP ioctl defines */
|
||||
|
@ -114,10 +114,10 @@ struct bnep_ext_hdr {
|
|||
#define BNEPGETCONNINFO _IOR('B', 211, int)
|
||||
|
||||
struct bnep_connadd_req {
|
||||
int sock; // Connected socket
|
||||
int sock; /* Connected socket */
|
||||
__u32 flags;
|
||||
__u16 role;
|
||||
char device[16]; // Name of the Ethernet device
|
||||
char device[16]; /* Name of the Ethernet device */
|
||||
};
|
||||
|
||||
struct bnep_conndel_req {
|
||||
|
@ -148,14 +148,14 @@ int bnep_del_connection(struct bnep_conndel_req *req);
|
|||
int bnep_get_connlist(struct bnep_connlist_req *req);
|
||||
int bnep_get_conninfo(struct bnep_conninfo *ci);
|
||||
|
||||
// BNEP sessions
|
||||
/* BNEP sessions */
|
||||
struct bnep_session {
|
||||
struct list_head list;
|
||||
|
||||
unsigned int role;
|
||||
unsigned long state;
|
||||
unsigned long flags;
|
||||
atomic_t killed;
|
||||
struct task_struct *task;
|
||||
|
||||
struct ethhdr eh;
|
||||
struct msghdr msg;
|
||||
|
@ -173,7 +173,7 @@ void bnep_sock_cleanup(void);
|
|||
|
||||
static inline int bnep_mc_hash(__u8 *addr)
|
||||
{
|
||||
return (crc32_be(~0, addr, ETH_ALEN) >> 26);
|
||||
return crc32_be(~0, addr, ETH_ALEN) >> 26;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <linux/socket.h>
|
||||
|
@ -131,7 +132,8 @@ static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len
|
|||
return -EILSEQ;
|
||||
|
||||
n = get_unaligned_be16(data);
|
||||
data++; len -= 2;
|
||||
data++;
|
||||
len -= 2;
|
||||
|
||||
if (len < n)
|
||||
return -EILSEQ;
|
||||
|
@ -176,7 +178,8 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
|
|||
return -EILSEQ;
|
||||
|
||||
n = get_unaligned_be16(data);
|
||||
data += 2; len -= 2;
|
||||
data += 2;
|
||||
len -= 2;
|
||||
|
||||
if (len < n)
|
||||
return -EILSEQ;
|
||||
|
@ -187,6 +190,8 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
|
|||
n /= (ETH_ALEN * 2);
|
||||
|
||||
if (n > 0) {
|
||||
int i;
|
||||
|
||||
s->mc_filter = 0;
|
||||
|
||||
/* Always send broadcast */
|
||||
|
@ -196,18 +201,22 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
|
|||
for (; n > 0; n--) {
|
||||
u8 a1[6], *a2;
|
||||
|
||||
memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
|
||||
a2 = data; data += ETH_ALEN;
|
||||
memcpy(a1, data, ETH_ALEN);
|
||||
data += ETH_ALEN;
|
||||
a2 = data;
|
||||
data += ETH_ALEN;
|
||||
|
||||
BT_DBG("mc filter %s -> %s",
|
||||
batostr((void *) a1), batostr((void *) a2));
|
||||
|
||||
#define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
|
||||
|
||||
/* Iterate from a1 to a2 */
|
||||
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
|
||||
while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
|
||||
INCA(a1);
|
||||
/* Increment a1 */
|
||||
i = 5;
|
||||
while (i >= 0 && ++a1[i--] == 0)
|
||||
;
|
||||
|
||||
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +236,8 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
|
|||
u8 cmd = *(u8 *)data;
|
||||
int err = 0;
|
||||
|
||||
data++; len--;
|
||||
data++;
|
||||
len--;
|
||||
|
||||
switch (cmd) {
|
||||
case BNEP_CMD_NOT_UNDERSTOOD:
|
||||
|
@ -302,7 +312,6 @@ static u8 __bnep_rx_hlen[] = {
|
|||
ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
|
||||
ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
|
||||
};
|
||||
#define BNEP_RX_TYPES (sizeof(__bnep_rx_hlen) - 1)
|
||||
|
||||
static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
|
||||
{
|
||||
|
@ -312,9 +321,10 @@ static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
|
|||
|
||||
dev->stats.rx_bytes += skb->len;
|
||||
|
||||
type = *(u8 *) skb->data; skb_pull(skb, 1);
|
||||
type = *(u8 *) skb->data;
|
||||
skb_pull(skb, 1);
|
||||
|
||||
if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
|
||||
if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
|
||||
goto badframe;
|
||||
|
||||
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
|
||||
|
@ -367,14 +377,14 @@ static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
|
|||
|
||||
case BNEP_COMPRESSED_DST_ONLY:
|
||||
memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
|
||||
ETH_ALEN);
|
||||
ETH_ALEN);
|
||||
memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
|
||||
ETH_ALEN + 2);
|
||||
ETH_ALEN + 2);
|
||||
break;
|
||||
|
||||
case BNEP_GENERAL:
|
||||
memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
|
||||
ETH_ALEN * 2);
|
||||
ETH_ALEN * 2);
|
||||
put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
|
||||
break;
|
||||
}
|
||||
|
@ -470,15 +480,14 @@ static int bnep_session(void *arg)
|
|||
|
||||
BT_DBG("");
|
||||
|
||||
daemonize("kbnepd %s", dev->name);
|
||||
set_user_nice(current, -15);
|
||||
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(sk_sleep(sk), &wait);
|
||||
while (!atomic_read(&s->killed)) {
|
||||
while (!kthread_should_stop()) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
// RX
|
||||
/* RX */
|
||||
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
||||
skb_orphan(skb);
|
||||
bnep_rx_frame(s, skb);
|
||||
|
@ -487,7 +496,7 @@ static int bnep_session(void *arg)
|
|||
if (sk->sk_state != BT_CONNECTED)
|
||||
break;
|
||||
|
||||
// TX
|
||||
/* TX */
|
||||
while ((skb = skb_dequeue(&sk->sk_write_queue)))
|
||||
if (bnep_tx_frame(s, skb))
|
||||
break;
|
||||
|
@ -555,8 +564,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
|
||||
/* session struct allocated as private part of net_device */
|
||||
dev = alloc_netdev(sizeof(struct bnep_session),
|
||||
(*req->device) ? req->device : "bnep%d",
|
||||
bnep_net_setup);
|
||||
(*req->device) ? req->device : "bnep%d",
|
||||
bnep_net_setup);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -571,7 +580,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
s = netdev_priv(dev);
|
||||
|
||||
/* This is rx header therefore addresses are swapped.
|
||||
* ie eh.h_dest is our local address. */
|
||||
* ie. eh.h_dest is our local address. */
|
||||
memcpy(s->eh.h_dest, &src, ETH_ALEN);
|
||||
memcpy(s->eh.h_source, &dst, ETH_ALEN);
|
||||
memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
|
||||
|
@ -597,17 +606,17 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
|
|||
SET_NETDEV_DEVTYPE(dev, &bnep_type);
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err) {
|
||||
if (err)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
__bnep_link_session(s);
|
||||
|
||||
err = kernel_thread(bnep_session, s, CLONE_KERNEL);
|
||||
if (err < 0) {
|
||||
s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
|
||||
if (IS_ERR(s->task)) {
|
||||
/* Session thread start failed, gotta cleanup. */
|
||||
unregister_netdev(dev);
|
||||
__bnep_unlink_session(s);
|
||||
err = PTR_ERR(s->task);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
|
@ -631,15 +640,9 @@ int bnep_del_connection(struct bnep_conndel_req *req)
|
|||
down_read(&bnep_session_sem);
|
||||
|
||||
s = __bnep_get_session(req->dst);
|
||||
if (s) {
|
||||
/* Wakeup user-space which is polling for socket errors.
|
||||
* This is temporary hack until we have shutdown in L2CAP */
|
||||
s->sock->sk->sk_err = EUNATCH;
|
||||
|
||||
/* Kill session thread */
|
||||
atomic_inc(&s->killed);
|
||||
wake_up_interruptible(sk_sleep(s->sock->sk));
|
||||
} else
|
||||
if (s)
|
||||
kthread_stop(s->task);
|
||||
else
|
||||
err = -ENOENT;
|
||||
|
||||
up_read(&bnep_session_sem);
|
||||
|
|
|
@ -39,10 +39,10 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "bnep.h"
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <linux/isdn/capilli.h>
|
||||
|
@ -143,7 +144,7 @@ static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
|
|||
|
||||
skb_queue_tail(&session->transmit, skb);
|
||||
|
||||
cmtp_schedule(session);
|
||||
wake_up_interruptible(sk_sleep(session->sock->sk));
|
||||
}
|
||||
|
||||
static void cmtp_send_interopmsg(struct cmtp_session *session,
|
||||
|
@ -386,8 +387,7 @@ static void cmtp_reset_ctr(struct capi_ctr *ctrl)
|
|||
|
||||
capi_ctr_down(ctrl);
|
||||
|
||||
atomic_inc(&session->terminate);
|
||||
cmtp_schedule(session);
|
||||
kthread_stop(session->task);
|
||||
}
|
||||
|
||||
static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#define CMTP_LOOPBACK 0
|
||||
|
||||
struct cmtp_connadd_req {
|
||||
int sock; // Connected socket
|
||||
int sock; /* Connected socket */
|
||||
__u32 flags;
|
||||
};
|
||||
|
||||
|
@ -81,7 +81,7 @@ struct cmtp_session {
|
|||
|
||||
char name[BTNAMSIZ];
|
||||
|
||||
atomic_t terminate;
|
||||
struct task_struct *task;
|
||||
|
||||
wait_queue_head_t wait;
|
||||
|
||||
|
@ -121,13 +121,6 @@ void cmtp_detach_device(struct cmtp_session *session);
|
|||
|
||||
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
|
||||
|
||||
static inline void cmtp_schedule(struct cmtp_session *session)
|
||||
{
|
||||
struct sock *sk = session->sock->sk;
|
||||
|
||||
wake_up_interruptible(sk_sleep(sk));
|
||||
}
|
||||
|
||||
/* CMTP init defines */
|
||||
int cmtp_init_sockets(void);
|
||||
void cmtp_cleanup_sockets(void);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <linux/isdn/capilli.h>
|
||||
|
@ -235,9 +236,12 @@ static void cmtp_process_transmit(struct cmtp_session *session)
|
|||
|
||||
size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
|
||||
|
||||
if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
|
||||
skb_queue_head(&session->transmit, skb);
|
||||
break;
|
||||
if (scb->id < 0) {
|
||||
scb->id = cmtp_alloc_block_id(session);
|
||||
if (scb->id < 0) {
|
||||
skb_queue_head(&session->transmit, skb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (size < 256) {
|
||||
|
@ -284,12 +288,11 @@ static int cmtp_session(void *arg)
|
|||
|
||||
BT_DBG("session %p", session);
|
||||
|
||||
daemonize("kcmtpd_ctr_%d", session->num);
|
||||
set_user_nice(current, -15);
|
||||
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(sk_sleep(sk), &wait);
|
||||
while (!atomic_read(&session->terminate)) {
|
||||
while (!kthread_should_stop()) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
if (sk->sk_state != BT_CONNECTED)
|
||||
|
@ -367,9 +370,12 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
|
|||
|
||||
__cmtp_link_session(session);
|
||||
|
||||
err = kernel_thread(cmtp_session, session, CLONE_KERNEL);
|
||||
if (err < 0)
|
||||
session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
|
||||
session->num);
|
||||
if (IS_ERR(session->task)) {
|
||||
err = PTR_ERR(session->task);
|
||||
goto unlink;
|
||||
}
|
||||
|
||||
if (!(session->flags & (1 << CMTP_LOOPBACK))) {
|
||||
err = cmtp_attach_device(session);
|
||||
|
@ -406,9 +412,8 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
|
|||
/* Flush the transmit queue */
|
||||
skb_queue_purge(&session->transmit);
|
||||
|
||||
/* Kill session thread */
|
||||
atomic_inc(&session->terminate);
|
||||
cmtp_schedule(session);
|
||||
/* Stop session thread */
|
||||
kthread_stop(session->task);
|
||||
} else
|
||||
err = -ENOENT;
|
||||
|
||||
|
|
|
@ -34,12 +34,12 @@
|
|||
#include <linux/file.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <linux/isdn/capilli.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "cmtp.h"
|
||||
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
static void hci_cmd_task(unsigned long arg);
|
||||
static void hci_rx_task(unsigned long arg);
|
||||
static void hci_tx_task(unsigned long arg);
|
||||
static void hci_notify(struct hci_dev *hdev, int event);
|
||||
|
||||
static DEFINE_RWLOCK(hci_task_lock);
|
||||
|
||||
|
@ -186,6 +185,7 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
|
|||
BT_DBG("%s %ld", hdev->name, opt);
|
||||
|
||||
/* Reset device */
|
||||
set_bit(HCI_RESET, &hdev->flags);
|
||||
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
|
||||
}
|
||||
|
||||
|
@ -213,8 +213,10 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
|
|||
/* Mandatory initialization */
|
||||
|
||||
/* Reset */
|
||||
if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks))
|
||||
if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) {
|
||||
set_bit(HCI_RESET, &hdev->flags);
|
||||
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
|
||||
}
|
||||
|
||||
/* Read Local Supported Features */
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
|
||||
|
@ -584,6 +586,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
|||
hci_req_cancel(hdev, ENODEV);
|
||||
hci_req_lock(hdev);
|
||||
|
||||
/* Stop timer, it might be running */
|
||||
del_timer_sync(&hdev->cmd_timer);
|
||||
|
||||
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
|
||||
hci_req_unlock(hdev);
|
||||
return 0;
|
||||
|
@ -623,7 +628,6 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
|||
|
||||
/* Drop last sent command */
|
||||
if (hdev->sent_cmd) {
|
||||
del_timer_sync(&hdev->cmd_timer);
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
hdev->sent_cmd = NULL;
|
||||
}
|
||||
|
@ -1074,9 +1078,74 @@ static void hci_cmd_timer(unsigned long arg)
|
|||
|
||||
BT_ERR("%s command tx timeout", hdev->name);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
clear_bit(HCI_RESET, &hdev->flags);
|
||||
tasklet_schedule(&hdev->cmd_task);
|
||||
}
|
||||
|
||||
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr)
|
||||
{
|
||||
struct oob_data *data;
|
||||
|
||||
list_for_each_entry(data, &hdev->remote_oob_data, list)
|
||||
if (bacmp(bdaddr, &data->bdaddr) == 0)
|
||||
return data;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct oob_data *data;
|
||||
|
||||
data = hci_find_remote_oob_data(hdev, bdaddr);
|
||||
if (!data)
|
||||
return -ENOENT;
|
||||
|
||||
BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
|
||||
|
||||
list_del(&data->list);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_remote_oob_data_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct oob_data *data, *n;
|
||||
|
||||
list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) {
|
||||
list_del(&data->list);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
|
||||
u8 *randomizer)
|
||||
{
|
||||
struct oob_data *data;
|
||||
|
||||
data = hci_find_remote_oob_data(hdev, bdaddr);
|
||||
|
||||
if (!data) {
|
||||
data = kmalloc(sizeof(*data), GFP_ATOMIC);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
bacpy(&data->bdaddr, bdaddr);
|
||||
list_add(&data->list, &hdev->remote_oob_data);
|
||||
}
|
||||
|
||||
memcpy(data->hash, hash, sizeof(data->hash));
|
||||
memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
|
||||
|
||||
BT_DBG("%s for %s", hdev->name, batostr(bdaddr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register HCI device */
|
||||
int hci_register_dev(struct hci_dev *hdev)
|
||||
{
|
||||
|
@ -1141,6 +1210,8 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
|
||||
INIT_LIST_HEAD(&hdev->link_keys);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->remote_oob_data);
|
||||
|
||||
INIT_WORK(&hdev->power_on, hci_power_on);
|
||||
INIT_WORK(&hdev->power_off, hci_power_off);
|
||||
setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
|
||||
|
@ -1220,6 +1291,7 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
|||
hci_blacklist_clear(hdev);
|
||||
hci_uuids_clear(hdev);
|
||||
hci_link_keys_clear(hdev);
|
||||
hci_remote_oob_data_clear(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
__hci_dev_put(hdev);
|
||||
|
@ -1269,7 +1341,7 @@ int hci_recv_frame(struct sk_buff *skb)
|
|||
EXPORT_SYMBOL(hci_recv_frame);
|
||||
|
||||
static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
|
||||
int count, __u8 index, gfp_t gfp_mask)
|
||||
int count, __u8 index)
|
||||
{
|
||||
int len = 0;
|
||||
int hlen = 0;
|
||||
|
@ -1299,7 +1371,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
|
|||
break;
|
||||
}
|
||||
|
||||
skb = bt_skb_alloc(len, gfp_mask);
|
||||
skb = bt_skb_alloc(len, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1385,8 +1457,7 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
|
|||
return -EILSEQ;
|
||||
|
||||
while (count) {
|
||||
rem = hci_reassembly(hdev, type, data, count,
|
||||
type - 1, GFP_ATOMIC);
|
||||
rem = hci_reassembly(hdev, type, data, count, type - 1);
|
||||
if (rem < 0)
|
||||
return rem;
|
||||
|
||||
|
@ -1420,8 +1491,8 @@ int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count)
|
|||
} else
|
||||
type = bt_cb(skb)->pkt_type;
|
||||
|
||||
rem = hci_reassembly(hdev, type, data,
|
||||
count, STREAM_REASSEMBLY, GFP_ATOMIC);
|
||||
rem = hci_reassembly(hdev, type, data, count,
|
||||
STREAM_REASSEMBLY);
|
||||
if (rem < 0)
|
||||
return rem;
|
||||
|
||||
|
|
|
@ -183,6 +183,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
clear_bit(HCI_RESET, &hdev->flags);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_RESET, status);
|
||||
}
|
||||
|
||||
|
@ -193,14 +195,17 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME);
|
||||
if (!sent)
|
||||
return;
|
||||
|
||||
memcpy(hdev->dev_name, sent, 248);
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_set_local_name_complete(hdev->id, sent, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -212,7 +217,7 @@ static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (rp->status)
|
||||
return;
|
||||
|
||||
memcpy(hdev->dev_name, rp->name, 248);
|
||||
memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
|
||||
}
|
||||
|
||||
static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -819,6 +824,17 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
|
|||
rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash,
|
||||
rp->randomizer, rp->status);
|
||||
}
|
||||
|
||||
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
@ -1212,7 +1228,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
for (; num_rsp; num_rsp--) {
|
||||
for (; num_rsp; num_rsp--, info++) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
data.pscan_period_mode = info->pscan_period_mode;
|
||||
|
@ -1221,8 +1237,9 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
|
|||
data.clock_offset = info->clock_offset;
|
||||
data.rssi = 0x00;
|
||||
data.ssp_mode = 0x00;
|
||||
info++;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -1480,6 +1497,9 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
||||
if (conn && hci_outgoing_auth_needed(hdev, conn)) {
|
||||
struct hci_cp_auth_requested cp;
|
||||
|
@ -1749,6 +1769,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
|
|||
hci_cc_pin_code_neg_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_LOCAL_OOB_DATA:
|
||||
hci_cc_read_local_oob_data_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_BUFFER_SIZE:
|
||||
hci_cc_le_read_buffer_size(hdev, skb);
|
||||
break;
|
||||
|
@ -1847,7 +1871,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (ev->opcode != HCI_OP_NOP)
|
||||
del_timer(&hdev->cmd_timer);
|
||||
|
||||
if (ev->ncmd) {
|
||||
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
if (!skb_queue_empty(&hdev->cmd_q))
|
||||
tasklet_schedule(&hdev->cmd_task);
|
||||
|
@ -2138,7 +2162,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
|
|||
struct inquiry_info_with_rssi_and_pscan_mode *info;
|
||||
info = (void *) (skb->data + 1);
|
||||
|
||||
for (; num_rsp; num_rsp--) {
|
||||
for (; num_rsp; num_rsp--, info++) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
data.pscan_period_mode = info->pscan_period_mode;
|
||||
|
@ -2147,13 +2171,15 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
|
|||
data.clock_offset = info->clock_offset;
|
||||
data.rssi = info->rssi;
|
||||
data.ssp_mode = 0x00;
|
||||
info++;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
mgmt_device_found(hdev->id, &info->bdaddr,
|
||||
info->dev_class, info->rssi,
|
||||
NULL);
|
||||
}
|
||||
} else {
|
||||
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
|
||||
|
||||
for (; num_rsp; num_rsp--) {
|
||||
for (; num_rsp; num_rsp--, info++) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
data.pscan_period_mode = info->pscan_period_mode;
|
||||
|
@ -2162,8 +2188,10 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
|
|||
data.clock_offset = info->clock_offset;
|
||||
data.rssi = info->rssi;
|
||||
data.ssp_mode = 0x00;
|
||||
info++;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
mgmt_device_found(hdev->id, &info->bdaddr,
|
||||
info->dev_class, info->rssi,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2294,7 +2322,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
for (; num_rsp; num_rsp--) {
|
||||
for (; num_rsp; num_rsp--, info++) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
data.pscan_period_mode = info->pscan_period_mode;
|
||||
|
@ -2303,8 +2331,9 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
|
|||
data.clock_offset = info->clock_offset;
|
||||
data.rssi = info->rssi;
|
||||
data.ssp_mode = 0x01;
|
||||
info++;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class,
|
||||
info->rssi, info->data);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -2353,9 +2382,14 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
|
|||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
cp.capability = conn->io_capability;
|
||||
cp.oob_data = 0;
|
||||
cp.authentication = hci_get_auth_req(conn);
|
||||
|
||||
if ((conn->out == 0x01 || conn->remote_oob == 0x01) &&
|
||||
hci_find_remote_oob_data(hdev, &conn->dst))
|
||||
cp.oob_data = 0x01;
|
||||
else
|
||||
cp.oob_data = 0x00;
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY,
|
||||
sizeof(cp), &cp);
|
||||
} else {
|
||||
|
@ -2453,6 +2487,37 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
|
|||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_remote_oob_data_request *ev = (void *) skb->data;
|
||||
struct oob_data *data;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
|
||||
if (data) {
|
||||
struct hci_cp_remote_oob_data_reply cp;
|
||||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
memcpy(cp.hash, data->hash, sizeof(cp.hash));
|
||||
memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp),
|
||||
&cp);
|
||||
} else {
|
||||
struct hci_cp_remote_oob_data_neg_reply cp;
|
||||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp),
|
||||
&cp);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_conn_complete *ev = (void *) skb->data;
|
||||
|
@ -2655,6 +2720,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_le_meta_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_REMOTE_OOB_DATA_REQUEST:
|
||||
hci_remote_oob_data_request_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s event 0x%x", hdev->name, event);
|
||||
break;
|
||||
|
|
|
@ -216,13 +216,13 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr, char
|
|||
static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hci_dev *hdev = dev_get_drvdata(dev);
|
||||
char name[249];
|
||||
char name[HCI_MAX_NAME_LENGTH + 1];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 248; i++)
|
||||
for (i = 0; i < HCI_MAX_NAME_LENGTH; i++)
|
||||
name[i] = hdev->dev_name[i];
|
||||
|
||||
name[248] = '\0';
|
||||
name[HCI_MAX_NAME_LENGTH] = '\0';
|
||||
return sprintf(buf, "%s\n", name);
|
||||
}
|
||||
|
||||
|
@ -277,10 +277,12 @@ static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *at
|
|||
static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct hci_dev *hdev = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
unsigned int val;
|
||||
int rv;
|
||||
|
||||
if (strict_strtoul(buf, 0, &val) < 0)
|
||||
return -EINVAL;
|
||||
rv = kstrtouint(buf, 0, &val);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
if (val != 0 && (val < 500 || val > 3600000))
|
||||
return -EINVAL;
|
||||
|
@ -299,15 +301,14 @@ static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribu
|
|||
static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct hci_dev *hdev = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
u16 val;
|
||||
int rv;
|
||||
|
||||
if (strict_strtoul(buf, 0, &val) < 0)
|
||||
return -EINVAL;
|
||||
rv = kstrtou16(buf, 0, &val);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
if (val < 0x0002 || val > 0xFFFE || val % 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < hdev->sniff_min_interval)
|
||||
if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
|
||||
return -EINVAL;
|
||||
|
||||
hdev->sniff_max_interval = val;
|
||||
|
@ -324,15 +325,14 @@ static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribu
|
|||
static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct hci_dev *hdev = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
u16 val;
|
||||
int rv;
|
||||
|
||||
if (strict_strtoul(buf, 0, &val) < 0)
|
||||
return -EINVAL;
|
||||
rv = kstrtou16(buf, 0, &val);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
if (val < 0x0002 || val > 0xFFFE || val % 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (val > hdev->sniff_max_interval)
|
||||
if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
|
||||
return -EINVAL;
|
||||
|
||||
hdev->sniff_min_interval = val;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
@ -55,22 +56,24 @@ static DECLARE_RWSEM(hidp_session_sem);
|
|||
static LIST_HEAD(hidp_session_list);
|
||||
|
||||
static unsigned char hidp_keycode[256] = {
|
||||
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
|
||||
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
|
||||
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
|
||||
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
|
||||
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
|
||||
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
|
||||
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
|
||||
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
|
||||
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
|
||||
150,158,159,128,136,177,178,176,142,152,173,140
|
||||
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36,
|
||||
37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45,
|
||||
21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1,
|
||||
14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52,
|
||||
53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
|
||||
99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69,
|
||||
98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73,
|
||||
82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190,
|
||||
191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135,
|
||||
136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94,
|
||||
95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115,
|
||||
114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140
|
||||
};
|
||||
|
||||
static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
|
||||
|
@ -461,8 +464,7 @@ static void hidp_idle_timeout(unsigned long arg)
|
|||
{
|
||||
struct hidp_session *session = (struct hidp_session *) arg;
|
||||
|
||||
atomic_inc(&session->terminate);
|
||||
hidp_schedule(session);
|
||||
kthread_stop(session->task);
|
||||
}
|
||||
|
||||
static void hidp_set_timer(struct hidp_session *session)
|
||||
|
@ -533,9 +535,7 @@ static void hidp_process_hid_control(struct hidp_session *session,
|
|||
skb_queue_purge(&session->ctrl_transmit);
|
||||
skb_queue_purge(&session->intr_transmit);
|
||||
|
||||
/* Kill session thread */
|
||||
atomic_inc(&session->terminate);
|
||||
hidp_schedule(session);
|
||||
kthread_stop(session->task);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -694,22 +694,10 @@ static int hidp_session(void *arg)
|
|||
struct sock *ctrl_sk = session->ctrl_sock->sk;
|
||||
struct sock *intr_sk = session->intr_sock->sk;
|
||||
struct sk_buff *skb;
|
||||
int vendor = 0x0000, product = 0x0000;
|
||||
wait_queue_t ctrl_wait, intr_wait;
|
||||
|
||||
BT_DBG("session %p", session);
|
||||
|
||||
if (session->input) {
|
||||
vendor = session->input->id.vendor;
|
||||
product = session->input->id.product;
|
||||
}
|
||||
|
||||
if (session->hid) {
|
||||
vendor = session->hid->vendor;
|
||||
product = session->hid->product;
|
||||
}
|
||||
|
||||
daemonize("khidpd_%04x%04x", vendor, product);
|
||||
set_user_nice(current, -15);
|
||||
|
||||
init_waitqueue_entry(&ctrl_wait, current);
|
||||
|
@ -718,10 +706,11 @@ static int hidp_session(void *arg)
|
|||
add_wait_queue(sk_sleep(intr_sk), &intr_wait);
|
||||
session->waiting_for_startup = 0;
|
||||
wake_up_interruptible(&session->startup_queue);
|
||||
while (!atomic_read(&session->terminate)) {
|
||||
while (!kthread_should_stop()) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
if (ctrl_sk->sk_state != BT_CONNECTED || intr_sk->sk_state != BT_CONNECTED)
|
||||
if (ctrl_sk->sk_state != BT_CONNECTED ||
|
||||
intr_sk->sk_state != BT_CONNECTED)
|
||||
break;
|
||||
|
||||
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
|
||||
|
@ -965,6 +954,7 @@ static int hidp_setup_hid(struct hidp_session *session,
|
|||
int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
|
||||
{
|
||||
struct hidp_session *session, *s;
|
||||
int vendor, product;
|
||||
int err;
|
||||
|
||||
BT_DBG("");
|
||||
|
@ -1026,9 +1016,24 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
|
||||
hidp_set_timer(session);
|
||||
|
||||
err = kernel_thread(hidp_session, session, CLONE_KERNEL);
|
||||
if (err < 0)
|
||||
if (session->hid) {
|
||||
vendor = session->hid->vendor;
|
||||
product = session->hid->product;
|
||||
} else if (session->input) {
|
||||
vendor = session->input->id.vendor;
|
||||
product = session->input->id.product;
|
||||
} else {
|
||||
vendor = 0x0000;
|
||||
product = 0x0000;
|
||||
}
|
||||
|
||||
session->task = kthread_run(hidp_session, session, "khidpd_%04x%04x",
|
||||
vendor, product);
|
||||
if (IS_ERR(session->task)) {
|
||||
err = PTR_ERR(session->task);
|
||||
goto unlink;
|
||||
}
|
||||
|
||||
while (session->waiting_for_startup) {
|
||||
wait_event_interruptible(session->startup_queue,
|
||||
!session->waiting_for_startup);
|
||||
|
@ -1053,8 +1058,7 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
err_add_device:
|
||||
hid_destroy_device(session->hid);
|
||||
session->hid = NULL;
|
||||
atomic_inc(&session->terminate);
|
||||
hidp_schedule(session);
|
||||
kthread_stop(session->task);
|
||||
|
||||
unlink:
|
||||
hidp_del_timer(session);
|
||||
|
@ -1105,13 +1109,7 @@ int hidp_del_connection(struct hidp_conndel_req *req)
|
|||
skb_queue_purge(&session->ctrl_transmit);
|
||||
skb_queue_purge(&session->intr_transmit);
|
||||
|
||||
/* Wakeup user-space polling for socket errors */
|
||||
session->intr_sock->sk->sk_err = EUNATCH;
|
||||
session->ctrl_sock->sk->sk_err = EUNATCH;
|
||||
|
||||
/* Kill session thread */
|
||||
atomic_inc(&session->terminate);
|
||||
hidp_schedule(session);
|
||||
kthread_stop(session->task);
|
||||
}
|
||||
} else
|
||||
err = -ENOENT;
|
||||
|
|
|
@ -84,8 +84,8 @@
|
|||
#define HIDP_WAITING_FOR_SEND_ACK 11
|
||||
|
||||
struct hidp_connadd_req {
|
||||
int ctrl_sock; // Connected control socket
|
||||
int intr_sock; // Connteted interrupt socket
|
||||
int ctrl_sock; /* Connected control socket */
|
||||
int intr_sock; /* Connected interrupt socket */
|
||||
__u16 parser;
|
||||
__u16 rd_size;
|
||||
__u8 __user *rd_data;
|
||||
|
@ -142,7 +142,7 @@ struct hidp_session {
|
|||
uint ctrl_mtu;
|
||||
uint intr_mtu;
|
||||
|
||||
atomic_t terminate;
|
||||
struct task_struct *task;
|
||||
|
||||
unsigned char keys[8];
|
||||
unsigned char leds;
|
||||
|
|
|
@ -85,7 +85,8 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
|
|||
return err;
|
||||
}
|
||||
|
||||
if (csock->sk->sk_state != BT_CONNECTED || isock->sk->sk_state != BT_CONNECTED) {
|
||||
if (csock->sk->sk_state != BT_CONNECTED ||
|
||||
isock->sk->sk_state != BT_CONNECTED) {
|
||||
sockfd_put(csock);
|
||||
sockfd_put(isock);
|
||||
return -EBADFD;
|
||||
|
@ -140,8 +141,8 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
|
|||
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_hidp_connadd_req {
|
||||
int ctrl_sock; // Connected control socket
|
||||
int intr_sock; // Connteted interrupt socket
|
||||
int ctrl_sock; /* Connected control socket */
|
||||
int intr_sock; /* Connected interrupt socket */
|
||||
__u16 parser;
|
||||
__u16 rd_size;
|
||||
compat_uptr_t rd_data;
|
||||
|
|
|
@ -169,7 +169,7 @@ static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
|
|||
__sock_put(sk);
|
||||
}
|
||||
|
||||
static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
|
||||
static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk)
|
||||
{
|
||||
struct l2cap_chan_list *l = &conn->chan_list;
|
||||
|
||||
|
@ -204,9 +204,6 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
|
|||
}
|
||||
|
||||
__l2cap_chan_link(l, sk);
|
||||
|
||||
if (parent)
|
||||
bt_accept_enqueue(parent, sk);
|
||||
}
|
||||
|
||||
/* Delete channel.
|
||||
|
@ -652,7 +649,9 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
|||
bacpy(&bt_sk(sk)->src, conn->src);
|
||||
bacpy(&bt_sk(sk)->dst, conn->dst);
|
||||
|
||||
__l2cap_chan_add(conn, sk, parent);
|
||||
bt_accept_enqueue(parent, sk);
|
||||
|
||||
__l2cap_chan_add(conn, sk);
|
||||
|
||||
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||
|
||||
|
@ -793,11 +792,11 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|||
kfree(conn);
|
||||
}
|
||||
|
||||
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
|
||||
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk)
|
||||
{
|
||||
struct l2cap_chan_list *l = &conn->chan_list;
|
||||
write_lock_bh(&l->lock);
|
||||
__l2cap_chan_add(conn, sk, parent);
|
||||
__l2cap_chan_add(conn, sk);
|
||||
write_unlock_bh(&l->lock);
|
||||
}
|
||||
|
||||
|
@ -876,7 +875,7 @@ int l2cap_do_connect(struct sock *sk)
|
|||
/* Update source addr of the socket */
|
||||
bacpy(src, conn->src);
|
||||
|
||||
l2cap_chan_add(conn, sk, NULL);
|
||||
l2cap_chan_add(conn, sk);
|
||||
|
||||
sk->sk_state = BT_CONNECT;
|
||||
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||
|
@ -1116,7 +1115,9 @@ int l2cap_ertm_send(struct sock *sk)
|
|||
bt_cb(skb)->tx_seq = pi->next_tx_seq;
|
||||
pi->next_tx_seq = (pi->next_tx_seq + 1) % 64;
|
||||
|
||||
pi->unacked_frames++;
|
||||
if (bt_cb(skb)->retries == 1)
|
||||
pi->unacked_frames++;
|
||||
|
||||
pi->frames_sent++;
|
||||
|
||||
if (skb_queue_is_last(TX_QUEUE(sk), skb))
|
||||
|
@ -2030,7 +2031,9 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||
l2cap_pi(sk)->psm = psm;
|
||||
l2cap_pi(sk)->dcid = scid;
|
||||
|
||||
__l2cap_chan_add(conn, sk, parent);
|
||||
bt_accept_enqueue(parent, sk);
|
||||
|
||||
__l2cap_chan_add(conn, sk);
|
||||
dcid = l2cap_pi(sk)->scid;
|
||||
|
||||
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||
|
@ -2460,6 +2463,11 @@ 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);
|
||||
|
||||
/* L2CAP Info req/rsp are unbound to channels, add extra checks */
|
||||
if (cmd->ident != conn->info_ident ||
|
||||
conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
|
||||
return 0;
|
||||
|
||||
del_timer(&conn->info_timer);
|
||||
|
||||
if (result != L2CAP_IR_SUCCESS) {
|
||||
|
@ -2670,7 +2678,8 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
|||
|
||||
if (err) {
|
||||
struct l2cap_cmd_rej rej;
|
||||
BT_DBG("error %d", err);
|
||||
|
||||
BT_ERR("Wrong link type (%d)", err);
|
||||
|
||||
/* FIXME: Map err to a valid reason */
|
||||
rej.reason = cpu_to_le16(0);
|
||||
|
|
|
@ -923,8 +923,9 @@ void __l2cap_sock_close(struct sock *sk, int reason)
|
|||
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);
|
||||
}
|
||||
|
||||
l2cap_chan_del(sk, reason);
|
||||
break;
|
||||
|
||||
case BT_CONNECT:
|
||||
|
|
|
@ -36,7 +36,7 @@ struct pending_cmd {
|
|||
struct list_head list;
|
||||
__u16 opcode;
|
||||
int index;
|
||||
void *cmd;
|
||||
void *param;
|
||||
struct sock *sk;
|
||||
void *user_data;
|
||||
};
|
||||
|
@ -179,10 +179,12 @@ static int read_controller_info(struct sock *sk, u16 index)
|
|||
|
||||
hci_del_off_timer(hdev);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
set_bit(HCI_MGMT, &hdev->flags);
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
|
||||
rp.type = hdev->dev_type;
|
||||
|
||||
rp.powered = test_bit(HCI_UP, &hdev->flags);
|
||||
|
@ -204,7 +206,9 @@ static int read_controller_info(struct sock *sk, u16 index)
|
|||
rp.hci_ver = hdev->hci_ver;
|
||||
put_unaligned_le16(hdev->hci_rev, &rp.hci_rev);
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
|
||||
|
@ -213,7 +217,7 @@ static int read_controller_info(struct sock *sk, u16 index)
|
|||
static void mgmt_pending_free(struct pending_cmd *cmd)
|
||||
{
|
||||
sock_put(cmd->sk);
|
||||
kfree(cmd->cmd);
|
||||
kfree(cmd->param);
|
||||
kfree(cmd);
|
||||
}
|
||||
|
||||
|
@ -229,13 +233,14 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
|
|||
cmd->opcode = opcode;
|
||||
cmd->index = index;
|
||||
|
||||
cmd->cmd = kmalloc(len, GFP_ATOMIC);
|
||||
if (!cmd->cmd) {
|
||||
cmd->param = kmalloc(len, GFP_ATOMIC);
|
||||
if (!cmd->param) {
|
||||
kfree(cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(cmd->cmd, data, len);
|
||||
if (data)
|
||||
memcpy(cmd->param, data, len);
|
||||
|
||||
cmd->sk = sk;
|
||||
sock_hold(sk);
|
||||
|
@ -311,7 +316,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
up = test_bit(HCI_UP, &hdev->flags);
|
||||
if ((cp->val && up) || (!cp->val && !up)) {
|
||||
|
@ -338,7 +343,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
err = 0;
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return err;
|
||||
}
|
||||
|
@ -363,7 +368,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN);
|
||||
|
@ -398,7 +403,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -424,7 +429,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN);
|
||||
|
@ -458,7 +463,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -517,7 +522,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (cp->val)
|
||||
set_bit(HCI_PAIRABLE, &hdev->flags);
|
||||
|
@ -533,12 +538,156 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
|
|||
err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#define EIR_FLAGS 0x01 /* flags */
|
||||
#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
|
||||
#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
|
||||
#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
|
||||
#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
|
||||
#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
|
||||
#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
|
||||
#define EIR_NAME_SHORT 0x08 /* shortened local name */
|
||||
#define EIR_NAME_COMPLETE 0x09 /* complete local name */
|
||||
#define EIR_TX_POWER 0x0A /* transmit power level */
|
||||
#define EIR_DEVICE_ID 0x10 /* device ID */
|
||||
|
||||
#define PNP_INFO_SVCLASS_ID 0x1200
|
||||
|
||||
static u8 bluetooth_base_uuid[] = {
|
||||
0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static u16 get_uuid16(u8 *uuid128)
|
||||
{
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 12; i++) {
|
||||
if (bluetooth_base_uuid[i] != uuid128[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&val, &uuid128[12], 4);
|
||||
|
||||
val = le32_to_cpu(val);
|
||||
if (val > 0xffff)
|
||||
return 0;
|
||||
|
||||
return (u16) val;
|
||||
}
|
||||
|
||||
static void create_eir(struct hci_dev *hdev, u8 *data)
|
||||
{
|
||||
u8 *ptr = data;
|
||||
u16 eir_len = 0;
|
||||
u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
|
||||
int i, truncated = 0;
|
||||
struct list_head *p;
|
||||
size_t name_len;
|
||||
|
||||
name_len = strlen(hdev->dev_name);
|
||||
|
||||
if (name_len > 0) {
|
||||
/* EIR Data type */
|
||||
if (name_len > 48) {
|
||||
name_len = 48;
|
||||
ptr[1] = EIR_NAME_SHORT;
|
||||
} else
|
||||
ptr[1] = EIR_NAME_COMPLETE;
|
||||
|
||||
/* EIR Data length */
|
||||
ptr[0] = name_len + 1;
|
||||
|
||||
memcpy(ptr + 2, hdev->dev_name, name_len);
|
||||
|
||||
eir_len += (name_len + 2);
|
||||
ptr += (name_len + 2);
|
||||
}
|
||||
|
||||
memset(uuid16_list, 0, sizeof(uuid16_list));
|
||||
|
||||
/* Group all UUID16 types */
|
||||
list_for_each(p, &hdev->uuids) {
|
||||
struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
|
||||
u16 uuid16;
|
||||
|
||||
uuid16 = get_uuid16(uuid->uuid);
|
||||
if (uuid16 == 0)
|
||||
return;
|
||||
|
||||
if (uuid16 < 0x1100)
|
||||
continue;
|
||||
|
||||
if (uuid16 == PNP_INFO_SVCLASS_ID)
|
||||
continue;
|
||||
|
||||
/* Stop if not enough space to put next UUID */
|
||||
if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
|
||||
truncated = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for duplicates */
|
||||
for (i = 0; uuid16_list[i] != 0; i++)
|
||||
if (uuid16_list[i] == uuid16)
|
||||
break;
|
||||
|
||||
if (uuid16_list[i] == 0) {
|
||||
uuid16_list[i] = uuid16;
|
||||
eir_len += sizeof(u16);
|
||||
}
|
||||
}
|
||||
|
||||
if (uuid16_list[0] != 0) {
|
||||
u8 *length = ptr;
|
||||
|
||||
/* EIR Data type */
|
||||
ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
|
||||
|
||||
ptr += 2;
|
||||
eir_len += 2;
|
||||
|
||||
for (i = 0; uuid16_list[i] != 0; i++) {
|
||||
*ptr++ = (uuid16_list[i] & 0x00ff);
|
||||
*ptr++ = (uuid16_list[i] & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
/* EIR Data length */
|
||||
*length = (i * sizeof(u16)) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int update_eir(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_write_eir cp;
|
||||
|
||||
if (!(hdev->features[6] & LMP_EXT_INQ))
|
||||
return 0;
|
||||
|
||||
if (hdev->ssp_mode == 0)
|
||||
return 0;
|
||||
|
||||
if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
|
||||
return 0;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
create_eir(hdev, cp.data);
|
||||
|
||||
if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
|
||||
return 0;
|
||||
|
||||
memcpy(hdev->eir, cp.data, sizeof(cp.data));
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static u8 get_service_classes(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
@ -590,7 +739,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
|
||||
if (!uuid) {
|
||||
|
@ -607,10 +756,14 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (err < 0)
|
||||
goto failed;
|
||||
|
||||
err = update_eir(hdev);
|
||||
if (err < 0)
|
||||
goto failed;
|
||||
|
||||
err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -635,7 +788,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
|
||||
err = hci_uuids_clear(hdev);
|
||||
|
@ -663,10 +816,14 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
err = update_eir(hdev);
|
||||
if (err < 0)
|
||||
goto unlock;
|
||||
|
||||
err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -690,7 +847,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hdev->major_class = cp->major;
|
||||
hdev->minor_class = cp->minor;
|
||||
|
@ -700,7 +857,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (err == 0)
|
||||
err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -722,7 +879,7 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
BT_DBG("hci%u enable %d", index, cp->enable);
|
||||
|
||||
|
@ -732,13 +889,15 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
|
|||
} else {
|
||||
clear_bit(HCI_SERVICE_CACHE, &hdev->flags);
|
||||
err = update_class(hdev);
|
||||
if (err == 0)
|
||||
err = update_eir(hdev);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL,
|
||||
0);
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -772,7 +931,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
|
||||
key_count);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hci_link_keys_clear(hdev);
|
||||
|
||||
|
@ -790,7 +949,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
key->pin_len);
|
||||
}
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return 0;
|
||||
|
@ -812,7 +971,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
err = hci_remove_link_key(hdev, &cp->bdaddr);
|
||||
if (err < 0) {
|
||||
|
@ -835,7 +994,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -861,7 +1020,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN);
|
||||
|
@ -893,7 +1052,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -914,7 +1073,7 @@ static int get_connections(struct sock *sk, u16 index)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
count = 0;
|
||||
list_for_each(p, &hdev->conn_hash.list) {
|
||||
|
@ -945,7 +1104,7 @@ static int get_connections(struct sock *sk, u16 index)
|
|||
|
||||
unlock:
|
||||
kfree(rp);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return err;
|
||||
}
|
||||
|
@ -970,7 +1129,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN);
|
||||
|
@ -992,7 +1151,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1019,7 +1178,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
|
||||
ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
|
||||
|
@ -1040,7 +1199,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1063,14 +1222,14 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hdev->io_capability = cp->io_capability;
|
||||
|
||||
BT_DBG("%s IO capability set to 0x%02x", hdev->name,
|
||||
hdev->io_capability);
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
|
||||
|
@ -1156,7 +1315,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (cp->io_cap == 0x03) {
|
||||
sec_level = BT_SECURITY_MEDIUM;
|
||||
|
@ -1198,7 +1357,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
err = 0;
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1230,6 +1389,8 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, mgmt_op, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
|
||||
goto failed;
|
||||
|
@ -1246,7 +1407,163 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
|
||||
u16 len)
|
||||
{
|
||||
struct mgmt_cp_set_local_name *mgmt_cp = (void *) data;
|
||||
struct hci_cp_write_local_name hci_cp;
|
||||
struct hci_dev *hdev;
|
||||
struct pending_cmd *cmd;
|
||||
int err;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
if (len != sizeof(*mgmt_cp))
|
||||
return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, EINVAL);
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
|
||||
err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
|
||||
&hci_cp);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int read_local_oob_data(struct sock *sk, u16 index)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct pending_cmd *cmd;
|
||||
int err;
|
||||
|
||||
BT_DBG("hci%u", index);
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||
ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||
ENETDOWN);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||
EOPNOTSUPP);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, index, NULL, 0);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
|
||||
u16 len)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_add_remote_oob_data *cp = (void *) data;
|
||||
int err;
|
||||
|
||||
BT_DBG("hci%u ", index);
|
||||
|
||||
if (len != sizeof(*cp))
|
||||
return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
|
||||
EINVAL);
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
|
||||
ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
|
||||
cp->randomizer);
|
||||
if (err < 0)
|
||||
err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -err);
|
||||
else
|
||||
err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
|
||||
0);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int remove_remote_oob_data(struct sock *sk, u16 index,
|
||||
unsigned char *data, u16 len)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_remove_remote_oob_data *cp = (void *) data;
|
||||
int err;
|
||||
|
||||
BT_DBG("hci%u ", index);
|
||||
|
||||
if (len != sizeof(*cp))
|
||||
return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
EINVAL);
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
|
||||
if (err < 0)
|
||||
err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
-err);
|
||||
else
|
||||
err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
NULL, 0);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1264,7 +1581,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
|||
if (msglen < sizeof(*hdr))
|
||||
return -EINVAL;
|
||||
|
||||
buf = kmalloc(msglen, GFP_ATOMIC);
|
||||
buf = kmalloc(msglen, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1347,6 +1664,20 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
|||
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
|
||||
err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0);
|
||||
break;
|
||||
case MGMT_OP_SET_LOCAL_NAME:
|
||||
err = set_local_name(sk, index, buf + sizeof(*hdr), len);
|
||||
break;
|
||||
case MGMT_OP_READ_LOCAL_OOB_DATA:
|
||||
err = read_local_oob_data(sk, index);
|
||||
break;
|
||||
case MGMT_OP_ADD_REMOTE_OOB_DATA:
|
||||
err = add_remote_oob_data(sk, index, buf + sizeof(*hdr), len);
|
||||
break;
|
||||
case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
|
||||
err = remove_remote_oob_data(sk, index, buf + sizeof(*hdr),
|
||||
len);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("Unknown op %u", opcode);
|
||||
err = cmd_status(sk, index, opcode, 0x01);
|
||||
|
@ -1380,7 +1711,7 @@ struct cmd_lookup {
|
|||
|
||||
static void mode_rsp(struct pending_cmd *cmd, void *data)
|
||||
{
|
||||
struct mgmt_mode *cp = cmd->cmd;
|
||||
struct mgmt_mode *cp = cmd->param;
|
||||
struct cmd_lookup *match = data;
|
||||
|
||||
if (cp->val != match->val)
|
||||
|
@ -1479,7 +1810,7 @@ int mgmt_connected(u16 index, bdaddr_t *bdaddr)
|
|||
|
||||
static void disconnect_rsp(struct pending_cmd *cmd, void *data)
|
||||
{
|
||||
struct mgmt_cp_disconnect *cp = cmd->cmd;
|
||||
struct mgmt_cp_disconnect *cp = cmd->param;
|
||||
struct sock **sk = data;
|
||||
struct mgmt_rp_disconnect rp;
|
||||
|
||||
|
@ -1643,3 +1974,104 @@ int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status)
|
|||
|
||||
return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
|
||||
int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status)
|
||||
{
|
||||
struct pending_cmd *cmd;
|
||||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_set_local_name ev;
|
||||
int err;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
|
||||
|
||||
cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, index);
|
||||
if (!cmd)
|
||||
goto send_event;
|
||||
|
||||
if (status) {
|
||||
err = cmd_status(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, EIO);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (hdev) {
|
||||
hci_dev_lock_bh(hdev);
|
||||
update_eir(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
}
|
||||
|
||||
err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev,
|
||||
sizeof(ev));
|
||||
if (err < 0)
|
||||
goto failed;
|
||||
|
||||
send_event:
|
||||
err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, index, &ev, sizeof(ev),
|
||||
cmd ? cmd->sk : NULL);
|
||||
|
||||
failed:
|
||||
if (cmd)
|
||||
mgmt_pending_remove(cmd);
|
||||
return err;
|
||||
}
|
||||
|
||||
int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
|
||||
u8 status)
|
||||
{
|
||||
struct pending_cmd *cmd;
|
||||
int err;
|
||||
|
||||
BT_DBG("hci%u status %u", index, status);
|
||||
|
||||
cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index);
|
||||
if (!cmd)
|
||||
return -ENOENT;
|
||||
|
||||
if (status) {
|
||||
err = cmd_status(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||
EIO);
|
||||
} else {
|
||||
struct mgmt_rp_read_local_oob_data rp;
|
||||
|
||||
memcpy(rp.hash, hash, sizeof(rp.hash));
|
||||
memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
|
||||
|
||||
err = cmd_complete(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||
&rp, sizeof(rp));
|
||||
}
|
||||
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
|
||||
u8 *eir)
|
||||
{
|
||||
struct mgmt_ev_device_found ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
|
||||
bacpy(&ev.bdaddr, bdaddr);
|
||||
memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
|
||||
ev.rssi = rssi;
|
||||
|
||||
if (eir)
|
||||
memcpy(ev.eir, eir, sizeof(ev.eir));
|
||||
|
||||
return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
|
||||
int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name)
|
||||
{
|
||||
struct mgmt_ev_remote_name ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
|
||||
bacpy(&ev.bdaddr, bdaddr);
|
||||
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
|
||||
|
||||
return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue