Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-next
Conflicts: net/bluetooth/l2cap_sock.c net/bluetooth/mgmt.c
This commit is contained in:
commit
312fef7d18
27 changed files with 2302 additions and 983 deletions
|
@ -30,6 +30,7 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
#define VERSION "1.0"
|
||||
#define ATH3K_FIRMWARE "ath3k-1.fw"
|
||||
|
||||
#define ATH3K_DNLOAD 0x01
|
||||
#define ATH3K_GETSTATE 0x05
|
||||
|
@ -400,9 +401,15 @@ static int ath3k_probe(struct usb_interface *intf,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) {
|
||||
BT_ERR("Error loading firmware");
|
||||
return -EIO;
|
||||
ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT)
|
||||
BT_ERR("Firmware file \"%s\" not found",
|
||||
ATH3K_FIRMWARE);
|
||||
else
|
||||
BT_ERR("Firmware file \"%s\" request failed (err=%d)",
|
||||
ATH3K_FIRMWARE, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ath3k_load_firmware(udev, firmware);
|
||||
|
@ -441,4 +448,4 @@ MODULE_AUTHOR("Atheros Communications");
|
|||
MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE("ath3k-1.fw");
|
||||
MODULE_FIRMWARE(ATH3K_FIRMWARE);
|
||||
|
|
|
@ -751,9 +751,7 @@ static void bfusb_disconnect(struct usb_interface *intf)
|
|||
|
||||
bfusb_close(hdev);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -844,9 +844,7 @@ static int bluecard_close(bluecard_info_t *info)
|
|||
/* Turn FPGA off */
|
||||
outb(0x80, iobase + 0x30);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -636,9 +636,7 @@ static int bt3c_close(bt3c_info_t *info)
|
|||
|
||||
bt3c_hci_close(hdev);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -565,9 +565,7 @@ static int btuart_close(btuart_info_t *info)
|
|||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -315,7 +315,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
err = usb_submit_urb(urb, mem_flags);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
@ -400,7 +401,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
err = usb_submit_urb(urb, mem_flags);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
@ -523,7 +525,8 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
err = usb_submit_urb(urb, mem_flags);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
@ -727,6 +730,9 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
usb_fill_bulk_urb(urb, data->udev, pipe,
|
||||
skb->data, skb->len, btusb_tx_complete, skb);
|
||||
|
||||
if (skb->priority >= HCI_PRIO_MAX - 1)
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
|
||||
hdev->stat.acl_tx++;
|
||||
break;
|
||||
|
||||
|
@ -770,7 +776,9 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s urb %p submission failed", hdev->name, urb);
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
kfree(urb->setup_packet);
|
||||
usb_unanchor_urb(urb);
|
||||
} else {
|
||||
|
|
|
@ -551,9 +551,7 @@ static int dtl1_close(dtl1_info_t *info)
|
|||
|
||||
spin_unlock_irqrestore(&(info->lock), flags);
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0)
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -264,10 +264,7 @@ static int vhci_release(struct inode *inode, struct file *file)
|
|||
struct vhci_data *data = file->private_data;
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
|
||||
if (hci_unregister_dev(hdev) < 0) {
|
||||
BT_ERR("Can't unregister HCI device %s", hdev->name);
|
||||
}
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
|
||||
file->private_data = NULL;
|
||||
|
|
|
@ -77,6 +77,33 @@ struct bt_power {
|
|||
#define BT_POWER_FORCE_ACTIVE_OFF 0
|
||||
#define BT_POWER_FORCE_ACTIVE_ON 1
|
||||
|
||||
#define BT_CHANNEL_POLICY 10
|
||||
|
||||
/* BR/EDR only (default policy)
|
||||
* AMP controllers cannot be used.
|
||||
* Channel move requests from the remote device are denied.
|
||||
* If the L2CAP channel is currently using AMP, move the channel to BR/EDR.
|
||||
*/
|
||||
#define BT_CHANNEL_POLICY_BREDR_ONLY 0
|
||||
|
||||
/* BR/EDR Preferred
|
||||
* Allow use of AMP controllers.
|
||||
* If the L2CAP channel is currently on AMP, move it to BR/EDR.
|
||||
* Channel move requests from the remote device are allowed.
|
||||
*/
|
||||
#define BT_CHANNEL_POLICY_BREDR_PREFERRED 1
|
||||
|
||||
/* AMP Preferred
|
||||
* Allow use of AMP controllers
|
||||
* If the L2CAP channel is currently on BR/EDR and AMP controller
|
||||
* resources are available, initiate a channel move to AMP.
|
||||
* Channel move requests from the remote device are allowed.
|
||||
* If the L2CAP socket has not been connected yet, try to create
|
||||
* and configure the channel directly on an AMP controller rather
|
||||
* than BR/EDR.
|
||||
*/
|
||||
#define BT_CHANNEL_POLICY_AMP_PREFERRED 2
|
||||
|
||||
__attribute__((format (printf, 2, 3)))
|
||||
int bt_printk(const char *level, const char *fmt, ...);
|
||||
|
||||
|
@ -158,7 +185,7 @@ struct bt_skb_cb {
|
|||
__u8 pkt_type;
|
||||
__u8 incoming;
|
||||
__u16 expect;
|
||||
__u8 tx_seq;
|
||||
__u16 tx_seq;
|
||||
__u8 retries;
|
||||
__u8 sar;
|
||||
unsigned short channel;
|
||||
|
|
|
@ -264,6 +264,13 @@ enum {
|
|||
#define HCI_LK_SMP_IRK 0x82
|
||||
#define HCI_LK_SMP_CSRK 0x83
|
||||
|
||||
/* ---- HCI Error Codes ---- */
|
||||
#define HCI_ERROR_AUTH_FAILURE 0x05
|
||||
#define HCI_ERROR_REJ_BAD_ADDR 0x0f
|
||||
#define HCI_ERROR_REMOTE_USER_TERM 0x13
|
||||
#define HCI_ERROR_LOCAL_HOST_TERM 0x16
|
||||
#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18
|
||||
|
||||
/* ----- HCI Commands ---- */
|
||||
#define HCI_OP_NOP 0x0000
|
||||
|
||||
|
@ -726,6 +733,21 @@ struct hci_cp_write_page_scan_activity {
|
|||
#define PAGE_SCAN_TYPE_STANDARD 0x00
|
||||
#define PAGE_SCAN_TYPE_INTERLACED 0x01
|
||||
|
||||
#define HCI_OP_READ_LOCAL_AMP_INFO 0x1409
|
||||
struct hci_rp_read_local_amp_info {
|
||||
__u8 status;
|
||||
__u8 amp_status;
|
||||
__le32 total_bw;
|
||||
__le32 max_bw;
|
||||
__le32 min_latency;
|
||||
__le32 max_pdu;
|
||||
__u8 amp_type;
|
||||
__le16 pal_cap;
|
||||
__le16 max_assoc_size;
|
||||
__le32 max_flush_to;
|
||||
__le32 be_flush_to;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_EVENT_MASK 0x2001
|
||||
struct hci_cp_le_set_event_mask {
|
||||
__u8 mask[8];
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
#define HCI_PROTO_L2CAP 0
|
||||
#define HCI_PROTO_SCO 1
|
||||
|
||||
/* HCI priority */
|
||||
#define HCI_PRIO_MAX 7
|
||||
|
||||
/* HCI Core structures */
|
||||
struct inquiry_data {
|
||||
bdaddr_t bdaddr;
|
||||
|
@ -64,6 +67,12 @@ struct hci_conn_hash {
|
|||
unsigned int le_num;
|
||||
};
|
||||
|
||||
struct hci_chan_hash {
|
||||
struct list_head list;
|
||||
spinlock_t lock;
|
||||
unsigned int num;
|
||||
};
|
||||
|
||||
struct bdaddr_list {
|
||||
struct list_head list;
|
||||
bdaddr_t bdaddr;
|
||||
|
@ -150,6 +159,17 @@ struct hci_dev {
|
|||
__u16 sniff_min_interval;
|
||||
__u16 sniff_max_interval;
|
||||
|
||||
__u8 amp_status;
|
||||
__u32 amp_total_bw;
|
||||
__u32 amp_max_bw;
|
||||
__u32 amp_min_latency;
|
||||
__u32 amp_max_pdu;
|
||||
__u8 amp_type;
|
||||
__u16 amp_pal_cap;
|
||||
__u16 amp_assoc_size;
|
||||
__u32 amp_max_flush_to;
|
||||
__u32 amp_be_flush_to;
|
||||
|
||||
unsigned int auto_accept_delay;
|
||||
|
||||
unsigned long quirks;
|
||||
|
@ -173,8 +193,10 @@ struct hci_dev {
|
|||
struct workqueue_struct *workqueue;
|
||||
|
||||
struct work_struct power_on;
|
||||
struct work_struct power_off;
|
||||
struct timer_list off_timer;
|
||||
struct delayed_work power_off;
|
||||
|
||||
__u16 discov_timeout;
|
||||
struct delayed_work discov_off;
|
||||
|
||||
struct timer_list cmd_timer;
|
||||
struct tasklet_struct cmd_task;
|
||||
|
@ -195,6 +217,8 @@ struct hci_dev {
|
|||
|
||||
__u16 init_last_cmd;
|
||||
|
||||
struct list_head mgmt_pending;
|
||||
|
||||
struct inquiry_cache inq_cache;
|
||||
struct hci_conn_hash conn_hash;
|
||||
struct list_head blacklist;
|
||||
|
@ -273,6 +297,7 @@ struct hci_conn {
|
|||
unsigned int sent;
|
||||
|
||||
struct sk_buff_head data_q;
|
||||
struct hci_chan_hash chan_hash;
|
||||
|
||||
struct timer_list disc_timer;
|
||||
struct timer_list idle_timer;
|
||||
|
@ -295,6 +320,14 @@ struct hci_conn {
|
|||
void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason);
|
||||
};
|
||||
|
||||
struct hci_chan {
|
||||
struct list_head list;
|
||||
|
||||
struct hci_conn *conn;
|
||||
struct sk_buff_head data_q;
|
||||
unsigned int sent;
|
||||
};
|
||||
|
||||
extern struct hci_proto *hci_proto[];
|
||||
extern struct list_head hci_dev_list;
|
||||
extern struct list_head hci_cb_list;
|
||||
|
@ -455,6 +488,28 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline void hci_chan_hash_init(struct hci_conn *c)
|
||||
{
|
||||
struct hci_chan_hash *h = &c->chan_hash;
|
||||
INIT_LIST_HEAD(&h->list);
|
||||
spin_lock_init(&h->lock);
|
||||
h->num = 0;
|
||||
}
|
||||
|
||||
static inline void hci_chan_hash_add(struct hci_conn *c, struct hci_chan *chan)
|
||||
{
|
||||
struct hci_chan_hash *h = &c->chan_hash;
|
||||
list_add(&chan->list, &h->list);
|
||||
h->num++;
|
||||
}
|
||||
|
||||
static inline void hci_chan_hash_del(struct hci_conn *c, struct hci_chan *chan)
|
||||
{
|
||||
struct hci_chan_hash *h = &c->chan_hash;
|
||||
list_del(&chan->list);
|
||||
h->num--;
|
||||
}
|
||||
|
||||
void hci_acl_connect(struct hci_conn *conn);
|
||||
void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
|
||||
void hci_add_sco(struct hci_conn *conn, __u16 handle);
|
||||
|
@ -466,6 +521,10 @@ int hci_conn_del(struct hci_conn *conn);
|
|||
void hci_conn_hash_flush(struct hci_dev *hdev);
|
||||
void hci_conn_check_pending(struct hci_dev *hdev);
|
||||
|
||||
struct hci_chan *hci_chan_create(struct hci_conn *conn);
|
||||
int hci_chan_del(struct hci_chan *chan);
|
||||
void hci_chan_hash_flush(struct hci_conn *conn);
|
||||
|
||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
__u8 sec_level, __u8 auth_type);
|
||||
int hci_conn_check_link_mode(struct hci_conn *conn);
|
||||
|
@ -543,7 +602,7 @@ struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
|
|||
struct hci_dev *hci_alloc_dev(void);
|
||||
void hci_free_dev(struct hci_dev *hdev);
|
||||
int hci_register_dev(struct hci_dev *hdev);
|
||||
int hci_unregister_dev(struct hci_dev *hdev);
|
||||
void hci_unregister_dev(struct hci_dev *hdev);
|
||||
int hci_suspend_dev(struct hci_dev *hdev);
|
||||
int hci_resume_dev(struct hci_dev *hdev);
|
||||
int hci_dev_open(__u16 dev);
|
||||
|
@ -597,8 +656,9 @@ int hci_recv_frame(struct sk_buff *skb);
|
|||
int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count);
|
||||
int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
|
||||
|
||||
int hci_register_sysfs(struct hci_dev *hdev);
|
||||
void hci_unregister_sysfs(struct hci_dev *hdev);
|
||||
void hci_init_sysfs(struct hci_dev *hdev);
|
||||
int hci_add_sysfs(struct hci_dev *hdev);
|
||||
void hci_del_sysfs(struct hci_dev *hdev);
|
||||
void hci_conn_init_sysfs(struct hci_conn *conn);
|
||||
void hci_conn_add_sysfs(struct hci_conn *conn);
|
||||
void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||
|
@ -674,7 +734,7 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
|
|||
static inline int hci_proto_disconn_ind(struct hci_conn *conn)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
int reason = 0x13;
|
||||
int reason = HCI_ERROR_REMOTE_USER_TERM;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->disconn_ind)
|
||||
|
@ -834,7 +894,7 @@ int hci_register_notifier(struct notifier_block *nb);
|
|||
int hci_unregister_notifier(struct notifier_block *nb);
|
||||
|
||||
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param);
|
||||
void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
|
||||
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
|
||||
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
|
||||
|
||||
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
|
||||
|
@ -847,34 +907,41 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
|
|||
|
||||
/* Management interface */
|
||||
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
|
||||
int mgmt_index_added(u16 index);
|
||||
int mgmt_index_removed(u16 index);
|
||||
int mgmt_powered(u16 index, u8 powered);
|
||||
int mgmt_discoverable(u16 index, u8 discoverable);
|
||||
int mgmt_connectable(u16 index, u8 connectable);
|
||||
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent);
|
||||
int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type);
|
||||
int mgmt_disconnected(u16 index, bdaddr_t *bdaddr);
|
||||
int mgmt_disconnect_failed(u16 index);
|
||||
int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status);
|
||||
int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure);
|
||||
int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
|
||||
int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
|
||||
int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value,
|
||||
u8 confirm_hint);
|
||||
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,
|
||||
int mgmt_index_added(struct hci_dev *hdev);
|
||||
int mgmt_index_removed(struct hci_dev *hdev);
|
||||
int mgmt_powered(struct hci_dev *hdev, u8 powered);
|
||||
int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
|
||||
int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
|
||||
int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
|
||||
int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
|
||||
u8 persistent);
|
||||
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
|
||||
int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
|
||||
int mgmt_disconnect_failed(struct hci_dev *hdev);
|
||||
int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
|
||||
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,
|
||||
int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
|
||||
int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
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);
|
||||
int mgmt_discovering(u16 index, u8 discovering);
|
||||
int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr);
|
||||
int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr);
|
||||
int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 status);
|
||||
int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
__le32 value, u8 confirm_hint);
|
||||
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 status);
|
||||
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr, u8 status);
|
||||
int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
|
||||
int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
|
||||
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
|
||||
u8 *randomizer, u8 status);
|
||||
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
|
||||
u8 *dev_class, s8 rssi, u8 *eir);
|
||||
int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name);
|
||||
int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status);
|
||||
int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
|
||||
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
|
||||
/* HCI info for socket */
|
||||
#define hci_pi(sk) ((struct hci_pinfo *) sk)
|
||||
|
@ -913,4 +980,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
|||
void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
|
||||
void hci_le_ltk_neg_reply(struct hci_conn *conn);
|
||||
|
||||
int hci_do_inquiry(struct hci_dev *hdev, u8 length);
|
||||
int hci_cancel_inquiry(struct hci_dev *hdev);
|
||||
|
||||
#endif /* __HCI_CORE_H */
|
||||
|
|
|
@ -27,17 +27,23 @@
|
|||
#ifndef __L2CAP_H
|
||||
#define __L2CAP_H
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* L2CAP defaults */
|
||||
#define L2CAP_DEFAULT_MTU 672
|
||||
#define L2CAP_DEFAULT_MIN_MTU 48
|
||||
#define L2CAP_DEFAULT_FLUSH_TO 0xffff
|
||||
#define L2CAP_DEFAULT_TX_WINDOW 63
|
||||
#define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF
|
||||
#define L2CAP_DEFAULT_MAX_TX 3
|
||||
#define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */
|
||||
#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
|
||||
#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */
|
||||
#define L2CAP_DEFAULT_ACK_TO 200
|
||||
#define L2CAP_LE_DEFAULT_MTU 23
|
||||
#define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF
|
||||
#define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF
|
||||
#define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF
|
||||
|
||||
#define L2CAP_DISC_TIMEOUT (100)
|
||||
#define L2CAP_DISC_REJ_TIMEOUT (5000) /* 5 seconds */
|
||||
|
@ -91,52 +97,82 @@ struct l2cap_conninfo {
|
|||
#define L2CAP_ECHO_RSP 0x09
|
||||
#define L2CAP_INFO_REQ 0x0a
|
||||
#define L2CAP_INFO_RSP 0x0b
|
||||
#define L2CAP_CREATE_CHAN_REQ 0x0c
|
||||
#define L2CAP_CREATE_CHAN_RSP 0x0d
|
||||
#define L2CAP_MOVE_CHAN_REQ 0x0e
|
||||
#define L2CAP_MOVE_CHAN_RSP 0x0f
|
||||
#define L2CAP_MOVE_CHAN_CFM 0x10
|
||||
#define L2CAP_MOVE_CHAN_CFM_RSP 0x11
|
||||
#define L2CAP_CONN_PARAM_UPDATE_REQ 0x12
|
||||
#define L2CAP_CONN_PARAM_UPDATE_RSP 0x13
|
||||
|
||||
/* L2CAP feature mask */
|
||||
/* L2CAP extended feature mask */
|
||||
#define L2CAP_FEAT_FLOWCTL 0x00000001
|
||||
#define L2CAP_FEAT_RETRANS 0x00000002
|
||||
#define L2CAP_FEAT_BIDIR_QOS 0x00000004
|
||||
#define L2CAP_FEAT_ERTM 0x00000008
|
||||
#define L2CAP_FEAT_STREAMING 0x00000010
|
||||
#define L2CAP_FEAT_FCS 0x00000020
|
||||
#define L2CAP_FEAT_EXT_FLOW 0x00000040
|
||||
#define L2CAP_FEAT_FIXED_CHAN 0x00000080
|
||||
#define L2CAP_FEAT_EXT_WINDOW 0x00000100
|
||||
#define L2CAP_FEAT_UCD 0x00000200
|
||||
|
||||
/* L2CAP checksum option */
|
||||
#define L2CAP_FCS_NONE 0x00
|
||||
#define L2CAP_FCS_CRC16 0x01
|
||||
|
||||
/* L2CAP Control Field bit masks */
|
||||
#define L2CAP_CTRL_SAR 0xC000
|
||||
#define L2CAP_CTRL_REQSEQ 0x3F00
|
||||
#define L2CAP_CTRL_TXSEQ 0x007E
|
||||
#define L2CAP_CTRL_RETRANS 0x0080
|
||||
#define L2CAP_CTRL_FINAL 0x0080
|
||||
#define L2CAP_CTRL_POLL 0x0010
|
||||
#define L2CAP_CTRL_SUPERVISE 0x000C
|
||||
#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
|
||||
/* L2CAP fixed channels */
|
||||
#define L2CAP_FC_L2CAP 0x02
|
||||
#define L2CAP_FC_A2MP 0x08
|
||||
|
||||
#define L2CAP_CTRL_TXSEQ_SHIFT 1
|
||||
#define L2CAP_CTRL_REQSEQ_SHIFT 8
|
||||
#define L2CAP_CTRL_SAR_SHIFT 14
|
||||
/* L2CAP Control Field bit masks */
|
||||
#define L2CAP_CTRL_SAR 0xC000
|
||||
#define L2CAP_CTRL_REQSEQ 0x3F00
|
||||
#define L2CAP_CTRL_TXSEQ 0x007E
|
||||
#define L2CAP_CTRL_SUPERVISE 0x000C
|
||||
|
||||
#define L2CAP_CTRL_RETRANS 0x0080
|
||||
#define L2CAP_CTRL_FINAL 0x0080
|
||||
#define L2CAP_CTRL_POLL 0x0010
|
||||
#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
|
||||
|
||||
#define L2CAP_CTRL_TXSEQ_SHIFT 1
|
||||
#define L2CAP_CTRL_SUPER_SHIFT 2
|
||||
#define L2CAP_CTRL_REQSEQ_SHIFT 8
|
||||
#define L2CAP_CTRL_SAR_SHIFT 14
|
||||
|
||||
/* L2CAP Extended Control Field bit mask */
|
||||
#define L2CAP_EXT_CTRL_TXSEQ 0xFFFC0000
|
||||
#define L2CAP_EXT_CTRL_SAR 0x00030000
|
||||
#define L2CAP_EXT_CTRL_SUPERVISE 0x00030000
|
||||
#define L2CAP_EXT_CTRL_REQSEQ 0x0000FFFC
|
||||
|
||||
#define L2CAP_EXT_CTRL_POLL 0x00040000
|
||||
#define L2CAP_EXT_CTRL_FINAL 0x00000002
|
||||
#define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */
|
||||
|
||||
#define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2
|
||||
#define L2CAP_EXT_CTRL_SAR_SHIFT 16
|
||||
#define L2CAP_EXT_CTRL_SUPER_SHIFT 16
|
||||
#define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18
|
||||
|
||||
/* L2CAP Supervisory Function */
|
||||
#define L2CAP_SUPER_RCV_READY 0x0000
|
||||
#define L2CAP_SUPER_REJECT 0x0004
|
||||
#define L2CAP_SUPER_RCV_NOT_READY 0x0008
|
||||
#define L2CAP_SUPER_SELECT_REJECT 0x000C
|
||||
#define L2CAP_SUPER_RR 0x00
|
||||
#define L2CAP_SUPER_REJ 0x01
|
||||
#define L2CAP_SUPER_RNR 0x02
|
||||
#define L2CAP_SUPER_SREJ 0x03
|
||||
|
||||
/* L2CAP Segmentation and Reassembly */
|
||||
#define L2CAP_SDU_UNSEGMENTED 0x0000
|
||||
#define L2CAP_SDU_START 0x4000
|
||||
#define L2CAP_SDU_END 0x8000
|
||||
#define L2CAP_SDU_CONTINUE 0xC000
|
||||
#define L2CAP_SAR_UNSEGMENTED 0x00
|
||||
#define L2CAP_SAR_START 0x01
|
||||
#define L2CAP_SAR_END 0x02
|
||||
#define L2CAP_SAR_CONTINUE 0x03
|
||||
|
||||
/* L2CAP Command rej. reasons */
|
||||
#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000
|
||||
#define L2CAP_REJ_MTU_EXCEEDED 0x0001
|
||||
#define L2CAP_REJ_INVALID_CID 0x0002
|
||||
|
||||
#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000
|
||||
#define L2CAP_REJ_MTU_EXCEEDED 0x0001
|
||||
#define L2CAP_REJ_INVALID_CID 0x0002
|
||||
|
||||
/* L2CAP structures */
|
||||
struct l2cap_hdr {
|
||||
|
@ -144,6 +180,12 @@ struct l2cap_hdr {
|
|||
__le16 cid;
|
||||
} __packed;
|
||||
#define L2CAP_HDR_SIZE 4
|
||||
#define L2CAP_ENH_HDR_SIZE 6
|
||||
#define L2CAP_EXT_HDR_SIZE 8
|
||||
|
||||
#define L2CAP_FCS_SIZE 2
|
||||
#define L2CAP_SDULEN_SIZE 2
|
||||
#define L2CAP_PSMLEN_SIZE 2
|
||||
|
||||
struct l2cap_cmd_hdr {
|
||||
__u8 code;
|
||||
|
@ -188,14 +230,15 @@ struct l2cap_conn_rsp {
|
|||
#define L2CAP_CID_DYN_START 0x0040
|
||||
#define L2CAP_CID_DYN_END 0xffff
|
||||
|
||||
/* connect result */
|
||||
/* connect/create channel results */
|
||||
#define L2CAP_CR_SUCCESS 0x0000
|
||||
#define L2CAP_CR_PEND 0x0001
|
||||
#define L2CAP_CR_BAD_PSM 0x0002
|
||||
#define L2CAP_CR_SEC_BLOCK 0x0003
|
||||
#define L2CAP_CR_NO_MEM 0x0004
|
||||
#define L2CAP_CR_BAD_AMP 0x0005
|
||||
|
||||
/* connect status */
|
||||
/* connect/create channel status */
|
||||
#define L2CAP_CS_NO_INFO 0x0000
|
||||
#define L2CAP_CS_AUTHEN_PEND 0x0001
|
||||
#define L2CAP_CS_AUTHOR_PEND 0x0002
|
||||
|
@ -217,6 +260,8 @@ struct l2cap_conf_rsp {
|
|||
#define L2CAP_CONF_UNACCEPT 0x0001
|
||||
#define L2CAP_CONF_REJECT 0x0002
|
||||
#define L2CAP_CONF_UNKNOWN 0x0003
|
||||
#define L2CAP_CONF_PENDING 0x0004
|
||||
#define L2CAP_CONF_EFS_REJECT 0x0005
|
||||
|
||||
struct l2cap_conf_opt {
|
||||
__u8 type;
|
||||
|
@ -233,6 +278,8 @@ struct l2cap_conf_opt {
|
|||
#define L2CAP_CONF_QOS 0x03
|
||||
#define L2CAP_CONF_RFC 0x04
|
||||
#define L2CAP_CONF_FCS 0x05
|
||||
#define L2CAP_CONF_EFS 0x06
|
||||
#define L2CAP_CONF_EWS 0x07
|
||||
|
||||
#define L2CAP_CONF_MAX_SIZE 22
|
||||
|
||||
|
@ -251,6 +298,21 @@ struct l2cap_conf_rfc {
|
|||
#define L2CAP_MODE_ERTM 0x03
|
||||
#define L2CAP_MODE_STREAMING 0x04
|
||||
|
||||
struct l2cap_conf_efs {
|
||||
__u8 id;
|
||||
__u8 stype;
|
||||
__le16 msdu;
|
||||
__le32 sdu_itime;
|
||||
__le32 acc_lat;
|
||||
__le32 flush_to;
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_SERV_NOTRAFIC 0x00
|
||||
#define L2CAP_SERV_BESTEFFORT 0x01
|
||||
#define L2CAP_SERV_GUARANTEED 0x02
|
||||
|
||||
#define L2CAP_BESTEFFORT_ID 0x01
|
||||
|
||||
struct l2cap_disconn_req {
|
||||
__le16 dcid;
|
||||
__le16 scid;
|
||||
|
@ -271,14 +333,57 @@ struct l2cap_info_rsp {
|
|||
__u8 data[0];
|
||||
} __packed;
|
||||
|
||||
struct l2cap_create_chan_req {
|
||||
__le16 psm;
|
||||
__le16 scid;
|
||||
__u8 amp_id;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_create_chan_rsp {
|
||||
__le16 dcid;
|
||||
__le16 scid;
|
||||
__le16 result;
|
||||
__le16 status;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_move_chan_req {
|
||||
__le16 icid;
|
||||
__u8 dest_amp_id;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_move_chan_rsp {
|
||||
__le16 icid;
|
||||
__le16 result;
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_MR_SUCCESS 0x0000
|
||||
#define L2CAP_MR_PEND 0x0001
|
||||
#define L2CAP_MR_BAD_ID 0x0002
|
||||
#define L2CAP_MR_SAME_ID 0x0003
|
||||
#define L2CAP_MR_NOT_SUPP 0x0004
|
||||
#define L2CAP_MR_COLLISION 0x0005
|
||||
#define L2CAP_MR_NOT_ALLOWED 0x0006
|
||||
|
||||
struct l2cap_move_chan_cfm {
|
||||
__le16 icid;
|
||||
__le16 result;
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_MC_CONFIRMED 0x0000
|
||||
#define L2CAP_MC_UNCONFIRMED 0x0001
|
||||
|
||||
struct l2cap_move_chan_cfm_rsp {
|
||||
__le16 icid;
|
||||
} __packed;
|
||||
|
||||
/* info type */
|
||||
#define L2CAP_IT_CL_MTU 0x0001
|
||||
#define L2CAP_IT_FEAT_MASK 0x0002
|
||||
#define L2CAP_IT_FIXED_CHAN 0x0003
|
||||
#define L2CAP_IT_CL_MTU 0x0001
|
||||
#define L2CAP_IT_FEAT_MASK 0x0002
|
||||
#define L2CAP_IT_FIXED_CHAN 0x0003
|
||||
|
||||
/* info result */
|
||||
#define L2CAP_IR_SUCCESS 0x0000
|
||||
#define L2CAP_IR_NOTSUPP 0x0001
|
||||
#define L2CAP_IR_SUCCESS 0x0000
|
||||
#define L2CAP_IR_NOTSUPP 0x0001
|
||||
|
||||
struct l2cap_conn_param_update_req {
|
||||
__le16 min;
|
||||
|
@ -297,7 +402,7 @@ struct l2cap_conn_param_update_rsp {
|
|||
|
||||
/* ----- L2CAP channels and connections ----- */
|
||||
struct srej_list {
|
||||
__u8 tx_seq;
|
||||
__u16 tx_seq;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
|
@ -319,14 +424,11 @@ struct l2cap_chan {
|
|||
__u16 flush_to;
|
||||
__u8 mode;
|
||||
__u8 chan_type;
|
||||
__u8 chan_policy;
|
||||
|
||||
__le16 sport;
|
||||
|
||||
__u8 sec_level;
|
||||
__u8 role_switch;
|
||||
__u8 force_reliable;
|
||||
__u8 flushable;
|
||||
__u8 force_active;
|
||||
|
||||
__u8 ident;
|
||||
|
||||
|
@ -337,7 +439,8 @@ struct l2cap_chan {
|
|||
|
||||
__u8 fcs;
|
||||
|
||||
__u8 tx_win;
|
||||
__u16 tx_win;
|
||||
__u16 tx_win_max;
|
||||
__u8 max_tx;
|
||||
__u16 retrans_timeout;
|
||||
__u16 monitor_timeout;
|
||||
|
@ -345,25 +448,40 @@ struct l2cap_chan {
|
|||
|
||||
unsigned long conf_state;
|
||||
unsigned long conn_state;
|
||||
unsigned long flags;
|
||||
|
||||
__u8 next_tx_seq;
|
||||
__u8 expected_ack_seq;
|
||||
__u8 expected_tx_seq;
|
||||
__u8 buffer_seq;
|
||||
__u8 buffer_seq_srej;
|
||||
__u8 srej_save_reqseq;
|
||||
__u8 frames_sent;
|
||||
__u8 unacked_frames;
|
||||
__u16 next_tx_seq;
|
||||
__u16 expected_ack_seq;
|
||||
__u16 expected_tx_seq;
|
||||
__u16 buffer_seq;
|
||||
__u16 buffer_seq_srej;
|
||||
__u16 srej_save_reqseq;
|
||||
__u16 frames_sent;
|
||||
__u16 unacked_frames;
|
||||
__u8 retry_count;
|
||||
__u8 num_acked;
|
||||
__u16 sdu_len;
|
||||
struct sk_buff *sdu;
|
||||
struct sk_buff *sdu_last_frag;
|
||||
|
||||
__u8 remote_tx_win;
|
||||
__u16 remote_tx_win;
|
||||
__u8 remote_max_tx;
|
||||
__u16 remote_mps;
|
||||
|
||||
__u8 local_id;
|
||||
__u8 local_stype;
|
||||
__u16 local_msdu;
|
||||
__u32 local_sdu_itime;
|
||||
__u32 local_acc_lat;
|
||||
__u32 local_flush_to;
|
||||
|
||||
__u8 remote_id;
|
||||
__u8 remote_stype;
|
||||
__u16 remote_msdu;
|
||||
__u32 remote_sdu_itime;
|
||||
__u32 remote_acc_lat;
|
||||
__u32 remote_flush_to;
|
||||
|
||||
struct timer_list chan_timer;
|
||||
struct timer_list retrans_timer;
|
||||
struct timer_list monitor_timer;
|
||||
|
@ -391,6 +509,7 @@ struct l2cap_ops {
|
|||
|
||||
struct l2cap_conn {
|
||||
struct hci_conn *hcon;
|
||||
struct hci_chan *hchan;
|
||||
|
||||
bdaddr_t *dst;
|
||||
bdaddr_t *src;
|
||||
|
@ -445,6 +564,9 @@ enum {
|
|||
CONF_CONNECT_PEND,
|
||||
CONF_NO_FCS_RECV,
|
||||
CONF_STATE2_DEVICE,
|
||||
CONF_EWS_RECV,
|
||||
CONF_LOC_CONF_PEND,
|
||||
CONF_REM_CONF_PEND,
|
||||
};
|
||||
|
||||
#define L2CAP_CONF_MAX_CONF_REQ 2
|
||||
|
@ -462,6 +584,16 @@ enum {
|
|||
CONN_RNR_SENT,
|
||||
};
|
||||
|
||||
/* Definitions for flags in l2cap_chan */
|
||||
enum {
|
||||
FLAG_ROLE_SWITCH,
|
||||
FLAG_FORCE_ACTIVE,
|
||||
FLAG_FORCE_RELIABLE,
|
||||
FLAG_FLUSHABLE,
|
||||
FLAG_EXT_CTRL,
|
||||
FLAG_EFS_ENABLE,
|
||||
};
|
||||
|
||||
#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
|
||||
#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
|
||||
#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
|
||||
|
@ -474,6 +606,22 @@ enum {
|
|||
L2CAP_DEFAULT_ACK_TO);
|
||||
#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer)
|
||||
|
||||
static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2)
|
||||
{
|
||||
int offset;
|
||||
|
||||
offset = (seq1 - seq2) % (chan->tx_win_max + 1);
|
||||
if (offset < 0)
|
||||
offset += (chan->tx_win_max + 1);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
|
||||
{
|
||||
return (seq + 1) % (chan->tx_win_max + 1);
|
||||
}
|
||||
|
||||
static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
|
||||
{
|
||||
int sub;
|
||||
|
@ -486,13 +634,165 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
|
|||
return sub == ch->remote_tx_win;
|
||||
}
|
||||
|
||||
#define __get_txseq(ctrl) (((ctrl) & L2CAP_CTRL_TXSEQ) >> 1)
|
||||
#define __get_reqseq(ctrl) (((ctrl) & L2CAP_CTRL_REQSEQ) >> 8)
|
||||
#define __is_iframe(ctrl) (!((ctrl) & L2CAP_CTRL_FRAME_TYPE))
|
||||
#define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE)
|
||||
#define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START)
|
||||
static inline __u16 __get_reqseq(struct l2cap_chan *chan, __u32 ctrl)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return (ctrl & L2CAP_EXT_CTRL_REQSEQ) >>
|
||||
L2CAP_EXT_CTRL_REQSEQ_SHIFT;
|
||||
else
|
||||
return (ctrl & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT;
|
||||
}
|
||||
|
||||
static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) &
|
||||
L2CAP_EXT_CTRL_REQSEQ;
|
||||
else
|
||||
return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ;
|
||||
}
|
||||
|
||||
static inline __u16 __get_txseq(struct l2cap_chan *chan, __u32 ctrl)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return (ctrl & L2CAP_EXT_CTRL_TXSEQ) >>
|
||||
L2CAP_EXT_CTRL_TXSEQ_SHIFT;
|
||||
else
|
||||
return (ctrl & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT;
|
||||
}
|
||||
|
||||
static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) &
|
||||
L2CAP_EXT_CTRL_TXSEQ;
|
||||
else
|
||||
return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ;
|
||||
}
|
||||
|
||||
static inline bool __is_sframe(struct l2cap_chan *chan, __u32 ctrl)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE;
|
||||
else
|
||||
return ctrl & L2CAP_CTRL_FRAME_TYPE;
|
||||
}
|
||||
|
||||
static inline __u32 __set_sframe(struct l2cap_chan *chan)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return L2CAP_EXT_CTRL_FRAME_TYPE;
|
||||
else
|
||||
return L2CAP_CTRL_FRAME_TYPE;
|
||||
}
|
||||
|
||||
static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return (ctrl & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT;
|
||||
else
|
||||
return (ctrl & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT;
|
||||
}
|
||||
|
||||
static inline __u32 __set_ctrl_sar(struct l2cap_chan *chan, __u32 sar)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) & L2CAP_EXT_CTRL_SAR;
|
||||
else
|
||||
return (sar << L2CAP_CTRL_SAR_SHIFT) & L2CAP_CTRL_SAR;
|
||||
}
|
||||
|
||||
static inline bool __is_sar_start(struct l2cap_chan *chan, __u32 ctrl)
|
||||
{
|
||||
return __get_ctrl_sar(chan, ctrl) == L2CAP_SAR_START;
|
||||
}
|
||||
|
||||
static inline __u32 __get_sar_mask(struct l2cap_chan *chan)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return L2CAP_EXT_CTRL_SAR;
|
||||
else
|
||||
return L2CAP_CTRL_SAR;
|
||||
}
|
||||
|
||||
static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return (ctrl & L2CAP_EXT_CTRL_SUPERVISE) >>
|
||||
L2CAP_EXT_CTRL_SUPER_SHIFT;
|
||||
else
|
||||
return (ctrl & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT;
|
||||
}
|
||||
|
||||
static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) &
|
||||
L2CAP_EXT_CTRL_SUPERVISE;
|
||||
else
|
||||
return (super << L2CAP_CTRL_SUPER_SHIFT) &
|
||||
L2CAP_CTRL_SUPERVISE;
|
||||
}
|
||||
|
||||
static inline __u32 __set_ctrl_final(struct l2cap_chan *chan)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return L2CAP_EXT_CTRL_FINAL;
|
||||
else
|
||||
return L2CAP_CTRL_FINAL;
|
||||
}
|
||||
|
||||
static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return ctrl & L2CAP_EXT_CTRL_FINAL;
|
||||
else
|
||||
return ctrl & L2CAP_CTRL_FINAL;
|
||||
}
|
||||
|
||||
static inline __u32 __set_ctrl_poll(struct l2cap_chan *chan)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return L2CAP_EXT_CTRL_POLL;
|
||||
else
|
||||
return L2CAP_CTRL_POLL;
|
||||
}
|
||||
|
||||
static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return ctrl & L2CAP_EXT_CTRL_POLL;
|
||||
else
|
||||
return ctrl & L2CAP_CTRL_POLL;
|
||||
}
|
||||
|
||||
static inline __u32 __get_control(struct l2cap_chan *chan, void *p)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return get_unaligned_le32(p);
|
||||
else
|
||||
return get_unaligned_le16(p);
|
||||
}
|
||||
|
||||
static inline void __put_control(struct l2cap_chan *chan, __u32 control,
|
||||
void *p)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return put_unaligned_le32(control, p);
|
||||
else
|
||||
return put_unaligned_le16(control, p);
|
||||
}
|
||||
|
||||
static inline __u8 __ctrl_size(struct l2cap_chan *chan)
|
||||
{
|
||||
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
||||
return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE;
|
||||
else
|
||||
return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE;
|
||||
}
|
||||
|
||||
extern int disable_ertm;
|
||||
extern int enable_hs;
|
||||
|
||||
int l2cap_init_sockets(void);
|
||||
void l2cap_cleanup_sockets(void);
|
||||
|
@ -507,7 +807,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk);
|
|||
void l2cap_chan_close(struct l2cap_chan *chan, int reason);
|
||||
void l2cap_chan_destroy(struct l2cap_chan *chan);
|
||||
int l2cap_chan_connect(struct l2cap_chan *chan);
|
||||
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);
|
||||
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
|
||||
u32 priority);
|
||||
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
|
||||
|
||||
#endif /* __L2CAP_H */
|
||||
|
|
|
@ -69,6 +69,10 @@ struct mgmt_mode {
|
|||
#define MGMT_OP_SET_POWERED 0x0005
|
||||
|
||||
#define MGMT_OP_SET_DISCOVERABLE 0x0006
|
||||
struct mgmt_cp_set_discoverable {
|
||||
__u8 val;
|
||||
__u16 timeout;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_SET_CONNECTABLE 0x0007
|
||||
|
||||
|
@ -96,24 +100,22 @@ struct mgmt_cp_set_service_cache {
|
|||
__u8 enable;
|
||||
} __packed;
|
||||
|
||||
struct mgmt_key_info {
|
||||
struct mgmt_link_key_info {
|
||||
bdaddr_t bdaddr;
|
||||
u8 type;
|
||||
u8 val[16];
|
||||
u8 pin_len;
|
||||
u8 dlen;
|
||||
u8 data[0];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_LOAD_KEYS 0x000D
|
||||
struct mgmt_cp_load_keys {
|
||||
#define MGMT_OP_LOAD_LINK_KEYS 0x000D
|
||||
struct mgmt_cp_load_link_keys {
|
||||
__u8 debug_keys;
|
||||
__le16 key_count;
|
||||
struct mgmt_key_info keys[0];
|
||||
struct mgmt_link_key_info keys[0];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_REMOVE_KEY 0x000E
|
||||
struct mgmt_cp_remove_key {
|
||||
#define MGMT_OP_REMOVE_KEYS 0x000E
|
||||
struct mgmt_cp_remove_keys {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 disconnect;
|
||||
} __packed;
|
||||
|
@ -126,10 +128,20 @@ struct mgmt_rp_disconnect {
|
|||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_ADDR_BREDR 0x00
|
||||
#define MGMT_ADDR_LE 0x01
|
||||
#define MGMT_ADDR_BREDR_LE 0x02
|
||||
#define MGMT_ADDR_INVALID 0xff
|
||||
|
||||
struct mgmt_addr_info {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 type;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_GET_CONNECTIONS 0x0010
|
||||
struct mgmt_rp_get_connections {
|
||||
__le16 conn_count;
|
||||
bdaddr_t conn[0];
|
||||
struct mgmt_addr_info addr[0];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_PIN_CODE_REPLY 0x0011
|
||||
|
@ -245,26 +257,19 @@ struct mgmt_ev_controller_error {
|
|||
|
||||
#define MGMT_EV_PAIRABLE 0x0009
|
||||
|
||||
#define MGMT_EV_NEW_KEY 0x000A
|
||||
struct mgmt_ev_new_key {
|
||||
#define MGMT_EV_NEW_LINK_KEY 0x000A
|
||||
struct mgmt_ev_new_link_key {
|
||||
__u8 store_hint;
|
||||
struct mgmt_key_info key;
|
||||
struct mgmt_link_key_info key;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CONNECTED 0x000B
|
||||
struct mgmt_ev_connected {
|
||||
bdaddr_t bdaddr;
|
||||
__u8 link_type;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_DISCONNECTED 0x000C
|
||||
struct mgmt_ev_disconnected {
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CONNECT_FAILED 0x000D
|
||||
struct mgmt_ev_connect_failed {
|
||||
bdaddr_t bdaddr;
|
||||
struct mgmt_addr_info addr;
|
||||
__u8 status;
|
||||
} __packed;
|
||||
|
||||
|
@ -294,7 +299,7 @@ struct mgmt_ev_local_name_changed {
|
|||
|
||||
#define MGMT_EV_DEVICE_FOUND 0x0012
|
||||
struct mgmt_ev_device_found {
|
||||
bdaddr_t bdaddr;
|
||||
struct mgmt_addr_info addr;
|
||||
__u8 dev_class[3];
|
||||
__s8 rssi;
|
||||
__u8 eir[HCI_MAX_EIR_LENGTH];
|
||||
|
|
|
@ -65,15 +65,13 @@ static DECLARE_RWSEM(bnep_session_sem);
|
|||
static struct bnep_session *__bnep_get_session(u8 *dst)
|
||||
{
|
||||
struct bnep_session *s;
|
||||
struct list_head *p;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
list_for_each(p, &bnep_session_list) {
|
||||
s = list_entry(p, struct bnep_session, list);
|
||||
list_for_each_entry(s, &bnep_session_list, list)
|
||||
if (!compare_ether_addr(dst, s->eh.h_source))
|
||||
return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -667,17 +665,14 @@ static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
|
|||
|
||||
int bnep_get_connlist(struct bnep_connlist_req *req)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct bnep_session *s;
|
||||
int err = 0, n = 0;
|
||||
|
||||
down_read(&bnep_session_sem);
|
||||
|
||||
list_for_each(p, &bnep_session_list) {
|
||||
struct bnep_session *s;
|
||||
list_for_each_entry(s, &bnep_session_list, list) {
|
||||
struct bnep_conninfo ci;
|
||||
|
||||
s = list_entry(p, struct bnep_session, list);
|
||||
|
||||
__bnep_copy_ci(&ci, s);
|
||||
|
||||
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
|
||||
|
|
|
@ -53,15 +53,13 @@ static LIST_HEAD(cmtp_session_list);
|
|||
static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
|
||||
{
|
||||
struct cmtp_session *session;
|
||||
struct list_head *p;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
list_for_each(p, &cmtp_session_list) {
|
||||
session = list_entry(p, struct cmtp_session, list);
|
||||
list_for_each_entry(session, &cmtp_session_list, list)
|
||||
if (!bacmp(bdaddr, &session->bdaddr))
|
||||
return session;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -431,19 +429,16 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
|
|||
|
||||
int cmtp_get_connlist(struct cmtp_connlist_req *req)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct cmtp_session *session;
|
||||
int err = 0, n = 0;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
down_read(&cmtp_session_sem);
|
||||
|
||||
list_for_each(p, &cmtp_session_list) {
|
||||
struct cmtp_session *session;
|
||||
list_for_each_entry(session, &cmtp_session_list, list) {
|
||||
struct cmtp_conninfo ci;
|
||||
|
||||
session = list_entry(p, struct cmtp_session, list);
|
||||
|
||||
__cmtp_copy_session(session, &ci);
|
||||
|
||||
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
|
||||
|
|
|
@ -374,6 +374,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
|||
|
||||
skb_queue_head_init(&conn->data_q);
|
||||
|
||||
hci_chan_hash_init(conn);
|
||||
|
||||
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
|
||||
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
|
||||
setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
|
||||
|
@ -432,6 +434,8 @@ int hci_conn_del(struct hci_conn *conn)
|
|||
|
||||
tasklet_disable(&hdev->tx_task);
|
||||
|
||||
hci_chan_hash_flush(conn);
|
||||
|
||||
hci_conn_hash_del(hdev, conn);
|
||||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
|
||||
|
@ -453,16 +457,13 @@ int hci_conn_del(struct hci_conn *conn)
|
|||
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
|
||||
{
|
||||
int use_src = bacmp(src, BDADDR_ANY);
|
||||
struct hci_dev *hdev = NULL;
|
||||
struct list_head *p;
|
||||
struct hci_dev *hdev = NULL, *d;
|
||||
|
||||
BT_DBG("%s -> %s", batostr(src), batostr(dst));
|
||||
|
||||
read_lock_bh(&hci_dev_list_lock);
|
||||
|
||||
list_for_each(p, &hci_dev_list) {
|
||||
struct hci_dev *d = list_entry(p, struct hci_dev, list);
|
||||
|
||||
list_for_each_entry(d, &hci_dev_list, list) {
|
||||
if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags))
|
||||
continue;
|
||||
|
||||
|
@ -819,7 +820,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
|
|||
|
||||
c->state = BT_CLOSED;
|
||||
|
||||
hci_proto_disconn_cfm(c, 0x16);
|
||||
hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
|
||||
hci_conn_del(c);
|
||||
}
|
||||
}
|
||||
|
@ -855,10 +856,10 @@ EXPORT_SYMBOL(hci_conn_put_device);
|
|||
|
||||
int hci_get_conn_list(void __user *arg)
|
||||
{
|
||||
register struct hci_conn *c;
|
||||
struct hci_conn_list_req req, *cl;
|
||||
struct hci_conn_info *ci;
|
||||
struct hci_dev *hdev;
|
||||
struct list_head *p;
|
||||
int n = 0, size, err;
|
||||
|
||||
if (copy_from_user(&req, arg, sizeof(req)))
|
||||
|
@ -882,10 +883,7 @@ int hci_get_conn_list(void __user *arg)
|
|||
ci = cl->conn_info;
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
list_for_each(p, &hdev->conn_hash.list) {
|
||||
register struct hci_conn *c;
|
||||
c = list_entry(p, struct hci_conn, list);
|
||||
|
||||
list_for_each_entry(c, &hdev->conn_hash.list, list) {
|
||||
bacpy(&(ci + n)->bdaddr, &c->dst);
|
||||
(ci + n)->handle = c->handle;
|
||||
(ci + n)->type = c->type;
|
||||
|
@ -956,3 +954,52 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)
|
|||
|
||||
return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
struct hci_chan *hci_chan_create(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_chan *chan;
|
||||
|
||||
BT_DBG("%s conn %p", hdev->name, conn);
|
||||
|
||||
chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC);
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
chan->conn = conn;
|
||||
skb_queue_head_init(&chan->data_q);
|
||||
|
||||
tasklet_disable(&hdev->tx_task);
|
||||
hci_chan_hash_add(conn, chan);
|
||||
tasklet_enable(&hdev->tx_task);
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
int hci_chan_del(struct hci_chan *chan)
|
||||
{
|
||||
struct hci_conn *conn = chan->conn;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
BT_DBG("%s conn %p chan %p", hdev->name, conn, chan);
|
||||
|
||||
tasklet_disable(&hdev->tx_task);
|
||||
hci_chan_hash_del(conn, chan);
|
||||
tasklet_enable(&hdev->tx_task);
|
||||
|
||||
skb_queue_purge(&chan->data_q);
|
||||
kfree(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hci_chan_hash_flush(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_chan_hash *h = &conn->chan_hash;
|
||||
struct hci_chan *chan, *tmp;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
list_for_each_entry_safe(chan, tmp, &h->list, list)
|
||||
hci_chan_del(chan);
|
||||
}
|
||||
|
|
|
@ -319,8 +319,7 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
|
|||
* Device is held on return. */
|
||||
struct hci_dev *hci_dev_get(int index)
|
||||
{
|
||||
struct hci_dev *hdev = NULL;
|
||||
struct list_head *p;
|
||||
struct hci_dev *hdev = NULL, *d;
|
||||
|
||||
BT_DBG("%d", index);
|
||||
|
||||
|
@ -328,8 +327,7 @@ struct hci_dev *hci_dev_get(int index)
|
|||
return NULL;
|
||||
|
||||
read_lock(&hci_dev_list_lock);
|
||||
list_for_each(p, &hci_dev_list) {
|
||||
struct hci_dev *d = list_entry(p, struct hci_dev, list);
|
||||
list_for_each_entry(d, &hci_dev_list, list) {
|
||||
if (d->id == index) {
|
||||
hdev = hci_dev_hold(d);
|
||||
break;
|
||||
|
@ -551,8 +549,11 @@ int hci_dev_open(__u16 dev)
|
|||
hci_dev_hold(hdev);
|
||||
set_bit(HCI_UP, &hdev->flags);
|
||||
hci_notify(hdev, HCI_DEV_UP);
|
||||
if (!test_bit(HCI_SETUP, &hdev->flags))
|
||||
mgmt_powered(hdev->id, 1);
|
||||
if (!test_bit(HCI_SETUP, &hdev->flags)) {
|
||||
hci_dev_lock_bh(hdev);
|
||||
mgmt_powered(hdev, 1);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
}
|
||||
} else {
|
||||
/* Init failed, cleanup */
|
||||
tasklet_kill(&hdev->rx_task);
|
||||
|
@ -597,6 +598,14 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
|||
tasklet_kill(&hdev->rx_task);
|
||||
tasklet_kill(&hdev->tx_task);
|
||||
|
||||
if (hdev->discov_timeout > 0) {
|
||||
cancel_delayed_work(&hdev->discov_off);
|
||||
hdev->discov_timeout = 0;
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
|
||||
cancel_delayed_work(&hdev->power_off);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
inquiry_cache_flush(hdev);
|
||||
hci_conn_hash_flush(hdev);
|
||||
|
@ -636,7 +645,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
|||
* and no tasks are scheduled. */
|
||||
hdev->close(hdev);
|
||||
|
||||
mgmt_powered(hdev->id, 0);
|
||||
hci_dev_lock_bh(hdev);
|
||||
mgmt_powered(hdev, 0);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
/* Clear flags */
|
||||
hdev->flags = 0;
|
||||
|
@ -794,9 +805,9 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
|
|||
|
||||
int hci_get_dev_list(void __user *arg)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct hci_dev_list_req *dl;
|
||||
struct hci_dev_req *dr;
|
||||
struct list_head *p;
|
||||
int n = 0, size, err;
|
||||
__u16 dev_num;
|
||||
|
||||
|
@ -815,12 +826,9 @@ int hci_get_dev_list(void __user *arg)
|
|||
dr = dl->dev_req;
|
||||
|
||||
read_lock_bh(&hci_dev_list_lock);
|
||||
list_for_each(p, &hci_dev_list) {
|
||||
struct hci_dev *hdev;
|
||||
|
||||
hdev = list_entry(p, struct hci_dev, list);
|
||||
|
||||
hci_del_off_timer(hdev);
|
||||
list_for_each_entry(hdev, &hci_dev_list, list) {
|
||||
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
|
||||
cancel_delayed_work(&hdev->power_off);
|
||||
|
||||
if (!test_bit(HCI_MGMT, &hdev->flags))
|
||||
set_bit(HCI_PAIRABLE, &hdev->flags);
|
||||
|
@ -855,7 +863,8 @@ int hci_get_dev_info(void __user *arg)
|
|||
if (!hdev)
|
||||
return -ENODEV;
|
||||
|
||||
hci_del_off_timer(hdev);
|
||||
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
|
||||
cancel_delayed_work_sync(&hdev->power_off);
|
||||
|
||||
if (!test_bit(HCI_MGMT, &hdev->flags))
|
||||
set_bit(HCI_PAIRABLE, &hdev->flags);
|
||||
|
@ -912,6 +921,7 @@ struct hci_dev *hci_alloc_dev(void)
|
|||
if (!hdev)
|
||||
return NULL;
|
||||
|
||||
hci_init_sysfs(hdev);
|
||||
skb_queue_head_init(&hdev->driver_init);
|
||||
|
||||
return hdev;
|
||||
|
@ -938,39 +948,41 @@ static void hci_power_on(struct work_struct *work)
|
|||
return;
|
||||
|
||||
if (test_bit(HCI_AUTO_OFF, &hdev->flags))
|
||||
mod_timer(&hdev->off_timer,
|
||||
jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT));
|
||||
queue_delayed_work(hdev->workqueue, &hdev->power_off,
|
||||
msecs_to_jiffies(AUTO_OFF_TIMEOUT));
|
||||
|
||||
if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
|
||||
mgmt_index_added(hdev->id);
|
||||
mgmt_index_added(hdev);
|
||||
}
|
||||
|
||||
static void hci_power_off(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev, power_off);
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
power_off.work);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
clear_bit(HCI_AUTO_OFF, &hdev->flags);
|
||||
|
||||
hci_dev_close(hdev->id);
|
||||
}
|
||||
|
||||
static void hci_auto_off(unsigned long data)
|
||||
static void hci_discov_off(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = (struct hci_dev *) data;
|
||||
struct hci_dev *hdev;
|
||||
u8 scan = SCAN_PAGE;
|
||||
|
||||
hdev = container_of(work, struct hci_dev, discov_off.work);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
clear_bit(HCI_AUTO_OFF, &hdev->flags);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
queue_work(hdev->workqueue, &hdev->power_off);
|
||||
}
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
|
||||
|
||||
void hci_del_off_timer(struct hci_dev *hdev)
|
||||
{
|
||||
BT_DBG("%s", hdev->name);
|
||||
hdev->discov_timeout = 0;
|
||||
|
||||
clear_bit(HCI_AUTO_OFF, &hdev->flags);
|
||||
del_timer(&hdev->off_timer);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
}
|
||||
|
||||
int hci_uuids_clear(struct hci_dev *hdev)
|
||||
|
@ -1007,16 +1019,11 @@ int hci_link_keys_clear(struct hci_dev *hdev)
|
|||
|
||||
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &hdev->link_keys) {
|
||||
struct link_key *k;
|
||||
|
||||
k = list_entry(p, struct link_key, list);
|
||||
struct link_key *k;
|
||||
|
||||
list_for_each_entry(k, &hdev->link_keys, list)
|
||||
if (bacmp(bdaddr, &k->bdaddr) == 0)
|
||||
return k;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1138,7 +1145,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
|
|||
|
||||
persistent = hci_persistent_key(hdev, conn, type, old_key_type);
|
||||
|
||||
mgmt_new_key(hdev->id, key, persistent);
|
||||
mgmt_new_link_key(hdev, key, persistent);
|
||||
|
||||
if (!persistent) {
|
||||
list_del(&key->list);
|
||||
|
@ -1181,7 +1188,7 @@ int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
|
|||
memcpy(id->rand, rand, sizeof(id->rand));
|
||||
|
||||
if (new_key)
|
||||
mgmt_new_key(hdev->id, key, old_key_type);
|
||||
mgmt_new_link_key(hdev, key, old_key_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1279,16 +1286,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
|
|||
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
struct bdaddr_list *b;
|
||||
|
||||
list_for_each_entry(b, &hdev->blacklist, list)
|
||||
if (bacmp(bdaddr, &b->bdaddr) == 0)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1327,7 +1329,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
|
||||
list_add(&entry->list, &hdev->blacklist);
|
||||
|
||||
return mgmt_device_blocked(hdev->id, bdaddr);
|
||||
return mgmt_device_blocked(hdev, bdaddr);
|
||||
}
|
||||
|
||||
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
|
@ -1346,7 +1348,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
|
||||
return mgmt_device_unblocked(hdev->id, bdaddr);
|
||||
return mgmt_device_unblocked(hdev, bdaddr);
|
||||
}
|
||||
|
||||
static void hci_clear_adv_cache(unsigned long arg)
|
||||
|
@ -1425,7 +1427,7 @@ int hci_add_adv_entry(struct hci_dev *hdev,
|
|||
int hci_register_dev(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *head = &hci_dev_list, *p;
|
||||
int i, id = 0;
|
||||
int i, id, error;
|
||||
|
||||
BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name,
|
||||
hdev->bus, hdev->owner);
|
||||
|
@ -1433,6 +1435,11 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
if (!hdev->open || !hdev->close || !hdev->destruct)
|
||||
return -EINVAL;
|
||||
|
||||
/* Do not allow HCI_AMP devices to register at index 0,
|
||||
* so the index can be used as the AMP controller ID.
|
||||
*/
|
||||
id = (hdev->dev_type == HCI_BREDR) ? 0 : 1;
|
||||
|
||||
write_lock_bh(&hci_dev_list_lock);
|
||||
|
||||
/* Find first available device id */
|
||||
|
@ -1479,6 +1486,8 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
|
||||
hci_conn_hash_init(hdev);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->mgmt_pending);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->blacklist);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->uuids);
|
||||
|
@ -1492,8 +1501,9 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
(unsigned long) hdev);
|
||||
|
||||
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);
|
||||
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
|
||||
|
||||
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
|
||||
|
||||
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
|
||||
|
||||
|
@ -1502,10 +1512,14 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
write_unlock_bh(&hci_dev_list_lock);
|
||||
|
||||
hdev->workqueue = create_singlethread_workqueue(hdev->name);
|
||||
if (!hdev->workqueue)
|
||||
goto nomem;
|
||||
if (!hdev->workqueue) {
|
||||
error = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
hci_register_sysfs(hdev);
|
||||
error = hci_add_sysfs(hdev);
|
||||
if (error < 0)
|
||||
goto err_wqueue;
|
||||
|
||||
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
|
||||
RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev);
|
||||
|
@ -1524,17 +1538,19 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
|
||||
return id;
|
||||
|
||||
nomem:
|
||||
err_wqueue:
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
err:
|
||||
write_lock_bh(&hci_dev_list_lock);
|
||||
list_del(&hdev->list);
|
||||
write_unlock_bh(&hci_dev_list_lock);
|
||||
|
||||
return -ENOMEM;
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_register_dev);
|
||||
|
||||
/* Unregister HCI device */
|
||||
int hci_unregister_dev(struct hci_dev *hdev)
|
||||
void hci_unregister_dev(struct hci_dev *hdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -1550,8 +1566,15 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
|||
kfree_skb(hdev->reassembly[i]);
|
||||
|
||||
if (!test_bit(HCI_INIT, &hdev->flags) &&
|
||||
!test_bit(HCI_SETUP, &hdev->flags))
|
||||
mgmt_index_removed(hdev->id);
|
||||
!test_bit(HCI_SETUP, &hdev->flags)) {
|
||||
hci_dev_lock_bh(hdev);
|
||||
mgmt_index_removed(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
}
|
||||
|
||||
/* mgmt_index_removed should take care of emptying the
|
||||
* pending list */
|
||||
BUG_ON(!list_empty(&hdev->mgmt_pending));
|
||||
|
||||
hci_notify(hdev, HCI_DEV_UNREG);
|
||||
|
||||
|
@ -1560,9 +1583,8 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
|||
rfkill_destroy(hdev->rfkill);
|
||||
}
|
||||
|
||||
hci_unregister_sysfs(hdev);
|
||||
hci_del_sysfs(hdev);
|
||||
|
||||
hci_del_off_timer(hdev);
|
||||
del_timer(&hdev->adv_timer);
|
||||
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
|
@ -1576,8 +1598,6 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
|||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
__hci_dev_put(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_unregister_dev);
|
||||
|
||||
|
@ -1948,23 +1968,18 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
|
|||
hdr->dlen = cpu_to_le16(len);
|
||||
}
|
||||
|
||||
void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
|
||||
static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
|
||||
struct sk_buff *skb, __u16 flags)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct sk_buff *list;
|
||||
|
||||
BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
|
||||
|
||||
skb->dev = (void *) hdev;
|
||||
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
|
||||
hci_add_acl_hdr(skb, conn->handle, flags);
|
||||
|
||||
list = skb_shinfo(skb)->frag_list;
|
||||
if (!list) {
|
||||
/* Non fragmented */
|
||||
BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
|
||||
|
||||
skb_queue_tail(&conn->data_q, skb);
|
||||
skb_queue_tail(queue, skb);
|
||||
} else {
|
||||
/* Fragmented */
|
||||
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
|
||||
|
@ -1972,9 +1987,9 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
|
|||
skb_shinfo(skb)->frag_list = NULL;
|
||||
|
||||
/* Queue all fragments atomically */
|
||||
spin_lock_bh(&conn->data_q.lock);
|
||||
spin_lock_bh(&queue->lock);
|
||||
|
||||
__skb_queue_tail(&conn->data_q, skb);
|
||||
__skb_queue_tail(queue, skb);
|
||||
|
||||
flags &= ~ACL_START;
|
||||
flags |= ACL_CONT;
|
||||
|
@ -1987,11 +2002,25 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
|
|||
|
||||
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
|
||||
|
||||
__skb_queue_tail(&conn->data_q, skb);
|
||||
__skb_queue_tail(queue, skb);
|
||||
} while (list);
|
||||
|
||||
spin_unlock_bh(&conn->data_q.lock);
|
||||
spin_unlock_bh(&queue->lock);
|
||||
}
|
||||
}
|
||||
|
||||
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
|
||||
{
|
||||
struct hci_conn *conn = chan->conn;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
BT_DBG("%s chan %p flags 0x%x", hdev->name, chan, flags);
|
||||
|
||||
skb->dev = (void *) hdev;
|
||||
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
|
||||
hci_add_acl_hdr(skb, conn->handle, flags);
|
||||
|
||||
hci_queue_acl(conn, &chan->data_q, skb, flags);
|
||||
|
||||
tasklet_schedule(&hdev->tx_task);
|
||||
}
|
||||
|
@ -2026,16 +2055,12 @@ EXPORT_SYMBOL(hci_send_sco);
|
|||
static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *conn = NULL;
|
||||
struct hci_conn *conn = NULL, *c;
|
||||
int num = 0, min = ~0;
|
||||
struct list_head *p;
|
||||
|
||||
/* We don't have to lock device here. Connections are always
|
||||
* added and removed with TX task disabled. */
|
||||
list_for_each(p, &h->list) {
|
||||
struct hci_conn *c;
|
||||
c = list_entry(p, struct hci_conn, list);
|
||||
|
||||
list_for_each_entry(c, &h->list, list) {
|
||||
if (c->type != type || skb_queue_empty(&c->data_q))
|
||||
continue;
|
||||
|
||||
|
@ -2084,14 +2109,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
|
|||
static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct list_head *p;
|
||||
struct hci_conn *c;
|
||||
struct hci_conn *c;
|
||||
|
||||
BT_ERR("%s link tx timeout", hdev->name);
|
||||
|
||||
/* Kill stalled connections */
|
||||
list_for_each(p, &h->list) {
|
||||
c = list_entry(p, struct hci_conn, list);
|
||||
list_for_each_entry(c, &h->list, list) {
|
||||
if (c->type == type && c->sent) {
|
||||
BT_ERR("%s killing stalled connection %s",
|
||||
hdev->name, batostr(&c->dst));
|
||||
|
@ -2100,11 +2123,137 @@ static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
|
|||
}
|
||||
}
|
||||
|
||||
static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
|
||||
int *quote)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_chan *chan = NULL;
|
||||
int num = 0, min = ~0, cur_prio = 0;
|
||||
struct hci_conn *conn;
|
||||
int cnt, q, conn_num = 0;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
list_for_each_entry(conn, &h->list, list) {
|
||||
struct hci_chan_hash *ch;
|
||||
struct hci_chan *tmp;
|
||||
|
||||
if (conn->type != type)
|
||||
continue;
|
||||
|
||||
if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
|
||||
continue;
|
||||
|
||||
conn_num++;
|
||||
|
||||
ch = &conn->chan_hash;
|
||||
|
||||
list_for_each_entry(tmp, &ch->list, list) {
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (skb_queue_empty(&tmp->data_q))
|
||||
continue;
|
||||
|
||||
skb = skb_peek(&tmp->data_q);
|
||||
if (skb->priority < cur_prio)
|
||||
continue;
|
||||
|
||||
if (skb->priority > cur_prio) {
|
||||
num = 0;
|
||||
min = ~0;
|
||||
cur_prio = skb->priority;
|
||||
}
|
||||
|
||||
num++;
|
||||
|
||||
if (conn->sent < min) {
|
||||
min = conn->sent;
|
||||
chan = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (hci_conn_num(hdev, type) == conn_num)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
switch (chan->conn->type) {
|
||||
case ACL_LINK:
|
||||
cnt = hdev->acl_cnt;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
cnt = hdev->sco_cnt;
|
||||
break;
|
||||
case LE_LINK:
|
||||
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
|
||||
break;
|
||||
default:
|
||||
cnt = 0;
|
||||
BT_ERR("Unknown link type");
|
||||
}
|
||||
|
||||
q = cnt / num;
|
||||
*quote = q ? q : 1;
|
||||
BT_DBG("chan %p quote %d", chan, *quote);
|
||||
return chan;
|
||||
}
|
||||
|
||||
static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *conn;
|
||||
int num = 0;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
list_for_each_entry(conn, &h->list, list) {
|
||||
struct hci_chan_hash *ch;
|
||||
struct hci_chan *chan;
|
||||
|
||||
if (conn->type != type)
|
||||
continue;
|
||||
|
||||
if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
|
||||
continue;
|
||||
|
||||
num++;
|
||||
|
||||
ch = &conn->chan_hash;
|
||||
list_for_each_entry(chan, &ch->list, list) {
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (chan->sent) {
|
||||
chan->sent = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skb_queue_empty(&chan->data_q))
|
||||
continue;
|
||||
|
||||
skb = skb_peek(&chan->data_q);
|
||||
if (skb->priority >= HCI_PRIO_MAX - 1)
|
||||
continue;
|
||||
|
||||
skb->priority = HCI_PRIO_MAX - 1;
|
||||
|
||||
BT_DBG("chan %p skb %p promoted to %d", chan, skb,
|
||||
skb->priority);
|
||||
}
|
||||
|
||||
if (hci_conn_num(hdev, type) == num)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hci_sched_acl(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct hci_chan *chan;
|
||||
struct sk_buff *skb;
|
||||
int quote;
|
||||
unsigned int cnt;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
|
@ -2118,19 +2267,35 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
|
|||
hci_link_tx_to(hdev, ACL_LINK);
|
||||
}
|
||||
|
||||
while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
cnt = hdev->acl_cnt;
|
||||
|
||||
hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active);
|
||||
while (hdev->acl_cnt &&
|
||||
(chan = hci_chan_sent(hdev, ACL_LINK, "e))) {
|
||||
u32 priority = (skb_peek(&chan->data_q))->priority;
|
||||
while (quote-- && (skb = skb_peek(&chan->data_q))) {
|
||||
BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
|
||||
skb->len, skb->priority);
|
||||
|
||||
/* Stop if priority has changed */
|
||||
if (skb->priority < priority)
|
||||
break;
|
||||
|
||||
skb = skb_dequeue(&chan->data_q);
|
||||
|
||||
hci_conn_enter_active_mode(chan->conn,
|
||||
bt_cb(skb)->force_active);
|
||||
|
||||
hci_send_frame(skb);
|
||||
hdev->acl_last_tx = jiffies;
|
||||
|
||||
hdev->acl_cnt--;
|
||||
conn->sent++;
|
||||
chan->sent++;
|
||||
chan->conn->sent++;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt != hdev->acl_cnt)
|
||||
hci_prio_recalculate(hdev, ACL_LINK);
|
||||
}
|
||||
|
||||
/* Schedule SCO */
|
||||
|
@ -2182,9 +2347,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
|
|||
|
||||
static inline void hci_sched_le(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct hci_chan *chan;
|
||||
struct sk_buff *skb;
|
||||
int quote, cnt;
|
||||
int quote, cnt, tmp;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
|
@ -2200,21 +2365,35 @@ static inline void hci_sched_le(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
|
||||
while (cnt && (conn = hci_low_sent(hdev, LE_LINK, "e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
tmp = cnt;
|
||||
while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) {
|
||||
u32 priority = (skb_peek(&chan->data_q))->priority;
|
||||
while (quote-- && (skb = skb_peek(&chan->data_q))) {
|
||||
BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
|
||||
skb->len, skb->priority);
|
||||
|
||||
/* Stop if priority has changed */
|
||||
if (skb->priority < priority)
|
||||
break;
|
||||
|
||||
skb = skb_dequeue(&chan->data_q);
|
||||
|
||||
hci_send_frame(skb);
|
||||
hdev->le_last_tx = jiffies;
|
||||
|
||||
cnt--;
|
||||
conn->sent++;
|
||||
chan->sent++;
|
||||
chan->conn->sent++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hdev->le_pkts)
|
||||
hdev->le_cnt = cnt;
|
||||
else
|
||||
hdev->acl_cnt = cnt;
|
||||
|
||||
if (cnt != tmp)
|
||||
hci_prio_recalculate(hdev, LE_LINK);
|
||||
}
|
||||
|
||||
static void hci_tx_task(unsigned long arg)
|
||||
|
@ -2407,3 +2586,31 @@ static void hci_cmd_task(unsigned long arg)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
int hci_do_inquiry(struct hci_dev *hdev, u8 length)
|
||||
{
|
||||
/* General inquiry access code (GIAC) */
|
||||
u8 lap[3] = { 0x33, 0x8b, 0x9e };
|
||||
struct hci_cp_inquiry cp;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (test_bit(HCI_INQUIRY, &hdev->flags))
|
||||
return -EINPROGRESS;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
memcpy(&cp.lap, lap, sizeof(cp.lap));
|
||||
cp.length = length;
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
int hci_cancel_inquiry(struct hci_dev *hdev)
|
||||
{
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!test_bit(HCI_INQUIRY, &hdev->flags))
|
||||
return -EPERM;
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
|
||||
}
|
||||
|
|
|
@ -58,9 +58,11 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (status)
|
||||
return;
|
||||
|
||||
if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
|
||||
test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_discovering(hdev->id, 0);
|
||||
clear_bit(HCI_INQUIRY, &hdev->flags);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
mgmt_discovering(hdev, 0);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
|
||||
|
||||
|
@ -76,10 +78,6 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (status)
|
||||
return;
|
||||
|
||||
if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
|
||||
test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_discovering(hdev->id, 0);
|
||||
|
||||
hci_conn_check_pending(hdev);
|
||||
}
|
||||
|
||||
|
@ -205,13 +203,15 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (!sent)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_set_local_name_complete(hdev->id, sent, status);
|
||||
mgmt_set_local_name_complete(hdev, sent, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
if (status == 0)
|
||||
memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
|
||||
|
||||
memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -274,7 +274,8 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
__u8 param, status = *((__u8 *) skb->data);
|
||||
int old_pscan, old_iscan;
|
||||
void *sent;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
@ -283,28 +284,40 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (!sent)
|
||||
return;
|
||||
|
||||
if (!status) {
|
||||
__u8 param = *((__u8 *) sent);
|
||||
int old_pscan, old_iscan;
|
||||
param = *((__u8 *) sent);
|
||||
|
||||
old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
|
||||
old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (param & SCAN_INQUIRY) {
|
||||
set_bit(HCI_ISCAN, &hdev->flags);
|
||||
if (!old_iscan)
|
||||
mgmt_discoverable(hdev->id, 1);
|
||||
} else if (old_iscan)
|
||||
mgmt_discoverable(hdev->id, 0);
|
||||
|
||||
if (param & SCAN_PAGE) {
|
||||
set_bit(HCI_PSCAN, &hdev->flags);
|
||||
if (!old_pscan)
|
||||
mgmt_connectable(hdev->id, 1);
|
||||
} else if (old_pscan)
|
||||
mgmt_connectable(hdev->id, 0);
|
||||
if (status != 0) {
|
||||
mgmt_write_scan_failed(hdev, param, status);
|
||||
hdev->discov_timeout = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
|
||||
old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
|
||||
|
||||
if (param & SCAN_INQUIRY) {
|
||||
set_bit(HCI_ISCAN, &hdev->flags);
|
||||
if (!old_iscan)
|
||||
mgmt_discoverable(hdev, 1);
|
||||
if (hdev->discov_timeout > 0) {
|
||||
int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
|
||||
queue_delayed_work(hdev->workqueue, &hdev->discov_off,
|
||||
to);
|
||||
}
|
||||
} else if (old_iscan)
|
||||
mgmt_discoverable(hdev, 0);
|
||||
|
||||
if (param & SCAN_PAGE) {
|
||||
set_bit(HCI_PSCAN, &hdev->flags);
|
||||
if (!old_pscan)
|
||||
mgmt_connectable(hdev, 1);
|
||||
} else if (old_pscan)
|
||||
mgmt_connectable(hdev, 0);
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status);
|
||||
}
|
||||
|
||||
|
@ -748,6 +761,30 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_local_amp_info *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hdev->amp_status = rp->amp_status;
|
||||
hdev->amp_total_bw = __le32_to_cpu(rp->total_bw);
|
||||
hdev->amp_max_bw = __le32_to_cpu(rp->max_bw);
|
||||
hdev->amp_min_latency = __le32_to_cpu(rp->min_latency);
|
||||
hdev->amp_max_pdu = __le32_to_cpu(rp->max_pdu);
|
||||
hdev->amp_type = rp->amp_type;
|
||||
hdev->amp_pal_cap = __le16_to_cpu(rp->pal_cap);
|
||||
hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size);
|
||||
hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to);
|
||||
hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
@ -804,19 +841,24 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status);
|
||||
mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
|
||||
|
||||
if (rp->status != 0)
|
||||
return;
|
||||
goto unlock;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
|
||||
if (!cp)
|
||||
return;
|
||||
goto unlock;
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
||||
if (conn)
|
||||
conn->pin_length = cp->pin_len;
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -825,10 +867,15 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr,
|
||||
mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr,
|
||||
rp->status);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
@ -855,9 +902,13 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_user_confirm_reply_complete(hdev->id, &rp->bdaddr,
|
||||
mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr,
|
||||
rp->status);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
|
||||
|
@ -867,9 +918,13 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
|
|||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_user_confirm_neg_reply_complete(hdev->id, &rp->bdaddr,
|
||||
mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
|
||||
rp->status);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
|
||||
|
@ -879,8 +934,10 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
|
|||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash,
|
||||
hci_dev_lock(hdev);
|
||||
mgmt_read_local_oob_data_reply_complete(hdev, rp->hash,
|
||||
rp->randomizer, rp->status);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
||||
|
@ -955,12 +1012,18 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
|||
if (status) {
|
||||
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
|
||||
hci_conn_check_pending(hdev);
|
||||
hci_dev_lock(hdev);
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_inquiry_failed(hdev, status);
|
||||
hci_dev_unlock(hdev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags) &&
|
||||
test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_discovering(hdev->id, 1);
|
||||
set_bit(HCI_INQUIRY, &hdev->flags);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
mgmt_discovering(hdev, 1);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
|
||||
|
@ -1339,13 +1402,16 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
|
|||
|
||||
BT_DBG("%s status %d", hdev->name, status);
|
||||
|
||||
if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
|
||||
test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_discovering(hdev->id, 0);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
|
||||
|
||||
hci_conn_check_pending(hdev);
|
||||
|
||||
if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
mgmt_discovering(hdev, 0);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -1361,12 +1427,6 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_discovering(hdev->id, 1);
|
||||
}
|
||||
|
||||
for (; num_rsp; num_rsp--, info++) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
|
@ -1377,8 +1437,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
|
|||
data.rssi = 0x00;
|
||||
data.ssp_mode = 0x00;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0,
|
||||
NULL);
|
||||
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
|
||||
info->dev_class, 0, NULL);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -1412,7 +1472,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
|
|||
conn->state = BT_CONFIG;
|
||||
hci_conn_hold(conn);
|
||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
|
||||
mgmt_connected(hdev, &ev->bdaddr, conn->type);
|
||||
} else
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
|
@ -1444,7 +1504,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
|
|||
} else {
|
||||
conn->state = BT_CLOSED;
|
||||
if (conn->type == ACL_LINK)
|
||||
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
|
||||
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
|
||||
ev->status);
|
||||
}
|
||||
|
||||
if (conn->type == ACL_LINK)
|
||||
|
@ -1531,7 +1592,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
|
|||
struct hci_cp_reject_conn_req cp;
|
||||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
cp.reason = 0x0f;
|
||||
cp.reason = HCI_ERROR_REJ_BAD_ADDR;
|
||||
hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp);
|
||||
}
|
||||
}
|
||||
|
@ -1544,7 +1605,9 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
|||
BT_DBG("%s status %d", hdev->name, ev->status);
|
||||
|
||||
if (ev->status) {
|
||||
mgmt_disconnect_failed(hdev->id);
|
||||
hci_dev_lock(hdev);
|
||||
mgmt_disconnect_failed(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1557,7 +1620,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
|||
conn->state = BT_CLOSED;
|
||||
|
||||
if (conn->type == ACL_LINK || conn->type == LE_LINK)
|
||||
mgmt_disconnected(hdev->id, &conn->dst);
|
||||
mgmt_disconnected(hdev, &conn->dst, conn->type);
|
||||
|
||||
hci_proto_disconn_cfm(conn, ev->reason);
|
||||
hci_conn_del(conn);
|
||||
|
@ -1588,7 +1651,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
|
|||
conn->sec_level = conn->pending_sec_level;
|
||||
}
|
||||
} else {
|
||||
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
|
||||
mgmt_auth_failed(hdev, &conn->dst, ev->status);
|
||||
}
|
||||
|
||||
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
|
||||
|
@ -1643,7 +1706,7 @@ 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);
|
||||
mgmt_remote_name(hdev, &ev->bdaddr, ev->name);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
||||
if (!conn)
|
||||
|
@ -1898,6 +1961,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
|
|||
hci_cc_write_ca_timeout(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_LOCAL_AMP_INFO:
|
||||
hci_cc_read_local_amp_info(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_DELETE_STORED_LINK_KEY:
|
||||
hci_cc_delete_stored_link_key(hdev, skb);
|
||||
break;
|
||||
|
@ -2029,7 +2096,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
case HCI_OP_DISCONNECT:
|
||||
if (ev->status != 0)
|
||||
mgmt_disconnect_failed(hdev->id);
|
||||
mgmt_disconnect_failed(hdev);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_CREATE_CONN:
|
||||
|
@ -2194,7 +2261,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
|
|||
else
|
||||
secure = 0;
|
||||
|
||||
mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure);
|
||||
mgmt_pin_code_request(hdev, &ev->bdaddr, secure);
|
||||
}
|
||||
|
||||
unlock:
|
||||
|
@ -2363,12 +2430,6 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_discovering(hdev->id, 1);
|
||||
}
|
||||
|
||||
if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) {
|
||||
struct inquiry_info_with_rssi_and_pscan_mode *info;
|
||||
info = (void *) (skb->data + 1);
|
||||
|
@ -2383,7 +2444,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
|
|||
data.rssi = info->rssi;
|
||||
data.ssp_mode = 0x00;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
mgmt_device_found(hdev->id, &info->bdaddr,
|
||||
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
|
||||
info->dev_class, info->rssi,
|
||||
NULL);
|
||||
}
|
||||
|
@ -2400,7 +2461,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
|
|||
data.rssi = info->rssi;
|
||||
data.ssp_mode = 0x00;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
mgmt_device_found(hdev->id, &info->bdaddr,
|
||||
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
|
||||
info->dev_class, info->rssi,
|
||||
NULL);
|
||||
}
|
||||
|
@ -2531,12 +2592,6 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
|
|||
if (!num_rsp)
|
||||
return;
|
||||
|
||||
if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||
|
||||
if (test_bit(HCI_MGMT, &hdev->flags))
|
||||
mgmt_discovering(hdev->id, 1);
|
||||
}
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
for (; num_rsp; num_rsp--, info++) {
|
||||
|
@ -2549,8 +2604,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
|
|||
data.rssi = info->rssi;
|
||||
data.ssp_mode = 0x01;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class,
|
||||
info->rssi, info->data);
|
||||
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
|
||||
info->dev_class, info->rssi, info->data);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -2614,7 +2669,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
|
|||
struct hci_cp_io_capability_neg_reply cp;
|
||||
|
||||
bacpy(&cp.bdaddr, &ev->bdaddr);
|
||||
cp.reason = 0x18; /* Pairing not allowed */
|
||||
cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED;
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY,
|
||||
sizeof(cp), &cp);
|
||||
|
@ -2706,7 +2761,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
|
|||
}
|
||||
|
||||
confirm:
|
||||
mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey,
|
||||
mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey,
|
||||
confirm_hint);
|
||||
|
||||
unlock:
|
||||
|
@ -2732,7 +2787,7 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_
|
|||
* event gets always produced as initiator and is also mapped to
|
||||
* the mgmt_auth_failed event */
|
||||
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0)
|
||||
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
|
||||
mgmt_auth_failed(hdev, &conn->dst, ev->status);
|
||||
|
||||
hci_conn_put(conn);
|
||||
|
||||
|
@ -2813,14 +2868,14 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
|||
}
|
||||
|
||||
if (ev->status) {
|
||||
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
|
||||
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status);
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
conn->state = BT_CLOSED;
|
||||
hci_conn_del(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
|
||||
mgmt_connected(hdev, &ev->bdaddr, conn->type);
|
||||
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
|
@ -3104,5 +3159,5 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
|
|||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
module_param(enable_le, bool, 0444);
|
||||
module_param(enable_le, bool, 0644);
|
||||
MODULE_PARM_DESC(enable_le, "Enable LE support");
|
||||
|
|
|
@ -435,17 +435,12 @@ static const struct file_operations inquiry_cache_fops = {
|
|||
static int blacklist_show(struct seq_file *f, void *p)
|
||||
{
|
||||
struct hci_dev *hdev = f->private;
|
||||
struct list_head *l;
|
||||
struct bdaddr_list *b;
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
list_for_each(l, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(l, struct bdaddr_list, list);
|
||||
|
||||
list_for_each_entry(b, &hdev->blacklist, list)
|
||||
seq_printf(f, "%s\n", batostr(&b->bdaddr));
|
||||
}
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
|
@ -484,17 +479,12 @@ static void print_bt_uuid(struct seq_file *f, u8 *uuid)
|
|||
static int uuids_show(struct seq_file *f, void *p)
|
||||
{
|
||||
struct hci_dev *hdev = f->private;
|
||||
struct list_head *l;
|
||||
struct bt_uuid *uuid;
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
list_for_each(l, &hdev->uuids) {
|
||||
struct bt_uuid *uuid;
|
||||
|
||||
uuid = list_entry(l, struct bt_uuid, list);
|
||||
|
||||
list_for_each_entry(uuid, &hdev->uuids, list)
|
||||
print_bt_uuid(f, uuid->uuid);
|
||||
}
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
|
@ -542,22 +532,28 @@ static int auto_accept_delay_get(void *data, u64 *val)
|
|||
DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
|
||||
auto_accept_delay_set, "%llu\n");
|
||||
|
||||
int hci_register_sysfs(struct hci_dev *hdev)
|
||||
void hci_init_sysfs(struct hci_dev *hdev)
|
||||
{
|
||||
struct device *dev = &hdev->dev;
|
||||
|
||||
dev->type = &bt_host;
|
||||
dev->class = bt_class;
|
||||
|
||||
dev_set_drvdata(dev, hdev);
|
||||
device_initialize(dev);
|
||||
}
|
||||
|
||||
int hci_add_sysfs(struct hci_dev *hdev)
|
||||
{
|
||||
struct device *dev = &hdev->dev;
|
||||
int err;
|
||||
|
||||
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
|
||||
|
||||
dev->type = &bt_host;
|
||||
dev->class = bt_class;
|
||||
dev->parent = hdev->parent;
|
||||
|
||||
dev_set_name(dev, "%s", hdev->name);
|
||||
|
||||
dev_set_drvdata(dev, hdev);
|
||||
|
||||
err = device_register(dev);
|
||||
err = device_add(dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -581,7 +577,7 @@ int hci_register_sysfs(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void hci_unregister_sysfs(struct hci_dev *hdev)
|
||||
void hci_del_sysfs(struct hci_dev *hdev)
|
||||
{
|
||||
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
|
||||
|
||||
|
|
|
@ -81,24 +81,20 @@ static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
|
|||
static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
|
||||
{
|
||||
struct hidp_session *session;
|
||||
struct list_head *p;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
list_for_each(p, &hidp_session_list) {
|
||||
session = list_entry(p, struct hidp_session, list);
|
||||
list_for_each_entry(session, &hidp_session_list, list) {
|
||||
if (!bacmp(bdaddr, &session->bdaddr))
|
||||
return session;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __hidp_link_session(struct hidp_session *session)
|
||||
{
|
||||
__module_get(THIS_MODULE);
|
||||
list_add(&session->list, &hidp_session_list);
|
||||
|
||||
hci_conn_hold_device(session->conn);
|
||||
}
|
||||
|
||||
static void __hidp_unlink_session(struct hidp_session *session)
|
||||
|
@ -106,7 +102,6 @@ static void __hidp_unlink_session(struct hidp_session *session)
|
|||
hci_conn_put_device(session->conn);
|
||||
|
||||
list_del(&session->list);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
|
||||
|
@ -255,6 +250,9 @@ static int __hidp_send_ctrl_message(struct hidp_session *session,
|
|||
|
||||
BT_DBG("session %p data %p size %d", session, data, size);
|
||||
|
||||
if (atomic_read(&session->terminate))
|
||||
return -EIO;
|
||||
|
||||
skb = alloc_skb(size + 1, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
BT_ERR("Can't allocate memory for new frame");
|
||||
|
@ -329,6 +327,7 @@ static int hidp_get_raw_report(struct hid_device *hid,
|
|||
struct sk_buff *skb;
|
||||
size_t len;
|
||||
int numbered_reports = hid->report_enum[report_type].numbered;
|
||||
int ret;
|
||||
|
||||
switch (report_type) {
|
||||
case HID_FEATURE_REPORT:
|
||||
|
@ -352,8 +351,9 @@ static int hidp_get_raw_report(struct hid_device *hid,
|
|||
session->waiting_report_number = numbered_reports ? report_number : -1;
|
||||
set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
data[0] = report_number;
|
||||
if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1))
|
||||
goto err_eio;
|
||||
ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* Wait for the return of the report. The returned report
|
||||
gets put in session->report_return. */
|
||||
|
@ -365,11 +365,13 @@ static int hidp_get_raw_report(struct hid_device *hid,
|
|||
5*HZ);
|
||||
if (res == 0) {
|
||||
/* timeout */
|
||||
goto err_eio;
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
if (res < 0) {
|
||||
/* signal */
|
||||
goto err_restartsys;
|
||||
ret = -ERESTARTSYS;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,14 +392,10 @@ static int hidp_get_raw_report(struct hid_device *hid,
|
|||
|
||||
return len;
|
||||
|
||||
err_restartsys:
|
||||
err:
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
mutex_unlock(&session->report_mutex);
|
||||
return -ERESTARTSYS;
|
||||
err_eio:
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
mutex_unlock(&session->report_mutex);
|
||||
return -EIO;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
|
||||
|
@ -422,11 +420,10 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s
|
|||
|
||||
/* Set up our wait, and send the report request to the device. */
|
||||
set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
|
||||
if (hidp_send_ctrl_message(hid->driver_data, report_type,
|
||||
data, count)) {
|
||||
ret = -ENOMEM;
|
||||
ret = hidp_send_ctrl_message(hid->driver_data, report_type, data,
|
||||
count);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Wait for the ACK from the device. */
|
||||
while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
|
||||
|
@ -496,10 +493,9 @@ static void hidp_process_handshake(struct hidp_session *session,
|
|||
case HIDP_HSHK_ERR_INVALID_REPORT_ID:
|
||||
case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
|
||||
case HIDP_HSHK_ERR_INVALID_PARAMETER:
|
||||
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) {
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags))
|
||||
wake_up_interruptible(&session->report_queue);
|
||||
}
|
||||
|
||||
/* FIXME: Call into SET_ GET_ handlers here */
|
||||
break;
|
||||
|
||||
|
@ -520,10 +516,8 @@ static void hidp_process_handshake(struct hidp_session *session,
|
|||
}
|
||||
|
||||
/* Wake up the waiting thread. */
|
||||
if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
|
||||
clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
|
||||
if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags))
|
||||
wake_up_interruptible(&session->report_queue);
|
||||
}
|
||||
}
|
||||
|
||||
static void hidp_process_hid_control(struct hidp_session *session,
|
||||
|
@ -663,7 +657,24 @@ static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
|
|||
return kernel_sendmsg(sock, &msg, &iv, 1, len);
|
||||
}
|
||||
|
||||
static void hidp_process_transmit(struct hidp_session *session)
|
||||
static void hidp_process_intr_transmit(struct hidp_session *session)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
BT_DBG("session %p", session);
|
||||
|
||||
while ((skb = skb_dequeue(&session->intr_transmit))) {
|
||||
if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
|
||||
skb_queue_head(&session->intr_transmit, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
hidp_set_timer(session);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void hidp_process_ctrl_transmit(struct hidp_session *session)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
|
@ -678,16 +689,6 @@ static void hidp_process_transmit(struct hidp_session *session)
|
|||
hidp_set_timer(session);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
while ((skb = skb_dequeue(&session->intr_transmit))) {
|
||||
if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
|
||||
skb_queue_head(&session->intr_transmit, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
hidp_set_timer(session);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static int hidp_session(void *arg)
|
||||
|
@ -700,6 +701,7 @@ static int hidp_session(void *arg)
|
|||
|
||||
BT_DBG("session %p", session);
|
||||
|
||||
__module_get(THIS_MODULE);
|
||||
set_user_nice(current, -15);
|
||||
|
||||
init_waitqueue_entry(&ctrl_wait, current);
|
||||
|
@ -714,14 +716,6 @@ static int hidp_session(void *arg)
|
|||
intr_sk->sk_state != BT_CONNECTED)
|
||||
break;
|
||||
|
||||
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
|
||||
skb_orphan(skb);
|
||||
if (!skb_linearize(skb))
|
||||
hidp_recv_ctrl_frame(session, skb);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
|
||||
skb_orphan(skb);
|
||||
if (!skb_linearize(skb))
|
||||
|
@ -730,7 +724,17 @@ static int hidp_session(void *arg)
|
|||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
hidp_process_transmit(session);
|
||||
hidp_process_intr_transmit(session);
|
||||
|
||||
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
|
||||
skb_orphan(skb);
|
||||
if (!skb_linearize(skb))
|
||||
hidp_recv_ctrl_frame(session, skb);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
hidp_process_ctrl_transmit(session);
|
||||
|
||||
schedule();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
@ -739,6 +743,10 @@ static int hidp_session(void *arg)
|
|||
remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
|
||||
remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
|
||||
|
||||
clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
|
||||
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
||||
wake_up_interruptible(&session->report_queue);
|
||||
|
||||
down_write(&hidp_session_sem);
|
||||
|
||||
hidp_del_timer(session);
|
||||
|
@ -772,34 +780,37 @@ static int hidp_session(void *arg)
|
|||
|
||||
kfree(session->rd_data);
|
||||
kfree(session);
|
||||
module_put_and_exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device *hidp_get_device(struct hidp_session *session)
|
||||
static struct hci_conn *hidp_get_connection(struct hidp_session *session)
|
||||
{
|
||||
bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
|
||||
bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
|
||||
struct device *device = NULL;
|
||||
struct hci_conn *conn;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
hdev = hci_get_route(dst, src);
|
||||
if (!hdev)
|
||||
return NULL;
|
||||
|
||||
session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
||||
if (session->conn)
|
||||
device = &session->conn->dev;
|
||||
hci_dev_lock_bh(hdev);
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
||||
if (conn)
|
||||
hci_conn_hold_device(conn);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return device;
|
||||
return conn;
|
||||
}
|
||||
|
||||
static int hidp_setup_input(struct hidp_session *session,
|
||||
struct hidp_connadd_req *req)
|
||||
{
|
||||
struct input_dev *input;
|
||||
int err, i;
|
||||
int i;
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input)
|
||||
|
@ -842,17 +853,10 @@ static int hidp_setup_input(struct hidp_session *session,
|
|||
input->relbit[0] |= BIT_MASK(REL_WHEEL);
|
||||
}
|
||||
|
||||
input->dev.parent = hidp_get_device(session);
|
||||
input->dev.parent = &session->conn->dev;
|
||||
|
||||
input->event = hidp_input_event;
|
||||
|
||||
err = input_register_device(input);
|
||||
if (err < 0) {
|
||||
input_free_device(input);
|
||||
session->input = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -949,7 +953,7 @@ static int hidp_setup_hid(struct hidp_session *session,
|
|||
strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
|
||||
strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
|
||||
|
||||
hid->dev.parent = hidp_get_device(session);
|
||||
hid->dev.parent = &session->conn->dev;
|
||||
hid->ll_driver = &hidp_hid_driver;
|
||||
|
||||
hid->hid_get_raw_report = hidp_get_raw_report;
|
||||
|
@ -976,18 +980,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
|
||||
return -ENOTUNIQ;
|
||||
|
||||
session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
|
||||
if (!session)
|
||||
return -ENOMEM;
|
||||
|
||||
BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
|
||||
|
||||
down_write(&hidp_session_sem);
|
||||
|
||||
s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
|
||||
if (s && s->state == BT_CONNECTED) {
|
||||
err = -EEXIST;
|
||||
goto failed;
|
||||
up_write(&hidp_session_sem);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
|
||||
if (!session) {
|
||||
up_write(&hidp_session_sem);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
|
||||
|
@ -1003,6 +1009,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
session->intr_sock = intr_sock;
|
||||
session->state = BT_CONNECTED;
|
||||
|
||||
session->conn = hidp_get_connection(session);
|
||||
if (!session->conn) {
|
||||
err = -ENOTCONN;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
|
||||
|
||||
skb_queue_head_init(&session->ctrl_transmit);
|
||||
|
@ -1015,9 +1027,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
|
||||
session->idle_to = req->idle_to;
|
||||
|
||||
__hidp_link_session(session);
|
||||
|
||||
if (req->rd_size > 0) {
|
||||
err = hidp_setup_hid(session, req);
|
||||
if (err && err != -ENODEV)
|
||||
if (err)
|
||||
goto purge;
|
||||
}
|
||||
|
||||
|
@ -1027,8 +1041,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
goto purge;
|
||||
}
|
||||
|
||||
__hidp_link_session(session);
|
||||
|
||||
hidp_set_timer(session);
|
||||
|
||||
if (session->hid) {
|
||||
|
@ -1054,7 +1066,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
!session->waiting_for_startup);
|
||||
}
|
||||
|
||||
err = hid_add_device(session->hid);
|
||||
if (session->hid)
|
||||
err = hid_add_device(session->hid);
|
||||
else
|
||||
err = input_register_device(session->input);
|
||||
|
||||
if (err < 0) {
|
||||
atomic_inc(&session->terminate);
|
||||
wake_up_process(session->task);
|
||||
|
@ -1077,8 +1093,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
unlink:
|
||||
hidp_del_timer(session);
|
||||
|
||||
__hidp_unlink_session(session);
|
||||
|
||||
if (session->input) {
|
||||
input_unregister_device(session->input);
|
||||
session->input = NULL;
|
||||
|
@ -1093,6 +1107,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
session->rd_data = NULL;
|
||||
|
||||
purge:
|
||||
__hidp_unlink_session(session);
|
||||
|
||||
skb_queue_purge(&session->ctrl_transmit);
|
||||
skb_queue_purge(&session->intr_transmit);
|
||||
|
||||
|
@ -1134,19 +1150,16 @@ int hidp_del_connection(struct hidp_conndel_req *req)
|
|||
|
||||
int hidp_get_connlist(struct hidp_connlist_req *req)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct hidp_session *session;
|
||||
int err = 0, n = 0;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
down_read(&hidp_session_sem);
|
||||
|
||||
list_for_each(p, &hidp_session_list) {
|
||||
struct hidp_session *session;
|
||||
list_for_each_entry(session, &hidp_session_list, list) {
|
||||
struct hidp_conninfo ci;
|
||||
|
||||
session = list_entry(p, struct hidp_session, list);
|
||||
|
||||
__hidp_copy_session(session, &ci);
|
||||
|
||||
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -333,7 +333,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
|
|||
opts.mode = chan->mode;
|
||||
opts.fcs = chan->fcs;
|
||||
opts.max_tx = chan->max_tx;
|
||||
opts.txwin_size = (__u16)chan->tx_win;
|
||||
opts.txwin_size = chan->tx_win;
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(opts));
|
||||
if (copy_to_user(optval, (char *) &opts, len))
|
||||
|
@ -358,10 +358,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
|
|||
break;
|
||||
}
|
||||
|
||||
if (chan->role_switch)
|
||||
if (test_bit(FLAG_ROLE_SWITCH, &chan->flags))
|
||||
opt |= L2CAP_LM_MASTER;
|
||||
|
||||
if (chan->force_reliable)
|
||||
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
|
||||
opt |= L2CAP_LM_RELIABLE;
|
||||
|
||||
if (put_user(opt, (u32 __user *) optval))
|
||||
|
@ -448,7 +448,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||
break;
|
||||
|
||||
case BT_FLUSHABLE:
|
||||
if (put_user(chan->flushable, (u32 __user *) optval))
|
||||
if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
|
||||
(u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
|
||||
break;
|
||||
|
@ -460,7 +461,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||
break;
|
||||
}
|
||||
|
||||
pwr.force_active = chan->force_active;
|
||||
pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(pwr));
|
||||
if (copy_to_user(optval, (char *) &pwr, len))
|
||||
|
@ -468,6 +469,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||
|
||||
break;
|
||||
|
||||
case BT_CHANNEL_POLICY:
|
||||
if (!enable_hs) {
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (put_user(chan->chan_policy, (u32 __user *) optval))
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
|
@ -502,7 +513,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
|||
opts.mode = chan->mode;
|
||||
opts.fcs = chan->fcs;
|
||||
opts.max_tx = chan->max_tx;
|
||||
opts.txwin_size = (__u16)chan->tx_win;
|
||||
opts.txwin_size = chan->tx_win;
|
||||
|
||||
len = min_t(unsigned int, sizeof(opts), optlen);
|
||||
if (copy_from_user((char *) &opts, optval, len)) {
|
||||
|
@ -510,7 +521,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
|||
break;
|
||||
}
|
||||
|
||||
if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) {
|
||||
if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
@ -534,7 +545,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
|||
chan->omtu = opts.omtu;
|
||||
chan->fcs = opts.fcs;
|
||||
chan->max_tx = opts.max_tx;
|
||||
chan->tx_win = (__u8)opts.txwin_size;
|
||||
chan->tx_win = opts.txwin_size;
|
||||
break;
|
||||
|
||||
case L2CAP_LM:
|
||||
|
@ -550,8 +561,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
|||
if (opt & L2CAP_LM_SECURE)
|
||||
chan->sec_level = BT_SECURITY_HIGH;
|
||||
|
||||
chan->role_switch = (opt & L2CAP_LM_MASTER);
|
||||
chan->force_reliable = (opt & L2CAP_LM_RELIABLE);
|
||||
if (opt & L2CAP_LM_MASTER)
|
||||
set_bit(FLAG_ROLE_SWITCH, &chan->flags);
|
||||
else
|
||||
clear_bit(FLAG_ROLE_SWITCH, &chan->flags);
|
||||
|
||||
if (opt & L2CAP_LM_RELIABLE)
|
||||
set_bit(FLAG_FORCE_RELIABLE, &chan->flags);
|
||||
else
|
||||
clear_bit(FLAG_FORCE_RELIABLE, &chan->flags);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -657,7 +675,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||
}
|
||||
}
|
||||
|
||||
chan->flushable = opt;
|
||||
if (opt)
|
||||
set_bit(FLAG_FLUSHABLE, &chan->flags);
|
||||
else
|
||||
clear_bit(FLAG_FLUSHABLE, &chan->flags);
|
||||
break;
|
||||
|
||||
case BT_POWER:
|
||||
|
@ -674,7 +695,36 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
chan->force_active = pwr.force_active;
|
||||
|
||||
if (pwr.force_active)
|
||||
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
|
||||
else
|
||||
clear_bit(FLAG_FORCE_ACTIVE, &chan->flags);
|
||||
break;
|
||||
|
||||
case BT_CHANNEL_POLICY:
|
||||
if (!enable_hs) {
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_user(opt, (u32 __user *) optval)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chan->mode != L2CAP_MODE_ERTM &&
|
||||
chan->mode != L2CAP_MODE_STREAMING) {
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
chan->chan_policy = (u8) opt;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -708,7 +758,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
|||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
err = l2cap_chan_send(chan, msg, len);
|
||||
err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
|
||||
|
||||
release_sock(sk);
|
||||
return err;
|
||||
|
@ -930,11 +980,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||
chan->fcs = pchan->fcs;
|
||||
chan->max_tx = pchan->max_tx;
|
||||
chan->tx_win = pchan->tx_win;
|
||||
chan->tx_win_max = pchan->tx_win_max;
|
||||
chan->sec_level = pchan->sec_level;
|
||||
chan->role_switch = pchan->role_switch;
|
||||
chan->force_reliable = pchan->force_reliable;
|
||||
chan->flushable = pchan->flushable;
|
||||
chan->force_active = pchan->force_active;
|
||||
chan->flags = pchan->flags;
|
||||
|
||||
security_sk_clone(parent, sk);
|
||||
} else {
|
||||
|
@ -963,12 +1011,10 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
|
||||
chan->fcs = L2CAP_FCS_CRC16;
|
||||
chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
|
||||
chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
|
||||
chan->sec_level = BT_SECURITY_LOW;
|
||||
chan->role_switch = 0;
|
||||
chan->force_reliable = 0;
|
||||
chan->flushable = BT_FLUSHABLE_OFF;
|
||||
chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
|
||||
|
||||
chan->flags = 0;
|
||||
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
|
||||
}
|
||||
|
||||
/* Default config options */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -65,7 +65,8 @@ static DEFINE_MUTEX(rfcomm_mutex);
|
|||
|
||||
static LIST_HEAD(session_list);
|
||||
|
||||
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
|
||||
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
|
||||
u32 priority);
|
||||
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
|
||||
static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
|
||||
static int rfcomm_queue_disc(struct rfcomm_dlc *d);
|
||||
|
@ -377,13 +378,11 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
|
|||
static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
|
||||
{
|
||||
struct rfcomm_dlc *d;
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &s->dlcs) {
|
||||
d = list_entry(p, struct rfcomm_dlc, list);
|
||||
list_for_each_entry(d, &s->dlcs, list)
|
||||
if (d->dlci == dlci)
|
||||
return d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -749,19 +748,34 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *d
|
|||
}
|
||||
|
||||
/* ---- RFCOMM frame sending ---- */
|
||||
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
|
||||
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
|
||||
u32 priority)
|
||||
{
|
||||
struct socket *sock = s->sock;
|
||||
struct sock *sk = sock->sk;
|
||||
struct kvec iv = { data, len };
|
||||
struct msghdr msg;
|
||||
|
||||
BT_DBG("session %p len %d", s, len);
|
||||
BT_DBG("session %p len %d priority %u", s, len, priority);
|
||||
|
||||
if (sk->sk_priority != priority) {
|
||||
lock_sock(sk);
|
||||
sk->sk_priority = priority;
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
return kernel_sendmsg(sock, &msg, &iv, 1, len);
|
||||
}
|
||||
|
||||
static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd)
|
||||
{
|
||||
BT_DBG("%p cmd %u", s, cmd->ctrl);
|
||||
|
||||
return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd), HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
|
||||
{
|
||||
struct rfcomm_cmd cmd;
|
||||
|
@ -773,7 +787,7 @@ static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
|
|||
cmd.len = __len8(0);
|
||||
cmd.fcs = __fcs2((u8 *) &cmd);
|
||||
|
||||
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
|
||||
return rfcomm_send_cmd(s, &cmd);
|
||||
}
|
||||
|
||||
static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
|
||||
|
@ -787,7 +801,7 @@ static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
|
|||
cmd.len = __len8(0);
|
||||
cmd.fcs = __fcs2((u8 *) &cmd);
|
||||
|
||||
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
|
||||
return rfcomm_send_cmd(s, &cmd);
|
||||
}
|
||||
|
||||
static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
|
||||
|
@ -801,7 +815,7 @@ static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
|
|||
cmd.len = __len8(0);
|
||||
cmd.fcs = __fcs2((u8 *) &cmd);
|
||||
|
||||
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
|
||||
return rfcomm_send_cmd(s, &cmd);
|
||||
}
|
||||
|
||||
static int rfcomm_queue_disc(struct rfcomm_dlc *d)
|
||||
|
@ -815,6 +829,8 @@ static int rfcomm_queue_disc(struct rfcomm_dlc *d)
|
|||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb->priority = HCI_PRIO_MAX;
|
||||
|
||||
cmd = (void *) __skb_put(skb, sizeof(*cmd));
|
||||
cmd->addr = d->addr;
|
||||
cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
|
||||
|
@ -837,7 +853,7 @@ static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
|
|||
cmd.len = __len8(0);
|
||||
cmd.fcs = __fcs2((u8 *) &cmd);
|
||||
|
||||
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
|
||||
return rfcomm_send_cmd(s, &cmd);
|
||||
}
|
||||
|
||||
static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
|
||||
|
@ -862,7 +878,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
|
|||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
|
||||
|
@ -904,7 +920,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
|
|||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
|
||||
|
@ -942,7 +958,7 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
|
|||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
|
||||
|
@ -969,7 +985,7 @@ static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
|
|||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
|
||||
|
@ -996,7 +1012,7 @@ static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig
|
|||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
|
||||
|
@ -1018,7 +1034,7 @@ static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
|
|||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
|
||||
|
@ -1040,7 +1056,7 @@ static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
|
|||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
|
||||
|
@ -1091,7 +1107,7 @@ static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
|
|||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
}
|
||||
|
||||
static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
|
||||
|
@ -1769,7 +1785,8 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
|
|||
return skb_queue_len(&d->tx_queue);
|
||||
|
||||
while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
|
||||
err = rfcomm_send_frame(d->session, skb->data, skb->len);
|
||||
err = rfcomm_send_frame(d->session, skb->data, skb->len,
|
||||
skb->priority);
|
||||
if (err < 0) {
|
||||
skb_queue_head(&d->tx_queue, skb);
|
||||
break;
|
||||
|
@ -2120,15 +2137,13 @@ static struct hci_cb rfcomm_cb = {
|
|||
static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
|
||||
{
|
||||
struct rfcomm_session *s;
|
||||
struct list_head *pp, *p;
|
||||
|
||||
rfcomm_lock();
|
||||
|
||||
list_for_each(p, &session_list) {
|
||||
s = list_entry(p, struct rfcomm_session, list);
|
||||
list_for_each(pp, &s->dlcs) {
|
||||
list_for_each_entry(s, &session_list, list) {
|
||||
struct rfcomm_dlc *d;
|
||||
list_for_each_entry(d, &s->dlcs, list) {
|
||||
struct sock *sk = s->sock->sk;
|
||||
struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list);
|
||||
|
||||
seq_printf(f, "%s %s %ld %d %d %d %d\n",
|
||||
batostr(&bt_sk(sk)->src),
|
||||
|
|
|
@ -600,6 +600,8 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||
break;
|
||||
}
|
||||
|
||||
skb->priority = sk->sk_priority;
|
||||
|
||||
err = rfcomm_dlc_send(d, skb);
|
||||
if (err < 0) {
|
||||
kfree_skb(skb);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/capability.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
@ -65,7 +66,7 @@ struct rfcomm_dev {
|
|||
struct rfcomm_dlc *dlc;
|
||||
struct tty_struct *tty;
|
||||
wait_queue_head_t wait;
|
||||
struct tasklet_struct wakeup_task;
|
||||
struct work_struct wakeup_task;
|
||||
|
||||
struct device *tty_dev;
|
||||
|
||||
|
@ -81,7 +82,7 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
|
|||
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
|
||||
static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
|
||||
|
||||
static void rfcomm_tty_wakeup(unsigned long arg);
|
||||
static void rfcomm_tty_wakeup(struct work_struct *work);
|
||||
|
||||
/* ---- Device functions ---- */
|
||||
static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
|
||||
|
@ -133,13 +134,10 @@ static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
|
|||
static struct rfcomm_dev *__rfcomm_dev_get(int id)
|
||||
{
|
||||
struct rfcomm_dev *dev;
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &rfcomm_dev_list) {
|
||||
dev = list_entry(p, struct rfcomm_dev, list);
|
||||
list_for_each_entry(dev, &rfcomm_dev_list, list)
|
||||
if (dev->id == id)
|
||||
return dev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -197,7 +195,7 @@ static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
|
|||
|
||||
static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
|
||||
{
|
||||
struct rfcomm_dev *dev;
|
||||
struct rfcomm_dev *dev, *entry;
|
||||
struct list_head *head = &rfcomm_dev_list, *p;
|
||||
int err = 0;
|
||||
|
||||
|
@ -212,8 +210,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
|
|||
if (req->dev_id < 0) {
|
||||
dev->id = 0;
|
||||
|
||||
list_for_each(p, &rfcomm_dev_list) {
|
||||
if (list_entry(p, struct rfcomm_dev, list)->id != dev->id)
|
||||
list_for_each_entry(entry, &rfcomm_dev_list, list) {
|
||||
if (entry->id != dev->id)
|
||||
break;
|
||||
|
||||
dev->id++;
|
||||
|
@ -222,9 +220,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
|
|||
} else {
|
||||
dev->id = req->dev_id;
|
||||
|
||||
list_for_each(p, &rfcomm_dev_list) {
|
||||
struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
|
||||
|
||||
list_for_each_entry(entry, &rfcomm_dev_list, list) {
|
||||
if (entry->id == dev->id) {
|
||||
err = -EADDRINUSE;
|
||||
goto out;
|
||||
|
@ -257,7 +253,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
|
|||
atomic_set(&dev->opened, 0);
|
||||
|
||||
init_waitqueue_head(&dev->wait);
|
||||
tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
|
||||
INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup);
|
||||
|
||||
skb_queue_head_init(&dev->pending);
|
||||
|
||||
|
@ -351,7 +347,7 @@ static void rfcomm_wfree(struct sk_buff *skb)
|
|||
struct rfcomm_dev *dev = (void *) skb->sk;
|
||||
atomic_sub(skb->truesize, &dev->wmem_alloc);
|
||||
if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
|
||||
tasklet_schedule(&dev->wakeup_task);
|
||||
queue_work(system_nrt_wq, &dev->wakeup_task);
|
||||
rfcomm_dev_put(dev);
|
||||
}
|
||||
|
||||
|
@ -455,9 +451,9 @@ static int rfcomm_release_dev(void __user *arg)
|
|||
|
||||
static int rfcomm_get_dev_list(void __user *arg)
|
||||
{
|
||||
struct rfcomm_dev *dev;
|
||||
struct rfcomm_dev_list_req *dl;
|
||||
struct rfcomm_dev_info *di;
|
||||
struct list_head *p;
|
||||
int n = 0, size, err;
|
||||
u16 dev_num;
|
||||
|
||||
|
@ -479,8 +475,7 @@ static int rfcomm_get_dev_list(void __user *arg)
|
|||
|
||||
read_lock_bh(&rfcomm_dev_lock);
|
||||
|
||||
list_for_each(p, &rfcomm_dev_list) {
|
||||
struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
|
||||
list_for_each_entry(dev, &rfcomm_dev_list, list) {
|
||||
if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
|
||||
continue;
|
||||
(di + n)->id = dev->id;
|
||||
|
@ -635,9 +630,10 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
|
|||
}
|
||||
|
||||
/* ---- TTY functions ---- */
|
||||
static void rfcomm_tty_wakeup(unsigned long arg)
|
||||
static void rfcomm_tty_wakeup(struct work_struct *work)
|
||||
{
|
||||
struct rfcomm_dev *dev = (void *) arg;
|
||||
struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev,
|
||||
wakeup_task);
|
||||
struct tty_struct *tty = dev->tty;
|
||||
if (!tty)
|
||||
return;
|
||||
|
@ -762,7 +758,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
|
|||
rfcomm_dlc_close(dev->dlc, 0);
|
||||
|
||||
clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
|
||||
tasklet_kill(&dev->wakeup_task);
|
||||
cancel_work_sync(&dev->wakeup_task);
|
||||
|
||||
rfcomm_dlc_lock(dev->dlc);
|
||||
tty->driver_data = NULL;
|
||||
|
@ -1155,9 +1151,11 @@ static const struct tty_operations rfcomm_ops = {
|
|||
|
||||
int __init rfcomm_init_ttys(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS);
|
||||
if (!rfcomm_tty_driver)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
rfcomm_tty_driver->owner = THIS_MODULE;
|
||||
rfcomm_tty_driver->driver_name = "rfcomm";
|
||||
|
@ -1172,10 +1170,11 @@ int __init rfcomm_init_ttys(void)
|
|||
rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
|
||||
tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
|
||||
|
||||
if (tty_register_driver(rfcomm_tty_driver)) {
|
||||
error = tty_register_driver(rfcomm_tty_driver);
|
||||
if (error) {
|
||||
BT_ERR("Can't register RFCOMM TTY driver");
|
||||
put_tty_driver(rfcomm_tty_driver);
|
||||
return -1;
|
||||
return error;
|
||||
}
|
||||
|
||||
BT_INFO("RFCOMM TTY layer initialized");
|
||||
|
|
|
@ -181,7 +181,8 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
|
|||
if (!skb)
|
||||
return;
|
||||
|
||||
hci_send_acl(conn->hcon, skb, 0);
|
||||
skb->priority = HCI_PRIO_MAX;
|
||||
hci_send_acl(conn->hchan, skb, 0);
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
|
Loading…
Reference in a new issue