Commit 9662cbc7 authored by John W. Linville's avatar John W. Linville
parents 640f5950 4b0b2f08
...@@ -210,6 +210,7 @@ enum { ...@@ -210,6 +210,7 @@ enum {
#define LMP_EV4 0x01 #define LMP_EV4 0x01
#define LMP_EV5 0x02 #define LMP_EV5 0x02
#define LMP_NO_BREDR 0x20
#define LMP_LE 0x40 #define LMP_LE 0x40
#define LMP_SNIFF_SUBR 0x02 #define LMP_SNIFF_SUBR 0x02
...@@ -745,6 +746,14 @@ struct hci_rp_read_bd_addr { ...@@ -745,6 +746,14 @@ struct hci_rp_read_bd_addr {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
#define HCI_OP_READ_DATA_BLOCK_SIZE 0x100a
struct hci_rp_read_data_block_size {
__u8 status;
__le16 max_acl_len;
__le16 block_len;
__le16 num_blocks;
} __packed;
#define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c #define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c
struct hci_cp_write_page_scan_activity { struct hci_cp_write_page_scan_activity {
__le16 interval; __le16 interval;
......
...@@ -61,18 +61,11 @@ struct inquiry_cache { ...@@ -61,18 +61,11 @@ struct inquiry_cache {
struct hci_conn_hash { struct hci_conn_hash {
struct list_head list; struct list_head list;
spinlock_t lock;
unsigned int acl_num; unsigned int acl_num;
unsigned int sco_num; unsigned int sco_num;
unsigned int le_num; unsigned int le_num;
}; };
struct hci_chan_hash {
struct list_head list;
spinlock_t lock;
unsigned int num;
};
struct bdaddr_list { struct bdaddr_list {
struct list_head list; struct list_head list;
bdaddr_t bdaddr; bdaddr_t bdaddr;
...@@ -124,7 +117,7 @@ struct adv_entry { ...@@ -124,7 +117,7 @@ struct adv_entry {
#define NUM_REASSEMBLY 4 #define NUM_REASSEMBLY 4
struct hci_dev { struct hci_dev {
struct list_head list; struct list_head list;
spinlock_t lock; struct mutex lock;
atomic_t refcnt; atomic_t refcnt;
char name[8]; char name[8];
...@@ -188,6 +181,11 @@ struct hci_dev { ...@@ -188,6 +181,11 @@ struct hci_dev {
unsigned int sco_pkts; unsigned int sco_pkts;
unsigned int le_pkts; unsigned int le_pkts;
__u16 block_len;
__u16 block_mtu;
__u16 num_blocks;
__u16 block_cnt;
unsigned long acl_last_tx; unsigned long acl_last_tx;
unsigned long sco_last_tx; unsigned long sco_last_tx;
unsigned long le_last_tx; unsigned long le_last_tx;
...@@ -200,10 +198,13 @@ struct hci_dev { ...@@ -200,10 +198,13 @@ struct hci_dev {
__u16 discov_timeout; __u16 discov_timeout;
struct delayed_work discov_off; struct delayed_work discov_off;
struct delayed_work service_cache;
struct timer_list cmd_timer; struct timer_list cmd_timer;
struct tasklet_struct cmd_task;
struct tasklet_struct rx_task; struct work_struct rx_work;
struct tasklet_struct tx_task; struct work_struct cmd_work;
struct work_struct tx_work;
struct sk_buff_head rx_q; struct sk_buff_head rx_q;
struct sk_buff_head raw_q; struct sk_buff_head raw_q;
...@@ -232,7 +233,7 @@ struct hci_dev { ...@@ -232,7 +233,7 @@ struct hci_dev {
struct list_head remote_oob_data; struct list_head remote_oob_data;
struct list_head adv_entries; struct list_head adv_entries;
struct timer_list adv_timer; struct delayed_work adv_work;
struct hci_dev_stats stat; struct hci_dev_stats stat;
...@@ -301,15 +302,12 @@ struct hci_conn { ...@@ -301,15 +302,12 @@ struct hci_conn {
unsigned int sent; unsigned int sent;
struct sk_buff_head data_q; struct sk_buff_head data_q;
struct hci_chan_hash chan_hash; struct list_head chan_list;
struct timer_list disc_timer; struct delayed_work disc_work;
struct timer_list idle_timer; struct timer_list idle_timer;
struct timer_list auto_accept_timer; struct timer_list auto_accept_timer;
struct work_struct work_add;
struct work_struct work_del;
struct device dev; struct device dev;
atomic_t devref; atomic_t devref;
...@@ -390,15 +388,15 @@ static inline void hci_conn_hash_init(struct hci_dev *hdev) ...@@ -390,15 +388,15 @@ static inline void hci_conn_hash_init(struct hci_dev *hdev)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
INIT_LIST_HEAD(&h->list); INIT_LIST_HEAD(&h->list);
spin_lock_init(&h->lock);
h->acl_num = 0; h->acl_num = 0;
h->sco_num = 0; h->sco_num = 0;
h->le_num = 0;
} }
static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
list_add(&c->list, &h->list); list_add_rcu(&c->list, &h->list);
switch (c->type) { switch (c->type) {
case ACL_LINK: case ACL_LINK:
h->acl_num++; h->acl_num++;
...@@ -416,7 +414,10 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) ...@@ -416,7 +414,10 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
list_del(&c->list);
list_del_rcu(&c->list);
synchronize_rcu();
switch (c->type) { switch (c->type) {
case ACL_LINK: case ACL_LINK:
h->acl_num--; h->acl_num--;
...@@ -451,14 +452,18 @@ static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, ...@@ -451,14 +452,18 @@ static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
__u16 handle) __u16 handle)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
struct list_head *p;
struct hci_conn *c; struct hci_conn *c;
list_for_each(p, &h->list) { rcu_read_lock();
c = list_entry(p, struct hci_conn, list);
if (c->handle == handle) list_for_each_entry_rcu(c, &h->list, list) {
if (c->handle == handle) {
rcu_read_unlock();
return c; return c;
}
} }
rcu_read_unlock();
return NULL; return NULL;
} }
...@@ -466,14 +471,19 @@ static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev, ...@@ -466,14 +471,19 @@ static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev,
__u8 type, bdaddr_t *ba) __u8 type, bdaddr_t *ba)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
struct list_head *p;
struct hci_conn *c; struct hci_conn *c;
list_for_each(p, &h->list) { rcu_read_lock();
c = list_entry(p, struct hci_conn, list);
if (c->type == type && !bacmp(&c->dst, ba)) list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && !bacmp(&c->dst, ba)) {
rcu_read_unlock();
return c; return c;
}
} }
rcu_read_unlock();
return NULL; return NULL;
} }
...@@ -481,37 +491,20 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, ...@@ -481,37 +491,20 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
__u8 type, __u16 state) __u8 type, __u16 state)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
struct list_head *p;
struct hci_conn *c; struct hci_conn *c;
list_for_each(p, &h->list) { rcu_read_lock();
c = list_entry(p, struct hci_conn, list);
if (c->type == type && c->state == state) list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && c->state == state) {
rcu_read_unlock();
return c; return c;
}
} }
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) rcu_read_unlock();
{
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) return NULL;
{
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_connect(struct hci_conn *conn);
...@@ -527,7 +520,7 @@ void hci_conn_check_pending(struct hci_dev *hdev); ...@@ -527,7 +520,7 @@ void hci_conn_check_pending(struct hci_dev *hdev);
struct hci_chan *hci_chan_create(struct hci_conn *conn); struct hci_chan *hci_chan_create(struct hci_conn *conn);
int hci_chan_del(struct hci_chan *chan); int hci_chan_del(struct hci_chan *chan);
void hci_chan_hash_flush(struct hci_conn *conn); void hci_chan_list_flush(struct hci_conn *conn);
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u8 sec_level, __u8 auth_type); __u8 sec_level, __u8 auth_type);
...@@ -538,7 +531,6 @@ int hci_conn_change_link_key(struct hci_conn *conn); ...@@ -538,7 +531,6 @@ int hci_conn_change_link_key(struct hci_conn *conn);
int hci_conn_switch_role(struct hci_conn *conn, __u8 role); int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active); void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active);
void hci_conn_enter_sniff_mode(struct hci_conn *conn);
void hci_conn_hold_device(struct hci_conn *conn); void hci_conn_hold_device(struct hci_conn *conn);
void hci_conn_put_device(struct hci_conn *conn); void hci_conn_put_device(struct hci_conn *conn);
...@@ -546,7 +538,7 @@ void hci_conn_put_device(struct hci_conn *conn); ...@@ -546,7 +538,7 @@ void hci_conn_put_device(struct hci_conn *conn);
static inline void hci_conn_hold(struct hci_conn *conn) static inline void hci_conn_hold(struct hci_conn *conn)
{ {
atomic_inc(&conn->refcnt); atomic_inc(&conn->refcnt);
del_timer(&conn->disc_timer); cancel_delayed_work_sync(&conn->disc_work);
} }
static inline void hci_conn_put(struct hci_conn *conn) static inline void hci_conn_put(struct hci_conn *conn)
...@@ -565,7 +557,9 @@ static inline void hci_conn_put(struct hci_conn *conn) ...@@ -565,7 +557,9 @@ static inline void hci_conn_put(struct hci_conn *conn)
} else { } else {
timeo = msecs_to_jiffies(10); timeo = msecs_to_jiffies(10);
} }
mod_timer(&conn->disc_timer, jiffies + timeo); cancel_delayed_work_sync(&conn->disc_work);
queue_delayed_work(conn->hdev->workqueue,
&conn->disc_work, jiffies + timeo);
} }
} }
...@@ -597,10 +591,8 @@ static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d) ...@@ -597,10 +591,8 @@ static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d)
try_module_get(d->owner) ? __hci_dev_hold(d) : NULL; \ try_module_get(d->owner) ? __hci_dev_hold(d) : NULL; \
}) })
#define hci_dev_lock(d) spin_lock(&d->lock) #define hci_dev_lock(d) mutex_lock(&d->lock)
#define hci_dev_unlock(d) spin_unlock(&d->lock) #define hci_dev_unlock(d) mutex_unlock(&d->lock)
#define hci_dev_lock_bh(d) spin_lock_bh(&d->lock)
#define hci_dev_unlock_bh(d) spin_unlock_bh(&d->lock)
struct hci_dev *hci_dev_get(int index); struct hci_dev *hci_dev_get(int index);
struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
...@@ -960,12 +952,16 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr); ...@@ -960,12 +952,16 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
/* HCI info for socket */ /* HCI info for socket */
#define hci_pi(sk) ((struct hci_pinfo *) sk) #define hci_pi(sk) ((struct hci_pinfo *) sk)
/* HCI socket flags */
#define HCI_PI_MGMT_INIT 0
struct hci_pinfo { struct hci_pinfo {
struct bt_sock bt; struct bt_sock bt;
struct hci_dev *hdev; struct hci_dev *hdev;
struct hci_filter filter; struct hci_filter filter;
__u32 cmsg_mask; __u32 cmsg_mask;
unsigned short channel; unsigned short channel;
unsigned long flags;
}; };
/* HCI security filter */ /* HCI security filter */
......
...@@ -482,10 +482,11 @@ struct l2cap_chan { ...@@ -482,10 +482,11 @@ struct l2cap_chan {
__u32 remote_acc_lat; __u32 remote_acc_lat;
__u32 remote_flush_to; __u32 remote_flush_to;
struct timer_list chan_timer; struct delayed_work chan_timer;
struct timer_list retrans_timer; struct delayed_work retrans_timer;
struct timer_list monitor_timer; struct delayed_work monitor_timer;
struct timer_list ack_timer; struct delayed_work ack_timer;
struct sk_buff *tx_send_head; struct sk_buff *tx_send_head;
struct sk_buff_head tx_q; struct sk_buff_head tx_q;
struct sk_buff_head srej_q; struct sk_buff_head srej_q;
...@@ -521,7 +522,7 @@ struct l2cap_conn { ...@@ -521,7 +522,7 @@ struct l2cap_conn {
__u8 info_state; __u8 info_state;
__u8 info_ident; __u8 info_ident;
struct timer_list info_timer; struct delayed_work info_work;
spinlock_t lock; spinlock_t lock;
...@@ -535,7 +536,7 @@ struct l2cap_conn { ...@@ -535,7 +536,7 @@ struct l2cap_conn {
struct smp_chan *smp_chan; struct smp_chan *smp_chan;
struct list_head chan_l; struct list_head chan_l;
rwlock_t chan_lock; struct mutex chan_lock;
}; };
#define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 #define L2CAP_INFO_CL_MTU_REQ_SENT 0x01
...@@ -595,16 +596,16 @@ enum { ...@@ -595,16 +596,16 @@ enum {
}; };
#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #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 __clear_chan_timer(c) l2cap_clear_timer(&c->chan_timer)
#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
L2CAP_DEFAULT_RETRANS_TO); L2CAP_DEFAULT_RETRANS_TO);
#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer) #define __clear_retrans_timer(c) l2cap_clear_timer(&c->retrans_timer)
#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \ #define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \
L2CAP_DEFAULT_MONITOR_TO); L2CAP_DEFAULT_MONITOR_TO);
#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer) #define __clear_monitor_timer(c) l2cap_clear_timer(&c->monitor_timer)
#define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \ #define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \
L2CAP_DEFAULT_ACK_TO); L2CAP_DEFAULT_ACK_TO);
#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) #define __clear_ack_timer(c) l2cap_clear_timer(&c->ack_timer)
static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2) static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2)
{ {
...@@ -805,7 +806,8 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); ...@@ -805,7 +806,8 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid);
struct l2cap_chan *l2cap_chan_create(struct sock *sk); struct l2cap_chan *l2cap_chan_create(struct sock *sk);
void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_close(struct l2cap_chan *chan, int reason);
void l2cap_chan_destroy(struct l2cap_chan *chan); void l2cap_chan_destroy(struct l2cap_chan *chan);
int l2cap_chan_connect(struct l2cap_chan *chan); inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst);
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); u32 priority);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy); void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
......
...@@ -61,22 +61,29 @@ struct mgmt_rp_read_index_list { ...@@ -61,22 +61,29 @@ struct mgmt_rp_read_index_list {
/* Reserve one extra byte for names in management messages so that they /* Reserve one extra byte for names in management messages so that they
* are always guaranteed to be nul-terminated */ * are always guaranteed to be nul-terminated */
#define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1) #define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1)
#define MGMT_MAX_SHORT_NAME_LENGTH (10 + 1)
#define MGMT_SETTING_POWERED 0x00000001
#define MGMT_SETTING_CONNECTABLE 0x00000002
#define MGMT_SETTING_FAST_CONNECTABLE 0x00000004
#define MGMT_SETTING_DISCOVERABLE 0x00000008
#define MGMT_SETTING_PAIRABLE 0x00000010
#define MGMT_SETTING_LINK_SECURITY 0x00000020
#define MGMT_SETTING_SSP 0x00000040
#define MGMT_SETTING_BREDR 0x00000080
#define MGMT_SETTING_HS 0x00000100
#define MGMT_SETTING_LE 0x00000200
#define MGMT_OP_READ_INFO 0x0004 #define MGMT_OP_READ_INFO 0x0004
struct mgmt_rp_read_info { struct mgmt_rp_read_info {
__u8 type;
__u8 powered;
__u8 connectable;
__u8 discoverable;
__u8 pairable;
__u8 sec_mode;
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 version;
__le16 manufacturer;
__le32 supported_settings;
__le32 current_settings;
__u8 dev_class[3]; __u8 dev_class[3];
__u8 features[8];
__u16 manufacturer;
__u8 hci_ver;
__u16 hci_rev;
__u8 name[MGMT_MAX_NAME_LENGTH]; __u8 name[MGMT_MAX_NAME_LENGTH];
__u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH];
} __packed; } __packed;
struct mgmt_mode { struct mgmt_mode {
...@@ -93,28 +100,38 @@ struct mgmt_cp_set_discoverable { ...@@ -93,28 +100,38 @@ struct mgmt_cp_set_discoverable {
#define MGMT_OP_SET_CONNECTABLE 0x0007 #define MGMT_OP_SET_CONNECTABLE 0x0007
#define MGMT_OP_SET_PAIRABLE 0x0008 #define MGMT_OP_SET_FAST_CONNECTABLE 0x0008
#define MGMT_OP_ADD_UUID 0x0009 #define MGMT_OP_SET_PAIRABLE 0x0009
struct mgmt_cp_add_uuid {
__u8 uuid[16];
__u8 svc_hint;
} __packed;
#define MGMT_OP_REMOVE_UUID 0x000A #define MGMT_OP_SET_LINK_SECURITY 0x000A
struct mgmt_cp_remove_uuid {
__u8 uuid[16];
} __packed;
#define MGMT_OP_SET_DEV_CLASS 0x000B #define MGMT_OP_SET_SSP 0x000B
#define MGMT_OP_SET_HS 0x000C
#define MGMT_OP_SET_LE 0x000D
#define MGMT_OP_SET_DEV_CLASS 0x000E
struct mgmt_cp_set_dev_class { struct mgmt_cp_set_dev_class {
__u8 major; __u8 major;
__u8 minor; __u8 minor;
} __packed; } __packed;
#define MGMT_OP_SET_SERVICE_CACHE 0x000C #define MGMT_OP_SET_LOCAL_NAME 0x000F
struct mgmt_cp_set_service_cache { struct mgmt_cp_set_local_name {
__u8 enable; __u8 name[MGMT_MAX_NAME_LENGTH];
} __packed;
#define MGMT_OP_ADD_UUID 0x0010
struct mgmt_cp_add_uuid {
__u8 uuid[16];
__u8 svc_hint;
} __packed;
#define MGMT_OP_REMOVE_UUID 0x0011
struct mgmt_cp_remove_uuid {
__u8 uuid[16];
} __packed; } __packed;
struct mgmt_link_key_info { struct mgmt_link_key_info {
...@@ -124,14 +141,14 @@ struct mgmt_link_key_info { ...@@ -124,14 +141,14 @@ struct mgmt_link_key_info {
u8 pin_len; u8 pin_len;
} __packed; } __packed;
#define MGMT_OP_LOAD_LINK_KEYS 0x000D #define MGMT_OP_LOAD_LINK_KEYS 0x0012
struct mgmt_cp_load_link_keys { struct mgmt_cp_load_link_keys {
__u8 debug_keys; __u8 debug_keys;
__le16 key_count; __le16 key_count;
struct mgmt_link_key_info keys[0]; struct mgmt_link_key_info keys[0];
} __packed; } __packed;
#define MGMT_OP_REMOVE_KEYS 0x000E #define MGMT_OP_REMOVE_KEYS 0x0013
struct mgmt_cp_remove_keys { struct mgmt_cp_remove_keys {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 disconnect; __u8 disconnect;
...@@ -141,7 +158,7 @@ struct mgmt_rp_remove_keys { ...@@ -141,7 +158,7 @@ struct mgmt_rp_remove_keys {
__u8 status; __u8 status;
}; };
#define MGMT_OP_DISCONNECT 0x000F #define MGMT_OP_DISCONNECT 0x0014
struct mgmt_cp_disconnect { struct mgmt_cp_disconnect {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
...@@ -160,13 +177,13 @@ struct mgmt_addr_info { ...@@ -160,13 +177,13 @@ struct mgmt_addr_info {
__u8 type; __u8 type;
} __packed; } __packed;
#define MGMT_OP_GET_CONNECTIONS 0x0010 #define MGMT_OP_GET_CONNECTIONS 0x0015
struct mgmt_rp_get_connections { struct mgmt_rp_get_connections {
__le16 conn_count; __le16 conn_count;
struct mgmt_addr_info addr[0]; struct mgmt_addr_info addr[0];
} __packed; } __packed;
#define MGMT_OP_PIN_CODE_REPLY 0x0011 #define MGMT_OP_PIN_CODE_REPLY 0x0016
struct mgmt_cp_pin_code_reply { struct mgmt_cp_pin_code_reply {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 pin_len; __u8 pin_len;
...@@ -177,17 +194,17 @@ struct mgmt_rp_pin_code_reply { ...@@ -177,17 +194,17 @@ struct mgmt_rp_pin_code_reply {
uint8_t status; uint8_t status;
} __packed; } __packed;
#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0012 #define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017
struct mgmt_cp_pin_code_neg_reply { struct mgmt_cp_pin_code_neg_reply {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
#define MGMT_OP_SET_IO_CAPABILITY 0x0013 #define MGMT_OP_SET_IO_CAPABILITY 0x0018
struct mgmt_cp_set_io_capability { struct mgmt_cp_set_io_capability {
__u8 io_capability; __u8 io_capability;
} __packed; } __packed;
#define MGMT_OP_PAIR_DEVICE 0x0014 #define MGMT_OP_PAIR_DEVICE 0x0019
struct mgmt_cp_pair_device { struct mgmt_cp_pair_device {
struct mgmt_addr_info addr; struct mgmt_addr_info addr;
__u8 io_cap; __u8 io_cap;
...@@ -197,7 +214,7 @@ struct mgmt_rp_pair_device { ...@@ -197,7 +214,7 @@ struct mgmt_rp_pair_device {
__u8 status; __u8 status;
} __packed; } __packed;
#define MGMT_OP_USER_CONFIRM_REPLY 0x0015 #define MGMT_OP_USER_CONFIRM_REPLY 0x001A
struct mgmt_cp_user_confirm_reply { struct mgmt_cp_user_confirm_reply {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
...@@ -206,61 +223,68 @@ struct mgmt_rp_user_confirm_reply { ...@@ -206,61 +223,68 @@ struct mgmt_rp_user_confirm_reply {
__u8 status; __u8 status;
} __packed; } __packed;
#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x0016 #define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001B
struct mgmt_cp_user_confirm_neg_reply {
bdaddr_t bdaddr;
} __packed;
#define MGMT_OP_SET_LOCAL_NAME 0x0017 #define MGMT_OP_USER_PASSKEY_REPLY 0x001C
struct mgmt_cp_set_local_name { struct mgmt_cp_user_passkey_reply {
__u8 name[MGMT_MAX_NAME_LENGTH]; bdaddr_t bdaddr;
__le32 passkey;
} __packed;
struct mgmt_rp_user_passkey_reply {
bdaddr_t bdaddr;
__u8 status;
} __packed; } __packed;
#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0018 #define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001D
struct mgmt_cp_user_passkey_neg_reply {
bdaddr_t bdaddr;
} __packed;
#define MGMT_OP_READ_LOCAL_OOB_DATA 0x001E
struct mgmt_rp_read_local_oob_data { struct mgmt_rp_read_local_oob_data {
__u8 hash[16]; __u8 hash[16];
__u8 randomizer[16]; __u8 randomizer[16];
} __packed; } __packed;
#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0019 #define MGMT_OP_ADD_REMOTE_OOB_DATA 0x001F
struct mgmt_cp_add_remote_oob_data { struct mgmt_cp_add_remote_oob_data {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 hash[16]; __u8 hash[16];
__u8 randomizer[16]; __u8 randomizer[16];
} __packed; } __packed;
#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x001A #define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0020
struct mgmt_cp_remove_remote_oob_data { struct mgmt_cp_remove_remote_oob_data {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
#define MGMT_OP_START_DISCOVERY 0x001B #define MGMT_OP_START_DISCOVERY 0x0021
struct mgmt_cp_start_discovery { struct mgmt_cp_start_discovery {
__u8 type; __u8 type;
} __packed; } __packed;
#define MGMT_OP_STOP_DISCOVERY 0x001C #define MGMT_OP_STOP_DISCOVERY 0x0022
#define MGMT_OP_BLOCK_DEVICE 0x001D #define MGMT_OP_CONFIRM_NAME 0x0023
struct mgmt_cp_block_device { struct mgmt_cp_confirm_name {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 name_known;
} __packed; } __packed;
struct mgmt_rp_confirm_name {
#define MGMT_OP_UNBLOCK_DEVICE 0x001E
struct mgmt_cp_unblock_device {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 status;
} __packed; } __packed;
#define MGMT_OP_SET_FAST_CONNECTABLE 0x001F #define MGMT_OP_BLOCK_DEVICE 0x0024
struct mgmt_cp_set_fast_connectable { struct mgmt_cp_block_device {
__u8 enable;
} __packed;
#define MGMT_OP_USER_PASSKEY_REPLY 0x0020
struct mgmt_cp_user_passkey_reply {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__le32 passkey;
} __packed; } __packed;
#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x0021 #define MGMT_OP_UNBLOCK_DEVICE 0x0025
struct mgmt_cp_user_passkey_neg_reply { struct mgmt_cp_unblock_device {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
...@@ -285,81 +309,82 @@ struct mgmt_ev_controller_error { ...@@ -285,81 +309,82 @@ struct mgmt_ev_controller_error {
#define MGMT_EV_INDEX_REMOVED 0x0005 #define MGMT_EV_INDEX_REMOVED 0x0005
#define MGMT_EV_POWERED 0x0006 #define MGMT_EV_NEW_SETTINGS 0x0006
#define MGMT_EV_DISCOVERABLE 0x0007 #define MGMT_EV_CLASS_OF_DEV_CHANGED 0x0007
struct mgmt_ev_class_of_dev_changed {
#define MGMT_EV_CONNECTABLE 0x0008 __u8 dev_class[3];
};
#define MGMT_EV_PAIRABLE 0x0009 #define MGMT_EV_LOCAL_NAME_CHANGED 0x0008
struct mgmt_ev_local_name_changed {
__u8 name[MGMT_MAX_NAME_LENGTH];
__u8 short_name[MGMT_MAX_SHORT_NAME_LENGTH];
} __packed;
#define MGMT_EV_NEW_LINK_KEY 0x000A #define MGMT_EV_NEW_LINK_KEY 0x0009
struct mgmt_ev_new_link_key { struct mgmt_ev_new_link_key {
__u8 store_hint; __u8 store_hint;
struct mgmt_link_key_info key; struct mgmt_link_key_info key;
} __packed; } __packed;
#define MGMT_EV_CONNECTED 0x000B #define MGMT_EV_CONNECTED 0x000A
#define MGMT_EV_DISCONNECTED 0x000C #define MGMT_EV_DISCONNECTED 0x000B
#define MGMT_EV_CONNECT_FAILED 0x000D #define MGMT_EV_CONNECT_FAILED 0x000C
struct mgmt_ev_connect_failed { struct mgmt_ev_connect_failed {
struct mgmt_addr_info addr; struct mgmt_addr_info addr;
__u8 status; __u8 status;
} __packed; } __packed;
#define MGMT_EV_PIN_CODE_REQUEST 0x000E #define MGMT_EV_PIN_CODE_REQUEST 0x000D
struct mgmt_ev_pin_code_request { struct mgmt_ev_pin_code_request {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 secure; __u8 secure;
} __packed; } __packed;
#define MGMT_EV_USER_CONFIRM_REQUEST 0x000F #define MGMT_EV_USER_CONFIRM_REQUEST 0x000E
struct mgmt_ev_user_confirm_request { struct mgmt_ev_user_confirm_request {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 confirm_hint; __u8 confirm_hint;
__le32 value; __le32 value;
} __packed; } __packed;
#define MGMT_EV_USER_PASSKEY_REQUEST 0x000F
struct mgmt_ev_user_passkey_request {
bdaddr_t bdaddr;
} __packed;
#define MGMT_EV_AUTH_FAILED 0x0010 #define MGMT_EV_AUTH_FAILED 0x0010
struct mgmt_ev_auth_failed { struct mgmt_ev_auth_failed {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 status; __u8 status;
} __packed; } __packed;
#define MGMT_EV_LOCAL_NAME_CHANGED 0x0011 #define MGMT_EV_DEVICE_FOUND 0x0011
struct mgmt_ev_local_name_changed {
__u8 name[MGMT_MAX_NAME_LENGTH];
} __packed;
#define MGMT_EV_DEVICE_FOUND 0x0012
struct mgmt_ev_device_found { struct mgmt_ev_device_found {
struct mgmt_addr_info addr; struct mgmt_addr_info addr;
__u8 dev_class[3]; __u8 dev_class[3];
__s8 rssi; __s8 rssi;
__u8 confirm_name;
__u8 eir[HCI_MAX_EIR_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH];
} __packed; } __packed;
#define MGMT_EV_REMOTE_NAME 0x0013 #define MGMT_EV_REMOTE_NAME 0x0012
struct mgmt_ev_remote_name { struct mgmt_ev_remote_name {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 name[MGMT_MAX_NAME_LENGTH]; __u8 name[MGMT_MAX_NAME_LENGTH];
} __packed; } __packed;
#define MGMT_EV_DISCOVERING 0x0014 #define MGMT_EV_DISCOVERING 0x0013
#define MGMT_EV_DEVICE_BLOCKED 0x0015 #define MGMT_EV_DEVICE_BLOCKED 0x0014
struct mgmt_ev_device_blocked { struct mgmt_ev_device_blocked {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
#define MGMT_EV_DEVICE_UNBLOCKED 0x0016 #define MGMT_EV_DEVICE_UNBLOCKED 0x0015
struct mgmt_ev_device_unblocked { struct mgmt_ev_device_unblocked {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
#define MGMT_EV_USER_PASSKEY_REQUEST 0x0017
struct mgmt_ev_user_passkey_request {
bdaddr_t bdaddr;
} __packed;
...@@ -275,9 +275,10 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status) ...@@ -275,9 +275,10 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
} }
} }
static void hci_conn_timeout(unsigned long arg) static void hci_conn_timeout(struct work_struct *work)
{ {
struct hci_conn *conn = (void *) arg; struct hci_conn *conn = container_of(work, struct hci_conn,
disc_work.work);
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
__u8 reason; __u8 reason;
...@@ -311,6 +312,42 @@ static void hci_conn_timeout(unsigned long arg) ...@@ -311,6 +312,42 @@ static void hci_conn_timeout(unsigned long arg)
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
} }
/* Enter sniff mode */
static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p mode %d", conn, conn->mode);
if (test_bit(HCI_RAW, &hdev->flags))
return;
if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
return;
if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF))
return;
if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
struct hci_cp_sniff_subrate cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_latency = cpu_to_le16(0);
cp.min_remote_timeout = cpu_to_le16(0);
cp.min_local_timeout = cpu_to_le16(0);
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
}
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
struct hci_cp_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
cp.min_interval = cpu_to_le16(hdev->sniff_min_interval);
cp.attempt = cpu_to_le16(4);
cp.timeout = cpu_to_le16(1);
hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp);
}
}
static void hci_conn_idle(unsigned long arg) static void hci_conn_idle(unsigned long arg)
{ {
struct hci_conn *conn = (void *) arg; struct hci_conn *conn = (void *) arg;
...@@ -325,12 +362,8 @@ static void hci_conn_auto_accept(unsigned long arg) ...@@ -325,12 +362,8 @@ static void hci_conn_auto_accept(unsigned long arg)
struct hci_conn *conn = (void *) arg; struct hci_conn *conn = (void *) arg;
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
hci_dev_lock(hdev);
hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst), hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
&conn->dst); &conn->dst);
hci_dev_unlock(hdev);
} }
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
...@@ -374,9 +407,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) ...@@ -374,9 +407,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
skb_queue_head_init(&conn->data_q); skb_queue_head_init(&conn->data_q);
hci_chan_hash_init(conn); INIT_LIST_HEAD(&conn->chan_list);;
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn); INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept, setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
(unsigned long) conn); (unsigned long) conn);
...@@ -385,8 +418,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) ...@@ -385,8 +418,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
hci_dev_hold(hdev); hci_dev_hold(hdev);
tasklet_disable(&hdev->tx_task);
hci_conn_hash_add(hdev, conn); hci_conn_hash_add(hdev, conn);
if (hdev->notify) if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
...@@ -395,8 +426,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) ...@@ -395,8 +426,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
hci_conn_init_sysfs(conn); hci_conn_init_sysfs(conn);
tasklet_enable(&hdev->tx_task);
return conn; return conn;
} }
...@@ -408,7 +437,7 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -408,7 +437,7 @@ int hci_conn_del(struct hci_conn *conn)
del_timer(&conn->idle_timer); del_timer(&conn->idle_timer);
del_timer(&conn->disc_timer); cancel_delayed_work_sync(&conn->disc_work);
del_timer(&conn->auto_accept_timer); del_timer(&conn->auto_accept_timer);
...@@ -432,16 +461,13 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -432,16 +461,13 @@ int hci_conn_del(struct hci_conn *conn)
} }
} }
tasklet_disable(&hdev->tx_task);
hci_chan_hash_flush(conn); hci_chan_list_flush(conn);
hci_conn_hash_del(hdev, conn); hci_conn_hash_del(hdev, conn);
if (hdev->notify) if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
tasklet_enable(&hdev->tx_task);
skb_queue_purge(&conn->data_q); skb_queue_purge(&conn->data_q);
hci_conn_put_device(conn); hci_conn_put_device(conn);
...@@ -674,7 +700,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) ...@@ -674,7 +700,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
goto encrypt; goto encrypt;
auth: auth:
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
return 0; return 0;
if (!hci_conn_auth(conn, sec_level, auth_type)) if (!hci_conn_auth(conn, sec_level, auth_type))
...@@ -767,57 +793,15 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) ...@@ -767,57 +793,15 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
jiffies + msecs_to_jiffies(hdev->idle_timeout)); jiffies + msecs_to_jiffies(hdev->idle_timeout));
} }
/* Enter sniff mode */
void hci_conn_enter_sniff_mode(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p mode %d", conn, conn->mode);
if (test_bit(HCI_RAW, &hdev->flags))
return;
if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
return;
if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF))
return;
if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
struct hci_cp_sniff_subrate cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_latency = cpu_to_le16(0);
cp.min_remote_timeout = cpu_to_le16(0);
cp.min_local_timeout = cpu_to_le16(0);
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
}
if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
struct hci_cp_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
cp.min_interval = cpu_to_le16(hdev->sniff_min_interval);
cp.attempt = cpu_to_le16(4);
cp.timeout = cpu_to_le16(1);
hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp);
}
}
/* Drop all connection on the device */ /* Drop all connection on the device */
void hci_conn_hash_flush(struct hci_dev *hdev) void hci_conn_hash_flush(struct hci_dev *hdev)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
struct list_head *p; struct hci_conn *c;
BT_DBG("hdev %s", hdev->name); BT_DBG("hdev %s", hdev->name);
p = h->list.next; list_for_each_entry_rcu(c, &h->list, list) {
while (p != &h->list) {
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
p = p->next;
c->state = BT_CLOSED; c->state = BT_CLOSED;
hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
...@@ -882,7 +866,7 @@ int hci_get_conn_list(void __user *arg) ...@@ -882,7 +866,7 @@ int hci_get_conn_list(void __user *arg)
ci = cl->conn_info; ci = cl->conn_info;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
list_for_each_entry(c, &hdev->conn_hash.list, list) { list_for_each_entry(c, &hdev->conn_hash.list, list) {
bacpy(&(ci + n)->bdaddr, &c->dst); bacpy(&(ci + n)->bdaddr, &c->dst);
(ci + n)->handle = c->handle; (ci + n)->handle = c->handle;
...@@ -893,7 +877,7 @@ int hci_get_conn_list(void __user *arg) ...@@ -893,7 +877,7 @@ int hci_get_conn_list(void __user *arg)
if (++n >= req.conn_num) if (++n >= req.conn_num)
break; break;
} }
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
cl->dev_id = hdev->id; cl->dev_id = hdev->id;
cl->conn_num = n; cl->conn_num = n;
...@@ -917,7 +901,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) ...@@ -917,7 +901,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
if (copy_from_user(&req, arg, sizeof(req))) if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT; return -EFAULT;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, req.type, &req.bdaddr); conn = hci_conn_hash_lookup_ba(hdev, req.type, &req.bdaddr);
if (conn) { if (conn) {
bacpy(&ci.bdaddr, &conn->dst); bacpy(&ci.bdaddr, &conn->dst);
...@@ -927,7 +911,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) ...@@ -927,7 +911,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
ci.state = conn->state; ci.state = conn->state;
ci.link_mode = conn->link_mode; ci.link_mode = conn->link_mode;
} }
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
if (!conn) if (!conn)
return -ENOENT; return -ENOENT;
...@@ -943,11 +927,11 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg) ...@@ -943,11 +927,11 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)
if (copy_from_user(&req, arg, sizeof(req))) if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT; return -EFAULT;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr);
if (conn) if (conn)
req.type = conn->auth_type; req.type = conn->auth_type;
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
if (!conn) if (!conn)
return -ENOENT; return -ENOENT;
...@@ -969,9 +953,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn) ...@@ -969,9 +953,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
chan->conn = conn; chan->conn = conn;
skb_queue_head_init(&chan->data_q); skb_queue_head_init(&chan->data_q);
tasklet_disable(&hdev->tx_task); list_add_rcu(&chan->list, &conn->chan_list);
hci_chan_hash_add(conn, chan);
tasklet_enable(&hdev->tx_task);
return chan; return chan;
} }
...@@ -983,9 +965,9 @@ int hci_chan_del(struct hci_chan *chan) ...@@ -983,9 +965,9 @@ int hci_chan_del(struct hci_chan *chan)
BT_DBG("%s conn %p chan %p", hdev->name, conn, chan); BT_DBG("%s conn %p chan %p", hdev->name, conn, chan);
tasklet_disable(&hdev->tx_task); list_del_rcu(&chan->list);
hci_chan_hash_del(conn, chan);
tasklet_enable(&hdev->tx_task); synchronize_rcu();
skb_queue_purge(&chan->data_q); skb_queue_purge(&chan->data_q);
kfree(chan); kfree(chan);
...@@ -993,13 +975,12 @@ int hci_chan_del(struct hci_chan *chan) ...@@ -993,13 +975,12 @@ int hci_chan_del(struct hci_chan *chan)
return 0; return 0;
} }
void hci_chan_hash_flush(struct hci_conn *conn) void hci_chan_list_flush(struct hci_conn *conn)
{ {
struct hci_chan_hash *h = &conn->chan_hash; struct hci_chan *chan;
struct hci_chan *chan, *tmp;
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
list_for_each_entry_safe(chan, tmp, &h->list, list) list_for_each_entry_rcu(chan, &conn->chan_list, list)
hci_chan_del(chan); hci_chan_del(chan);
} }
/* /*
BlueZ - Bluetooth protocol stack for Linux BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2000-2001 Qualcomm Incorporated
Copyright (C) 2011 ProFUSION Embedded Systems
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
...@@ -56,11 +57,11 @@ ...@@ -56,11 +57,11 @@
int enable_hs; int enable_hs;
static void hci_cmd_task(unsigned long arg); static void hci_rx_work(struct work_struct *work);
static void hci_rx_task(unsigned long arg); static void hci_cmd_work(struct work_struct *work);
static void hci_tx_task(unsigned long arg); static void hci_tx_work(struct work_struct *work);
static DEFINE_RWLOCK(hci_task_lock); static DEFINE_MUTEX(hci_task_lock);
/* HCI device list */ /* HCI device list */
LIST_HEAD(hci_dev_list); LIST_HEAD(hci_dev_list);
...@@ -209,7 +210,7 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) ...@@ -209,7 +210,7 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
skb->dev = (void *) hdev; skb->dev = (void *) hdev;
skb_queue_tail(&hdev->cmd_q, skb); skb_queue_tail(&hdev->cmd_q, skb);
tasklet_schedule(&hdev->cmd_task); queue_work(hdev->workqueue, &hdev->cmd_work);
} }
skb_queue_purge(&hdev->driver_init); skb_queue_purge(&hdev->driver_init);
...@@ -433,14 +434,14 @@ int hci_inquiry(void __user *arg) ...@@ -433,14 +434,14 @@ int hci_inquiry(void __user *arg)
if (!hdev) if (!hdev)
return -ENODEV; return -ENODEV;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
inquiry_cache_empty(hdev) || inquiry_cache_empty(hdev) ||
ir.flags & IREQ_CACHE_FLUSH) { ir.flags & IREQ_CACHE_FLUSH) {
inquiry_cache_flush(hdev); inquiry_cache_flush(hdev);
do_inquiry = 1; do_inquiry = 1;
} }
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
timeo = ir.length * msecs_to_jiffies(2000); timeo = ir.length * msecs_to_jiffies(2000);
...@@ -462,9 +463,9 @@ int hci_inquiry(void __user *arg) ...@@ -462,9 +463,9 @@ int hci_inquiry(void __user *arg)
goto done; goto done;
} }
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf); ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
BT_DBG("num_rsp %d", ir.num_rsp); BT_DBG("num_rsp %d", ir.num_rsp);
...@@ -541,15 +542,15 @@ int hci_dev_open(__u16 dev) ...@@ -541,15 +542,15 @@ int hci_dev_open(__u16 dev)
set_bit(HCI_UP, &hdev->flags); set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP); hci_notify(hdev, HCI_DEV_UP);
if (!test_bit(HCI_SETUP, &hdev->flags)) { if (!test_bit(HCI_SETUP, &hdev->flags)) {
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
mgmt_powered(hdev, 1); mgmt_powered(hdev, 1);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
} }
} else { } else {
/* Init failed, cleanup */ /* Init failed, cleanup */
tasklet_kill(&hdev->rx_task); flush_work(&hdev->tx_work);
tasklet_kill(&hdev->tx_task); flush_work(&hdev->cmd_work);
tasklet_kill(&hdev->cmd_task); flush_work(&hdev->rx_work);
skb_queue_purge(&hdev->cmd_q); skb_queue_purge(&hdev->cmd_q);
skb_queue_purge(&hdev->rx_q); skb_queue_purge(&hdev->rx_q);
...@@ -585,9 +586,9 @@ static int hci_dev_do_close(struct hci_dev *hdev) ...@@ -585,9 +586,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
return 0; return 0;
} }
/* Kill RX and TX tasks */ /* Flush RX and TX works */
tasklet_kill(&hdev->rx_task); flush_work(&hdev->tx_work);
tasklet_kill(&hdev->tx_task); flush_work(&hdev->rx_work);
if (hdev->discov_timeout > 0) { if (hdev->discov_timeout > 0) {
cancel_delayed_work(&hdev->discov_off); cancel_delayed_work(&hdev->discov_off);
...@@ -597,10 +598,13 @@ static int hci_dev_do_close(struct hci_dev *hdev) ...@@ -597,10 +598,13 @@ static int hci_dev_do_close(struct hci_dev *hdev)
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
cancel_delayed_work(&hdev->power_off); cancel_delayed_work(&hdev->power_off);
hci_dev_lock_bh(hdev); if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags))
cancel_delayed_work(&hdev->service_cache);
hci_dev_lock(hdev);
inquiry_cache_flush(hdev); inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev); hci_conn_hash_flush(hdev);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_notify(hdev, HCI_DEV_DOWN); hci_notify(hdev, HCI_DEV_DOWN);
...@@ -617,8 +621,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) ...@@ -617,8 +621,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
clear_bit(HCI_INIT, &hdev->flags); clear_bit(HCI_INIT, &hdev->flags);
} }
/* Kill cmd task */ /* flush cmd work */
tasklet_kill(&hdev->cmd_task); flush_work(&hdev->cmd_work);
/* Drop queues */ /* Drop queues */
skb_queue_purge(&hdev->rx_q); skb_queue_purge(&hdev->rx_q);
...@@ -636,9 +640,9 @@ static int hci_dev_do_close(struct hci_dev *hdev) ...@@ -636,9 +640,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
* and no tasks are scheduled. */ * and no tasks are scheduled. */
hdev->close(hdev); hdev->close(hdev);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
mgmt_powered(hdev, 0); mgmt_powered(hdev, 0);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
/* Clear flags */ /* Clear flags */
hdev->flags = 0; hdev->flags = 0;
...@@ -672,7 +676,6 @@ int hci_dev_reset(__u16 dev) ...@@ -672,7 +676,6 @@ int hci_dev_reset(__u16 dev)
return -ENODEV; return -ENODEV;
hci_req_lock(hdev); hci_req_lock(hdev);
tasklet_disable(&hdev->tx_task);
if (!test_bit(HCI_UP, &hdev->flags)) if (!test_bit(HCI_UP, &hdev->flags))
goto done; goto done;
...@@ -681,10 +684,10 @@ int hci_dev_reset(__u16 dev) ...@@ -681,10 +684,10 @@ int hci_dev_reset(__u16 dev)
skb_queue_purge(&hdev->rx_q); skb_queue_purge(&hdev->rx_q);
skb_queue_purge(&hdev->cmd_q); skb_queue_purge(&hdev->cmd_q);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
inquiry_cache_flush(hdev); inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev); hci_conn_hash_flush(hdev);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
if (hdev->flush) if (hdev->flush)
hdev->flush(hdev); hdev->flush(hdev);
...@@ -697,7 +700,6 @@ int hci_dev_reset(__u16 dev) ...@@ -697,7 +700,6 @@ int hci_dev_reset(__u16 dev)
msecs_to_jiffies(HCI_INIT_TIMEOUT)); msecs_to_jiffies(HCI_INIT_TIMEOUT));
done: done:
tasklet_enable(&hdev->tx_task);
hci_req_unlock(hdev); hci_req_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return ret; return ret;
...@@ -939,7 +941,7 @@ static void hci_power_on(struct work_struct *work) ...@@ -939,7 +941,7 @@ static void hci_power_on(struct work_struct *work)
return; return;
if (test_bit(HCI_AUTO_OFF, &hdev->flags)) if (test_bit(HCI_AUTO_OFF, &hdev->flags))
queue_delayed_work(hdev->workqueue, &hdev->power_off, schedule_delayed_work(&hdev->power_off,
msecs_to_jiffies(AUTO_OFF_TIMEOUT)); msecs_to_jiffies(AUTO_OFF_TIMEOUT));
if (test_and_clear_bit(HCI_SETUP, &hdev->flags)) if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
...@@ -967,13 +969,13 @@ static void hci_discov_off(struct work_struct *work) ...@@ -967,13 +969,13 @@ static void hci_discov_off(struct work_struct *work)
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
hdev->discov_timeout = 0; hdev->discov_timeout = 0;
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
} }
int hci_uuids_clear(struct hci_dev *hdev) int hci_uuids_clear(struct hci_dev *hdev)
...@@ -1207,7 +1209,7 @@ static void hci_cmd_timer(unsigned long arg) ...@@ -1207,7 +1209,7 @@ static void hci_cmd_timer(unsigned long arg)
BT_ERR("%s command tx timeout", hdev->name); BT_ERR("%s command tx timeout", hdev->name);
atomic_set(&hdev->cmd_cnt, 1); atomic_set(&hdev->cmd_cnt, 1);
tasklet_schedule(&hdev->cmd_task); queue_work(hdev->workqueue, &hdev->cmd_work);
} }
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
...@@ -1340,9 +1342,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) ...@@ -1340,9 +1342,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
return mgmt_device_unblocked(hdev, bdaddr); return mgmt_device_unblocked(hdev, bdaddr);
} }
static void hci_clear_adv_cache(unsigned long arg) static void hci_clear_adv_cache(struct work_struct *work)
{ {
struct hci_dev *hdev = (void *) arg; struct hci_dev *hdev = container_of(work, struct hci_dev,
adv_work.work);
hci_dev_lock(hdev); hci_dev_lock(hdev);
...@@ -1443,7 +1446,7 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -1443,7 +1446,7 @@ int hci_register_dev(struct hci_dev *hdev)
list_add_tail(&hdev->list, head); list_add_tail(&hdev->list, head);
atomic_set(&hdev->refcnt, 1); atomic_set(&hdev->refcnt, 1);
spin_lock_init(&hdev->lock); mutex_init(&hdev->lock);
hdev->flags = 0; hdev->flags = 0;
hdev->dev_flags = 0; hdev->dev_flags = 0;
...@@ -1456,9 +1459,10 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -1456,9 +1459,10 @@ int hci_register_dev(struct hci_dev *hdev)
hdev->sniff_max_interval = 800; hdev->sniff_max_interval = 800;
hdev->sniff_min_interval = 80; hdev->sniff_min_interval = 80;
tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev); INIT_WORK(&hdev->rx_work, hci_rx_work);
tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev); INIT_WORK(&hdev->cmd_work, hci_cmd_work);
tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev); INIT_WORK(&hdev->tx_work, hci_tx_work);
skb_queue_head_init(&hdev->rx_q); skb_queue_head_init(&hdev->rx_q);
skb_queue_head_init(&hdev->cmd_q); skb_queue_head_init(&hdev->cmd_q);
...@@ -1487,9 +1491,8 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -1487,9 +1491,8 @@ int hci_register_dev(struct hci_dev *hdev)
INIT_LIST_HEAD(&hdev->remote_oob_data); INIT_LIST_HEAD(&hdev->remote_oob_data);
INIT_LIST_HEAD(&hdev->adv_entries); INIT_LIST_HEAD(&hdev->adv_entries);
setup_timer(&hdev->adv_timer, hci_clear_adv_cache,
(unsigned long) hdev);
INIT_DELAYED_WORK(&hdev->adv_work, hci_clear_adv_cache);
INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_on, hci_power_on);
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
...@@ -1501,7 +1504,8 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -1501,7 +1504,8 @@ int hci_register_dev(struct hci_dev *hdev)
write_unlock_bh(&hci_dev_list_lock); write_unlock_bh(&hci_dev_list_lock);
hdev->workqueue = create_singlethread_workqueue(hdev->name); hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND |
WQ_MEM_RECLAIM, 1);
if (!hdev->workqueue) { if (!hdev->workqueue) {
error = -ENOMEM; error = -ENOMEM;
goto err; goto err;
...@@ -1522,7 +1526,7 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -1522,7 +1526,7 @@ int hci_register_dev(struct hci_dev *hdev)
set_bit(HCI_AUTO_OFF, &hdev->flags); set_bit(HCI_AUTO_OFF, &hdev->flags);
set_bit(HCI_SETUP, &hdev->flags); set_bit(HCI_SETUP, &hdev->flags);
queue_work(hdev->workqueue, &hdev->power_on); schedule_work(&hdev->power_on);
hci_notify(hdev, HCI_DEV_REG); hci_notify(hdev, HCI_DEV_REG);
...@@ -1557,9 +1561,9 @@ void hci_unregister_dev(struct hci_dev *hdev) ...@@ -1557,9 +1561,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
if (!test_bit(HCI_INIT, &hdev->flags) && if (!test_bit(HCI_INIT, &hdev->flags) &&
!test_bit(HCI_SETUP, &hdev->flags)) { !test_bit(HCI_SETUP, &hdev->flags)) {
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
mgmt_index_removed(hdev); mgmt_index_removed(hdev);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
} }
/* mgmt_index_removed should take care of emptying the /* mgmt_index_removed should take care of emptying the
...@@ -1575,17 +1579,17 @@ void hci_unregister_dev(struct hci_dev *hdev) ...@@ -1575,17 +1579,17 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_del_sysfs(hdev); hci_del_sysfs(hdev);
del_timer(&hdev->adv_timer); cancel_delayed_work_sync(&hdev->adv_work);
destroy_workqueue(hdev->workqueue); destroy_workqueue(hdev->workqueue);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
hci_blacklist_clear(hdev); hci_blacklist_clear(hdev);
hci_uuids_clear(hdev); hci_uuids_clear(hdev);
hci_link_keys_clear(hdev); hci_link_keys_clear(hdev);
hci_remote_oob_data_clear(hdev); hci_remote_oob_data_clear(hdev);
hci_adv_entries_clear(hdev); hci_adv_entries_clear(hdev);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
__hci_dev_put(hdev); __hci_dev_put(hdev);
} }
...@@ -1623,9 +1627,8 @@ int hci_recv_frame(struct sk_buff *skb) ...@@ -1623,9 +1627,8 @@ int hci_recv_frame(struct sk_buff *skb)
/* Time stamp */ /* Time stamp */
__net_timestamp(skb); __net_timestamp(skb);
/* Queue frame for rx task */
skb_queue_tail(&hdev->rx_q, skb); skb_queue_tail(&hdev->rx_q, skb);
tasklet_schedule(&hdev->rx_task); queue_work(hdev->workqueue, &hdev->rx_work);
return 0; return 0;
} }
...@@ -1808,14 +1811,14 @@ int hci_register_proto(struct hci_proto *hp) ...@@ -1808,14 +1811,14 @@ int hci_register_proto(struct hci_proto *hp)
if (hp->id >= HCI_MAX_PROTO) if (hp->id >= HCI_MAX_PROTO)
return -EINVAL; return -EINVAL;
write_lock_bh(&hci_task_lock); mutex_lock(&hci_task_lock);
if (!hci_proto[hp->id]) if (!hci_proto[hp->id])
hci_proto[hp->id] = hp; hci_proto[hp->id] = hp;
else else
err = -EEXIST; err = -EEXIST;
write_unlock_bh(&hci_task_lock); mutex_unlock(&hci_task_lock);
return err; return err;
} }
...@@ -1830,14 +1833,14 @@ int hci_unregister_proto(struct hci_proto *hp) ...@@ -1830,14 +1833,14 @@ int hci_unregister_proto(struct hci_proto *hp)
if (hp->id >= HCI_MAX_PROTO) if (hp->id >= HCI_MAX_PROTO)
return -EINVAL; return -EINVAL;
write_lock_bh(&hci_task_lock); mutex_lock(&hci_task_lock);
if (hci_proto[hp->id]) if (hci_proto[hp->id])
hci_proto[hp->id] = NULL; hci_proto[hp->id] = NULL;
else else
err = -ENOENT; err = -ENOENT;
write_unlock_bh(&hci_task_lock); mutex_unlock(&hci_task_lock);
return err; return err;
} }
...@@ -1922,7 +1925,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) ...@@ -1922,7 +1925,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
hdev->init_last_cmd = opcode; hdev->init_last_cmd = opcode;
skb_queue_tail(&hdev->cmd_q, skb); skb_queue_tail(&hdev->cmd_q, skb);
tasklet_schedule(&hdev->cmd_task); queue_work(hdev->workqueue, &hdev->cmd_work);
return 0; return 0;
} }
...@@ -2012,7 +2015,7 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) ...@@ -2012,7 +2015,7 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
hci_queue_acl(conn, &chan->data_q, skb, flags); hci_queue_acl(conn, &chan->data_q, skb, flags);
tasklet_schedule(&hdev->tx_task); queue_work(hdev->workqueue, &hdev->tx_work);
} }
EXPORT_SYMBOL(hci_send_acl); EXPORT_SYMBOL(hci_send_acl);
...@@ -2035,7 +2038,7 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb) ...@@ -2035,7 +2038,7 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
skb_queue_tail(&conn->data_q, skb); skb_queue_tail(&conn->data_q, skb);
tasklet_schedule(&hdev->tx_task); queue_work(hdev->workqueue, &hdev->tx_work);
} }
EXPORT_SYMBOL(hci_send_sco); EXPORT_SYMBOL(hci_send_sco);
...@@ -2050,7 +2053,10 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int ...@@ -2050,7 +2053,10 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
/* We don't have to lock device here. Connections are always /* We don't have to lock device here. Connections are always
* added and removed with TX task disabled. */ * added and removed with TX task disabled. */
list_for_each_entry(c, &h->list, list) {
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type != type || skb_queue_empty(&c->data_q)) if (c->type != type || skb_queue_empty(&c->data_q))
continue; continue;
...@@ -2068,6 +2074,8 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int ...@@ -2068,6 +2074,8 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
break; break;
} }
rcu_read_unlock();
if (conn) { if (conn) {
int cnt, q; int cnt, q;
...@@ -2103,14 +2111,18 @@ static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type) ...@@ -2103,14 +2111,18 @@ static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
BT_ERR("%s link tx timeout", hdev->name); BT_ERR("%s link tx timeout", hdev->name);
rcu_read_lock();
/* Kill stalled connections */ /* Kill stalled connections */
list_for_each_entry(c, &h->list, list) { list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && c->sent) { if (c->type == type && c->sent) {
BT_ERR("%s killing stalled connection %s", BT_ERR("%s killing stalled connection %s",
hdev->name, batostr(&c->dst)); hdev->name, batostr(&c->dst));
hci_acl_disconn(c, 0x13); hci_acl_disconn(c, 0x13);
} }
} }
rcu_read_unlock();
} }
static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
...@@ -2124,8 +2136,9 @@ static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, ...@@ -2124,8 +2136,9 @@ static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
list_for_each_entry(conn, &h->list, list) { rcu_read_lock();
struct hci_chan_hash *ch;
list_for_each_entry_rcu(conn, &h->list, list) {
struct hci_chan *tmp; struct hci_chan *tmp;
if (conn->type != type) if (conn->type != type)
...@@ -2136,9 +2149,7 @@ static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, ...@@ -2136,9 +2149,7 @@ static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
conn_num++; conn_num++;
ch = &conn->chan_hash; list_for_each_entry_rcu(tmp, &conn->chan_list, list) {
list_for_each_entry(tmp, &ch->list, list) {
struct sk_buff *skb; struct sk_buff *skb;
if (skb_queue_empty(&tmp->data_q)) if (skb_queue_empty(&tmp->data_q))
...@@ -2166,6 +2177,8 @@ static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, ...@@ -2166,6 +2177,8 @@ static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
break; break;
} }
rcu_read_unlock();
if (!chan) if (!chan)
return NULL; return NULL;
...@@ -2199,8 +2212,9 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type) ...@@ -2199,8 +2212,9 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
list_for_each_entry(conn, &h->list, list) { rcu_read_lock();
struct hci_chan_hash *ch;
list_for_each_entry_rcu(conn, &h->list, list) {
struct hci_chan *chan; struct hci_chan *chan;
if (conn->type != type) if (conn->type != type)
...@@ -2211,8 +2225,7 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type) ...@@ -2211,8 +2225,7 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
num++; num++;
ch = &conn->chan_hash; list_for_each_entry_rcu(chan, &conn->chan_list, list) {
list_for_each_entry(chan, &ch->list, list) {
struct sk_buff *skb; struct sk_buff *skb;
if (chan->sent) { if (chan->sent) {
...@@ -2236,6 +2249,9 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type) ...@@ -2236,6 +2249,9 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
if (hci_conn_num(hdev, type) == num) if (hci_conn_num(hdev, type) == num)
break; break;
} }
rcu_read_unlock();
} }
static inline void hci_sched_acl(struct hci_dev *hdev) static inline void hci_sched_acl(struct hci_dev *hdev)
...@@ -2386,12 +2402,12 @@ static inline void hci_sched_le(struct hci_dev *hdev) ...@@ -2386,12 +2402,12 @@ static inline void hci_sched_le(struct hci_dev *hdev)
hci_prio_recalculate(hdev, LE_LINK); hci_prio_recalculate(hdev, LE_LINK);
} }
static void hci_tx_task(unsigned long arg) static void hci_tx_work(struct work_struct *work)
{ {
struct hci_dev *hdev = (struct hci_dev *) arg; struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work);
struct sk_buff *skb; struct sk_buff *skb;
read_lock(&hci_task_lock); mutex_lock(&hci_task_lock);
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt, BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
hdev->sco_cnt, hdev->le_cnt); hdev->sco_cnt, hdev->le_cnt);
...@@ -2410,7 +2426,7 @@ static void hci_tx_task(unsigned long arg) ...@@ -2410,7 +2426,7 @@ static void hci_tx_task(unsigned long arg)
while ((skb = skb_dequeue(&hdev->raw_q))) while ((skb = skb_dequeue(&hdev->raw_q)))
hci_send_frame(skb); hci_send_frame(skb);
read_unlock(&hci_task_lock); mutex_unlock(&hci_task_lock);
} }
/* ----- HCI RX task (incoming data processing) ----- */ /* ----- HCI RX task (incoming data processing) ----- */
...@@ -2439,7 +2455,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -2439,7 +2455,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
if (conn) { if (conn) {
register struct hci_proto *hp; register struct hci_proto *hp;
hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF);
/* Send to upper protocol */ /* Send to upper protocol */
hp = hci_proto[HCI_PROTO_L2CAP]; hp = hci_proto[HCI_PROTO_L2CAP];
...@@ -2491,14 +2507,14 @@ static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -2491,14 +2507,14 @@ static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
kfree_skb(skb); kfree_skb(skb);
} }
static void hci_rx_task(unsigned long arg) static void hci_rx_work(struct work_struct *work)
{ {
struct hci_dev *hdev = (struct hci_dev *) arg; struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work);
struct sk_buff *skb; struct sk_buff *skb;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
read_lock(&hci_task_lock); mutex_lock(&hci_task_lock);
while ((skb = skb_dequeue(&hdev->rx_q))) { while ((skb = skb_dequeue(&hdev->rx_q))) {
if (atomic_read(&hdev->promisc)) { if (atomic_read(&hdev->promisc)) {
...@@ -2524,6 +2540,7 @@ static void hci_rx_task(unsigned long arg) ...@@ -2524,6 +2540,7 @@ static void hci_rx_task(unsigned long arg)
/* Process frame */ /* Process frame */
switch (bt_cb(skb)->pkt_type) { switch (bt_cb(skb)->pkt_type) {
case HCI_EVENT_PKT: case HCI_EVENT_PKT:
BT_DBG("%s Event packet", hdev->name);
hci_event_packet(hdev, skb); hci_event_packet(hdev, skb);
break; break;
...@@ -2543,12 +2560,12 @@ static void hci_rx_task(unsigned long arg) ...@@ -2543,12 +2560,12 @@ static void hci_rx_task(unsigned long arg)
} }
} }
read_unlock(&hci_task_lock); mutex_unlock(&hci_task_lock);
} }
static void hci_cmd_task(unsigned long arg) static void hci_cmd_work(struct work_struct *work)
{ {
struct hci_dev *hdev = (struct hci_dev *) arg; struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_work);
struct sk_buff *skb; struct sk_buff *skb;
BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt)); BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
...@@ -2572,7 +2589,7 @@ static void hci_cmd_task(unsigned long arg) ...@@ -2572,7 +2589,7 @@ static void hci_cmd_task(unsigned long arg)
jiffies + msecs_to_jiffies(HCI_CMD_TIMEOUT)); jiffies + msecs_to_jiffies(HCI_CMD_TIMEOUT));
} else { } else {
skb_queue_head(&hdev->cmd_q, skb); skb_queue_head(&hdev->cmd_q, skb);
tasklet_schedule(&hdev->cmd_task); queue_work(hdev->workqueue, &hdev->cmd_work);
} }
} }
} }
......
...@@ -378,11 +378,8 @@ static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -378,11 +378,8 @@ static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s voice setting 0x%04x", hdev->name, setting); BT_DBG("%s voice setting 0x%04x", hdev->name, setting);
if (hdev->notify) { if (hdev->notify)
tasklet_disable(&hdev->tx_task);
hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
tasklet_enable(&hdev->tx_task);
}
} }
static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
...@@ -409,11 +406,8 @@ static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb ...@@ -409,11 +406,8 @@ static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb
BT_DBG("%s voice setting 0x%04x", hdev->name, setting); BT_DBG("%s voice setting 0x%04x", hdev->name, setting);
if (hdev->notify) { if (hdev->notify)
tasklet_disable(&hdev->tx_task);
hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
tasklet_enable(&hdev->tx_task);
}
} }
static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
...@@ -773,6 +767,28 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -773,6 +767,28 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status); hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status);
} }
static void hci_cc_read_data_block_size(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_rp_read_data_block_size *rp = (void *) skb->data;
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
return;
hdev->block_mtu = __le16_to_cpu(rp->max_acl_len);
hdev->block_len = __le16_to_cpu(rp->block_len);
hdev->num_blocks = __le16_to_cpu(rp->num_blocks);
hdev->block_cnt = hdev->num_blocks;
BT_DBG("%s blk mtu %d cnt %d len %d", hdev->name, hdev->block_mtu,
hdev->block_cnt, hdev->block_len);
hci_req_complete(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, rp->status);
}
static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb)
{ {
__u8 status = *((__u8 *) skb->data); __u8 status = *((__u8 *) skb->data);
...@@ -1017,7 +1033,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, ...@@ -1017,7 +1033,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
if (cp->enable == 0x01) { if (cp->enable == 0x01) {
set_bit(HCI_LE_SCAN, &hdev->dev_flags); set_bit(HCI_LE_SCAN, &hdev->dev_flags);
del_timer(&hdev->adv_timer); cancel_delayed_work_sync(&hdev->adv_work);
hci_dev_lock(hdev); hci_dev_lock(hdev);
hci_adv_entries_clear(hdev); hci_adv_entries_clear(hdev);
...@@ -1025,7 +1041,9 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, ...@@ -1025,7 +1041,9 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
} else if (cp->enable == 0x00) { } else if (cp->enable == 0x00) {
clear_bit(HCI_LE_SCAN, &hdev->dev_flags); clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT); cancel_delayed_work_sync(&hdev->adv_work);
queue_delayed_work(hdev->workqueue, &hdev->adv_work,
jiffies + ADV_CLEAR_TIMEOUT);
} }
} }
...@@ -2022,6 +2040,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk ...@@ -2022,6 +2040,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_read_bd_addr(hdev, skb); hci_cc_read_bd_addr(hdev, skb);
break; break;
case HCI_OP_READ_DATA_BLOCK_SIZE:
hci_cc_read_data_block_size(hdev, skb);
break;
case HCI_OP_WRITE_CA_TIMEOUT: case HCI_OP_WRITE_CA_TIMEOUT:
hci_cc_write_ca_timeout(hdev, skb); hci_cc_write_ca_timeout(hdev, skb);
break; break;
...@@ -2116,7 +2138,7 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk ...@@ -2116,7 +2138,7 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
if (ev->ncmd) { if (ev->ncmd) {
atomic_set(&hdev->cmd_cnt, 1); atomic_set(&hdev->cmd_cnt, 1);
if (!skb_queue_empty(&hdev->cmd_q)) if (!skb_queue_empty(&hdev->cmd_q))
tasklet_schedule(&hdev->cmd_task); queue_work(hdev->workqueue, &hdev->cmd_work);
} }
} }
...@@ -2198,7 +2220,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -2198,7 +2220,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
atomic_set(&hdev->cmd_cnt, 1); atomic_set(&hdev->cmd_cnt, 1);
if (!skb_queue_empty(&hdev->cmd_q)) if (!skb_queue_empty(&hdev->cmd_q))
tasklet_schedule(&hdev->cmd_task); queue_work(hdev->workqueue, &hdev->cmd_work);
} }
} }
...@@ -2243,8 +2265,6 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -2243,8 +2265,6 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
return; return;
} }
tasklet_disable(&hdev->tx_task);
for (i = 0, ptr = (__le16 *) skb->data; i < ev->num_hndl; i++) { for (i = 0, ptr = (__le16 *) skb->data; i < ev->num_hndl; i++) {
struct hci_conn *conn; struct hci_conn *conn;
__u16 handle, count; __u16 handle, count;
...@@ -2253,34 +2273,43 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -2253,34 +2273,43 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
count = get_unaligned_le16(ptr++); count = get_unaligned_le16(ptr++);
conn = hci_conn_hash_lookup_handle(hdev, handle); conn = hci_conn_hash_lookup_handle(hdev, handle);
if (conn) { if (!conn)
conn->sent -= count; continue;
if (conn->type == ACL_LINK) { conn->sent -= count;
switch (conn->type) {
case ACL_LINK:
hdev->acl_cnt += count;
if (hdev->acl_cnt > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts;
break;
case LE_LINK:
if (hdev->le_pkts) {
hdev->le_cnt += count;
if (hdev->le_cnt > hdev->le_pkts)
hdev->le_cnt = hdev->le_pkts;
} else {
hdev->acl_cnt += count; hdev->acl_cnt += count;
if (hdev->acl_cnt > hdev->acl_pkts) if (hdev->acl_cnt > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts; hdev->acl_cnt = hdev->acl_pkts;
} else if (conn->type == LE_LINK) {
if (hdev->le_pkts) {
hdev->le_cnt += count;
if (hdev->le_cnt > hdev->le_pkts)
hdev->le_cnt = hdev->le_pkts;
} else {
hdev->acl_cnt += count;
if (hdev->acl_cnt > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts;
}
} else {
hdev->sco_cnt += count;
if (hdev->sco_cnt > hdev->sco_pkts)
hdev->sco_cnt = hdev->sco_pkts;
} }
break;
case SCO_LINK:
hdev->sco_cnt += count;
if (hdev->sco_cnt > hdev->sco_pkts)
hdev->sco_cnt = hdev->sco_pkts;
break;
default:
BT_ERR("Unknown type %d conn %p", conn->type, conn);
break;
} }
} }
tasklet_schedule(&hdev->tx_task); queue_work(hdev->workqueue, &hdev->tx_work);
tasklet_enable(&hdev->tx_task);
} }
static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
......
...@@ -188,11 +188,11 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) ...@@ -188,11 +188,11 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
return -EFAULT; return -EFAULT;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
err = hci_blacklist_add(hdev, &bdaddr); err = hci_blacklist_add(hdev, &bdaddr);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
return err; return err;
} }
...@@ -205,11 +205,11 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) ...@@ -205,11 +205,11 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
return -EFAULT; return -EFAULT;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
err = hci_blacklist_del(hdev, &bdaddr); err = hci_blacklist_del(hdev, &bdaddr);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
return err; return err;
} }
...@@ -343,8 +343,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le ...@@ -343,8 +343,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
if (haddr.hci_channel > HCI_CHANNEL_CONTROL) if (haddr.hci_channel > HCI_CHANNEL_CONTROL)
return -EINVAL; return -EINVAL;
if (haddr.hci_channel == HCI_CHANNEL_CONTROL && !enable_mgmt) if (haddr.hci_channel == HCI_CHANNEL_CONTROL) {
return -EINVAL; if (!enable_mgmt)
return -EINVAL;
set_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags);
}
lock_sock(sk); lock_sock(sk);
...@@ -535,10 +538,10 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, ...@@ -535,10 +538,10 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) { if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) {
skb_queue_tail(&hdev->raw_q, skb); skb_queue_tail(&hdev->raw_q, skb);
tasklet_schedule(&hdev->tx_task); queue_work(hdev->workqueue, &hdev->tx_work);
} else { } else {
skb_queue_tail(&hdev->cmd_q, skb); skb_queue_tail(&hdev->cmd_q, skb);
tasklet_schedule(&hdev->cmd_task); queue_work(hdev->workqueue, &hdev->cmd_work);
} }
} else { } else {
if (!capable(CAP_NET_RAW)) { if (!capable(CAP_NET_RAW)) {
...@@ -547,7 +550,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, ...@@ -547,7 +550,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
} }
skb_queue_tail(&hdev->raw_q, skb); skb_queue_tail(&hdev->raw_q, skb);
tasklet_schedule(&hdev->tx_task); queue_work(hdev->workqueue, &hdev->tx_work);
} }
err = len; err = len;
......
...@@ -89,11 +89,35 @@ static struct device_type bt_link = { ...@@ -89,11 +89,35 @@ static struct device_type bt_link = {
.release = bt_link_release, .release = bt_link_release,
}; };
static void add_conn(struct work_struct *work) /*
* The rfcomm tty device will possibly retain even when conn
* is down, and sysfs doesn't support move zombie device,
* so we should move the device before conn device is destroyed.
*/
static int __match_tty(struct device *dev, void *data)
{
return !strncmp(dev_name(dev), "rfcomm", 6);
}
void hci_conn_init_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p", conn);
conn->dev.type = &bt_link;
conn->dev.class = bt_class;
conn->dev.parent = &hdev->dev;
device_initialize(&conn->dev);
}
void hci_conn_add_sysfs(struct hci_conn *conn)
{ {
struct hci_conn *conn = container_of(work, struct hci_conn, work_add);
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p", conn);
dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle);
dev_set_drvdata(&conn->dev, conn); dev_set_drvdata(&conn->dev, conn);
...@@ -106,19 +130,8 @@ static void add_conn(struct work_struct *work) ...@@ -106,19 +130,8 @@ static void add_conn(struct work_struct *work)
hci_dev_hold(hdev); hci_dev_hold(hdev);
} }
/* void hci_conn_del_sysfs(struct hci_conn *conn)
* The rfcomm tty device will possibly retain even when conn
* is down, and sysfs doesn't support move zombie device,
* so we should move the device before conn device is destroyed.
*/
static int __match_tty(struct device *dev, void *data)
{
return !strncmp(dev_name(dev), "rfcomm", 6);
}
static void del_conn(struct work_struct *work)
{ {
struct hci_conn *conn = container_of(work, struct hci_conn, work_del);
struct hci_dev *hdev = conn->hdev; struct hci_dev *hdev = conn->hdev;
if (!device_is_registered(&conn->dev)) if (!device_is_registered(&conn->dev))
...@@ -140,36 +153,6 @@ static void del_conn(struct work_struct *work) ...@@ -140,36 +153,6 @@ static void del_conn(struct work_struct *work)
hci_dev_put(hdev); hci_dev_put(hdev);
} }
void hci_conn_init_sysfs(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("conn %p", conn);
conn->dev.type = &bt_link;
conn->dev.class = bt_class;
conn->dev.parent = &hdev->dev;
device_initialize(&conn->dev);
INIT_WORK(&conn->work_add, add_conn);
INIT_WORK(&conn->work_del, del_conn);
}
void hci_conn_add_sysfs(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
queue_work(conn->hdev->workqueue, &conn->work_add);
}
void hci_conn_del_sysfs(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
queue_work(conn->hdev->workqueue, &conn->work_del);
}
static inline char *host_bustostr(int bus) static inline char *host_bustostr(int bus)
{ {
switch (bus) { switch (bus) {
...@@ -403,7 +386,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p) ...@@ -403,7 +386,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p)
struct inquiry_cache *cache = &hdev->inq_cache; struct inquiry_cache *cache = &hdev->inq_cache;
struct inquiry_entry *e; struct inquiry_entry *e;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
for (e = cache->list; e; e = e->next) { for (e = cache->list; e; e = e->next) {
struct inquiry_data *data = &e->data; struct inquiry_data *data = &e->data;
...@@ -416,7 +399,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p) ...@@ -416,7 +399,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p)
data->rssi, data->ssp_mode, e->timestamp); data->rssi, data->ssp_mode, e->timestamp);
} }
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
return 0; return 0;
} }
...@@ -438,12 +421,12 @@ static int blacklist_show(struct seq_file *f, void *p) ...@@ -438,12 +421,12 @@ static int blacklist_show(struct seq_file *f, void *p)
struct hci_dev *hdev = f->private; struct hci_dev *hdev = f->private;
struct bdaddr_list *b; struct bdaddr_list *b;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
list_for_each_entry(b, &hdev->blacklist, list) list_for_each_entry(b, &hdev->blacklist, list)
seq_printf(f, "%s\n", batostr(&b->bdaddr)); seq_printf(f, "%s\n", batostr(&b->bdaddr));
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
return 0; return 0;
} }
...@@ -482,12 +465,12 @@ static int uuids_show(struct seq_file *f, void *p) ...@@ -482,12 +465,12 @@ static int uuids_show(struct seq_file *f, void *p)
struct hci_dev *hdev = f->private; struct hci_dev *hdev = f->private;
struct bt_uuid *uuid; struct bt_uuid *uuid;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
list_for_each_entry(uuid, &hdev->uuids, list) list_for_each_entry(uuid, &hdev->uuids, list)
print_bt_uuid(f, uuid->uuid); print_bt_uuid(f, uuid->uuid);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
return 0; return 0;
} }
...@@ -508,11 +491,11 @@ static int auto_accept_delay_set(void *data, u64 val) ...@@ -508,11 +491,11 @@ static int auto_accept_delay_set(void *data, u64 val)
{ {
struct hci_dev *hdev = data; struct hci_dev *hdev = data;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
hdev->auto_accept_delay = val; hdev->auto_accept_delay = val;
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
return 0; return 0;
} }
...@@ -521,11 +504,11 @@ static int auto_accept_delay_get(void *data, u64 *val) ...@@ -521,11 +504,11 @@ static int auto_accept_delay_get(void *data, u64 *val)
{ {
struct hci_dev *hdev = data; struct hci_dev *hdev = data;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
*val = hdev->auto_accept_delay; *val = hdev->auto_accept_delay;
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
return 0; return 0;
} }
......
...@@ -795,11 +795,11 @@ static struct hci_conn *hidp_get_connection(struct hidp_session *session) ...@@ -795,11 +795,11 @@ static struct hci_conn *hidp_get_connection(struct hidp_session *session)
if (!hdev) if (!hdev)
return NULL; return NULL;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (conn) if (conn)
hci_conn_hold_device(conn); hci_conn_hold_device(conn);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2000-2001 Qualcomm Incorporated
Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org> Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
Copyright (C) 2010 Google Inc. Copyright (C) 2010 Google Inc.
Copyright (C) 2011 ProFUSION Embedded Systems
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
...@@ -89,24 +90,36 @@ static inline void chan_put(struct l2cap_chan *c) ...@@ -89,24 +90,36 @@ static inline void chan_put(struct l2cap_chan *c)
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
{ {
struct l2cap_chan *c; struct l2cap_chan *c, *r = NULL;
list_for_each_entry(c, &conn->chan_l, list) { rcu_read_lock();
if (c->dcid == cid)
return c; list_for_each_entry_rcu(c, &conn->chan_l, list) {
if (c->dcid == cid) {
r = c;
break;
}
} }
return NULL;
rcu_read_unlock();
return r;
} }
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
{ {
struct l2cap_chan *c; struct l2cap_chan *c, *r = NULL;
list_for_each_entry(c, &conn->chan_l, list) { rcu_read_lock();
if (c->scid == cid)
return c; list_for_each_entry_rcu(c, &conn->chan_l, list) {
if (c->scid == cid) {
r = c;
break;
}
} }
return NULL;
rcu_read_unlock();
return r;
} }
/* Find channel with given SCID. /* Find channel with given SCID.
...@@ -115,34 +128,36 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci ...@@ -115,34 +128,36 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci
{ {
struct l2cap_chan *c; struct l2cap_chan *c;
read_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_scid(conn, cid); c = __l2cap_get_chan_by_scid(conn, cid);
if (c) if (c)
bh_lock_sock(c->sk); lock_sock(c->sk);
read_unlock(&conn->chan_lock);
return c; return c;
} }
static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
{ {
struct l2cap_chan *c; struct l2cap_chan *c, *r = NULL;
list_for_each_entry(c, &conn->chan_l, list) { rcu_read_lock();
if (c->ident == ident)
return c; list_for_each_entry_rcu(c, &conn->chan_l, list) {
if (c->ident == ident) {
r = c;
break;
}
} }
return NULL;
rcu_read_unlock();
return r;
} }
static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
{ {
struct l2cap_chan *c; struct l2cap_chan *c;
read_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_ident(conn, ident); c = __l2cap_get_chan_by_ident(conn, ident);
if (c) if (c)
bh_lock_sock(c->sk); lock_sock(c->sk);
read_unlock(&conn->chan_lock);
return c; return c;
} }
...@@ -213,20 +228,18 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) ...@@ -213,20 +228,18 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
return 0; return 0;
} }
static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout) static void l2cap_set_timer(struct l2cap_chan *chan, struct delayed_work *work, long timeout)
{ {
BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout); BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout);
if (!mod_timer(timer, jiffies + msecs_to_jiffies(timeout))) cancel_delayed_work_sync(work);
chan_hold(chan);
schedule_delayed_work(work, timeout);
} }
static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer) static void l2cap_clear_timer(struct delayed_work *work)
{ {
BT_DBG("chan %p state %d", chan, chan->state); cancel_delayed_work_sync(work);
if (timer_pending(timer) && del_timer(timer))
chan_put(chan);
} }
static char *state_to_string(int state) static char *state_to_string(int state)
...@@ -264,23 +277,16 @@ static void l2cap_state_change(struct l2cap_chan *chan, int state) ...@@ -264,23 +277,16 @@ static void l2cap_state_change(struct l2cap_chan *chan, int state)
chan->ops->state_change(chan->data, state); chan->ops->state_change(chan->data, state);
} }
static void l2cap_chan_timeout(unsigned long arg) static void l2cap_chan_timeout(struct work_struct *work)
{ {
struct l2cap_chan *chan = (struct l2cap_chan *) arg; struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
chan_timer.work);
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
int reason; int reason;
BT_DBG("chan %p state %d", chan, chan->state); BT_DBG("chan %p state %d", chan, chan->state);
bh_lock_sock(sk); lock_sock(sk);
if (sock_owned_by_user(sk)) {
/* sk is owned by user. Try again later */
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
chan_put(chan);
return;
}
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED; reason = ECONNREFUSED;
...@@ -292,7 +298,7 @@ static void l2cap_chan_timeout(unsigned long arg) ...@@ -292,7 +298,7 @@ static void l2cap_chan_timeout(unsigned long arg)
l2cap_chan_close(chan, reason); l2cap_chan_close(chan, reason);
bh_unlock_sock(sk); release_sock(sk);
chan->ops->close(chan->data); chan->ops->close(chan->data);
chan_put(chan); chan_put(chan);
...@@ -312,7 +318,7 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) ...@@ -312,7 +318,7 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
list_add(&chan->global_l, &chan_list); list_add(&chan->global_l, &chan_list);
write_unlock_bh(&chan_list_lock); write_unlock_bh(&chan_list_lock);
setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan); INIT_DELAYED_WORK(&chan->chan_timer, l2cap_chan_timeout);
chan->state = BT_OPEN; chan->state = BT_OPEN;
...@@ -332,7 +338,7 @@ void l2cap_chan_destroy(struct l2cap_chan *chan) ...@@ -332,7 +338,7 @@ void l2cap_chan_destroy(struct l2cap_chan *chan)
chan_put(chan); chan_put(chan);
} }
static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{ {
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
chan->psm, chan->dcid); chan->psm, chan->dcid);
...@@ -373,7 +379,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) ...@@ -373,7 +379,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan_hold(chan); chan_hold(chan);
list_add(&chan->list, &conn->chan_l); list_add_rcu(&chan->list, &conn->chan_l);
} }
/* Delete channel. /* Delete channel.
...@@ -390,9 +396,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) ...@@ -390,9 +396,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
if (conn) { if (conn) {
/* Delete from channel list */ /* Delete from channel list */
write_lock_bh(&conn->chan_lock); list_del_rcu(&chan->list);
list_del(&chan->list); synchronize_rcu();
write_unlock_bh(&conn->chan_lock);
chan_put(chan); chan_put(chan);
chan->conn = NULL; chan->conn = NULL;
...@@ -707,7 +713,7 @@ static void l2cap_do_start(struct l2cap_chan *chan) ...@@ -707,7 +713,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn); conn->info_ident = l2cap_get_ident(conn);
mod_timer(&conn->info_timer, jiffies + schedule_delayed_work(&conn->info_work,
msecs_to_jiffies(L2CAP_INFO_TIMEOUT)); msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
l2cap_send_cmd(conn, conn->info_ident, l2cap_send_cmd(conn, conn->info_ident,
...@@ -759,13 +765,13 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c ...@@ -759,13 +765,13 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
/* ---- L2CAP connections ---- */ /* ---- L2CAP connections ---- */
static void l2cap_conn_start(struct l2cap_conn *conn) static void l2cap_conn_start(struct l2cap_conn *conn)
{ {
struct l2cap_chan *chan, *tmp; struct l2cap_chan *chan;
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
read_lock(&conn->chan_lock); rcu_read_lock();
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { list_for_each_entry_rcu(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
bh_lock_sock(sk); bh_lock_sock(sk);
...@@ -789,9 +795,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -789,9 +795,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
&chan->conf_state)) { &chan->conf_state)) {
/* l2cap_chan_close() calls list_del(chan) /* l2cap_chan_close() calls list_del(chan)
* so release the lock */ * so release the lock */
read_unlock(&conn->chan_lock);
l2cap_chan_close(chan, ECONNRESET); l2cap_chan_close(chan, ECONNRESET);
read_lock(&conn->chan_lock);
bh_unlock_sock(sk); bh_unlock_sock(sk);
continue; continue;
} }
...@@ -847,7 +851,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -847,7 +851,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
bh_unlock_sock(sk); bh_unlock_sock(sk);
} }
read_unlock(&conn->chan_lock); rcu_read_unlock();
} }
/* Find socket with cid and source bdaddr. /* Find socket with cid and source bdaddr.
...@@ -898,7 +902,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) ...@@ -898,7 +902,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
parent = pchan->sk; parent = pchan->sk;
bh_lock_sock(parent); lock_sock(parent);
/* Check for backlog size */ /* Check for backlog size */
if (sk_acceptq_is_full(parent)) { if (sk_acceptq_is_full(parent)) {
...@@ -912,8 +916,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) ...@@ -912,8 +916,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
sk = chan->sk; sk = chan->sk;
write_lock_bh(&conn->chan_lock);
hci_conn_hold(conn->hcon); hci_conn_hold(conn->hcon);
bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->src, conn->src);
...@@ -921,17 +923,15 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) ...@@ -921,17 +923,15 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
bt_accept_enqueue(parent, sk); bt_accept_enqueue(parent, sk);
__l2cap_chan_add(conn, chan); l2cap_chan_add(conn, chan);
__set_chan_timer(chan, sk->sk_sndtimeo); __set_chan_timer(chan, sk->sk_sndtimeo);
l2cap_state_change(chan, BT_CONNECTED); l2cap_state_change(chan, BT_CONNECTED);
parent->sk_data_ready(parent, 0); parent->sk_data_ready(parent, 0);
write_unlock_bh(&conn->chan_lock);
clean: clean:
bh_unlock_sock(parent); release_sock(parent);
} }
static void l2cap_chan_ready(struct sock *sk) static void l2cap_chan_ready(struct sock *sk)
...@@ -963,9 +963,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) ...@@ -963,9 +963,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (conn->hcon->out && conn->hcon->type == LE_LINK) if (conn->hcon->out && conn->hcon->type == LE_LINK)
smp_conn_security(conn, conn->hcon->pending_sec_level); smp_conn_security(conn, conn->hcon->pending_sec_level);
read_lock(&conn->chan_lock); rcu_read_lock();
list_for_each_entry(chan, &conn->chan_l, list) { list_for_each_entry_rcu(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
bh_lock_sock(sk); bh_lock_sock(sk);
...@@ -985,7 +985,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) ...@@ -985,7 +985,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
bh_unlock_sock(sk); bh_unlock_sock(sk);
} }
read_unlock(&conn->chan_lock); rcu_read_unlock();
} }
/* Notify sockets that we cannot guaranty reliability anymore */ /* Notify sockets that we cannot guaranty reliability anymore */
...@@ -995,21 +995,22 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) ...@@ -995,21 +995,22 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
read_lock(&conn->chan_lock); rcu_read_lock();
list_for_each_entry(chan, &conn->chan_l, list) { list_for_each_entry_rcu(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
sk->sk_err = err; sk->sk_err = err;
} }
read_unlock(&conn->chan_lock); rcu_read_unlock();
} }
static void l2cap_info_timeout(unsigned long arg) static void l2cap_info_timeout(struct work_struct *work)
{ {
struct l2cap_conn *conn = (void *) arg; struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
info_work.work);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0; conn->info_ident = 0;
...@@ -1033,16 +1034,16 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) ...@@ -1033,16 +1034,16 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
/* Kill channels */ /* Kill channels */
list_for_each_entry_safe(chan, l, &conn->chan_l, list) { list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
sk = chan->sk; sk = chan->sk;
bh_lock_sock(sk); lock_sock(sk);
l2cap_chan_del(chan, err); l2cap_chan_del(chan, err);
bh_unlock_sock(sk); release_sock(sk);
chan->ops->close(chan->data); chan->ops->close(chan->data);
} }
hci_chan_del(conn->hchan); hci_chan_del(conn->hchan);
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
del_timer_sync(&conn->info_timer); cancel_delayed_work_sync(&conn->info_work);
if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) { if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) {
del_timer(&conn->security_timer); del_timer(&conn->security_timer);
...@@ -1095,7 +1096,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) ...@@ -1095,7 +1096,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->feat_mask = 0; conn->feat_mask = 0;
spin_lock_init(&conn->lock); spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_lock);
INIT_LIST_HEAD(&conn->chan_l); INIT_LIST_HEAD(&conn->chan_l);
...@@ -1103,21 +1103,13 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) ...@@ -1103,21 +1103,13 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
setup_timer(&conn->security_timer, security_timeout, setup_timer(&conn->security_timer, security_timeout,
(unsigned long) conn); (unsigned long) conn);
else else
setup_timer(&conn->info_timer, l2cap_info_timeout, INIT_DELAYED_WORK(&conn->info_work, l2cap_info_timeout);
(unsigned long) conn);
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
return conn; return conn;
} }
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
write_lock_bh(&conn->chan_lock);
__l2cap_chan_add(conn, chan);
write_unlock_bh(&conn->chan_lock);
}
/* ---- Socket interface ---- */ /* ---- Socket interface ---- */
/* Find socket with psm and source bdaddr. /* Find socket with psm and source bdaddr.
...@@ -1153,11 +1145,10 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr ...@@ -1153,11 +1145,10 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
return c1; return c1;
} }
int l2cap_chan_connect(struct l2cap_chan *chan) inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst)
{ {
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
bdaddr_t *src = &bt_sk(sk)->src; bdaddr_t *src = &bt_sk(sk)->src;
bdaddr_t *dst = &bt_sk(sk)->dst;
struct l2cap_conn *conn; struct l2cap_conn *conn;
struct hci_conn *hcon; struct hci_conn *hcon;
struct hci_dev *hdev; struct hci_dev *hdev;
...@@ -1171,7 +1162,62 @@ int l2cap_chan_connect(struct l2cap_chan *chan) ...@@ -1171,7 +1162,62 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
if (!hdev) if (!hdev)
return -EHOSTUNREACH; return -EHOSTUNREACH;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
lock_sock(sk);
/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
goto done;
}
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
err = -EINVAL;
goto done;
}
switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
if (!disable_ertm)
break;
/* fall through */
default:
err = -ENOTSUPP;
goto done;
}
switch (sk->sk_state) {
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
/* Already connecting */
err = 0;
goto done;
case BT_CONNECTED:
/* Already connected */
err = -EISCONN;
goto done;
case BT_OPEN:
case BT_BOUND:
/* Can connect */
break;
default:
err = -EBADFD;
goto done;
}
/* Set destination address and psm */
bacpy(&bt_sk(sk)->dst, src);
chan->psm = psm;
chan->dcid = cid;
auth_type = l2cap_get_auth_type(chan); auth_type = l2cap_get_auth_type(chan);
...@@ -1214,7 +1260,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan) ...@@ -1214,7 +1260,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
err = 0; err = 0;
done: done:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
} }
...@@ -1251,17 +1297,18 @@ int __l2cap_wait_ack(struct sock *sk) ...@@ -1251,17 +1297,18 @@ int __l2cap_wait_ack(struct sock *sk)
return err; return err;
} }
static void l2cap_monitor_timeout(unsigned long arg) static void l2cap_monitor_timeout(struct work_struct *work)
{ {
struct l2cap_chan *chan = (void *) arg; struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
monitor_timer.work);
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
BT_DBG("chan %p", chan); BT_DBG("chan %p", chan);
bh_lock_sock(sk); lock_sock(sk);
if (chan->retry_count >= chan->remote_max_tx) { if (chan->retry_count >= chan->remote_max_tx) {
l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
bh_unlock_sock(sk); release_sock(sk);
return; return;
} }
...@@ -1269,24 +1316,25 @@ static void l2cap_monitor_timeout(unsigned long arg) ...@@ -1269,24 +1316,25 @@ static void l2cap_monitor_timeout(unsigned long arg)
__set_monitor_timer(chan); __set_monitor_timer(chan);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
bh_unlock_sock(sk); release_sock(sk);
} }
static void l2cap_retrans_timeout(unsigned long arg) static void l2cap_retrans_timeout(struct work_struct *work)
{ {
struct l2cap_chan *chan = (void *) arg; struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
retrans_timer.work);
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
BT_DBG("chan %p", chan); BT_DBG("chan %p", chan);
bh_lock_sock(sk); lock_sock(sk);
chan->retry_count = 1; chan->retry_count = 1;
__set_monitor_timer(chan); __set_monitor_timer(chan);
set_bit(CONN_WAIT_F, &chan->conn_state); set_bit(CONN_WAIT_F, &chan->conn_state);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
bh_unlock_sock(sk); release_sock(sk);
} }
static void l2cap_drop_acked_frames(struct l2cap_chan *chan) static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
...@@ -1778,8 +1826,9 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -1778,8 +1826,9 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
read_lock(&conn->chan_lock); rcu_read_lock();
list_for_each_entry(chan, &conn->chan_l, list) {
list_for_each_entry_rcu(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
if (chan->chan_type != L2CAP_CHAN_RAW) if (chan->chan_type != L2CAP_CHAN_RAW)
continue; continue;
...@@ -1794,7 +1843,8 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -1794,7 +1843,8 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
if (chan->ops->recv(chan->data, nskb)) if (chan->ops->recv(chan->data, nskb))
kfree_skb(nskb); kfree_skb(nskb);
} }
read_unlock(&conn->chan_lock);
rcu_read_unlock();
} }
/* ---- L2CAP signalling commands ---- */ /* ---- L2CAP signalling commands ---- */
...@@ -1955,37 +2005,31 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) ...@@ -1955,37 +2005,31 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
(unsigned long) &efs); (unsigned long) &efs);
} }
static void l2cap_ack_timeout(unsigned long arg) static void l2cap_ack_timeout(struct work_struct *work)
{ {
struct l2cap_chan *chan = (void *) arg; struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
ack_timer.work);
bh_lock_sock(chan->sk); lock_sock(chan->sk);
l2cap_send_ack(chan); l2cap_send_ack(chan);
bh_unlock_sock(chan->sk); release_sock(chan->sk);
} }
static inline void l2cap_ertm_init(struct l2cap_chan *chan) static inline void l2cap_ertm_init(struct l2cap_chan *chan)
{ {
struct sock *sk = chan->sk;
chan->expected_ack_seq = 0; chan->expected_ack_seq = 0;
chan->unacked_frames = 0; chan->unacked_frames = 0;
chan->buffer_seq = 0; chan->buffer_seq = 0;
chan->num_acked = 0; chan->num_acked = 0;
chan->frames_sent = 0; chan->frames_sent = 0;
setup_timer(&chan->retrans_timer, l2cap_retrans_timeout, INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout);
(unsigned long) chan); INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout);
setup_timer(&chan->monitor_timer, l2cap_monitor_timeout, INIT_DELAYED_WORK(&chan->ack_timer, l2cap_ack_timeout);
(unsigned long) chan);
setup_timer(&chan->ack_timer, l2cap_ack_timeout, (unsigned long) chan);
skb_queue_head_init(&chan->srej_q); skb_queue_head_init(&chan->srej_q);
INIT_LIST_HEAD(&chan->srej_l); INIT_LIST_HEAD(&chan->srej_l);
sk->sk_backlog_rcv = l2cap_ertm_data_rcv;
} }
static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
...@@ -2372,7 +2416,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi ...@@ -2372,7 +2416,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
void *ptr = req->data; void *ptr = req->data;
int type, olen; int type, olen;
unsigned long val; unsigned long val;
struct l2cap_conf_rfc rfc; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
struct l2cap_conf_efs efs; struct l2cap_conf_efs efs;
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
...@@ -2522,6 +2566,16 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) ...@@ -2522,6 +2566,16 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
} }
} }
/* Use sane default values in case a misbehaving remote device
* did not send an RFC option.
*/
rfc.mode = chan->mode;
rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
rfc.max_pdu_size = cpu_to_le16(chan->imtu);
BT_ERR("Expected RFC option was not found, using defaults");
done: done:
switch (rfc.mode) { switch (rfc.mode) {
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
...@@ -2543,7 +2597,7 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2543,7 +2597,7 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
cmd->ident == conn->info_ident) { cmd->ident == conn->info_ident) {
del_timer(&conn->info_timer); cancel_delayed_work_sync(&conn->info_work);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0; conn->info_ident = 0;
...@@ -2576,7 +2630,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2576,7 +2630,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
parent = pchan->sk; parent = pchan->sk;
bh_lock_sock(parent); lock_sock(parent);
/* Check if the ACL is secure enough (if not SDP) */ /* Check if the ACL is secure enough (if not SDP) */
if (psm != cpu_to_le16(0x0001) && if (psm != cpu_to_le16(0x0001) &&
...@@ -2600,11 +2654,8 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2600,11 +2654,8 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
sk = chan->sk; sk = chan->sk;
write_lock_bh(&conn->chan_lock);
/* Check if we already have channel with that dcid */ /* Check if we already have channel with that dcid */
if (__l2cap_get_chan_by_dcid(conn, scid)) { if (__l2cap_get_chan_by_dcid(conn, scid)) {
write_unlock_bh(&conn->chan_lock);
sock_set_flag(sk, SOCK_ZAPPED); sock_set_flag(sk, SOCK_ZAPPED);
chan->ops->close(chan->data); chan->ops->close(chan->data);
goto response; goto response;
...@@ -2619,7 +2670,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2619,7 +2670,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
bt_accept_enqueue(parent, sk); bt_accept_enqueue(parent, sk);
__l2cap_chan_add(conn, chan); l2cap_chan_add(conn, chan);
dcid = chan->scid; dcid = chan->scid;
...@@ -2650,10 +2701,8 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2650,10 +2701,8 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
status = L2CAP_CS_NO_INFO; status = L2CAP_CS_NO_INFO;
} }
write_unlock_bh(&conn->chan_lock);
response: response:
bh_unlock_sock(parent); release_sock(parent);
sendresp: sendresp:
rsp.scid = cpu_to_le16(scid); rsp.scid = cpu_to_le16(scid);
...@@ -2669,7 +2718,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2669,7 +2718,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn); conn->info_ident = l2cap_get_ident(conn);
mod_timer(&conn->info_timer, jiffies + schedule_delayed_work(&conn->info_work,
msecs_to_jiffies(L2CAP_INFO_TIMEOUT)); msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
l2cap_send_cmd(conn, conn->info_ident, l2cap_send_cmd(conn, conn->info_ident,
...@@ -2735,19 +2784,11 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd ...@@ -2735,19 +2784,11 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
break; break;
default: default:
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
break;
}
l2cap_chan_del(chan, ECONNREFUSED); l2cap_chan_del(chan, ECONNREFUSED);
break; break;
} }
bh_unlock_sock(sk); release_sock(sk);
return 0; return 0;
} }
...@@ -2869,7 +2910,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr ...@@ -2869,7 +2910,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
} }
unlock: unlock:
bh_unlock_sock(sk); release_sock(sk);
return 0; return 0;
} }
...@@ -2976,7 +3017,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr ...@@ -2976,7 +3017,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
} }
done: done:
bh_unlock_sock(sk); release_sock(sk);
return 0; return 0;
} }
...@@ -3005,17 +3046,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -3005,17 +3046,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
sk->sk_shutdown = SHUTDOWN_MASK; sk->sk_shutdown = SHUTDOWN_MASK;
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
return 0;
}
l2cap_chan_del(chan, ECONNRESET); l2cap_chan_del(chan, ECONNRESET);
bh_unlock_sock(sk); release_sock(sk);
chan->ops->close(chan->data); chan->ops->close(chan->data);
return 0; return 0;
...@@ -3039,17 +3071,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd ...@@ -3039,17 +3071,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
sk = chan->sk; sk = chan->sk;
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
return 0;
}
l2cap_chan_del(chan, 0); l2cap_chan_del(chan, 0);
bh_unlock_sock(sk); release_sock(sk);
chan->ops->close(chan->data); chan->ops->close(chan->data);
return 0; return 0;
...@@ -3120,7 +3143,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm ...@@ -3120,7 +3143,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
return 0; return 0;
del_timer(&conn->info_timer); cancel_delayed_work_sync(&conn->info_work);
if (result != L2CAP_IR_SUCCESS) { if (result != L2CAP_IR_SUCCESS) {
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
...@@ -4237,12 +4260,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk ...@@ -4237,12 +4260,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
break; break;
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
if (!sock_owned_by_user(sk)) { l2cap_ertm_data_rcv(sk, skb);
l2cap_ertm_data_rcv(sk, skb);
} else {
if (sk_add_backlog(sk, skb))
goto drop;
}
goto done; goto done;
...@@ -4292,7 +4310,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk ...@@ -4292,7 +4310,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
done: done:
if (sk) if (sk)
bh_unlock_sock(sk); release_sock(sk);
return 0; return 0;
} }
...@@ -4308,7 +4326,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str ...@@ -4308,7 +4326,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
sk = chan->sk; sk = chan->sk;
bh_lock_sock(sk); lock_sock(sk);
BT_DBG("sk %p, len %d", sk, skb->len); BT_DBG("sk %p, len %d", sk, skb->len);
...@@ -4326,7 +4344,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str ...@@ -4326,7 +4344,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
done: done:
if (sk) if (sk)
bh_unlock_sock(sk); release_sock(sk);
return 0; return 0;
} }
...@@ -4341,7 +4359,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct ...@@ -4341,7 +4359,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct
sk = chan->sk; sk = chan->sk;
bh_lock_sock(sk); lock_sock(sk);
BT_DBG("sk %p, len %d", sk, skb->len); BT_DBG("sk %p, len %d", sk, skb->len);
...@@ -4359,7 +4377,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct ...@@ -4359,7 +4377,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct
done: done:
if (sk) if (sk)
bh_unlock_sock(sk); release_sock(sk);
return 0; return 0;
} }
...@@ -4518,9 +4536,9 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) ...@@ -4518,9 +4536,9 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
del_timer(&conn->security_timer); del_timer(&conn->security_timer);
} }
read_lock(&conn->chan_lock); rcu_read_lock();
list_for_each_entry(chan, &conn->chan_l, list) { list_for_each_entry_rcu(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk; struct sock *sk = chan->sk;
bh_lock_sock(sk); bh_lock_sock(sk);
...@@ -4598,7 +4616,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) ...@@ -4598,7 +4616,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
bh_unlock_sock(sk); bh_unlock_sock(sk);
} }
read_unlock(&conn->chan_lock); rcu_read_unlock();
return 0; return 0;
} }
...@@ -4664,11 +4682,11 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl ...@@ -4664,11 +4682,11 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
BT_ERR("Frame exceeding recv MTU (len %d, " BT_ERR("Frame exceeding recv MTU (len %d, "
"MTU %d)", len, "MTU %d)", len,
chan->imtu); chan->imtu);
bh_unlock_sock(sk); release_sock(sk);
l2cap_conn_unreliable(conn, ECOMM); l2cap_conn_unreliable(conn, ECOMM);
goto drop; goto drop;
} }
bh_unlock_sock(sk); release_sock(sk);
} }
/* Allocate skb for the complete frame (with header) */ /* Allocate skb for the complete frame (with header) */
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (C) 2000-2001 Qualcomm Incorporated
Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org> Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
Copyright (C) 2010 Google Inc. Copyright (C) 2010 Google Inc.
Copyright (C) 2011 ProFUSION Embedded Systems
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
...@@ -122,70 +123,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al ...@@ -122,70 +123,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
if (la.l2_cid && la.l2_psm) if (la.l2_cid && la.l2_psm)
return -EINVAL; return -EINVAL;
lock_sock(sk); err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr);
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED
&& !(la.l2_psm || la.l2_cid)) {
err = -EINVAL;
goto done;
}
switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
if (!disable_ertm)
break;
/* fall through */
default:
err = -ENOTSUPP;
goto done;
}
switch (sk->sk_state) {
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
/* Already connecting */
goto wait;
case BT_CONNECTED:
/* Already connected */
err = -EISCONN;
goto done;
case BT_OPEN:
case BT_BOUND:
/* Can connect */
break;
default:
err = -EBADFD;
goto done;
}
/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && !la.l2_cid &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
goto done;
}
/* Set destination address and psm */
bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
chan->psm = la.l2_psm;
chan->dcid = la.l2_cid;
err = l2cap_chan_connect(l2cap_pi(sk)->chan);
if (err) if (err)
goto done; goto done;
wait:
err = bt_sock_wait_state(sk, BT_CONNECTED, err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK)); sock_sndtimeo(sk, flags & O_NONBLOCK));
done: done:
release_sock(sk); if (sock_owned_by_user(sk))
release_sock(sk);
return err; return err;
} }
......
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */ #define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
#define SERVICE_CACHE_TIMEOUT (5 * 1000)
struct pending_cmd { struct pending_cmd {
struct list_head list; struct list_head list;
u16 opcode; u16 opcode;
...@@ -243,6 +245,262 @@ static int read_index_list(struct sock *sk) ...@@ -243,6 +245,262 @@ static int read_index_list(struct sock *sk)
return err; return err;
} }
static u32 get_supported_settings(struct hci_dev *hdev)
{
u32 settings = 0;
settings |= MGMT_SETTING_POWERED;
settings |= MGMT_SETTING_CONNECTABLE;
settings |= MGMT_SETTING_FAST_CONNECTABLE;
settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_PAIRABLE;
if (hdev->features[6] & LMP_SIMPLE_PAIR)
settings |= MGMT_SETTING_SSP;
if (!(hdev->features[4] & LMP_NO_BREDR)) {
settings |= MGMT_SETTING_BREDR;
settings |= MGMT_SETTING_LINK_SECURITY;
}
if (hdev->features[4] & LMP_LE)
settings |= MGMT_SETTING_LE;
return settings;
}
static u32 get_current_settings(struct hci_dev *hdev)
{
u32 settings = 0;
if (test_bit(HCI_UP, &hdev->flags))
settings |= MGMT_SETTING_POWERED;
else
return settings;
if (test_bit(HCI_PSCAN, &hdev->flags))
settings |= MGMT_SETTING_CONNECTABLE;
if (test_bit(HCI_ISCAN, &hdev->flags))
settings |= MGMT_SETTING_DISCOVERABLE;
if (test_bit(HCI_PAIRABLE, &hdev->flags))
settings |= MGMT_SETTING_PAIRABLE;
if (!(hdev->features[4] & LMP_NO_BREDR))
settings |= MGMT_SETTING_BREDR;
if (hdev->extfeatures[0] & LMP_HOST_LE)
settings |= MGMT_SETTING_LE;
if (test_bit(HCI_AUTH, &hdev->flags))
settings |= MGMT_SETTING_LINK_SECURITY;
if (hdev->ssp_mode > 0)
settings |= MGMT_SETTING_SSP;
return settings;
}
#define EIR_FLAGS 0x01 /* flags */
#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
#define EIR_NAME_SHORT 0x08 /* shortened local name */
#define EIR_NAME_COMPLETE 0x09 /* complete local name */
#define EIR_TX_POWER 0x0A /* transmit power level */
#define EIR_DEVICE_ID 0x10 /* device ID */
#define PNP_INFO_SVCLASS_ID 0x1200
static u8 bluetooth_base_uuid[] = {
0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static u16 get_uuid16(u8 *uuid128)
{
u32 val;
int i;
for (i = 0; i < 12; i++) {
if (bluetooth_base_uuid[i] != uuid128[i])
return 0;
}
memcpy(&val, &uuid128[12], 4);
val = le32_to_cpu(val);
if (val > 0xffff)
return 0;
return (u16) val;
}
static void create_eir(struct hci_dev *hdev, u8 *data)
{
u8 *ptr = data;
u16 eir_len = 0;
u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
int i, truncated = 0;
struct bt_uuid *uuid;
size_t name_len;
name_len = strlen(hdev->dev_name);
if (name_len > 0) {
/* EIR Data type */
if (name_len > 48) {
name_len = 48;
ptr[1] = EIR_NAME_SHORT;
} else
ptr[1] = EIR_NAME_COMPLETE;
/* EIR Data length */
ptr[0] = name_len + 1;
memcpy(ptr + 2, hdev->dev_name, name_len);
eir_len += (name_len + 2);
ptr += (name_len + 2);
}
memset(uuid16_list, 0, sizeof(uuid16_list));
/* Group all UUID16 types */
list_for_each_entry(uuid, &hdev->uuids, list) {
u16 uuid16;
uuid16 = get_uuid16(uuid->uuid);
if (uuid16 == 0)
return;
if (uuid16 < 0x1100)
continue;
if (uuid16 == PNP_INFO_SVCLASS_ID)
continue;
/* Stop if not enough space to put next UUID */
if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
truncated = 1;
break;
}
/* Check for duplicates */
for (i = 0; uuid16_list[i] != 0; i++)
if (uuid16_list[i] == uuid16)
break;
if (uuid16_list[i] == 0) {
uuid16_list[i] = uuid16;
eir_len += sizeof(u16);
}
}
if (uuid16_list[0] != 0) {
u8 *length = ptr;
/* EIR Data type */
ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
ptr += 2;
eir_len += 2;
for (i = 0; uuid16_list[i] != 0; i++) {
*ptr++ = (uuid16_list[i] & 0x00ff);
*ptr++ = (uuid16_list[i] & 0xff00) >> 8;
}
/* EIR Data length */
*length = (i * sizeof(u16)) + 1;
}
}
static int update_eir(struct hci_dev *hdev)
{
struct hci_cp_write_eir cp;
if (!(hdev->features[6] & LMP_EXT_INQ))
return 0;
if (hdev->ssp_mode == 0)
return 0;
if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
return 0;
memset(&cp, 0, sizeof(cp));
create_eir(hdev, cp.data);
if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
return 0;
memcpy(hdev->eir, cp.data, sizeof(cp.data));
return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
}
static u8 get_service_classes(struct hci_dev *hdev)
{
struct bt_uuid *uuid;
u8 val = 0;
list_for_each_entry(uuid, &hdev->uuids, list)
val |= uuid->svc_hint;
return val;
}
static int update_class(struct hci_dev *hdev)
{
u8 cod[3];
BT_DBG("%s", hdev->name);
if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
return 0;
cod[0] = hdev->minor_class;
cod[1] = hdev->major_class;
cod[2] = get_service_classes(hdev);
if (memcmp(cod, hdev->dev_class, 3) == 0)
return 0;
return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
}
static void service_cache_off(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev,
service_cache.work);
if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags))
return;
hci_dev_lock(hdev);
update_eir(hdev);
update_class(hdev);
hci_dev_unlock(hdev);
}
static void mgmt_init_hdev(struct hci_dev *hdev)
{
if (!test_and_set_bit(HCI_MGMT, &hdev->flags))
INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->flags))
schedule_delayed_work(&hdev->service_cache,
msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
}
static int read_controller_info(struct sock *sk, u16 index) static int read_controller_info(struct sock *sk, u16 index)
{ {
struct mgmt_rp_read_info rp; struct mgmt_rp_read_info rp;
...@@ -258,36 +516,27 @@ static int read_controller_info(struct sock *sk, u16 index) ...@@ -258,36 +516,27 @@ static int read_controller_info(struct sock *sk, u16 index)
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
cancel_delayed_work_sync(&hdev->power_off); cancel_delayed_work_sync(&hdev->power_off);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
set_bit(HCI_MGMT, &hdev->flags); if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
mgmt_init_hdev(hdev);
memset(&rp, 0, sizeof(rp)); memset(&rp, 0, sizeof(rp));
rp.type = hdev->dev_type; bacpy(&rp.bdaddr, &hdev->bdaddr);
rp.powered = test_bit(HCI_UP, &hdev->flags); rp.version = hdev->hci_ver;
rp.connectable = test_bit(HCI_PSCAN, &hdev->flags);
rp.discoverable = test_bit(HCI_ISCAN, &hdev->flags);
rp.pairable = test_bit(HCI_PSCAN, &hdev->flags);
if (test_bit(HCI_AUTH, &hdev->flags)) put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
rp.sec_mode = 3;
else if (hdev->ssp_mode > 0) rp.supported_settings = cpu_to_le32(get_supported_settings(hdev));
rp.sec_mode = 4; rp.current_settings = cpu_to_le32(get_current_settings(hdev));
else
rp.sec_mode = 2;
bacpy(&rp.bdaddr, &hdev->bdaddr);
memcpy(rp.features, hdev->features, 8);
memcpy(rp.dev_class, hdev->dev_class, 3); memcpy(rp.dev_class, hdev->dev_class, 3);
put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
rp.hci_ver = hdev->hci_ver;
put_unaligned_le16(hdev->hci_rev, &rp.hci_rev);
memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name)); memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp)); return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
...@@ -366,13 +615,11 @@ static void mgmt_pending_remove(struct pending_cmd *cmd) ...@@ -366,13 +615,11 @@ static void mgmt_pending_remove(struct pending_cmd *cmd)
mgmt_pending_free(cmd); mgmt_pending_free(cmd);
} }
static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
{ {
struct mgmt_mode rp; __le32 settings = cpu_to_le32(get_current_settings(hdev));
rp.val = val; return cmd_complete(sk, hdev->id, opcode, &settings, sizeof(settings));
return cmd_complete(sk, index, opcode, &rp, sizeof(rp));
} }
static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
...@@ -395,11 +642,11 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -395,11 +642,11 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
return cmd_status(sk, index, MGMT_OP_SET_POWERED, return cmd_status(sk, index, MGMT_OP_SET_POWERED,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
up = test_bit(HCI_UP, &hdev->flags); up = test_bit(HCI_UP, &hdev->flags);
if ((cp->val && up) || (!cp->val && !up)) { if ((cp->val && up) || (!cp->val && !up)) {
err = send_mode_rsp(sk, index, MGMT_OP_SET_POWERED, cp->val); err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
goto failed; goto failed;
} }
...@@ -416,14 +663,14 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -416,14 +663,14 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
} }
if (cp->val) if (cp->val)
queue_work(hdev->workqueue, &hdev->power_on); schedule_work(&hdev->power_on);
else else
queue_work(hdev->workqueue, &hdev->power_off.work); schedule_work(&hdev->power_off.work);
err = 0; err = 0;
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
} }
...@@ -450,7 +697,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, ...@@ -450,7 +697,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
...@@ -467,8 +714,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, ...@@ -467,8 +714,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
test_bit(HCI_PSCAN, &hdev->flags)) { test_bit(HCI_PSCAN, &hdev->flags)) {
err = send_mode_rsp(sk, index, MGMT_OP_SET_DISCOVERABLE, err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
cp->val);
goto failed; goto failed;
} }
...@@ -493,7 +739,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, ...@@ -493,7 +739,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
hdev->discov_timeout = get_unaligned_le16(&cp->timeout); hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -521,7 +767,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, ...@@ -521,7 +767,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
...@@ -537,8 +783,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, ...@@ -537,8 +783,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
} }
if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
err = send_mode_rsp(sk, index, MGMT_OP_SET_CONNECTABLE, err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
cp->val);
goto failed; goto failed;
} }
...@@ -558,7 +803,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, ...@@ -558,7 +803,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -596,8 +841,9 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, ...@@ -596,8 +841,9 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
static int set_pairable(struct sock *sk, u16 index, unsigned char *data, static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
u16 len) u16 len)
{ {
struct mgmt_mode *cp, ev; struct mgmt_mode *cp;
struct hci_dev *hdev; struct hci_dev *hdev;
__le32 ev;
int err; int err;
cp = (void *) data; cp = (void *) data;
...@@ -613,201 +859,28 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, ...@@ -613,201 +859,28 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (cp->val) if (cp->val)
set_bit(HCI_PAIRABLE, &hdev->flags); set_bit(HCI_PAIRABLE, &hdev->flags);
else else
clear_bit(HCI_PAIRABLE, &hdev->flags); clear_bit(HCI_PAIRABLE, &hdev->flags);
err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, index, cp->val); err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
if (err < 0) if (err < 0)
goto failed; goto failed;
ev.val = cp->val; ev = cpu_to_le32(get_current_settings(hdev));
err = mgmt_event(MGMT_EV_PAIRABLE, hdev, &ev, sizeof(ev), sk); err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
} }
#define EIR_FLAGS 0x01 /* flags */
#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
#define EIR_NAME_SHORT 0x08 /* shortened local name */
#define EIR_NAME_COMPLETE 0x09 /* complete local name */
#define EIR_TX_POWER 0x0A /* transmit power level */
#define EIR_DEVICE_ID 0x10 /* device ID */
#define PNP_INFO_SVCLASS_ID 0x1200
static u8 bluetooth_base_uuid[] = {
0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static u16 get_uuid16(u8 *uuid128)
{
u32 val;
int i;
for (i = 0; i < 12; i++) {
if (bluetooth_base_uuid[i] != uuid128[i])
return 0;
}
memcpy(&val, &uuid128[12], 4);
val = le32_to_cpu(val);
if (val > 0xffff)
return 0;
return (u16) val;
}
static void create_eir(struct hci_dev *hdev, u8 *data)
{
u8 *ptr = data;
u16 eir_len = 0;
u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
int i, truncated = 0;
struct bt_uuid *uuid;
size_t name_len;
name_len = strlen(hdev->dev_name);
if (name_len > 0) {
/* EIR Data type */
if (name_len > 48) {
name_len = 48;
ptr[1] = EIR_NAME_SHORT;
} else
ptr[1] = EIR_NAME_COMPLETE;
/* EIR Data length */
ptr[0] = name_len + 1;
memcpy(ptr + 2, hdev->dev_name, name_len);
eir_len += (name_len + 2);
ptr += (name_len + 2);
}
memset(uuid16_list, 0, sizeof(uuid16_list));
/* Group all UUID16 types */
list_for_each_entry(uuid, &hdev->uuids, list) {
u16 uuid16;
uuid16 = get_uuid16(uuid->uuid);
if (uuid16 == 0)
return;
if (uuid16 < 0x1100)
continue;
if (uuid16 == PNP_INFO_SVCLASS_ID)
continue;
/* Stop if not enough space to put next UUID */
if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
truncated = 1;
break;
}
/* Check for duplicates */
for (i = 0; uuid16_list[i] != 0; i++)
if (uuid16_list[i] == uuid16)
break;
if (uuid16_list[i] == 0) {
uuid16_list[i] = uuid16;
eir_len += sizeof(u16);
}
}
if (uuid16_list[0] != 0) {
u8 *length = ptr;
/* EIR Data type */
ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
ptr += 2;
eir_len += 2;
for (i = 0; uuid16_list[i] != 0; i++) {
*ptr++ = (uuid16_list[i] & 0x00ff);
*ptr++ = (uuid16_list[i] & 0xff00) >> 8;
}
/* EIR Data length */
*length = (i * sizeof(u16)) + 1;
}
}
static int update_eir(struct hci_dev *hdev)
{
struct hci_cp_write_eir cp;
if (!(hdev->features[6] & LMP_EXT_INQ))
return 0;
if (hdev->ssp_mode == 0)
return 0;
if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
return 0;
memset(&cp, 0, sizeof(cp));
create_eir(hdev, cp.data);
if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
return 0;
memcpy(hdev->eir, cp.data, sizeof(cp.data));
return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
}
static u8 get_service_classes(struct hci_dev *hdev)
{
struct bt_uuid *uuid;
u8 val = 0;
list_for_each_entry(uuid, &hdev->uuids, list)
val |= uuid->svc_hint;
return val;
}
static int update_class(struct hci_dev *hdev)
{
u8 cod[3];
BT_DBG("%s", hdev->name);
if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
return 0;
cod[0] = hdev->minor_class;
cod[1] = hdev->major_class;
cod[2] = get_service_classes(hdev);
if (memcmp(cod, hdev->dev_class, 3) == 0)
return 0;
return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
}
static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
{ {
struct mgmt_cp_add_uuid *cp; struct mgmt_cp_add_uuid *cp;
...@@ -828,7 +901,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -828,7 +901,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
return cmd_status(sk, index, MGMT_OP_ADD_UUID, return cmd_status(sk, index, MGMT_OP_ADD_UUID,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
if (!uuid) { if (!uuid) {
...@@ -852,7 +925,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -852,7 +925,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -879,7 +952,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -879,7 +952,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
err = hci_uuids_clear(hdev); err = hci_uuids_clear(hdev);
...@@ -915,7 +988,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -915,7 +988,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
unlock: unlock:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -941,62 +1014,24 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, ...@@ -941,62 +1014,24 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
hdev->major_class = cp->major; hdev->major_class = cp->major;
hdev->minor_class = cp->minor; hdev->minor_class = cp->minor;
if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags)) {
hci_dev_unlock(hdev);
cancel_delayed_work_sync(&hdev->service_cache);
hci_dev_lock(hdev);
update_eir(hdev);
}
err = update_class(hdev); err = update_class(hdev);
if (err == 0) if (err == 0)
err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0); err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
}
static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
struct hci_dev *hdev;
struct mgmt_cp_set_service_cache *cp;
int err;
cp = (void *) data;
if (len != sizeof(*cp))
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE,
MGMT_STATUS_INVALID_PARAMS);
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE,
MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev);
BT_DBG("hci%u enable %d", index, cp->enable);
if (cp->enable) {
set_bit(HCI_SERVICE_CACHE, &hdev->flags);
err = 0;
} else {
clear_bit(HCI_SERVICE_CACHE, &hdev->flags);
err = update_class(hdev);
if (err == 0)
err = update_eir(hdev);
}
if (err == 0)
err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL,
0);
else
cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, -err);
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1035,7 +1070,7 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data, ...@@ -1035,7 +1070,7 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys, BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
key_count); key_count);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
hci_link_keys_clear(hdev); hci_link_keys_clear(hdev);
...@@ -1055,7 +1090,7 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data, ...@@ -1055,7 +1090,7 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0); cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return 0; return 0;
...@@ -1083,7 +1118,7 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data, ...@@ -1083,7 +1118,7 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
memset(&rp, 0, sizeof(rp)); memset(&rp, 0, sizeof(rp));
bacpy(&rp.bdaddr, &cp->bdaddr); bacpy(&rp.bdaddr, &cp->bdaddr);
...@@ -1124,7 +1159,7 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data, ...@@ -1124,7 +1159,7 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
if (err < 0) if (err < 0)
err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
sizeof(rp)); sizeof(rp));
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1152,7 +1187,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -1152,7 +1187,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
return cmd_status(sk, index, MGMT_OP_DISCONNECT, return cmd_status(sk, index, MGMT_OP_DISCONNECT,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
...@@ -1190,7 +1225,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -1190,7 +1225,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1232,7 +1267,7 @@ static int get_connections(struct sock *sk, u16 index) ...@@ -1232,7 +1267,7 @@ static int get_connections(struct sock *sk, u16 index)
return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
count = 0; count = 0;
list_for_each(p, &hdev->conn_hash.list) { list_for_each(p, &hdev->conn_hash.list) {
...@@ -1264,7 +1299,7 @@ static int get_connections(struct sock *sk, u16 index) ...@@ -1264,7 +1299,7 @@ static int get_connections(struct sock *sk, u16 index)
unlock: unlock:
kfree(rp); kfree(rp);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
} }
...@@ -1312,7 +1347,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, ...@@ -1312,7 +1347,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
...@@ -1355,7 +1390,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, ...@@ -1355,7 +1390,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1381,7 +1416,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, ...@@ -1381,7 +1416,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
...@@ -1392,7 +1427,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, ...@@ -1392,7 +1427,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
err = send_pin_code_neg_reply(sk, index, hdev, cp); err = send_pin_code_neg_reply(sk, index, hdev, cp);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1417,14 +1452,14 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data, ...@@ -1417,14 +1452,14 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
hdev->io_capability = cp->io_capability; hdev->io_capability = cp->io_capability;
BT_DBG("%s IO capability set to 0x%02x", hdev->name, BT_DBG("%s IO capability set to 0x%02x", hdev->name,
hdev->io_capability); hdev->io_capability);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0); return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
...@@ -1505,7 +1540,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -1505,7 +1540,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
sec_level = BT_SECURITY_MEDIUM; sec_level = BT_SECURITY_MEDIUM;
if (cp->io_cap == 0x03) if (cp->io_cap == 0x03)
...@@ -1562,7 +1597,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) ...@@ -1562,7 +1597,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
err = 0; err = 0;
unlock: unlock:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1581,7 +1616,7 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr, ...@@ -1581,7 +1616,7 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
return cmd_status(sk, index, mgmt_op, return cmd_status(sk, index, mgmt_op,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED); err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
...@@ -1632,7 +1667,7 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr, ...@@ -1632,7 +1667,7 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
done: done:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1656,7 +1691,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len) ...@@ -1656,7 +1691,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data, static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
u16 len) u16 len)
{ {
struct mgmt_cp_user_confirm_reply *cp = (void *) data; struct mgmt_cp_user_confirm_neg_reply *cp = data;
BT_DBG(""); BT_DBG("");
...@@ -1720,7 +1755,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data, ...@@ -1720,7 +1755,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len); cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len);
if (!cmd) { if (!cmd) {
...@@ -1735,7 +1770,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data, ...@@ -1735,7 +1770,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1754,7 +1789,7 @@ static int read_local_oob_data(struct sock *sk, u16 index) ...@@ -1754,7 +1789,7 @@ static int read_local_oob_data(struct sock *sk, u16 index)
return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
...@@ -1785,7 +1820,7 @@ static int read_local_oob_data(struct sock *sk, u16 index) ...@@ -1785,7 +1820,7 @@ static int read_local_oob_data(struct sock *sk, u16 index)
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
unlock: unlock:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1809,7 +1844,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data, ...@@ -1809,7 +1844,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash, err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
cp->randomizer); cp->randomizer);
...@@ -1820,7 +1855,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data, ...@@ -1820,7 +1855,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL, err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
0); 0);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1844,7 +1879,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, ...@@ -1844,7 +1879,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
err = hci_remove_remote_oob_data(hdev, &cp->bdaddr); err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
if (err < 0) if (err < 0)
...@@ -1854,7 +1889,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, ...@@ -1854,7 +1889,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
NULL, 0); NULL, 0);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1879,7 +1914,7 @@ static int start_discovery(struct sock *sk, u16 index, ...@@ -1879,7 +1914,7 @@ static int start_discovery(struct sock *sk, u16 index,
return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) { if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
...@@ -1898,7 +1933,7 @@ static int start_discovery(struct sock *sk, u16 index, ...@@ -1898,7 +1933,7 @@ static int start_discovery(struct sock *sk, u16 index,
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1917,7 +1952,7 @@ static int stop_discovery(struct sock *sk, u16 index) ...@@ -1917,7 +1952,7 @@ static int stop_discovery(struct sock *sk, u16 index)
return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
if (!cmd) { if (!cmd) {
...@@ -1930,7 +1965,7 @@ static int stop_discovery(struct sock *sk, u16 index) ...@@ -1930,7 +1965,7 @@ static int stop_discovery(struct sock *sk, u16 index)
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
failed: failed:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1954,7 +1989,7 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, ...@@ -1954,7 +1989,7 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
err = hci_blacklist_add(hdev, &cp->bdaddr); err = hci_blacklist_add(hdev, &cp->bdaddr);
if (err < 0) if (err < 0)
...@@ -1964,7 +1999,7 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, ...@@ -1964,7 +1999,7 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
NULL, 0); NULL, 0);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -1988,7 +2023,7 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, ...@@ -1988,7 +2023,7 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
MGMT_STATUS_INVALID_PARAMS); MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
err = hci_blacklist_del(hdev, &cp->bdaddr); err = hci_blacklist_del(hdev, &cp->bdaddr);
...@@ -1999,7 +2034,7 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, ...@@ -1999,7 +2034,7 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
NULL, 0); NULL, 0);
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
...@@ -2009,7 +2044,7 @@ static int set_fast_connectable(struct sock *sk, u16 index, ...@@ -2009,7 +2044,7 @@ static int set_fast_connectable(struct sock *sk, u16 index,
unsigned char *data, u16 len) unsigned char *data, u16 len)
{ {
struct hci_dev *hdev; struct hci_dev *hdev;
struct mgmt_cp_set_fast_connectable *cp = (void *) data; struct mgmt_mode *cp = (void *) data;
struct hci_cp_write_page_scan_activity acp; struct hci_cp_write_page_scan_activity acp;
u8 type; u8 type;
int err; int err;
...@@ -2027,7 +2062,7 @@ static int set_fast_connectable(struct sock *sk, u16 index, ...@@ -2027,7 +2062,7 @@ static int set_fast_connectable(struct sock *sk, u16 index,
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (cp->enable) { if (cp->val) {
type = PAGE_SCAN_TYPE_INTERLACED; type = PAGE_SCAN_TYPE_INTERLACED;
acp.interval = 0x0024; /* 22.5 msec page scan interval */ acp.interval = 0x0024; /* 22.5 msec page scan interval */
} else { } else {
...@@ -2111,6 +2146,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) ...@@ -2111,6 +2146,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_SET_CONNECTABLE: case MGMT_OP_SET_CONNECTABLE:
err = set_connectable(sk, index, buf + sizeof(*hdr), len); err = set_connectable(sk, index, buf + sizeof(*hdr), len);
break; break;
case MGMT_OP_SET_FAST_CONNECTABLE:
err = set_fast_connectable(sk, index, buf + sizeof(*hdr),
len);
break;
case MGMT_OP_SET_PAIRABLE: case MGMT_OP_SET_PAIRABLE:
err = set_pairable(sk, index, buf + sizeof(*hdr), len); err = set_pairable(sk, index, buf + sizeof(*hdr), len);
break; break;
...@@ -2123,9 +2162,6 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) ...@@ -2123,9 +2162,6 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_SET_DEV_CLASS: case MGMT_OP_SET_DEV_CLASS:
err = set_dev_class(sk, index, buf + sizeof(*hdr), len); err = set_dev_class(sk, index, buf + sizeof(*hdr), len);
break; break;
case MGMT_OP_SET_SERVICE_CACHE:
err = set_service_cache(sk, index, buf + sizeof(*hdr), len);
break;
case MGMT_OP_LOAD_LINK_KEYS: case MGMT_OP_LOAD_LINK_KEYS:
err = load_link_keys(sk, index, buf + sizeof(*hdr), len); err = load_link_keys(sk, index, buf + sizeof(*hdr), len);
break; break;
...@@ -2189,10 +2225,6 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) ...@@ -2189,10 +2225,6 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_UNBLOCK_DEVICE: case MGMT_OP_UNBLOCK_DEVICE:
err = unblock_device(sk, index, buf + sizeof(*hdr), len); err = unblock_device(sk, index, buf + sizeof(*hdr), len);
break; break;
case MGMT_OP_SET_FAST_CONNECTABLE:
err = set_fast_connectable(sk, index, buf + sizeof(*hdr),
len);
break;
default: default:
BT_DBG("Unknown op %u", opcode); BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode, err = cmd_status(sk, index, opcode,
...@@ -2235,17 +2267,14 @@ int mgmt_index_removed(struct hci_dev *hdev) ...@@ -2235,17 +2267,14 @@ int mgmt_index_removed(struct hci_dev *hdev)
struct cmd_lookup { struct cmd_lookup {
u8 val; u8 val;
struct sock *sk; struct sock *sk;
struct hci_dev *hdev;
}; };
static void mode_rsp(struct pending_cmd *cmd, void *data) static void settings_rsp(struct pending_cmd *cmd, void *data)
{ {
struct mgmt_mode *cp = cmd->param;
struct cmd_lookup *match = data; struct cmd_lookup *match = data;
if (cp->val != match->val) send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
return;
send_mode_rsp(cmd->sk, cmd->opcode, cmd->index, cp->val);
list_del(&cmd->list); list_del(&cmd->list);
...@@ -2259,20 +2288,21 @@ static void mode_rsp(struct pending_cmd *cmd, void *data) ...@@ -2259,20 +2288,21 @@ static void mode_rsp(struct pending_cmd *cmd, void *data)
int mgmt_powered(struct hci_dev *hdev, u8 powered) int mgmt_powered(struct hci_dev *hdev, u8 powered)
{ {
struct mgmt_mode ev; struct cmd_lookup match = { powered, NULL, hdev };
struct cmd_lookup match = { powered, NULL }; __le32 ev;
int ret; int ret;
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, mode_rsp, &match); mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
if (!powered) { if (!powered) {
u8 status = ENETDOWN; u8 status = ENETDOWN;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
} }
ev.val = powered; ev = cpu_to_le32(get_current_settings(hdev));
ret = mgmt_event(MGMT_EV_POWERED, hdev, &ev, sizeof(ev), match.sk); ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
match.sk);
if (match.sk) if (match.sk)
sock_put(match.sk); sock_put(match.sk);
...@@ -2282,17 +2312,16 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) ...@@ -2282,17 +2312,16 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
{ {
struct mgmt_mode ev; struct cmd_lookup match = { discoverable, NULL, hdev };
struct cmd_lookup match = { discoverable, NULL }; __le32 ev;
int ret; int ret;
mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, mode_rsp, &match); mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
ev.val = discoverable; ev = cpu_to_le32(get_current_settings(hdev));
ret = mgmt_event(MGMT_EV_DISCOVERABLE, hdev, &ev, sizeof(ev), ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
match.sk); match.sk);
if (match.sk) if (match.sk)
sock_put(match.sk); sock_put(match.sk);
...@@ -2301,15 +2330,16 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) ...@@ -2301,15 +2330,16 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
int mgmt_connectable(struct hci_dev *hdev, u8 connectable) int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
{ {
struct mgmt_mode ev; __le32 ev;
struct cmd_lookup match = { connectable, NULL }; struct cmd_lookup match = { connectable, NULL, hdev };
int ret; int ret;
mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, mode_rsp, &match); mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
&match);
ev.val = connectable; ev = cpu_to_le32(get_current_settings(hdev));
ret = mgmt_event(MGMT_EV_CONNECTABLE, hdev, &ev, sizeof(ev), match.sk); ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
if (match.sk) if (match.sk)
sock_put(match.sk); sock_put(match.sk);
......
...@@ -1162,6 +1162,7 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) ...@@ -1162,6 +1162,7 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
if (list_empty(&s->dlcs)) { if (list_empty(&s->dlcs)) {
s->state = BT_DISCONN; s->state = BT_DISCONN;
rfcomm_send_disc(s, 0); rfcomm_send_disc(s, 0);
rfcomm_session_clear_timer(s);
} }
break; break;
......
...@@ -189,7 +189,7 @@ static int sco_connect(struct sock *sk) ...@@ -189,7 +189,7 @@ static int sco_connect(struct sock *sk)
if (!hdev) if (!hdev)
return -EHOSTUNREACH; return -EHOSTUNREACH;
hci_dev_lock_bh(hdev); hci_dev_lock(hdev);
if (lmp_esco_capable(hdev) && !disable_esco) if (lmp_esco_capable(hdev) && !disable_esco)
type = ESCO_LINK; type = ESCO_LINK;
...@@ -225,7 +225,7 @@ static int sco_connect(struct sock *sk) ...@@ -225,7 +225,7 @@ static int sco_connect(struct sock *sk)
} }
done: done:
hci_dev_unlock_bh(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment