summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/bnep/core.c13
-rw-r--r--net/bluetooth/cmtp/core.c13
-rw-r--r--net/bluetooth/hci_conn.c71
-rw-r--r--net/bluetooth/hci_core.c399
-rw-r--r--net/bluetooth/hci_event.c221
-rw-r--r--net/bluetooth/hci_sysfs.c40
-rw-r--r--net/bluetooth/hidp/core.c157
-rw-r--r--net/bluetooth/l2cap_core.c963
-rw-r--r--net/bluetooth/l2cap_sock.c90
-rw-r--r--net/bluetooth/mgmt.c500
-rw-r--r--net/bluetooth/rfcomm/core.c65
-rw-r--r--net/bluetooth/rfcomm/sock.c2
-rw-r--r--net/bluetooth/rfcomm/tty.c45
-rw-r--r--net/bluetooth/smp.c3
-rw-r--r--net/core/skbuff.c20
-rw-r--r--net/core/sock.c9
-rw-r--r--net/mac80211/agg-rx.c3
-rw-r--r--net/mac80211/agg-tx.c10
-rw-r--r--net/mac80211/cfg.c167
-rw-r--r--net/mac80211/debugfs.c2
-rw-r--r--net/mac80211/debugfs_sta.c4
-rw-r--r--net/mac80211/driver-ops.h76
-rw-r--r--net/mac80211/ht.c86
-rw-r--r--net/mac80211/ibss.c3
-rw-r--r--net/mac80211/ieee80211_i.h102
-rw-r--r--net/mac80211/iface.c44
-rw-r--r--net/mac80211/key.c9
-rw-r--r--net/mac80211/main.c57
-rw-r--r--net/mac80211/mesh.c70
-rw-r--r--net/mac80211/mesh.h7
-rw-r--r--net/mac80211/mesh_hwmp.c53
-rw-r--r--net/mac80211/mesh_pathtbl.c30
-rw-r--r--net/mac80211/mesh_plink.c51
-rw-r--r--net/mac80211/mlme.c119
-rw-r--r--net/mac80211/offchannel.c4
-rw-r--r--net/mac80211/pm.c2
-rw-r--r--net/mac80211/rc80211_minstrel.c7
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c62
-rw-r--r--net/mac80211/rx.c73
-rw-r--r--net/mac80211/scan.c199
-rw-r--r--net/mac80211/sta_info.c8
-rw-r--r--net/mac80211/sta_info.h10
-rw-r--r--net/mac80211/status.c87
-rw-r--r--net/mac80211/tx.c332
-rw-r--r--net/mac80211/util.c300
-rw-r--r--net/mac80211/wep.c5
-rw-r--r--net/mac80211/wme.c10
-rw-r--r--net/mac80211/work.c60
-rw-r--r--net/mac80211/wpa.c34
-rw-r--r--net/nfc/nci/core.c18
-rw-r--r--net/nfc/nci/data.c5
-rw-r--r--net/nfc/nci/lib.c8
-rw-r--r--net/nfc/nci/ntf.c152
-rw-r--r--net/nfc/nci/rsp.c83
-rw-r--r--net/rfkill/core.c4
-rw-r--r--net/socket.c18
-rw-r--r--net/wireless/core.c4
-rw-r--r--net/wireless/core.h14
-rw-r--r--net/wireless/mlme.c72
-rw-r--r--net/wireless/nl80211.c552
-rw-r--r--net/wireless/nl80211.h5
-rw-r--r--net/wireless/reg.c39
-rw-r--r--net/wireless/reg.h1
-rw-r--r--net/wireless/scan.c130
-rw-r--r--net/wireless/sme.c7
-rw-r--r--net/wireless/util.c186
-rw-r--r--net/wireless/wext-compat.c12
67 files changed, 4151 insertions, 1856 deletions
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 91bcd3a961ec..a6cd856046ab 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -65,15 +65,13 @@ static DECLARE_RWSEM(bnep_session_sem);
static struct bnep_session *__bnep_get_session(u8 *dst)
{
struct bnep_session *s;
- struct list_head *p;
BT_DBG("");
- list_for_each(p, &bnep_session_list) {
- s = list_entry(p, struct bnep_session, list);
+ list_for_each_entry(s, &bnep_session_list, list)
if (!compare_ether_addr(dst, s->eh.h_source))
return s;
- }
+
return NULL;
}
@@ -667,17 +665,14 @@ static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
int bnep_get_connlist(struct bnep_connlist_req *req)
{
- struct list_head *p;
+ struct bnep_session *s;
int err = 0, n = 0;
down_read(&bnep_session_sem);
- list_for_each(p, &bnep_session_list) {
- struct bnep_session *s;
+ list_for_each_entry(s, &bnep_session_list, list) {
struct bnep_conninfo ci;
- s = list_entry(p, struct bnep_session, list);
-
__bnep_copy_ci(&ci, s);
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 7d00ddf9e9dc..9e8940b24bba 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -53,15 +53,13 @@ static LIST_HEAD(cmtp_session_list);
static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
{
struct cmtp_session *session;
- struct list_head *p;
BT_DBG("");
- list_for_each(p, &cmtp_session_list) {
- session = list_entry(p, struct cmtp_session, list);
+ list_for_each_entry(session, &cmtp_session_list, list)
if (!bacmp(bdaddr, &session->bdaddr))
return session;
- }
+
return NULL;
}
@@ -431,19 +429,16 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
int cmtp_get_connlist(struct cmtp_connlist_req *req)
{
- struct list_head *p;
+ struct cmtp_session *session;
int err = 0, n = 0;
BT_DBG("");
down_read(&cmtp_session_sem);
- list_for_each(p, &cmtp_session_list) {
- struct cmtp_session *session;
+ list_for_each_entry(session, &cmtp_session_list, list) {
struct cmtp_conninfo ci;
- session = list_entry(p, struct cmtp_session, list);
-
__cmtp_copy_session(session, &ci);
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index c1c597e3e198..de0b93e45980 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -374,6 +374,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
skb_queue_head_init(&conn->data_q);
+ hci_chan_hash_init(conn);
+
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
@@ -432,6 +434,8 @@ int hci_conn_del(struct hci_conn *conn)
tasklet_disable(&hdev->tx_task);
+ hci_chan_hash_flush(conn);
+
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
@@ -453,16 +457,13 @@ int hci_conn_del(struct hci_conn *conn)
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
{
int use_src = bacmp(src, BDADDR_ANY);
- struct hci_dev *hdev = NULL;
- struct list_head *p;
+ struct hci_dev *hdev = NULL, *d;
BT_DBG("%s -> %s", batostr(src), batostr(dst));
read_lock_bh(&hci_dev_list_lock);
- list_for_each(p, &hci_dev_list) {
- struct hci_dev *d = list_entry(p, struct hci_dev, list);
-
+ list_for_each_entry(d, &hci_dev_list, list) {
if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags))
continue;
@@ -673,7 +674,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
goto encrypt;
auth:
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
return 0;
if (!hci_conn_auth(conn, sec_level, auth_type))
@@ -819,7 +820,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
c->state = BT_CLOSED;
- hci_proto_disconn_cfm(c, 0x16);
+ hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
hci_conn_del(c);
}
}
@@ -855,10 +856,10 @@ EXPORT_SYMBOL(hci_conn_put_device);
int hci_get_conn_list(void __user *arg)
{
+ register struct hci_conn *c;
struct hci_conn_list_req req, *cl;
struct hci_conn_info *ci;
struct hci_dev *hdev;
- struct list_head *p;
int n = 0, size, err;
if (copy_from_user(&req, arg, sizeof(req)))
@@ -882,10 +883,7 @@ int hci_get_conn_list(void __user *arg)
ci = cl->conn_info;
hci_dev_lock_bh(hdev);
- list_for_each(p, &hdev->conn_hash.list) {
- register struct hci_conn *c;
- c = list_entry(p, struct hci_conn, list);
-
+ list_for_each_entry(c, &hdev->conn_hash.list, list) {
bacpy(&(ci + n)->bdaddr, &c->dst);
(ci + n)->handle = c->handle;
(ci + n)->type = c->type;
@@ -956,3 +954,52 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)
return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
}
+
+struct hci_chan *hci_chan_create(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_chan *chan;
+
+ BT_DBG("%s conn %p", hdev->name, conn);
+
+ chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC);
+ if (!chan)
+ return NULL;
+
+ chan->conn = conn;
+ skb_queue_head_init(&chan->data_q);
+
+ tasklet_disable(&hdev->tx_task);
+ hci_chan_hash_add(conn, chan);
+ tasklet_enable(&hdev->tx_task);
+
+ return chan;
+}
+
+int hci_chan_del(struct hci_chan *chan)
+{
+ struct hci_conn *conn = chan->conn;
+ struct hci_dev *hdev = conn->hdev;
+
+ BT_DBG("%s conn %p chan %p", hdev->name, conn, chan);
+
+ tasklet_disable(&hdev->tx_task);
+ hci_chan_hash_del(conn, chan);
+ tasklet_enable(&hdev->tx_task);
+
+ skb_queue_purge(&chan->data_q);
+ kfree(chan);
+
+ return 0;
+}
+
+void hci_chan_hash_flush(struct hci_conn *conn)
+{
+ struct hci_chan_hash *h = &conn->chan_hash;
+ struct hci_chan *chan, *tmp;
+
+ BT_DBG("conn %p", conn);
+
+ list_for_each_entry_safe(chan, tmp, &h->list, list)
+ hci_chan_del(chan);
+}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index be84ae33ae36..fb3feeb185d7 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -319,8 +319,7 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
* Device is held on return. */
struct hci_dev *hci_dev_get(int index)
{
- struct hci_dev *hdev = NULL;
- struct list_head *p;
+ struct hci_dev *hdev = NULL, *d;
BT_DBG("%d", index);
@@ -328,8 +327,7 @@ struct hci_dev *hci_dev_get(int index)
return NULL;
read_lock(&hci_dev_list_lock);
- list_for_each(p, &hci_dev_list) {
- struct hci_dev *d = list_entry(p, struct hci_dev, list);
+ list_for_each_entry(d, &hci_dev_list, list) {
if (d->id == index) {
hdev = hci_dev_hold(d);
break;
@@ -551,8 +549,11 @@ int hci_dev_open(__u16 dev)
hci_dev_hold(hdev);
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
- if (!test_bit(HCI_SETUP, &hdev->flags))
- mgmt_powered(hdev->id, 1);
+ if (!test_bit(HCI_SETUP, &hdev->flags)) {
+ hci_dev_lock_bh(hdev);
+ mgmt_powered(hdev, 1);
+ hci_dev_unlock_bh(hdev);
+ }
} else {
/* Init failed, cleanup */
tasklet_kill(&hdev->rx_task);
@@ -597,6 +598,14 @@ static int hci_dev_do_close(struct hci_dev *hdev)
tasklet_kill(&hdev->rx_task);
tasklet_kill(&hdev->tx_task);
+ if (hdev->discov_timeout > 0) {
+ cancel_delayed_work(&hdev->discov_off);
+ hdev->discov_timeout = 0;
+ }
+
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+ cancel_delayed_work(&hdev->power_off);
+
hci_dev_lock_bh(hdev);
inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev);
@@ -636,7 +645,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
* and no tasks are scheduled. */
hdev->close(hdev);
- mgmt_powered(hdev->id, 0);
+ hci_dev_lock_bh(hdev);
+ mgmt_powered(hdev, 0);
+ hci_dev_unlock_bh(hdev);
/* Clear flags */
hdev->flags = 0;
@@ -794,9 +805,9 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
int hci_get_dev_list(void __user *arg)
{
+ struct hci_dev *hdev;
struct hci_dev_list_req *dl;
struct hci_dev_req *dr;
- struct list_head *p;
int n = 0, size, err;
__u16 dev_num;
@@ -815,12 +826,9 @@ int hci_get_dev_list(void __user *arg)
dr = dl->dev_req;
read_lock_bh(&hci_dev_list_lock);
- list_for_each(p, &hci_dev_list) {
- struct hci_dev *hdev;
-
- hdev = list_entry(p, struct hci_dev, list);
-
- hci_del_off_timer(hdev);
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+ cancel_delayed_work(&hdev->power_off);
if (!test_bit(HCI_MGMT, &hdev->flags))
set_bit(HCI_PAIRABLE, &hdev->flags);
@@ -855,7 +863,8 @@ int hci_get_dev_info(void __user *arg)
if (!hdev)
return -ENODEV;
- hci_del_off_timer(hdev);
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+ cancel_delayed_work_sync(&hdev->power_off);
if (!test_bit(HCI_MGMT, &hdev->flags))
set_bit(HCI_PAIRABLE, &hdev->flags);
@@ -912,6 +921,7 @@ struct hci_dev *hci_alloc_dev(void)
if (!hdev)
return NULL;
+ hci_init_sysfs(hdev);
skb_queue_head_init(&hdev->driver_init);
return hdev;
@@ -938,39 +948,41 @@ static void hci_power_on(struct work_struct *work)
return;
if (test_bit(HCI_AUTO_OFF, &hdev->flags))
- mod_timer(&hdev->off_timer,
- jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT));
+ queue_delayed_work(hdev->workqueue, &hdev->power_off,
+ msecs_to_jiffies(AUTO_OFF_TIMEOUT));
if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
- mgmt_index_added(hdev->id);
+ mgmt_index_added(hdev);
}
static void hci_power_off(struct work_struct *work)
{
- struct hci_dev *hdev = container_of(work, struct hci_dev, power_off);
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ power_off.work);
BT_DBG("%s", hdev->name);
+ clear_bit(HCI_AUTO_OFF, &hdev->flags);
+
hci_dev_close(hdev->id);
}
-static void hci_auto_off(unsigned long data)
+static void hci_discov_off(struct work_struct *work)
{
- struct hci_dev *hdev = (struct hci_dev *) data;
+ struct hci_dev *hdev;
+ u8 scan = SCAN_PAGE;
+
+ hdev = container_of(work, struct hci_dev, discov_off.work);
BT_DBG("%s", hdev->name);
- clear_bit(HCI_AUTO_OFF, &hdev->flags);
+ hci_dev_lock_bh(hdev);
- queue_work(hdev->workqueue, &hdev->power_off);
-}
+ hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
-void hci_del_off_timer(struct hci_dev *hdev)
-{
- BT_DBG("%s", hdev->name);
+ hdev->discov_timeout = 0;
- clear_bit(HCI_AUTO_OFF, &hdev->flags);
- del_timer(&hdev->off_timer);
+ hci_dev_unlock_bh(hdev);
}
int hci_uuids_clear(struct hci_dev *hdev)
@@ -1007,16 +1019,11 @@ int hci_link_keys_clear(struct hci_dev *hdev)
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
- struct list_head *p;
-
- list_for_each(p, &hdev->link_keys) {
- struct link_key *k;
-
- k = list_entry(p, struct link_key, list);
+ struct link_key *k;
+ list_for_each_entry(k, &hdev->link_keys, list)
if (bacmp(bdaddr, &k->bdaddr) == 0)
return k;
- }
return NULL;
}
@@ -1138,7 +1145,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
persistent = hci_persistent_key(hdev, conn, type, old_key_type);
- mgmt_new_key(hdev->id, key, persistent);
+ mgmt_new_link_key(hdev, key, persistent);
if (!persistent) {
list_del(&key->list);
@@ -1181,7 +1188,7 @@ int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
memcpy(id->rand, rand, sizeof(id->rand));
if (new_key)
- mgmt_new_key(hdev->id, key, old_key_type);
+ mgmt_new_link_key(hdev, key, old_key_type);
return 0;
}
@@ -1279,16 +1286,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
bdaddr_t *bdaddr)
{
- struct list_head *p;
-
- list_for_each(p, &hdev->blacklist) {
- struct bdaddr_list *b;
-
- b = list_entry(p, struct bdaddr_list, list);
+ struct bdaddr_list *b;
+ list_for_each_entry(b, &hdev->blacklist, list)
if (bacmp(bdaddr, &b->bdaddr) == 0)
return b;
- }
return NULL;
}
@@ -1327,7 +1329,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
list_add(&entry->list, &hdev->blacklist);
- return mgmt_device_blocked(hdev->id, bdaddr);
+ return mgmt_device_blocked(hdev, bdaddr);
}
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
@@ -1346,7 +1348,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
list_del(&entry->list);
kfree(entry);
- return mgmt_device_unblocked(hdev->id, bdaddr);
+ return mgmt_device_unblocked(hdev, bdaddr);
}
static void hci_clear_adv_cache(unsigned long arg)
@@ -1425,7 +1427,7 @@ int hci_add_adv_entry(struct hci_dev *hdev,
int hci_register_dev(struct hci_dev *hdev)
{
struct list_head *head = &hci_dev_list, *p;
- int i, id = 0;
+ int i, id, error;
BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name,
hdev->bus, hdev->owner);
@@ -1433,6 +1435,11 @@ int hci_register_dev(struct hci_dev *hdev)
if (!hdev->open || !hdev->close || !hdev->destruct)
return -EINVAL;
+ /* Do not allow HCI_AMP devices to register at index 0,
+ * so the index can be used as the AMP controller ID.
+ */
+ id = (hdev->dev_type == HCI_BREDR) ? 0 : 1;
+
write_lock_bh(&hci_dev_list_lock);
/* Find first available device id */
@@ -1479,6 +1486,8 @@ int hci_register_dev(struct hci_dev *hdev)
hci_conn_hash_init(hdev);
+ INIT_LIST_HEAD(&hdev->mgmt_pending);
+
INIT_LIST_HEAD(&hdev->blacklist);
INIT_LIST_HEAD(&hdev->uuids);
@@ -1492,8 +1501,9 @@ int hci_register_dev(struct hci_dev *hdev)
(unsigned long) hdev);
INIT_WORK(&hdev->power_on, hci_power_on);
- INIT_WORK(&hdev->power_off, hci_power_off);
- setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
+ INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
+
+ INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
@@ -1502,10 +1512,14 @@ int hci_register_dev(struct hci_dev *hdev)
write_unlock_bh(&hci_dev_list_lock);
hdev->workqueue = create_singlethread_workqueue(hdev->name);
- if (!hdev->workqueue)
- goto nomem;
+ if (!hdev->workqueue) {
+ error = -ENOMEM;
+ goto err;
+ }
- hci_register_sysfs(hdev);
+ error = hci_add_sysfs(hdev);
+ if (error < 0)
+ goto err_wqueue;
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev);
@@ -1524,17 +1538,19 @@ int hci_register_dev(struct hci_dev *hdev)
return id;
-nomem:
+err_wqueue:
+ destroy_workqueue(hdev->workqueue);
+err:
write_lock_bh(&hci_dev_list_lock);
list_del(&hdev->list);
write_unlock_bh(&hci_dev_list_lock);
- return -ENOMEM;
+ return error;
}
EXPORT_SYMBOL(hci_register_dev);
/* Unregister HCI device */
-int hci_unregister_dev(struct hci_dev *hdev)
+void hci_unregister_dev(struct hci_dev *hdev)
{
int i;
@@ -1550,8 +1566,15 @@ int hci_unregister_dev(struct hci_dev *hdev)
kfree_skb(hdev->reassembly[i]);
if (!test_bit(HCI_INIT, &hdev->flags) &&
- !test_bit(HCI_SETUP, &hdev->flags))
- mgmt_index_removed(hdev->id);
+ !test_bit(HCI_SETUP, &hdev->flags)) {
+ hci_dev_lock_bh(hdev);
+ mgmt_index_removed(hdev);
+ hci_dev_unlock_bh(hdev);
+ }
+
+ /* mgmt_index_removed should take care of emptying the
+ * pending list */
+ BUG_ON(!list_empty(&hdev->mgmt_pending));
hci_notify(hdev, HCI_DEV_UNREG);
@@ -1560,9 +1583,8 @@ int hci_unregister_dev(struct hci_dev *hdev)
rfkill_destroy(hdev->rfkill);
}
- hci_unregister_sysfs(hdev);
+ hci_del_sysfs(hdev);
- hci_del_off_timer(hdev);
del_timer(&hdev->adv_timer);
destroy_workqueue(hdev->workqueue);
@@ -1576,8 +1598,6 @@ int hci_unregister_dev(struct hci_dev *hdev)
hci_dev_unlock_bh(hdev);
__hci_dev_put(hdev);
-
- return 0;
}
EXPORT_SYMBOL(hci_unregister_dev);
@@ -1948,23 +1968,18 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
hdr->dlen = cpu_to_le16(len);
}
-void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
+static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
+ struct sk_buff *skb, __u16 flags)
{
struct hci_dev *hdev = conn->hdev;
struct sk_buff *list;
- BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
-
- skb->dev = (void *) hdev;
- bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
- hci_add_acl_hdr(skb, conn->handle, flags);
-
list = skb_shinfo(skb)->frag_list;
if (!list) {
/* Non fragmented */
BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
- skb_queue_tail(&conn->data_q, skb);
+ skb_queue_tail(queue, skb);
} else {
/* Fragmented */
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
@@ -1972,9 +1987,9 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
skb_shinfo(skb)->frag_list = NULL;
/* Queue all fragments atomically */
- spin_lock_bh(&conn->data_q.lock);
+ spin_lock_bh(&queue->lock);
- __skb_queue_tail(&conn->data_q, skb);
+ __skb_queue_tail(queue, skb);
flags &= ~ACL_START;
flags |= ACL_CONT;
@@ -1987,11 +2002,25 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
- __skb_queue_tail(&conn->data_q, skb);
+ __skb_queue_tail(queue, skb);
} while (list);
- spin_unlock_bh(&conn->data_q.lock);
+ spin_unlock_bh(&queue->lock);
}
+}
+
+void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
+{
+ struct hci_conn *conn = chan->conn;
+ struct hci_dev *hdev = conn->hdev;
+
+ BT_DBG("%s chan %p flags 0x%x", hdev->name, chan, flags);
+
+ skb->dev = (void *) hdev;
+ bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+ hci_add_acl_hdr(skb, conn->handle, flags);
+
+ hci_queue_acl(conn, &chan->data_q, skb, flags);
tasklet_schedule(&hdev->tx_task);
}
@@ -2026,16 +2055,12 @@ EXPORT_SYMBOL(hci_send_sco);
static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
{
struct hci_conn_hash *h = &hdev->conn_hash;
- struct hci_conn *conn = NULL;
+ struct hci_conn *conn = NULL, *c;
int num = 0, min = ~0;
- struct list_head *p;
/* We don't have to lock device here. Connections are always
* added and removed with TX task disabled. */
- list_for_each(p, &h->list) {
- struct hci_conn *c;
- c = list_entry(p, struct hci_conn, list);
-
+ list_for_each_entry(c, &h->list, list) {
if (c->type != type || skb_queue_empty(&c->data_q))
continue;
@@ -2084,14 +2109,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
{
struct hci_conn_hash *h = &hdev->conn_hash;
- struct list_head *p;
- struct hci_conn *c;
+ struct hci_conn *c;
BT_ERR("%s link tx timeout", hdev->name);
/* Kill stalled connections */
- list_for_each(p, &h->list) {
- c = list_entry(p, struct hci_conn, list);
+ list_for_each_entry(c, &h->list, list) {
if (c->type == type && c->sent) {
BT_ERR("%s killing stalled connection %s",
hdev->name, batostr(&c->dst));
@@ -2100,11 +2123,137 @@ static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
}
}
-static inline void hci_sched_acl(struct hci_dev *hdev)
+static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
+ int *quote)
{
+ struct hci_conn_hash *h = &hdev->conn_hash;
+ struct hci_chan *chan = NULL;
+ int num = 0, min = ~0, cur_prio = 0;
struct hci_conn *conn;
+ int cnt, q, conn_num = 0;
+
+ BT_DBG("%s", hdev->name);
+
+ list_for_each_entry(conn, &h->list, list) {
+ struct hci_chan_hash *ch;
+ struct hci_chan *tmp;
+
+ if (conn->type != type)
+ continue;
+
+ if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+ continue;
+
+ conn_num++;
+
+ ch = &conn->chan_hash;
+
+ list_for_each_entry(tmp, &ch->list, list) {
+ struct sk_buff *skb;
+
+ if (skb_queue_empty(&tmp->data_q))
+ continue;
+
+ skb = skb_peek(&tmp->data_q);
+ if (skb->priority < cur_prio)
+ continue;
+
+ if (skb->priority > cur_prio) {
+ num = 0;
+ min = ~0;
+ cur_prio = skb->priority;
+ }
+
+ num++;
+
+ if (conn->sent < min) {
+ min = conn->sent;
+ chan = tmp;
+ }
+ }
+
+ if (hci_conn_num(hdev, type) == conn_num)
+ break;
+ }
+
+ if (!chan)
+ return NULL;
+
+ switch (chan->conn->type) {
+ case ACL_LINK:
+ cnt = hdev->acl_cnt;
+ break;
+ case SCO_LINK:
+ case ESCO_LINK:
+ cnt = hdev->sco_cnt;
+ break;
+ case LE_LINK:
+ cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
+ break;
+ default:
+ cnt = 0;
+ BT_ERR("Unknown link type");
+ }
+
+ q = cnt / num;
+ *quote = q ? q : 1;
+ BT_DBG("chan %p quote %d", chan, *quote);
+ return chan;
+}
+
+static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
+{
+ struct hci_conn_hash *h = &hdev->conn_hash;
+ struct hci_conn *conn;
+ int num = 0;
+
+ BT_DBG("%s", hdev->name);
+
+ list_for_each_entry(conn, &h->list, list) {
+ struct hci_chan_hash *ch;
+ struct hci_chan *chan;
+
+ if (conn->type != type)
+ continue;
+
+ if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+ continue;
+
+ num++;
+
+ ch = &conn->chan_hash;
+ list_for_each_entry(chan, &ch->list, list) {
+ struct sk_buff *skb;
+
+ if (chan->sent) {
+ chan->sent = 0;
+ continue;
+ }
+
+ if (skb_queue_empty(&chan->data_q))
+ continue;
+
+ skb = skb_peek(&chan->data_q);
+ if (skb->priority >= HCI_PRIO_MAX - 1)
+ continue;
+
+ skb->priority = HCI_PRIO_MAX - 1;
+
+ BT_DBG("chan %p skb %p promoted to %d", chan, skb,
+ skb->priority);
+ }
+
+ if (hci_conn_num(hdev, type) == num)
+ break;
+ }
+}
+
+static inline void hci_sched_acl(struct hci_dev *hdev)
+{
+ struct hci_chan *chan;
struct sk_buff *skb;
int quote;
+ unsigned int cnt;
BT_DBG("%s", hdev->name);
@@ -2118,19 +2267,35 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
hci_link_tx_to(hdev, ACL_LINK);
}
- while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
- while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
- BT_DBG("skb %p len %d", skb, skb->len);
+ cnt = hdev->acl_cnt;
+
+ while (hdev->acl_cnt &&
+ (chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
+ u32 priority = (skb_peek(&chan->data_q))->priority;
+ while (quote-- && (skb = skb_peek(&chan->data_q))) {
+ BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
+ skb->len, skb->priority);
- hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active);
+ /* Stop if priority has changed */
+ if (skb->priority < priority)
+ break;
+
+ skb = skb_dequeue(&chan->data_q);
+
+ hci_conn_enter_active_mode(chan->conn,
+ bt_cb(skb)->force_active);
hci_send_frame(skb);
hdev->acl_last_tx = jiffies;
hdev->acl_cnt--;
- conn->sent++;
+ chan->sent++;
+ chan->conn->sent++;
}
}
+
+ if (cnt != hdev->acl_cnt)
+ hci_prio_recalculate(hdev, ACL_LINK);
}
/* Schedule SCO */
@@ -2182,9 +2347,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
static inline void hci_sched_le(struct hci_dev *hdev)
{
- struct hci_conn *conn;
+ struct hci_chan *chan;
struct sk_buff *skb;
- int quote, cnt;
+ int quote, cnt, tmp;
BT_DBG("%s", hdev->name);
@@ -2200,21 +2365,35 @@ static inline void hci_sched_le(struct hci_dev *hdev)
}
cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
- while (cnt && (conn = hci_low_sent(hdev, LE_LINK, &quote))) {
- while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
- BT_DBG("skb %p len %d", skb, skb->len);
+ tmp = cnt;
+ while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, &quote))) {
+ u32 priority = (skb_peek(&chan->data_q))->priority;
+ while (quote-- && (skb = skb_peek(&chan->data_q))) {
+ BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
+ skb->len, skb->priority);
+
+ /* Stop if priority has changed */
+ if (skb->priority < priority)
+ break;
+
+ skb = skb_dequeue(&chan->data_q);
hci_send_frame(skb);
hdev->le_last_tx = jiffies;
cnt--;
- conn->sent++;
+ chan->sent++;
+ chan->conn->sent++;
}
}
+
if (hdev->le_pkts)
hdev->le_cnt = cnt;
else
hdev->acl_cnt = cnt;
+
+ if (cnt != tmp)
+ hci_prio_recalculate(hdev, LE_LINK);
}
static void hci_tx_task(unsigned long arg)
@@ -2407,3 +2586,31 @@ static void hci_cmd_task(unsigned long arg)
}
}
}
+
+int hci_do_inquiry(struct hci_dev *hdev, u8 length)
+{
+ /* General inquiry access code (GIAC) */
+ u8 lap[3] = { 0x33, 0x8b, 0x9e };
+ struct hci_cp_inquiry cp;
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_bit(HCI_INQUIRY, &hdev->flags))
+ return -EINPROGRESS;
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(&cp.lap, lap, sizeof(cp.lap));
+ cp.length = length;
+
+ return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+}
+
+int hci_cancel_inquiry(struct hci_dev *hdev)
+{
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_INQUIRY, &hdev->flags))
+ return -EPERM;
+
+ return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d7d96b6b1f0d..a89cf1f24e47 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -58,9 +58,11 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
if (status)
return;
- if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
- test_bit(HCI_MGMT, &hdev->flags))
- mgmt_discovering(hdev->id, 0);
+ clear_bit(HCI_INQUIRY, &hdev->flags);
+
+ hci_dev_lock(hdev);
+ mgmt_discovering(hdev, 0);
+ hci_dev_unlock(hdev);
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
@@ -76,10 +78,6 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb)
if (status)
return;
- if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
- test_bit(HCI_MGMT, &hdev->flags))
- mgmt_discovering(hdev->id, 0);
-
hci_conn_check_pending(hdev);
}
@@ -205,13 +203,15 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
if (!sent)
return;
+ hci_dev_lock(hdev);
+
if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_set_local_name_complete(hdev->id, sent, status);
+ mgmt_set_local_name_complete(hdev, sent, status);
- if (status)
- return;
+ if (status == 0)
+ memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
- memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
+ hci_dev_unlock(hdev);
}
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -274,7 +274,8 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
{
- __u8 status = *((__u8 *) skb->data);
+ __u8 param, status = *((__u8 *) skb->data);
+ int old_pscan, old_iscan;
void *sent;
BT_DBG("%s status 0x%x", hdev->name, status);
@@ -283,28 +284,40 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
if (!sent)
return;
- if (!status) {
- __u8 param = *((__u8 *) sent);
- int old_pscan, old_iscan;
-
- old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
- old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
+ param = *((__u8 *) sent);
- if (param & SCAN_INQUIRY) {
- set_bit(HCI_ISCAN, &hdev->flags);
- if (!old_iscan)
- mgmt_discoverable(hdev->id, 1);
- } else if (old_iscan)
- mgmt_discoverable(hdev->id, 0);
+ hci_dev_lock(hdev);
- if (param & SCAN_PAGE) {
- set_bit(HCI_PSCAN, &hdev->flags);
- if (!old_pscan)
- mgmt_connectable(hdev->id, 1);
- } else if (old_pscan)
- mgmt_connectable(hdev->id, 0);
+ if (status != 0) {
+ mgmt_write_scan_failed(hdev, param, status);
+ hdev->discov_timeout = 0;
+ goto done;
}
+ old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
+ old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
+
+ if (param & SCAN_INQUIRY) {
+ set_bit(HCI_ISCAN, &hdev->flags);
+ if (!old_iscan)
+ mgmt_discoverable(hdev, 1);
+ if (hdev->discov_timeout > 0) {
+ int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+ queue_delayed_work(hdev->workqueue, &hdev->discov_off,
+ to);
+ }
+ } else if (old_iscan)
+ mgmt_discoverable(hdev, 0);
+
+ if (param & SCAN_PAGE) {
+ set_bit(HCI_PSCAN, &hdev->flags);
+ if (!old_pscan)
+ mgmt_connectable(hdev, 1);
+ } else if (old_pscan)
+ mgmt_connectable(hdev, 0);
+
+done:
+ hci_dev_unlock(hdev);
hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status);
}
@@ -748,6 +761,30 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status);
}
+static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_read_local_amp_info *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hdev->amp_status = rp->amp_status;
+ hdev->amp_total_bw = __le32_to_cpu(rp->total_bw);
+ hdev->amp_max_bw = __le32_to_cpu(rp->max_bw);
+ hdev->amp_min_latency = __le32_to_cpu(rp->min_latency);
+ hdev->amp_max_pdu = __le32_to_cpu(rp->max_pdu);
+ hdev->amp_type = rp->amp_type;
+ hdev->amp_pal_cap = __le16_to_cpu(rp->pal_cap);
+ hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size);
+ hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to);
+ hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to);
+
+ hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status);
+}
+
static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -804,19 +841,24 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status);
+ hci_dev_lock(hdev);
+
if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status);
+ mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
if (rp->status != 0)
- return;
+ goto unlock;
cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
if (!cp)
- return;
+ goto unlock;
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
if (conn)
conn->pin_length = cp->pin_len;
+
+unlock:
+ hci_dev_unlock(hdev);
}
static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
@@ -825,10 +867,15 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status);
+ hci_dev_lock(hdev);
+
if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr,
+ mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr,
rp->status);
+
+ hci_dev_unlock(hdev);
}
+
static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -855,9 +902,13 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status);
+ hci_dev_lock(hdev);
+
if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_confirm_reply_complete(hdev->id, &rp->bdaddr,
+ mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr,
rp->status);
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
@@ -867,9 +918,13 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, rp->status);
+ hci_dev_lock(hdev);
+
if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_confirm_neg_reply_complete(hdev->id, &rp->bdaddr,
+ mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
rp->status);
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
@@ -879,8 +934,10 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, rp->status);
- mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash,
+ hci_dev_lock(hdev);
+ mgmt_read_local_oob_data_reply_complete(hdev, rp->hash,
rp->randomizer, rp->status);
+ hci_dev_unlock(hdev);
}
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
@@ -955,12 +1012,18 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
if (status) {
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
hci_conn_check_pending(hdev);
+ hci_dev_lock(hdev);
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_inquiry_failed(hdev, status);
+ hci_dev_unlock(hdev);
return;
}
- if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags) &&
- test_bit(HCI_MGMT, &hdev->flags))
- mgmt_discovering(hdev->id, 1);
+ set_bit(HCI_INQUIRY, &hdev->flags);
+
+ hci_dev_lock(hdev);
+ mgmt_discovering(hdev, 1);
+ hci_dev_unlock(hdev);
}
static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
@@ -1339,13 +1402,16 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, status);
- if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
- test_bit(HCI_MGMT, &hdev->flags))
- mgmt_discovering(hdev->id, 0);
-
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
hci_conn_check_pending(hdev);
+
+ if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
+ return;
+
+ hci_dev_lock(hdev);
+ mgmt_discovering(hdev, 0);
+ hci_dev_unlock(hdev);
}
static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1361,12 +1427,6 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
hci_dev_lock(hdev);
- if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
-
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_discovering(hdev->id, 1);
- }
-
for (; num_rsp; num_rsp--, info++) {
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
@@ -1377,8 +1437,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.rssi = 0x00;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0,
- NULL);
+ mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
+ info->dev_class, 0, NULL);
}
hci_dev_unlock(hdev);
@@ -1412,7 +1472,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG;
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
- mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
+ mgmt_connected(hdev, &ev->bdaddr, conn->type);
} else
conn->state = BT_CONNECTED;
@@ -1444,7 +1504,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
} else {
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK)
- mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
+ mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
+ ev->status);
}
if (conn->type == ACL_LINK)
@@ -1531,7 +1592,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
struct hci_cp_reject_conn_req cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
- cp.reason = 0x0f;
+ cp.reason = HCI_ERROR_REJ_BAD_ADDR;
hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp);
}
}
@@ -1544,7 +1605,9 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, ev->status);
if (ev->status) {
- mgmt_disconnect_failed(hdev->id);
+ hci_dev_lock(hdev);
+ mgmt_disconnect_failed(hdev);
+ hci_dev_unlock(hdev);
return;
}
@@ -1557,7 +1620,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK || conn->type == LE_LINK)
- mgmt_disconnected(hdev->id, &conn->dst);
+ mgmt_disconnected(hdev, &conn->dst, conn->type);
hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
@@ -1588,7 +1651,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->sec_level = conn->pending_sec_level;
}
} else {
- mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
+ mgmt_auth_failed(hdev, &conn->dst, ev->status);
}
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
@@ -1643,7 +1706,7 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb
hci_dev_lock(hdev);
if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
- mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name);
+ mgmt_remote_name(hdev, &ev->bdaddr, ev->name);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (!conn)
@@ -1898,6 +1961,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_write_ca_timeout(hdev, skb);
break;
+ case HCI_OP_READ_LOCAL_AMP_INFO:
+ hci_cc_read_local_amp_info(hdev, skb);
+ break;
+
case HCI_OP_DELETE_STORED_LINK_KEY:
hci_cc_delete_stored_link_key(hdev, skb);
break;
@@ -2029,7 +2096,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
case HCI_OP_DISCONNECT:
if (ev->status != 0)
- mgmt_disconnect_failed(hdev->id);
+ mgmt_disconnect_failed(hdev);
break;
case HCI_OP_LE_CREATE_CONN:
@@ -2194,7 +2261,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
else
secure = 0;
- mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure);
+ mgmt_pin_code_request(hdev, &ev->bdaddr, secure);
}
unlock:
@@ -2363,12 +2430,6 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
hci_dev_lock(hdev);
- if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
-
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_discovering(hdev->id, 1);
- }
-
if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) {
struct inquiry_info_with_rssi_and_pscan_mode *info;
info = (void *) (skb->data + 1);
@@ -2383,7 +2444,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr,
+ mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
info->dev_class, info->rssi,
NULL);
}
@@ -2400,7 +2461,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr,
+ mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
info->dev_class, info->rssi,
NULL);
}
@@ -2531,12 +2592,6 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
if (!num_rsp)
return;
- if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
-
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_discovering(hdev->id, 1);
- }
-
hci_dev_lock(hdev);
for (; num_rsp; num_rsp--, info++) {
@@ -2549,8 +2604,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x01;
hci_inquiry_cache_update(hdev, &data);
- mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class,
- info->rssi, info->data);
+ mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
+ info->dev_class, info->rssi, info->data);
}
hci_dev_unlock(hdev);
@@ -2614,7 +2669,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
struct hci_cp_io_capability_neg_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
- cp.reason = 0x18; /* Pairing not allowed */
+ cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED;
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY,
sizeof(cp), &cp);
@@ -2706,7 +2761,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
}
confirm:
- mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey,
+ mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey,
confirm_hint);
unlock:
@@ -2732,7 +2787,7 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_
* event gets always produced as initiator and is also mapped to
* the mgmt_auth_failed event */
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0)
- mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
+ mgmt_auth_failed(hdev, &conn->dst, ev->status);
hci_conn_put(conn);
@@ -2813,14 +2868,14 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
}
if (ev->status) {
- mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
+ mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status);
hci_proto_connect_cfm(conn, ev->status);
conn->state = BT_CLOSED;
hci_conn_del(conn);
goto unlock;
}
- mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
+ mgmt_connected(hdev, &ev->bdaddr, conn->type);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
@@ -3104,5 +3159,5 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
kfree_skb(skb);
}
-module_param(enable_le, bool, 0444);
+module_param(enable_le, bool, 0644);
MODULE_PARM_DESC(enable_le, "Enable LE support");
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 661b461cf0b0..c62d254a1379 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -436,17 +436,12 @@ static const struct file_operations inquiry_cache_fops = {
static int blacklist_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
- struct list_head *l;
+ struct bdaddr_list *b;
hci_dev_lock_bh(hdev);
- list_for_each(l, &hdev->blacklist) {
- struct bdaddr_list *b;
-
- b = list_entry(l, struct bdaddr_list, list);
-
+ list_for_each_entry(b, &hdev->blacklist, list)
seq_printf(f, "%s\n", batostr(&b->bdaddr));
- }
hci_dev_unlock_bh(hdev);
@@ -485,17 +480,12 @@ static void print_bt_uuid(struct seq_file *f, u8 *uuid)
static int uuids_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
- struct list_head *l;
+ struct bt_uuid *uuid;
hci_dev_lock_bh(hdev);
- list_for_each(l, &hdev->uuids) {
- struct bt_uuid *uuid;
-
- uuid = list_entry(l, struct bt_uuid, list);
-
+ list_for_each_entry(uuid, &hdev->uuids, list)
print_bt_uuid(f, uuid->uuid);
- }
hci_dev_unlock_bh(hdev);
@@ -543,22 +533,28 @@ static int auto_accept_delay_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
auto_accept_delay_set, "%llu\n");
-int hci_register_sysfs(struct hci_dev *hdev)
+void hci_init_sysfs(struct hci_dev *hdev)
+{
+ struct device *dev = &hdev->dev;
+
+ dev->type = &bt_host;
+ dev->class = bt_class;
+
+ dev_set_drvdata(dev, hdev);
+ device_initialize(dev);
+}
+
+int hci_add_sysfs(struct hci_dev *hdev)
{
struct device *dev = &hdev->dev;
int err;
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
- dev->type = &bt_host;
- dev->class = bt_class;
dev->parent = hdev->parent;
-
dev_set_name(dev, "%s", hdev->name);
- dev_set_drvdata(dev, hdev);
-
- err = device_register(dev);
+ err = device_add(dev);
if (err < 0)
return err;
@@ -582,7 +578,7 @@ int hci_register_sysfs(struct hci_dev *hdev)
return 0;
}
-void hci_unregister_sysfs(struct hci_dev *hdev)
+void hci_del_sysfs(struct hci_dev *hdev)
{
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 075a3e920caf..3c2d888925d7 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -81,24 +81,20 @@ static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
{
struct hidp_session *session;
- struct list_head *p;
BT_DBG("");
- list_for_each(p, &hidp_session_list) {
- session = list_entry(p, struct hidp_session, list);
+ list_for_each_entry(session, &hidp_session_list, list) {
if (!bacmp(bdaddr, &session->bdaddr))
return session;
}
+
return NULL;
}
static void __hidp_link_session(struct hidp_session *session)
{
- __module_get(THIS_MODULE);
list_add(&session->list, &hidp_session_list);
-
- hci_conn_hold_device(session->conn);
}
static void __hidp_unlink_session(struct hidp_session *session)
@@ -106,7 +102,6 @@ static void __hidp_unlink_session(struct hidp_session *session)
hci_conn_put_device(session->conn);
list_del(&session->list);
- module_put(THIS_MODULE);
}
static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
@@ -255,6 +250,9 @@ static int __hidp_send_ctrl_message(struct hidp_session *session,
BT_DBG("session %p data %p size %d", session, data, size);
+ if (atomic_read(&session->terminate))
+ return -EIO;
+
skb = alloc_skb(size + 1, GFP_ATOMIC);
if (!skb) {
BT_ERR("Can't allocate memory for new frame");
@@ -329,6 +327,7 @@ static int hidp_get_raw_report(struct hid_device *hid,
struct sk_buff *skb;
size_t len;
int numbered_reports = hid->report_enum[report_type].numbered;
+ int ret;
switch (report_type) {
case HID_FEATURE_REPORT:
@@ -352,8 +351,9 @@ static int hidp_get_raw_report(struct hid_device *hid,
session->waiting_report_number = numbered_reports ? report_number : -1;
set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
data[0] = report_number;
- if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1))
- goto err_eio;
+ ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1);
+ if (ret)
+ goto err;
/* Wait for the return of the report. The returned report
gets put in session->report_return. */
@@ -365,11 +365,13 @@ static int hidp_get_raw_report(struct hid_device *hid,
5*HZ);
if (res == 0) {
/* timeout */
- goto err_eio;
+ ret = -EIO;
+ goto err;
}
if (res < 0) {
/* signal */
- goto err_restartsys;
+ ret = -ERESTARTSYS;
+ goto err;
}
}
@@ -390,14 +392,10 @@ static int hidp_get_raw_report(struct hid_device *hid,
return len;
-err_restartsys:
- clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
- mutex_unlock(&session->report_mutex);
- return -ERESTARTSYS;
-err_eio:
+err:
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
mutex_unlock(&session->report_mutex);
- return -EIO;
+ return ret;
}
static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
@@ -422,11 +420,10 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s
/* Set up our wait, and send the report request to the device. */
set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
- if (hidp_send_ctrl_message(hid->driver_data, report_type,
- data, count)) {
- ret = -ENOMEM;
+ ret = hidp_send_ctrl_message(hid->driver_data, report_type, data,
+ count);
+ if (ret)
goto err;
- }
/* Wait for the ACK from the device. */
while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
@@ -496,10 +493,9 @@ static void hidp_process_handshake(struct hidp_session *session,
case HIDP_HSHK_ERR_INVALID_REPORT_ID:
case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
case HIDP_HSHK_ERR_INVALID_PARAMETER:
- if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) {
- clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
+ if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags))
wake_up_interruptible(&session->report_queue);
- }
+
/* FIXME: Call into SET_ GET_ handlers here */
break;
@@ -520,10 +516,8 @@ static void hidp_process_handshake(struct hidp_session *session,
}
/* Wake up the waiting thread. */
- if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
- clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
+ if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags))
wake_up_interruptible(&session->report_queue);
- }
}
static void hidp_process_hid_control(struct hidp_session *session,
@@ -663,25 +657,32 @@ static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
return kernel_sendmsg(sock, &msg, &iv, 1, len);
}
-static void hidp_process_transmit(struct hidp_session *session)
+static void hidp_process_intr_transmit(struct hidp_session *session)
{
struct sk_buff *skb;
BT_DBG("session %p", session);
- while ((skb = skb_dequeue(&session->ctrl_transmit))) {
- if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
- skb_queue_head(&session->ctrl_transmit, skb);
+ while ((skb = skb_dequeue(&session->intr_transmit))) {
+ if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
+ skb_queue_head(&session->intr_transmit, skb);
break;
}
hidp_set_timer(session);
kfree_skb(skb);
}
+}
- while ((skb = skb_dequeue(&session->intr_transmit))) {
- if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
- skb_queue_head(&session->intr_transmit, skb);
+static void hidp_process_ctrl_transmit(struct hidp_session *session)
+{
+ struct sk_buff *skb;
+
+ BT_DBG("session %p", session);
+
+ while ((skb = skb_dequeue(&session->ctrl_transmit))) {
+ if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
+ skb_queue_head(&session->ctrl_transmit, skb);
break;
}
@@ -700,6 +701,7 @@ static int hidp_session(void *arg)
BT_DBG("session %p", session);
+ __module_get(THIS_MODULE);
set_user_nice(current, -15);
init_waitqueue_entry(&ctrl_wait, current);
@@ -714,23 +716,25 @@ static int hidp_session(void *arg)
intr_sk->sk_state != BT_CONNECTED)
break;
- while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
+ while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
skb_orphan(skb);
if (!skb_linearize(skb))
- hidp_recv_ctrl_frame(session, skb);
+ hidp_recv_intr_frame(session, skb);
else
kfree_skb(skb);
}
- while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
+ hidp_process_intr_transmit(session);
+
+ while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
skb_orphan(skb);
if (!skb_linearize(skb))
- hidp_recv_intr_frame(session, skb);
+ hidp_recv_ctrl_frame(session, skb);
else
kfree_skb(skb);
}
- hidp_process_transmit(session);
+ hidp_process_ctrl_transmit(session);
schedule();
set_current_state(TASK_INTERRUPTIBLE);
@@ -739,6 +743,10 @@ static int hidp_session(void *arg)
remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
+ clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
+ clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
+ wake_up_interruptible(&session->report_queue);
+
down_write(&hidp_session_sem);
hidp_del_timer(session);
@@ -772,34 +780,37 @@ static int hidp_session(void *arg)
kfree(session->rd_data);
kfree(session);
+ module_put_and_exit(0);
return 0;
}
-static struct device *hidp_get_device(struct hidp_session *session)
+static struct hci_conn *hidp_get_connection(struct hidp_session *session)
{
bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
- struct device *device = NULL;
+ struct hci_conn *conn;
struct hci_dev *hdev;
hdev = hci_get_route(dst, src);
if (!hdev)
return NULL;
- session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
- if (session->conn)
- device = &session->conn->dev;
+ hci_dev_lock_bh(hdev);
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
+ if (conn)
+ hci_conn_hold_device(conn);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
- return device;
+ return conn;
}
static int hidp_setup_input(struct hidp_session *session,
struct hidp_connadd_req *req)
{
struct input_dev *input;
- int err, i;
+ int i;
input = input_allocate_device();
if (!input)
@@ -842,17 +853,10 @@ static int hidp_setup_input(struct hidp_session *session,
input->relbit[0] |= BIT_MASK(REL_WHEEL);
}
- input->dev.parent = hidp_get_device(session);
+ input->dev.parent = &session->conn->dev;
input->event = hidp_input_event;
- err = input_register_device(input);
- if (err < 0) {
- input_free_device(input);
- session->input = NULL;
- return err;
- }
-
return 0;
}
@@ -949,7 +953,7 @@ static int hidp_setup_hid(struct hidp_session *session,
strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
- hid->dev.parent = hidp_get_device(session);
+ hid->dev.parent = &session->conn->dev;
hid->ll_driver = &hidp_hid_driver;
hid->hid_get_raw_report = hidp_get_raw_report;
@@ -976,18 +980,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
return -ENOTUNIQ;
- session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
- if (!session)
- return -ENOMEM;
-
BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
down_write(&hidp_session_sem);
s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
if (s && s->state == BT_CONNECTED) {
- err = -EEXIST;
- goto failed;
+ up_write(&hidp_session_sem);
+ return -EEXIST;
+ }
+
+ session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
+ if (!session) {
+ up_write(&hidp_session_sem);
+ return -ENOMEM;
}
bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
@@ -1003,6 +1009,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
session->intr_sock = intr_sock;
session->state = BT_CONNECTED;
+ session->conn = hidp_get_connection(session);
+ if (!session->conn) {
+ err = -ENOTCONN;
+ goto failed;
+ }
+
setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
skb_queue_head_init(&session->ctrl_transmit);
@@ -1015,9 +1027,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
session->idle_to = req->idle_to;
+ __hidp_link_session(session);
+
if (req->rd_size > 0) {
err = hidp_setup_hid(session, req);
- if (err && err != -ENODEV)
+ if (err)
goto purge;
}
@@ -1027,8 +1041,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
goto purge;
}
- __hidp_link_session(session);
-
hidp_set_timer(session);
if (session->hid) {
@@ -1054,7 +1066,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
!session->waiting_for_startup);
}
- err = hid_add_device(session->hid);
+ if (session->hid)
+ err = hid_add_device(session->hid);
+ else
+ err = input_register_device(session->input);
+
if (err < 0) {
atomic_inc(&session->terminate);
wake_up_process(session->task);
@@ -1077,8 +1093,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
unlink:
hidp_del_timer(session);
- __hidp_unlink_session(session);
-
if (session->input) {
input_unregister_device(session->input);
session->input = NULL;
@@ -1093,6 +1107,8 @@ unlink:
session->rd_data = NULL;
purge:
+ __hidp_unlink_session(session);
+
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
@@ -1134,19 +1150,16 @@ int hidp_del_connection(struct hidp_conndel_req *req)
int hidp_get_connlist(struct hidp_connlist_req *req)
{
- struct list_head *p;
+ struct hidp_session *session;
int err = 0, n = 0;
BT_DBG("");
down_read(&hidp_session_sem);
- list_for_each(p, &hidp_session_list) {
- struct hidp_session *session;
+ list_for_each_entry(session, &hidp_session_list, list) {
struct hidp_conninfo ci;
- session = list_entry(p, struct hidp_session, list);
-
__hidp_copy_session(session, &ci);
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 8cd12917733b..e8a6837996cf 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -57,9 +57,10 @@
#include <net/bluetooth/smp.h>
int disable_ertm;
+int enable_hs;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
-static u8 l2cap_fixed_chan[8] = { 0x02, };
+static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
@@ -219,7 +220,7 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout)
{
- BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout);
+ BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout);
if (!mod_timer(timer, jiffies + msecs_to_jiffies(timeout)))
chan_hold(chan);
@@ -251,7 +252,7 @@ static void l2cap_chan_timeout(unsigned long arg)
if (sock_owned_by_user(sk)) {
/* sk is owned by user. Try again later */
- __set_chan_timer(chan, HZ / 5);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
chan_put(chan);
return;
@@ -293,6 +294,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
atomic_set(&chan->refcnt, 1);
+ BT_DBG("sk %p chan %p", sk, chan);
+
return chan;
}
@@ -310,7 +313,7 @@ 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,
chan->psm, chan->dcid);
- conn->disc_reason = 0x13;
+ conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
chan->conn = conn;
@@ -337,6 +340,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->omtu = L2CAP_DEFAULT_MTU;
}
+ chan->local_id = L2CAP_BESTEFFORT_ID;
+ chan->local_stype = L2CAP_SERV_BESTEFFORT;
+ chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE;
+ chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME;
+ chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT;
+ chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO;
+
chan_hold(chan);
list_add(&chan->list, &conn->chan_l);
@@ -556,34 +566,58 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
flags = ACL_START;
bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON;
+ skb->priority = HCI_PRIO_MAX;
+
+ hci_send_acl(conn->hchan, skb, flags);
+}
+
+static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+ struct hci_conn *hcon = chan->conn->hcon;
+ u16 flags;
+
+ BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
+ skb->priority);
+
+ if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
+ lmp_no_flush_capable(hcon->hdev))
+ flags = ACL_START_NO_FLUSH;
+ else
+ flags = ACL_START;
- hci_send_acl(conn->hcon, skb, flags);
+ bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
+ hci_send_acl(chan->conn->hchan, skb, flags);
}
-static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
+static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control)
{
struct sk_buff *skb;
struct l2cap_hdr *lh;
struct l2cap_conn *conn = chan->conn;
- int count, hlen = L2CAP_HDR_SIZE + 2;
- u8 flags;
+ int count, hlen;
if (chan->state != BT_CONNECTED)
return;
+ if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+ hlen = L2CAP_EXT_HDR_SIZE;
+ else
+ hlen = L2CAP_ENH_HDR_SIZE;
+
if (chan->fcs == L2CAP_FCS_CRC16)
- hlen += 2;
+ hlen += L2CAP_FCS_SIZE;
- BT_DBG("chan %p, control 0x%2.2x", chan, control);
+ BT_DBG("chan %p, control 0x%8.8x", chan, control);
count = min_t(unsigned int, conn->mtu, hlen);
- control |= L2CAP_CTRL_FRAME_TYPE;
+
+ control |= __set_sframe(chan);
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
- control |= L2CAP_CTRL_FINAL;
+ control |= __set_ctrl_final(chan);
if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
- control |= L2CAP_CTRL_POLL;
+ control |= __set_ctrl_poll(chan);
skb = bt_skb_alloc(count, GFP_ATOMIC);
if (!skb)
@@ -592,32 +626,27 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control)
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
- put_unaligned_le16(control, skb_put(skb, 2));
+
+ __put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
if (chan->fcs == L2CAP_FCS_CRC16) {
- u16 fcs = crc16(0, (u8 *)lh, count - 2);
- put_unaligned_le16(fcs, skb_put(skb, 2));
+ u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
}
- if (lmp_no_flush_capable(conn->hcon->hdev))
- flags = ACL_START_NO_FLUSH;
- else
- flags = ACL_START;
-
- bt_cb(skb)->force_active = chan->force_active;
-
- hci_send_acl(chan->conn->hcon, skb, flags);
+ skb->priority = HCI_PRIO_MAX;
+ l2cap_do_send(chan, skb);
}
-static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
+static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control)
{
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
- control |= L2CAP_SUPER_RCV_NOT_READY;
+ control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
set_bit(CONN_RNR_SENT, &chan->conn_state);
} else
- control |= L2CAP_SUPER_RCV_READY;
+ control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
- control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= __set_reqseq(chan, chan->buffer_seq);
l2cap_send_sframe(chan, control);
}
@@ -947,7 +976,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;
- if (chan->force_reliable)
+ if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
sk->sk_err = err;
}
@@ -986,6 +1015,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
chan->ops->close(chan->data);
}
+ hci_chan_del(conn->hchan);
+
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
del_timer_sync(&conn->info_timer);
@@ -1008,18 +1039,26 @@ static void security_timeout(unsigned long arg)
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
{
struct l2cap_conn *conn = hcon->l2cap_data;
+ struct hci_chan *hchan;
if (conn || status)
return conn;
+ hchan = hci_chan_create(hcon);
+ if (!hchan)
+ return NULL;
+
conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC);
- if (!conn)
+ if (!conn) {
+ hci_chan_del(hchan);
return NULL;
+ }
hcon->l2cap_data = conn;
conn->hcon = hcon;
+ conn->hchan = hchan;
- BT_DBG("hcon %p conn %p", hcon, conn);
+ BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
if (hcon->hdev->le_mtu && hcon->type == LE_LINK)
conn->mtu = hcon->hdev->le_mtu;
@@ -1043,7 +1082,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
setup_timer(&conn->info_timer, l2cap_info_timeout,
(unsigned long) conn);
- conn->disc_reason = 0x13;
+ conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
return conn;
}
@@ -1245,47 +1284,35 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
__clear_retrans_timer(chan);
}
-static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
-{
- struct hci_conn *hcon = chan->conn->hcon;
- u16 flags;
-
- BT_DBG("chan %p, skb %p len %d", chan, skb, skb->len);
-
- if (!chan->flushable && lmp_no_flush_capable(hcon->hdev))
- flags = ACL_START_NO_FLUSH;
- else
- flags = ACL_START;
-
- bt_cb(skb)->force_active = chan->force_active;
- hci_send_acl(hcon, skb, flags);
-}
-
static void l2cap_streaming_send(struct l2cap_chan *chan)
{
struct sk_buff *skb;
- u16 control, fcs;
+ u32 control;
+ u16 fcs;
while ((skb = skb_dequeue(&chan->tx_q))) {
- control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE);
- control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT;
- put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE);
+ control = __get_control(chan, skb->data + L2CAP_HDR_SIZE);
+ control |= __set_txseq(chan, chan->next_tx_seq);
+ __put_control(chan, control, skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
- fcs = crc16(0, (u8 *)skb->data, skb->len - 2);
- put_unaligned_le16(fcs, skb->data + skb->len - 2);
+ fcs = crc16(0, (u8 *)skb->data,
+ skb->len - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs,
+ skb->data + skb->len - L2CAP_FCS_SIZE);
}
l2cap_do_send(chan, skb);
- chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
+ chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
}
}
-static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
{
struct sk_buff *skb, *tx_skb;
- u16 control, fcs;
+ u16 fcs;
+ u32 control;
skb = skb_peek(&chan->tx_q);
if (!skb)
@@ -1308,20 +1335,23 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
tx_skb = skb_clone(skb, GFP_ATOMIC);
bt_cb(skb)->retries++;
- control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
- control &= L2CAP_CTRL_SAR;
+
+ control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
+ control &= __get_sar_mask(chan);
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
- control |= L2CAP_CTRL_FINAL;
+ control |= __set_ctrl_final(chan);
- control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
- | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
+ control |= __set_reqseq(chan, chan->buffer_seq);
+ control |= __set_txseq(chan, tx_seq);
- put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+ __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
- fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2);
- put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2);
+ fcs = crc16(0, (u8 *)tx_skb->data,
+ tx_skb->len - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs,
+ tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE);
}
l2cap_do_send(chan, tx_skb);
@@ -1330,7 +1360,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq)
static int l2cap_ertm_send(struct l2cap_chan *chan)
{
struct sk_buff *skb, *tx_skb;
- u16 control, fcs;
+ u16 fcs;
+ u32 control;
int nsent = 0;
if (chan->state != BT_CONNECTED)
@@ -1348,20 +1379,22 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
bt_cb(skb)->retries++;
- control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
- control &= L2CAP_CTRL_SAR;
+ control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE);
+ control &= __get_sar_mask(chan);
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
- control |= L2CAP_CTRL_FINAL;
+ control |= __set_ctrl_final(chan);
- control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
- | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
- put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+ control |= __set_reqseq(chan, chan->buffer_seq);
+ control |= __set_txseq(chan, chan->next_tx_seq);
+ __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE);
if (chan->fcs == L2CAP_FCS_CRC16) {
- fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2);
- put_unaligned_le16(fcs, skb->data + tx_skb->len - 2);
+ fcs = crc16(0, (u8 *)skb->data,
+ tx_skb->len - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs, skb->data +
+ tx_skb->len - L2CAP_FCS_SIZE);
}
l2cap_do_send(chan, tx_skb);
@@ -1369,7 +1402,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
__set_retrans_timer(chan);
bt_cb(skb)->tx_seq = chan->next_tx_seq;
- chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
+
+ chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
if (bt_cb(skb)->retries == 1)
chan->unacked_frames++;
@@ -1401,12 +1435,12 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan)
static void l2cap_send_ack(struct l2cap_chan *chan)
{
- u16 control = 0;
+ u32 control = 0;
- control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= __set_reqseq(chan, chan->buffer_seq);
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
- control |= L2CAP_SUPER_RCV_NOT_READY;
+ control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
set_bit(CONN_RNR_SENT, &chan->conn_state);
l2cap_send_sframe(chan, control);
return;
@@ -1415,20 +1449,20 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
if (l2cap_ertm_send(chan) > 0)
return;
- control |= L2CAP_SUPER_RCV_READY;
+ control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
l2cap_send_sframe(chan, control);
}
static void l2cap_send_srejtail(struct l2cap_chan *chan)
{
struct srej_list *tail;
- u16 control;
+ u32 control;
- control = L2CAP_SUPER_SELECT_REJECT;
- control |= L2CAP_CTRL_FINAL;
+ control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
+ control |= __set_ctrl_final(chan);
tail = list_entry((&chan->srej_l)->prev, struct srej_list, list);
- control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= __set_reqseq(chan, tail->tx_seq);
l2cap_send_sframe(chan, control);
}
@@ -1456,6 +1490,8 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
return -EFAULT;
+ (*frag)->priority = skb->priority;
+
sent += count;
len -= count;
@@ -1465,15 +1501,17 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
return sent;
}
-static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
+ struct msghdr *msg, size_t len,
+ u32 priority)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
- int err, count, hlen = L2CAP_HDR_SIZE + 2;
+ int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
struct l2cap_hdr *lh;
- BT_DBG("sk %p len %d", sk, (int)len);
+ BT_DBG("sk %p len %d priority %u", sk, (int)len, priority);
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = bt_skb_send_alloc(sk, count + hlen,
@@ -1481,6 +1519,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct
if (!skb)
return ERR_PTR(err);
+ skb->priority = priority;
+
/* Create L2CAP header */
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
@@ -1495,7 +1535,9 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct
return skb;
}
-static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
+ struct msghdr *msg, size_t len,
+ u32 priority)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
@@ -1511,6 +1553,8 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms
if (!skb)
return ERR_PTR(err);
+ skb->priority = priority;
+
/* Create L2CAP header */
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
@@ -1526,12 +1570,12 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms
static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
- u16 control, u16 sdulen)
+ u32 control, u16 sdulen)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
- int err, count, hlen = L2CAP_HDR_SIZE + 2;
+ int err, count, hlen;
struct l2cap_hdr *lh;
BT_DBG("sk %p len %d", sk, (int)len);
@@ -1539,11 +1583,16 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
if (!conn)
return ERR_PTR(-ENOTCONN);
+ if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+ hlen = L2CAP_EXT_HDR_SIZE;
+ else
+ hlen = L2CAP_ENH_HDR_SIZE;
+
if (sdulen)
- hlen += 2;
+ hlen += L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
- hlen += 2;
+ hlen += L2CAP_FCS_SIZE;
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = bt_skb_send_alloc(sk, count + hlen,
@@ -1555,9 +1604,11 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
- put_unaligned_le16(control, skb_put(skb, 2));
+
+ __put_control(chan, control, skb_put(skb, __ctrl_size(chan)));
+
if (sdulen)
- put_unaligned_le16(sdulen, skb_put(skb, 2));
+ put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
if (unlikely(err < 0)) {
@@ -1566,7 +1617,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
}
if (chan->fcs == L2CAP_FCS_CRC16)
- put_unaligned_le16(0, skb_put(skb, 2));
+ put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE));
bt_cb(skb)->retries = 0;
return skb;
@@ -1576,11 +1627,11 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
{
struct sk_buff *skb;
struct sk_buff_head sar_queue;
- u16 control;
+ u32 control;
size_t size = 0;
skb_queue_head_init(&sar_queue);
- control = L2CAP_SDU_START;
+ control = __set_ctrl_sar(chan, L2CAP_SAR_START);
skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -1593,10 +1644,10 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
size_t buflen;
if (len > chan->remote_mps) {
- control = L2CAP_SDU_CONTINUE;
+ control = __set_ctrl_sar(chan, L2CAP_SAR_CONTINUE);
buflen = chan->remote_mps;
} else {
- control = L2CAP_SDU_END;
+ control = __set_ctrl_sar(chan, L2CAP_SAR_END);
buflen = len;
}
@@ -1617,15 +1668,16 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si
return size;
}
-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)
{
struct sk_buff *skb;
- u16 control;
+ u32 control;
int err;
/* Connectionless channel */
if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
- skb = l2cap_create_connless_pdu(chan, msg, len);
+ skb = l2cap_create_connless_pdu(chan, msg, len, priority);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -1640,7 +1692,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
return -EMSGSIZE;
/* Create a basic PDU */
- skb = l2cap_create_basic_pdu(chan, msg, len);
+ skb = l2cap_create_basic_pdu(chan, msg, len, priority);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -1652,7 +1704,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
case L2CAP_MODE_STREAMING:
/* Entire SDU fits into one PDU */
if (len <= chan->remote_mps) {
- control = L2CAP_SDU_UNSEGMENTED;
+ control = __set_ctrl_sar(chan, L2CAP_SAR_UNSEGMENTED);
skb = l2cap_create_iframe_pdu(chan, msg, len, control,
0);
if (IS_ERR(skb))
@@ -1850,6 +1902,37 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
*ptr += L2CAP_CONF_OPT_SIZE + len;
}
+static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
+{
+ struct l2cap_conf_efs efs;
+
+ switch(chan->mode) {
+ case L2CAP_MODE_ERTM:
+ efs.id = chan->local_id;
+ efs.stype = chan->local_stype;
+ efs.msdu = cpu_to_le16(chan->local_msdu);
+ efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
+ efs.acc_lat = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT);
+ efs.flush_to = cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO);
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ efs.id = 1;
+ efs.stype = L2CAP_SERV_BESTEFFORT;
+ efs.msdu = cpu_to_le16(chan->local_msdu);
+ efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
+ efs.acc_lat = 0;
+ efs.flush_to = 0;
+ break;
+
+ default:
+ return;
+ }
+
+ l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs),
+ (unsigned long) &efs);
+}
+
static void l2cap_ack_timeout(unsigned long arg)
{
struct l2cap_chan *chan = (void *) arg;
@@ -1896,11 +1979,36 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
}
}
+static inline bool __l2cap_ews_supported(struct l2cap_chan *chan)
+{
+ return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+}
+
+static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
+{
+ return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
+}
+
+static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
+{
+ if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
+ __l2cap_ews_supported(chan)) {
+ /* use extended control field */
+ set_bit(FLAG_EXT_CTRL, &chan->flags);
+ chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
+ } else {
+ chan->tx_win = min_t(u16, chan->tx_win,
+ L2CAP_DEFAULT_TX_WINDOW);
+ chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
+ }
+}
+
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
{
struct l2cap_conf_req *req = data;
struct l2cap_conf_rfc rfc = { .mode = chan->mode };
void *ptr = req->data;
+ u16 size;
BT_DBG("chan %p", chan);
@@ -1913,6 +2021,9 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
break;
+ if (__l2cap_efs_supported(chan))
+ set_bit(FLAG_EFS_ENABLE, &chan->flags);
+
/* fall through */
default:
chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask);
@@ -1942,17 +2053,27 @@ done:
case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM;
- rfc.txwin_size = chan->tx_win;
rfc.max_transmit = chan->max_tx;
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0;
- rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
- if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10)
- rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+
+ size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
+ L2CAP_EXT_HDR_SIZE -
+ L2CAP_SDULEN_SIZE -
+ L2CAP_FCS_SIZE);
+ rfc.max_pdu_size = cpu_to_le16(size);
+
+ l2cap_txwin_setup(chan);
+
+ rfc.txwin_size = min_t(u16, chan->tx_win,
+ L2CAP_DEFAULT_TX_WINDOW);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc);
+ if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
+ l2cap_add_opt_efs(&ptr, chan);
+
if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
break;
@@ -1961,6 +2082,10 @@ done:
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
+
+ if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
+ chan->tx_win);
break;
case L2CAP_MODE_STREAMING:
@@ -1969,13 +2094,19 @@ done:
rfc.max_transmit = 0;
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0;
- rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
- if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10)
- rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+
+ size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
+ L2CAP_EXT_HDR_SIZE -
+ L2CAP_SDULEN_SIZE -
+ L2CAP_FCS_SIZE);
+ rfc.max_pdu_size = cpu_to_le16(size);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
(unsigned long) &rfc);
+ if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
+ l2cap_add_opt_efs(&ptr, chan);
+
if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
break;
@@ -2002,8 +2133,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
int type, hint, olen;
unsigned long val;
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
+ struct l2cap_conf_efs efs;
+ u8 remote_efs = 0;
u16 mtu = L2CAP_DEFAULT_MTU;
u16 result = L2CAP_CONF_SUCCESS;
+ u16 size;
BT_DBG("chan %p", chan);
@@ -2033,7 +2167,22 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
case L2CAP_CONF_FCS:
if (val == L2CAP_FCS_NONE)
set_bit(CONF_NO_FCS_RECV, &chan->conf_state);
+ break;
+
+ case L2CAP_CONF_EFS:
+ remote_efs = 1;
+ if (olen == sizeof(efs))
+ memcpy(&efs, (void *) val, olen);
+ break;
+
+ case L2CAP_CONF_EWS:
+ if (!enable_hs)
+ return -ECONNREFUSED;
+ set_bit(FLAG_EXT_CTRL, &chan->flags);
+ set_bit(CONF_EWS_RECV, &chan->conf_state);
+ chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
+ chan->remote_tx_win = val;
break;
default:
@@ -2058,6 +2207,13 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
break;
}
+ if (remote_efs) {
+ if (__l2cap_efs_supported(chan))
+ set_bit(FLAG_EFS_ENABLE, &chan->flags);
+ else
+ return -ECONNREFUSED;
+ }
+
if (chan->mode != rfc.mode)
return -ECONNREFUSED;
@@ -2076,7 +2232,6 @@ done:
sizeof(rfc), (unsigned long) &rfc);
}
-
if (result == L2CAP_CONF_SUCCESS) {
/* Configure output options and let the other side know
* which ones we don't like. */
@@ -2089,6 +2244,26 @@ done:
}
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
+ if (remote_efs) {
+ if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != chan->local_stype) {
+
+ result = L2CAP_CONF_UNACCEPT;
+
+ if (chan->num_conf_req >= 1)
+ return -ECONNREFUSED;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
+ sizeof(efs),
+ (unsigned long) &efs);
+ } else {
+ /* Send PENDING Conf Rsp */
+ result = L2CAP_CONF_PENDING;
+ set_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
+ }
+ }
+
switch (rfc.mode) {
case L2CAP_MODE_BASIC:
chan->fcs = L2CAP_FCS_NONE;
@@ -2096,13 +2271,20 @@ done:
break;
case L2CAP_MODE_ERTM:
- chan->remote_tx_win = rfc.txwin_size;
- chan->remote_max_tx = rfc.max_transmit;
+ if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
+ chan->remote_tx_win = rfc.txwin_size;
+ else
+ rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
- if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10)
- rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
+ chan->remote_max_tx = rfc.max_transmit;
- chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
+ size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
+ chan->conn->mtu -
+ L2CAP_EXT_HDR_SIZE -
+ L2CAP_SDULEN_SIZE -
+ L2CAP_FCS_SIZE);
+ rfc.max_pdu_size = cpu_to_le16(size);
+ chan->remote_mps = size;
rfc.retrans_timeout =
le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO);
@@ -2114,13 +2296,29 @@ done:
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
+ if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
+ chan->remote_id = efs.id;
+ chan->remote_stype = efs.stype;
+ chan->remote_msdu = le16_to_cpu(efs.msdu);
+ chan->remote_flush_to =
+ le32_to_cpu(efs.flush_to);
+ chan->remote_acc_lat =
+ le32_to_cpu(efs.acc_lat);
+ chan->remote_sdu_itime =
+ le32_to_cpu(efs.sdu_itime);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
+ sizeof(efs), (unsigned long) &efs);
+ }
break;
case L2CAP_MODE_STREAMING:
- if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10)
- rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10);
-
- chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
+ size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
+ chan->conn->mtu -
+ L2CAP_EXT_HDR_SIZE -
+ L2CAP_SDULEN_SIZE -
+ L2CAP_FCS_SIZE);
+ rfc.max_pdu_size = cpu_to_le16(size);
+ chan->remote_mps = size;
set_bit(CONF_MODE_DONE, &chan->conf_state);
@@ -2153,6 +2351,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
int type, olen;
unsigned long val;
struct l2cap_conf_rfc rfc;
+ struct l2cap_conf_efs efs;
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
@@ -2188,6 +2387,26 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
break;
+
+ case L2CAP_CONF_EWS:
+ chan->tx_win = min_t(u16, val,
+ L2CAP_DEFAULT_EXT_WINDOW);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
+ chan->tx_win);
+ break;
+
+ case L2CAP_CONF_EFS:
+ if (olen == sizeof(efs))
+ memcpy(&efs, (void *)val, olen);
+
+ if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != chan->local_stype)
+ return -ECONNREFUSED;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
+ sizeof(efs), (unsigned long) &efs);
+ break;
}
}
@@ -2196,13 +2415,23 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
chan->mode = rfc.mode;
- if (*result == L2CAP_CONF_SUCCESS) {
+ if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) {
switch (rfc.mode) {
case L2CAP_MODE_ERTM:
chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
chan->mps = le16_to_cpu(rfc.max_pdu_size);
+
+ if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
+ chan->local_msdu = le16_to_cpu(efs.msdu);
+ chan->local_sdu_itime =
+ le32_to_cpu(efs.sdu_itime);
+ chan->local_acc_lat = le32_to_cpu(efs.acc_lat);
+ chan->local_flush_to =
+ le32_to_cpu(efs.flush_to);
+ }
break;
+
case L2CAP_MODE_STREAMING:
chan->mps = le16_to_cpu(rfc.max_pdu_size);
}
@@ -2330,7 +2559,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
/* Check if the ACL is secure enough (if not SDP) */
if (psm != cpu_to_le16(0x0001) &&
!hci_conn_check_link_mode(conn->hcon)) {
- conn->disc_reason = 0x05;
+ conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
result = L2CAP_CR_SEC_BLOCK;
goto response;
}
@@ -2488,7 +2717,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ / 5);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
break;
}
@@ -2602,6 +2831,21 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
chan->num_conf_req++;
}
+ /* Got Conf Rsp PENDING from remote side and asume we sent
+ Conf Rsp PENDING in the code above */
+ if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
+ test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
+
+ /* check compatibility */
+
+ clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
+ set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
+
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
+ l2cap_build_conf_rsp(chan, rsp,
+ L2CAP_CONF_SUCCESS, 0x0000), rsp);
+ }
+
unlock:
bh_unlock_sock(sk);
return 0;
@@ -2631,8 +2875,33 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
switch (result) {
case L2CAP_CONF_SUCCESS:
l2cap_conf_rfc_get(chan, rsp->data, len);
+ clear_bit(CONF_REM_CONF_PEND, &chan->conf_state);
break;
+ case L2CAP_CONF_PENDING:
+ set_bit(CONF_REM_CONF_PEND, &chan->conf_state);
+
+ if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
+ char buf[64];
+
+ len = l2cap_parse_conf_rsp(chan, rsp->data, len,
+ buf, &result);
+ if (len < 0) {
+ l2cap_send_disconn_req(conn, chan, ECONNRESET);
+ goto done;
+ }
+
+ /* check compatibility */
+
+ clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
+ set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
+
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
+ l2cap_build_conf_rsp(chan, buf,
+ L2CAP_CONF_SUCCESS, 0x0000), buf);
+ }
+ goto done;
+
case L2CAP_CONF_UNACCEPT:
if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
char req[64];
@@ -2661,7 +2930,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
default:
sk->sk_err = ECONNRESET;
- __set_chan_timer(chan, HZ * 5);
+ __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
}
@@ -2718,7 +2987,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ / 5);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
return 0;
}
@@ -2752,7 +3021,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan,BT_DISCONN);
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ / 5);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
return 0;
}
@@ -2782,15 +3051,25 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
if (!disable_ertm)
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
| L2CAP_FEAT_FCS;
+ if (enable_hs)
+ feat_mask |= L2CAP_FEAT_EXT_FLOW
+ | L2CAP_FEAT_EXT_WINDOW;
+
put_unaligned_le32(feat_mask, rsp->data);
l2cap_send_cmd(conn, cmd->ident,
L2CAP_INFO_RSP, sizeof(buf), buf);
} else if (type == L2CAP_IT_FIXED_CHAN) {
u8 buf[12];
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
+
+ if (enable_hs)
+ l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
+ else
+ l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
+
rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
- memcpy(buf + 4, l2cap_fixed_chan, 8);
+ memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan));
l2cap_send_cmd(conn, cmd->ident,
L2CAP_INFO_RSP, sizeof(buf), buf);
} else {
@@ -2857,6 +3136,165 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
return 0;
}
+static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ void *data)
+{
+ struct l2cap_create_chan_req *req = data;
+ struct l2cap_create_chan_rsp rsp;
+ u16 psm, scid;
+
+ if (cmd_len != sizeof(*req))
+ return -EPROTO;
+
+ if (!enable_hs)
+ return -EINVAL;
+
+ psm = le16_to_cpu(req->psm);
+ scid = le16_to_cpu(req->scid);
+
+ BT_DBG("psm %d, scid %d, amp_id %d", psm, scid, req->amp_id);
+
+ /* Placeholder: Always reject */
+ rsp.dcid = 0;
+ rsp.scid = cpu_to_le16(scid);
+ rsp.result = L2CAP_CR_NO_MEM;
+ rsp.status = L2CAP_CS_NO_INFO;
+
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
+ sizeof(rsp), &rsp);
+
+ return 0;
+}
+
+static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, void *data)
+{
+ BT_DBG("conn %p", conn);
+
+ return l2cap_connect_rsp(conn, cmd, data);
+}
+
+static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
+ u16 icid, u16 result)
+{
+ struct l2cap_move_chan_rsp rsp;
+
+ BT_DBG("icid %d, result %d", icid, result);
+
+ rsp.icid = cpu_to_le16(icid);
+ rsp.result = cpu_to_le16(result);
+
+ l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp);
+}
+
+static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn,
+ struct l2cap_chan *chan, u16 icid, u16 result)
+{
+ struct l2cap_move_chan_cfm cfm;
+ u8 ident;
+
+ BT_DBG("icid %d, result %d", icid, result);
+
+ ident = l2cap_get_ident(conn);
+ if (chan)
+ chan->ident = ident;
+
+ cfm.icid = cpu_to_le16(icid);
+ cfm.result = cpu_to_le16(result);
+
+ l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm);
+}
+
+static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
+ u16 icid)
+{
+ struct l2cap_move_chan_cfm_rsp rsp;
+
+ BT_DBG("icid %d", icid);
+
+ rsp.icid = cpu_to_le16(icid);
+ l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
+}
+
+static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data)
+{
+ struct l2cap_move_chan_req *req = data;
+ u16 icid = 0;
+ u16 result = L2CAP_MR_NOT_ALLOWED;
+
+ if (cmd_len != sizeof(*req))
+ return -EPROTO;
+
+ icid = le16_to_cpu(req->icid);
+
+ BT_DBG("icid %d, dest_amp_id %d", icid, req->dest_amp_id);
+
+ if (!enable_hs)
+ return -EINVAL;
+
+ /* Placeholder: Always refuse */
+ l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
+
+ return 0;
+}
+
+static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data)
+{
+ struct l2cap_move_chan_rsp *rsp = data;
+ u16 icid, result;
+
+ if (cmd_len != sizeof(*rsp))
+ return -EPROTO;
+
+ icid = le16_to_cpu(rsp->icid);
+ result = le16_to_cpu(rsp->result);
+
+ BT_DBG("icid %d, result %d", icid, result);
+
+ /* Placeholder: Always unconfirmed */
+ l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED);
+
+ return 0;
+}
+
+static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data)
+{
+ struct l2cap_move_chan_cfm *cfm = data;
+ u16 icid, result;
+
+ if (cmd_len != sizeof(*cfm))
+ return -EPROTO;
+
+ icid = le16_to_cpu(cfm->icid);
+ result = le16_to_cpu(cfm->result);
+
+ BT_DBG("icid %d, result %d", icid, result);
+
+ l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
+
+ return 0;
+}
+
+static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data)
+{
+ struct l2cap_move_chan_cfm_rsp *rsp = data;
+ u16 icid;
+
+ if (cmd_len != sizeof(*rsp))
+ return -EPROTO;
+
+ icid = le16_to_cpu(rsp->icid);
+
+ BT_DBG("icid %d", icid);
+
+ return 0;
+}
+
static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
u16 to_multiplier)
{
@@ -2969,6 +3407,30 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
err = l2cap_information_rsp(conn, cmd, data);
break;
+ case L2CAP_CREATE_CHAN_REQ:
+ err = l2cap_create_channel_req(conn, cmd, cmd_len, data);
+ break;
+
+ case L2CAP_CREATE_CHAN_RSP:
+ err = l2cap_create_channel_rsp(conn, cmd, data);
+ break;
+
+ case L2CAP_MOVE_CHAN_REQ:
+ err = l2cap_move_channel_req(conn, cmd, cmd_len, data);
+ break;
+
+ case L2CAP_MOVE_CHAN_RSP:
+ err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
+ break;
+
+ case L2CAP_MOVE_CHAN_CFM:
+ err = l2cap_move_channel_confirm(conn, cmd, cmd_len, data);
+ break;
+
+ case L2CAP_MOVE_CHAN_CFM_RSP:
+ err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
+ break;
+
default:
BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code);
err = -EINVAL;
@@ -3047,10 +3509,15 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
{
u16 our_fcs, rcv_fcs;
- int hdr_size = L2CAP_HDR_SIZE + 2;
+ int hdr_size;
+
+ if (test_bit(FLAG_EXT_CTRL, &chan->flags))
+ hdr_size = L2CAP_EXT_HDR_SIZE;
+ else
+ hdr_size = L2CAP_ENH_HDR_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16) {
- skb_trim(skb, skb->len - 2);
+ skb_trim(skb, skb->len - L2CAP_FCS_SIZE);
rcv_fcs = get_unaligned_le16(skb->data + skb->len);
our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size);
@@ -3062,14 +3529,14 @@ static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
{
- u16 control = 0;
+ u32 control = 0;
chan->frames_sent = 0;
- control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control |= __set_reqseq(chan, chan->buffer_seq);
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
- control |= L2CAP_SUPER_RCV_NOT_READY;
+ control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
l2cap_send_sframe(chan, control);
set_bit(CONN_RNR_SENT, &chan->conn_state);
}
@@ -3081,12 +3548,12 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
chan->frames_sent == 0) {
- control |= L2CAP_SUPER_RCV_READY;
+ control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
l2cap_send_sframe(chan, control);
}
}
-static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u8 tx_seq, u8 sar)
+static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar)
{
struct sk_buff *next_skb;
int tx_seq_offset, next_tx_seq_offset;
@@ -3100,18 +3567,14 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
return 0;
}
- tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
- if (tx_seq_offset < 0)
- tx_seq_offset += 64;
+ tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
do {
if (bt_cb(next_skb)->tx_seq == tx_seq)
return -EINVAL;
- next_tx_seq_offset = (bt_cb(next_skb)->tx_seq -
- chan->buffer_seq) % 64;
- if (next_tx_seq_offset < 0)
- next_tx_seq_offset += 64;
+ next_tx_seq_offset = __seq_offset(chan,
+ bt_cb(next_skb)->tx_seq, chan->buffer_seq);
if (next_tx_seq_offset > tx_seq_offset) {
__skb_queue_before(&chan->srej_q, next_skb, skb);
@@ -3147,24 +3610,24 @@ static void append_skb_frag(struct sk_buff *skb,
skb->truesize += new_frag->truesize;
}
-static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
+static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control)
{
int err = -EINVAL;
- switch (control & L2CAP_CTRL_SAR) {
- case L2CAP_SDU_UNSEGMENTED:
+ switch (__get_ctrl_sar(chan, control)) {
+ case L2CAP_SAR_UNSEGMENTED:
if (chan->sdu)
break;
err = chan->ops->recv(chan->data, skb);
break;
- case L2CAP_SDU_START:
+ case L2CAP_SAR_START:
if (chan->sdu)
break;
chan->sdu_len = get_unaligned_le16(skb->data);
- skb_pull(skb, 2);
+ skb_pull(skb, L2CAP_SDULEN_SIZE);
if (chan->sdu_len > chan->imtu) {
err = -EMSGSIZE;
@@ -3181,7 +3644,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
err = 0;
break;
- case L2CAP_SDU_CONTINUE:
+ case L2CAP_SAR_CONTINUE:
if (!chan->sdu)
break;
@@ -3195,7 +3658,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
err = 0;
break;
- case L2CAP_SDU_END:
+ case L2CAP_SAR_END:
if (!chan->sdu)
break;
@@ -3230,14 +3693,14 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1
static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
{
- u16 control;
+ u32 control;
BT_DBG("chan %p, Enter local busy", chan);
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
- control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- control |= L2CAP_SUPER_RCV_NOT_READY;
+ control = __set_reqseq(chan, chan->buffer_seq);
+ control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
l2cap_send_sframe(chan, control);
set_bit(CONN_RNR_SENT, &chan->conn_state);
@@ -3247,13 +3710,14 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan)
{
- u16 control;
+ u32 control;
if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
goto done;
- control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL;
+ control = __set_reqseq(chan, chan->buffer_seq);
+ control |= __set_ctrl_poll(chan);
+ control |= __set_ctrl_super(chan, L2CAP_SUPER_RR);
l2cap_send_sframe(chan, control);
chan->retry_count = 1;
@@ -3279,10 +3743,10 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
}
}
-static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq)
{
struct sk_buff *skb;
- u16 control;
+ u32 control;
while ((skb = skb_peek(&chan->srej_q)) &&
!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
@@ -3292,7 +3756,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
break;
skb = skb_dequeue(&chan->srej_q);
- control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
+ control = __set_ctrl_sar(chan, bt_cb(skb)->sar);
err = l2cap_reassemble_sdu(chan, skb, control);
if (err < 0) {
@@ -3300,16 +3764,15 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
break;
}
- chan->buffer_seq_srej =
- (chan->buffer_seq_srej + 1) % 64;
- tx_seq = (tx_seq + 1) % 64;
+ chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej);
+ tx_seq = __next_seq(chan, tx_seq);
}
}
-static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
{
struct srej_list *l, *tmp;
- u16 control;
+ u32 control;
list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
if (l->tx_seq == tx_seq) {
@@ -3317,45 +3780,48 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq)
kfree(l);
return;
}
- control = L2CAP_SUPER_SELECT_REJECT;
- control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
+ control |= __set_reqseq(chan, l->tx_seq);
l2cap_send_sframe(chan, control);
list_del(&l->list);
list_add_tail(&l->list, &chan->srej_l);
}
}
-static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq)
+static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
{
struct srej_list *new;
- u16 control;
+ u32 control;
while (tx_seq != chan->expected_tx_seq) {
- control = L2CAP_SUPER_SELECT_REJECT;
- control |= chan->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ);
+ control |= __set_reqseq(chan, chan->expected_tx_seq);
l2cap_send_sframe(chan, control);
new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
new->tx_seq = chan->expected_tx_seq;
- chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+
+ chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
+
list_add_tail(&new->list, &chan->srej_l);
}
- chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+
+ chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
}
-static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb)
+static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
{
- u8 tx_seq = __get_txseq(rx_control);
- u8 req_seq = __get_reqseq(rx_control);
- u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT;
+ u16 tx_seq = __get_txseq(chan, rx_control);
+ u16 req_seq = __get_reqseq(chan, rx_control);
+ u8 sar = __get_ctrl_sar(chan, rx_control);
int tx_seq_offset, expected_tx_seq_offset;
int num_to_ack = (chan->tx_win/6) + 1;
int err = 0;
- BT_DBG("chan %p len %d tx_seq %d rx_control 0x%4.4x", chan, skb->len,
+ BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len,
tx_seq, rx_control);
- if (L2CAP_CTRL_FINAL & rx_control &&
+ if (__is_ctrl_final(chan, rx_control) &&
test_bit(CONN_WAIT_F, &chan->conn_state)) {
__clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
@@ -3366,9 +3832,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
chan->expected_ack_seq = req_seq;
l2cap_drop_acked_frames(chan);
- tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
- if (tx_seq_offset < 0)
- tx_seq_offset += 64;
+ tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
/* invalid tx_seq */
if (tx_seq_offset >= chan->tx_win) {
@@ -3416,10 +3880,8 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
l2cap_send_srejframe(chan, tx_seq);
}
} else {
- expected_tx_seq_offset =
- (chan->expected_tx_seq - chan->buffer_seq) % 64;
- if (expected_tx_seq_offset < 0)
- expected_tx_seq_offset += 64;
+ expected_tx_seq_offset = __seq_offset(chan,
+ chan->expected_tx_seq, chan->buffer_seq);
/* duplicated tx_seq */
if (tx_seq_offset < expected_tx_seq_offset)
@@ -3444,7 +3906,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
return 0;
expected:
- chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
+ chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
bt_cb(skb)->tx_seq = tx_seq;
@@ -3454,13 +3916,14 @@ expected:
}
err = l2cap_reassemble_sdu(chan, skb, rx_control);
- chan->buffer_seq = (chan->buffer_seq + 1) % 64;
+ chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
+
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
return err;
}
- if (rx_control & L2CAP_CTRL_FINAL) {
+ if (__is_ctrl_final(chan, rx_control)) {
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
}
@@ -3478,15 +3941,15 @@ drop:
return 0;
}
-static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control)
{
- BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, __get_reqseq(rx_control),
- rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan,
+ __get_reqseq(chan, rx_control), rx_control);
- chan->expected_ack_seq = __get_reqseq(rx_control);
+ chan->expected_ack_seq = __get_reqseq(chan, rx_control);
l2cap_drop_acked_frames(chan);
- if (rx_control & L2CAP_CTRL_POLL) {
+ if (__is_ctrl_poll(chan, rx_control)) {
set_bit(CONN_SEND_FBIT, &chan->conn_state);
if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
@@ -3499,7 +3962,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co
l2cap_send_i_or_rr_or_rnr(chan);
}
- } else if (rx_control & L2CAP_CTRL_FINAL) {
+ } else if (__is_ctrl_final(chan, rx_control)) {
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
@@ -3518,18 +3981,18 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co
}
}
-static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control)
{
- u8 tx_seq = __get_reqseq(rx_control);
+ u16 tx_seq = __get_reqseq(chan, rx_control);
- BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
- if (rx_control & L2CAP_CTRL_FINAL) {
+ if (__is_ctrl_final(chan, rx_control)) {
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
} else {
@@ -3539,15 +4002,15 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c
set_bit(CONN_REJ_ACT, &chan->conn_state);
}
}
-static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control)
{
- u8 tx_seq = __get_reqseq(rx_control);
+ u16 tx_seq = __get_reqseq(chan, rx_control);
- BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
- if (rx_control & L2CAP_CTRL_POLL) {
+ if (__is_ctrl_poll(chan, rx_control)) {
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
@@ -3560,7 +4023,7 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_
chan->srej_save_reqseq = tx_seq;
set_bit(CONN_SREJ_ACT, &chan->conn_state);
}
- } else if (rx_control & L2CAP_CTRL_FINAL) {
+ } else if (__is_ctrl_final(chan, rx_control)) {
if (test_bit(CONN_SREJ_ACT, &chan->conn_state) &&
chan->srej_save_reqseq == tx_seq)
clear_bit(CONN_SREJ_ACT, &chan->conn_state);
@@ -3575,37 +4038,39 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_
}
}
-static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_control)
+static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control)
{
- u8 tx_seq = __get_reqseq(rx_control);
+ u16 tx_seq = __get_reqseq(chan, rx_control);
- BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
+ BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control);
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
- if (rx_control & L2CAP_CTRL_POLL)
+ if (__is_ctrl_poll(chan, rx_control))
set_bit(CONN_SEND_FBIT, &chan->conn_state);
if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
__clear_retrans_timer(chan);
- if (rx_control & L2CAP_CTRL_POLL)
+ if (__is_ctrl_poll(chan, rx_control))
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL);
return;
}
- if (rx_control & L2CAP_CTRL_POLL)
+ if (__is_ctrl_poll(chan, rx_control)) {
l2cap_send_srejtail(chan);
- else
- l2cap_send_sframe(chan, L2CAP_SUPER_RCV_READY);
+ } else {
+ rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR);
+ l2cap_send_sframe(chan, rx_control);
+ }
}
-static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb)
+static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
{
- BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len);
+ BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len);
- if (L2CAP_CTRL_FINAL & rx_control &&
+ if (__is_ctrl_final(chan, rx_control) &&
test_bit(CONN_WAIT_F, &chan->conn_state)) {
__clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
@@ -3613,20 +4078,20 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont
clear_bit(CONN_WAIT_F, &chan->conn_state);
}
- switch (rx_control & L2CAP_CTRL_SUPERVISE) {
- case L2CAP_SUPER_RCV_READY:
+ switch (__get_ctrl_super(chan, rx_control)) {
+ case L2CAP_SUPER_RR:
l2cap_data_channel_rrframe(chan, rx_control);
break;
- case L2CAP_SUPER_REJECT:
+ case L2CAP_SUPER_REJ:
l2cap_data_channel_rejframe(chan, rx_control);
break;
- case L2CAP_SUPER_SELECT_REJECT:
+ case L2CAP_SUPER_SREJ:
l2cap_data_channel_srejframe(chan, rx_control);
break;
- case L2CAP_SUPER_RCV_NOT_READY:
+ case L2CAP_SUPER_RNR:
l2cap_data_channel_rnrframe(chan, rx_control);
break;
}
@@ -3638,12 +4103,12 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
{
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
- u16 control;
- u8 req_seq;
+ u32 control;
+ u16 req_seq;
int len, next_tx_seq_offset, req_seq_offset;
- control = get_unaligned_le16(skb->data);
- skb_pull(skb, 2);
+ control = __get_control(chan, skb->data);
+ skb_pull(skb, __ctrl_size(chan));
len = skb->len;
/*
@@ -3654,26 +4119,23 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
if (l2cap_check_fcs(chan, skb))
goto drop;
- if (__is_sar_start(control) && __is_iframe(control))
- len -= 2;
+ if (__is_sar_start(chan, control) && !__is_sframe(chan, control))
+ len -= L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
- len -= 2;
+ len -= L2CAP_FCS_SIZE;
if (len > chan->mps) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
}
- req_seq = __get_reqseq(control);
- req_seq_offset = (req_seq - chan->expected_ack_seq) % 64;
- if (req_seq_offset < 0)
- req_seq_offset += 64;
+ req_seq = __get_reqseq(chan, control);
- next_tx_seq_offset =
- (chan->next_tx_seq - chan->expected_ack_seq) % 64;
- if (next_tx_seq_offset < 0)
- next_tx_seq_offset += 64;
+ req_seq_offset = __seq_offset(chan, req_seq, chan->expected_ack_seq);
+
+ next_tx_seq_offset = __seq_offset(chan, chan->next_tx_seq,
+ chan->expected_ack_seq);
/* check for invalid req-seq */
if (req_seq_offset > next_tx_seq_offset) {
@@ -3681,7 +4143,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- if (__is_iframe(control)) {
+ if (!__is_sframe(chan, control)) {
if (len < 0) {
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
goto drop;
@@ -3709,8 +4171,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
{
struct l2cap_chan *chan;
struct sock *sk = NULL;
- u16 control;
- u8 tx_seq;
+ u32 control;
+ u16 tx_seq;
int len;
chan = l2cap_get_chan_by_scid(conn, cid);
@@ -3751,23 +4213,23 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
goto done;
case L2CAP_MODE_STREAMING:
- control = get_unaligned_le16(skb->data);
- skb_pull(skb, 2);
+ control = __get_control(chan, skb->data);
+ skb_pull(skb, __ctrl_size(chan));
len = skb->len;
if (l2cap_check_fcs(chan, skb))
goto drop;
- if (__is_sar_start(control))
- len -= 2;
+ if (__is_sar_start(chan, control))
+ len -= L2CAP_SDULEN_SIZE;
if (chan->fcs == L2CAP_FCS_CRC16)
- len -= 2;
+ len -= L2CAP_FCS_SIZE;
- if (len > chan->mps || len < 0 || __is_sframe(control))
+ if (len > chan->mps || len < 0 || __is_sframe(chan, control))
goto drop;
- tx_seq = __get_txseq(control);
+ tx_seq = __get_txseq(chan, control);
if (chan->expected_tx_seq != tx_seq) {
/* Frame(s) missing - must discard partial SDU */
@@ -3779,7 +4241,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
/* TODO: Notify userland of missing data */
}
- chan->expected_tx_seq = (tx_seq + 1) % 64;
+ chan->expected_tx_seq = __next_seq(chan, tx_seq);
if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE)
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
@@ -3933,12 +4395,12 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
lm1 |= HCI_LM_ACCEPT;
- if (c->role_switch)
+ if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
lm1 |= HCI_LM_MASTER;
exact++;
} else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
lm2 |= HCI_LM_ACCEPT;
- if (c->role_switch)
+ if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
lm2 |= HCI_LM_MASTER;
}
}
@@ -3973,7 +4435,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon)
BT_DBG("hcon %p", hcon);
if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn)
- return 0x13;
+ return HCI_ERROR_REMOTE_USER_TERM;
return conn->disc_reason;
}
@@ -3998,7 +4460,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
if (encrypt == 0x00) {
if (chan->sec_level == BT_SECURITY_MEDIUM) {
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ * 5);
+ __set_chan_timer(chan, L2CAP_ENC_TIMEOUT);
} else if (chan->sec_level == BT_SECURITY_HIGH)
l2cap_chan_close(chan, ECONNREFUSED);
} else {
@@ -4066,7 +4528,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
L2CAP_CONN_REQ, sizeof(req), &req);
} else {
__clear_chan_timer(chan);
- __set_chan_timer(chan, HZ / 10);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
}
} else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
@@ -4086,7 +4548,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
}
} else {
l2cap_state_change(chan, BT_DISCONN);
- __set_chan_timer(chan, HZ / 10);
+ __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
res = L2CAP_CR_SEC_BLOCK;
stat = L2CAP_CS_NO_INFO;
}
@@ -4306,3 +4768,6 @@ void l2cap_exit(void)
module_param(disable_ertm, bool, 0644);
MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
+
+module_param(enable_hs, bool, 0644);
+MODULE_PARM_DESC(enable_hs, "Enable High Speed");
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 5c406d3136f7..e2e785c74630 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -334,7 +334,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
opts.mode = chan->mode;
opts.fcs = chan->fcs;
opts.max_tx = chan->max_tx;
- opts.txwin_size = (__u16)chan->tx_win;
+ opts.txwin_size = chan->tx_win;
len = min_t(unsigned int, len, sizeof(opts));
if (copy_to_user(optval, (char *) &opts, len))
@@ -359,10 +359,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
break;
}
- if (chan->role_switch)
+ if (test_bit(FLAG_ROLE_SWITCH, &chan->flags))
opt |= L2CAP_LM_MASTER;
- if (chan->force_reliable)
+ if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
opt |= L2CAP_LM_RELIABLE;
if (put_user(opt, (u32 __user *) optval))
@@ -449,7 +449,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
break;
case BT_FLUSHABLE:
- if (put_user(chan->flushable, (u32 __user *) optval))
+ if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
+ (u32 __user *) optval))
err = -EFAULT;
break;
@@ -461,7 +462,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
break;
}
- pwr.force_active = chan->force_active;
+ pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
len = min_t(unsigned int, len, sizeof(pwr));
if (copy_to_user(optval, (char *) &pwr, len))
@@ -469,6 +470,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
break;
+ case BT_CHANNEL_POLICY:
+ if (!enable_hs) {
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ if (put_user(chan->chan_policy, (u32 __user *) optval))
+ err = -EFAULT;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -503,7 +514,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
opts.mode = chan->mode;
opts.fcs = chan->fcs;
opts.max_tx = chan->max_tx;
- opts.txwin_size = (__u16)chan->tx_win;
+ opts.txwin_size = chan->tx_win;
len = min_t(unsigned int, sizeof(opts), optlen);
if (copy_from_user((char *) &opts, optval, len)) {
@@ -511,7 +522,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
break;
}
- if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) {
+ if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) {
err = -EINVAL;
break;
}
@@ -535,7 +546,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
chan->omtu = opts.omtu;
chan->fcs = opts.fcs;
chan->max_tx = opts.max_tx;
- chan->tx_win = (__u8)opts.txwin_size;
+ chan->tx_win = opts.txwin_size;
break;
case L2CAP_LM:
@@ -551,8 +562,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
if (opt & L2CAP_LM_SECURE)
chan->sec_level = BT_SECURITY_HIGH;
- chan->role_switch = (opt & L2CAP_LM_MASTER);
- chan->force_reliable = (opt & L2CAP_LM_RELIABLE);
+ if (opt & L2CAP_LM_MASTER)
+ set_bit(FLAG_ROLE_SWITCH, &chan->flags);
+ else
+ clear_bit(FLAG_ROLE_SWITCH, &chan->flags);
+
+ if (opt & L2CAP_LM_RELIABLE)
+ set_bit(FLAG_FORCE_RELIABLE, &chan->flags);
+ else
+ clear_bit(FLAG_FORCE_RELIABLE, &chan->flags);
break;
default:
@@ -658,7 +676,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
}
}
- chan->flushable = opt;
+ if (opt)
+ set_bit(FLAG_FLUSHABLE, &chan->flags);
+ else
+ clear_bit(FLAG_FLUSHABLE, &chan->flags);
break;
case BT_POWER:
@@ -675,7 +696,36 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
err = -EFAULT;
break;
}
- chan->force_active = pwr.force_active;
+
+ if (pwr.force_active)
+ set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
+ else
+ clear_bit(FLAG_FORCE_ACTIVE, &chan->flags);
+ break;
+
+ case BT_CHANNEL_POLICY:
+ if (!enable_hs) {
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ if (get_user(opt, (u32 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (chan->mode != L2CAP_MODE_ERTM &&
+ chan->mode != L2CAP_MODE_STREAMING) {
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ chan->chan_policy = (u8) opt;
break;
default:
@@ -709,7 +759,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
return -ENOTCONN;
}
- err = l2cap_chan_send(chan, msg, len);
+ err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
release_sock(sk);
return err;
@@ -931,11 +981,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->fcs = pchan->fcs;
chan->max_tx = pchan->max_tx;
chan->tx_win = pchan->tx_win;
+ chan->tx_win_max = pchan->tx_win_max;
chan->sec_level = pchan->sec_level;
- chan->role_switch = pchan->role_switch;
- chan->force_reliable = pchan->force_reliable;
- chan->flushable = pchan->flushable;
- chan->force_active = pchan->force_active;
+ chan->flags = pchan->flags;
security_sk_clone(parent, sk);
} else {
@@ -964,12 +1012,10 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
chan->fcs = L2CAP_FCS_CRC16;
chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
+ chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
chan->sec_level = BT_SECURITY_LOW;
- chan->role_switch = 0;
- chan->force_reliable = 0;
- chan->flushable = BT_FLUSHABLE_OFF;
- chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
-
+ chan->flags = 0;
+ set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
/* Default config options */
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 2c7634296866..94739d3c4f59 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -33,22 +33,23 @@
#define MGMT_VERSION 0
#define MGMT_REVISION 1
+#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
+
struct pending_cmd {
struct list_head list;
- __u16 opcode;
+ u16 opcode;
int index;
void *param;
struct sock *sk;
void *user_data;
};
-static LIST_HEAD(cmd_list);
-
static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_status *ev;
+ int err;
BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
@@ -66,10 +67,11 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
ev->status = status;
put_unaligned_le16(cmd, &ev->opcode);
- if (sock_queue_rcv_skb(sk, skb) < 0)
+ err = sock_queue_rcv_skb(sk, skb);
+ if (err < 0)
kfree_skb(skb);
- return 0;
+ return err;
}
static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
@@ -78,6 +80,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev;
+ int err;
BT_DBG("sock %p", sk);
@@ -97,10 +100,11 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
if (rp)
memcpy(ev->data, rp, rp_len);
- if (sock_queue_rcv_skb(sk, skb) < 0)
+ err = sock_queue_rcv_skb(sk, skb);
+ if (err < 0)
kfree_skb(skb);
- return 0;
+ return err;;
}
static int read_version(struct sock *sk)
@@ -120,6 +124,7 @@ static int read_index_list(struct sock *sk)
{
struct mgmt_rp_read_index_list *rp;
struct list_head *p;
+ struct hci_dev *d;
size_t rp_len;
u16 count;
int i, err;
@@ -143,10 +148,9 @@ static int read_index_list(struct sock *sk)
put_unaligned_le16(count, &rp->num_controllers);
i = 0;
- list_for_each(p, &hci_dev_list) {
- struct hci_dev *d = list_entry(p, struct hci_dev, list);
-
- hci_del_off_timer(d);
+ list_for_each_entry(d, &hci_dev_list, list) {
+ if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags))
+ cancel_delayed_work(&d->power_off);
if (test_bit(HCI_SETUP, &d->flags))
continue;
@@ -176,7 +180,8 @@ static int read_controller_info(struct sock *sk, u16 index)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_READ_INFO, ENODEV);
- hci_del_off_timer(hdev);
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+ cancel_delayed_work_sync(&hdev->power_off);
hci_dev_lock_bh(hdev);
@@ -221,7 +226,8 @@ static void mgmt_pending_free(struct pending_cmd *cmd)
}
static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
- u16 index, void *data, u16 len)
+ struct hci_dev *hdev,
+ void *data, u16 len)
{
struct pending_cmd *cmd;
@@ -230,7 +236,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
return NULL;
cmd->opcode = opcode;
- cmd->index = index;
+ cmd->index = hdev->id;
cmd->param = kmalloc(len, GFP_ATOMIC);
if (!cmd->param) {
@@ -244,48 +250,36 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
cmd->sk = sk;
sock_hold(sk);
- list_add(&cmd->list, &cmd_list);
+ list_add(&cmd->list, &hdev->mgmt_pending);
return cmd;
}
-static void mgmt_pending_foreach(u16 opcode, int index,
+static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
void (*cb)(struct pending_cmd *cmd, void *data),
void *data)
{
struct list_head *p, *n;
- list_for_each_safe(p, n, &cmd_list) {
+ list_for_each_safe(p, n, &hdev->mgmt_pending) {
struct pending_cmd *cmd;
cmd = list_entry(p, struct pending_cmd, list);
- if (cmd->opcode != opcode)
- continue;
-
- if (index >= 0 && cmd->index != index)
+ if (opcode > 0 && cmd->opcode != opcode)
continue;
cb(cmd, data);
}
}
-static struct pending_cmd *mgmt_pending_find(u16 opcode, int index)
+static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
{
- struct list_head *p;
-
- list_for_each(p, &cmd_list) {
- struct pending_cmd *cmd;
-
- cmd = list_entry(p, struct pending_cmd, list);
-
- if (cmd->opcode != opcode)
- continue;
-
- if (index >= 0 && cmd->index != index)
- continue;
+ struct pending_cmd *cmd;
- return cmd;
+ list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+ if (cmd->opcode == opcode)
+ return cmd;
}
return NULL;
@@ -323,12 +317,12 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
goto failed;
}
- if (mgmt_pending_find(MGMT_OP_SET_POWERED, index)) {
+ if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY);
goto failed;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, index, data, len);
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
@@ -337,7 +331,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (cp->val)
queue_work(hdev->workqueue, &hdev->power_on);
else
- queue_work(hdev->workqueue, &hdev->power_off);
+ queue_work(hdev->workqueue, &hdev->power_off.work);
err = 0;
@@ -350,7 +344,7 @@ failed:
static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
- struct mgmt_mode *cp;
+ struct mgmt_cp_set_discoverable *cp;
struct hci_dev *hdev;
struct pending_cmd *cmd;
u8 scan;
@@ -374,8 +368,8 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) ||
- mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) {
+ if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
+ mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY);
goto failed;
}
@@ -386,7 +380,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, index, data, len);
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
@@ -396,11 +390,16 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
if (cp->val)
scan |= SCAN_INQUIRY;
+ else
+ cancel_delayed_work(&hdev->discov_off);
err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
if (err < 0)
mgmt_pending_remove(cmd);
+ if (cp->val)
+ hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
+
failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
@@ -435,8 +434,8 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) ||
- mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) {
+ if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
+ mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY);
goto failed;
}
@@ -446,7 +445,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, index, data, len);
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
@@ -468,8 +467,8 @@ failed:
return err;
}
-static int mgmt_event(u16 event, u16 index, void *data, u16 data_len,
- struct sock *skip_sk)
+static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
+ u16 data_len, struct sock *skip_sk)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
@@ -482,7 +481,10 @@ static int mgmt_event(u16 event, u16 index, void *data, u16 data_len,
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(event);
- hdr->index = cpu_to_le16(index);
+ if (hdev)
+ hdr->index = cpu_to_le16(hdev->id);
+ else
+ hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
@@ -534,7 +536,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
ev.val = cp->val;
- err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk);
+ err = mgmt_event(MGMT_EV_PAIRABLE, hdev, &ev, sizeof(ev), sk);
failed:
hci_dev_unlock_bh(hdev);
@@ -587,7 +589,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
u16 eir_len = 0;
u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
int i, truncated = 0;
- struct list_head *p;
+ struct bt_uuid *uuid;
size_t name_len;
name_len = strlen(hdev->dev_name);
@@ -612,8 +614,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
memset(uuid16_list, 0, sizeof(uuid16_list));
/* Group all UUID16 types */
- list_for_each(p, &hdev->uuids) {
- struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
+ list_for_each_entry(uuid, &hdev->uuids, list) {
u16 uuid16;
uuid16 = get_uuid16(uuid->uuid);
@@ -689,14 +690,11 @@ static int update_eir(struct hci_dev *hdev)
static u8 get_service_classes(struct hci_dev *hdev)
{
- struct list_head *p;
+ struct bt_uuid *uuid;
u8 val = 0;
- list_for_each(p, &hdev->uuids) {
- struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
-
+ list_for_each_entry(uuid, &hdev->uuids, list)
val |= uuid->svc_hint;
- }
return val;
}
@@ -895,6 +893,9 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
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);
@@ -902,30 +903,32 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
return err;
}
-static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
{
struct hci_dev *hdev;
- struct mgmt_cp_load_keys *cp;
+ struct mgmt_cp_load_link_keys *cp;
u16 key_count, expected_len;
int i;
cp = (void *) data;
if (len < sizeof(*cp))
- return -EINVAL;
+ return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL);
key_count = get_unaligned_le16(&cp->key_count);
- expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
+ expected_len = sizeof(*cp) + key_count *
+ sizeof(struct mgmt_link_key_info);
if (expected_len != len) {
- BT_ERR("load_keys: expected %u bytes, got %u bytes",
+ BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
len, expected_len);
- return -EINVAL;
+ return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL);
}
hdev = hci_dev_get(index);
if (!hdev)
- return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, ENODEV);
+ return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, ENODEV);
BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
key_count);
@@ -942,7 +945,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
for (i = 0; i < key_count; i++) {
- struct mgmt_key_info *key = &cp->keys[i];
+ struct mgmt_link_key_info *key = &cp->keys[i];
hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
key->pin_len);
@@ -954,27 +957,28 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
return 0;
}
-static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
{
struct hci_dev *hdev;
- struct mgmt_cp_remove_key *cp;
+ struct mgmt_cp_remove_keys *cp;
struct hci_conn *conn;
int err;
cp = (void *) data;
if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, EINVAL);
+ return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, EINVAL);
hdev = hci_dev_get(index);
if (!hdev)
- return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV);
+ return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, ENODEV);
hci_dev_lock_bh(hdev);
err = hci_remove_link_key(hdev, &cp->bdaddr);
if (err < 0) {
- err = cmd_status(sk, index, MGMT_OP_REMOVE_KEY, -err);
+ err = cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, -err);
goto unlock;
}
@@ -1026,7 +1030,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
goto failed;
}
- if (mgmt_pending_find(MGMT_OP_DISCONNECT, index)) {
+ if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY);
goto failed;
}
@@ -1040,7 +1044,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
goto failed;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, index, data, len);
+ cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
@@ -1060,10 +1064,23 @@ failed:
return err;
}
+static u8 link_to_mgmt(u8 link_type)
+{
+ switch (link_type) {
+ case LE_LINK:
+ return MGMT_ADDR_LE;
+ case ACL_LINK:
+ return MGMT_ADDR_BREDR;
+ default:
+ return MGMT_ADDR_INVALID;
+ }
+}
+
static int get_connections(struct sock *sk, u16 index)
{
struct mgmt_rp_get_connections *rp;
struct hci_dev *hdev;
+ struct hci_conn *c;
struct list_head *p;
size_t rp_len;
u16 count;
@@ -1082,7 +1099,7 @@ static int get_connections(struct sock *sk, u16 index)
count++;
}
- rp_len = sizeof(*rp) + (count * sizeof(bdaddr_t));
+ rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
rp = kmalloc(rp_len, GFP_ATOMIC);
if (!rp) {
err = -ENOMEM;
@@ -1092,12 +1109,17 @@ static int get_connections(struct sock *sk, u16 index)
put_unaligned_le16(count, &rp->conn_count);
i = 0;
- list_for_each(p, &hdev->conn_hash.list) {
- struct hci_conn *c = list_entry(p, struct hci_conn, list);
-
- bacpy(&rp->conn[i++], &c->dst);
+ list_for_each_entry(c, &hdev->conn_hash.list, list) {
+ bacpy(&rp->addr[i].bdaddr, &c->dst);
+ rp->addr[i].type = link_to_mgmt(c->type);
+ if (rp->addr[i].type == MGMT_ADDR_INVALID)
+ continue;
+ i++;
}
+ /* Recalculate length in case of filtered SCO connections, etc */
+ rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
+
err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
unlock:
@@ -1113,7 +1135,7 @@ static int send_pin_code_neg_reply(struct sock *sk, u16 index,
struct pending_cmd *cmd;
int err;
- cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, cp,
+ cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
sizeof(*cp));
if (!cmd)
return -ENOMEM;
@@ -1174,7 +1196,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len);
+ cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
@@ -1265,19 +1287,12 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
- struct list_head *p;
-
- list_for_each(p, &cmd_list) {
- struct pending_cmd *cmd;
-
- cmd = list_entry(p, struct pending_cmd, list);
+ struct pending_cmd *cmd;
+ list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
continue;
- if (cmd->index != hdev->id)
- continue;
-
if (cmd->user_data != conn)
continue;
@@ -1310,16 +1325,19 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
static void pairing_complete_cb(struct hci_conn *conn, u8 status)
{
struct pending_cmd *cmd;
+ struct hci_dev *hdev = conn->hdev;
BT_DBG("status %u", status);
+ hci_dev_lock_bh(hdev);
+
cmd = find_pairing(conn);
- if (!cmd) {
+ if (!cmd)
BT_DBG("Unable to find a pending command");
- return;
- }
+ else
+ pairing_complete(cmd, status);
- pairing_complete(cmd, status);
+ hci_dev_unlock_bh(hdev);
}
static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
@@ -1370,7 +1388,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
goto unlock;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, index, data, len);
+ cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
hci_conn_put(conn);
@@ -1432,7 +1450,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- cmd = mgmt_pending_add(sk, mgmt_op, index, data, len);
+ cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
@@ -1469,7 +1487,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
hci_dev_lock_bh(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len);
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
@@ -1515,12 +1533,12 @@ static int read_local_oob_data(struct sock *sk, u16 index)
goto unlock;
}
- if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index)) {
+ if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY);
goto unlock;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, index, NULL, 0);
+ cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
if (!cmd) {
err = -ENOMEM;
goto unlock;
@@ -1607,8 +1625,6 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
static int start_discovery(struct sock *sk, u16 index)
{
- u8 lap[3] = { 0x33, 0x8b, 0x9e };
- struct hci_cp_inquiry cp;
struct pending_cmd *cmd;
struct hci_dev *hdev;
int err;
@@ -1621,18 +1637,18 @@ static int start_discovery(struct sock *sk, u16 index)
hci_dev_lock_bh(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0);
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENETDOWN);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
if (!cmd) {
err = -ENOMEM;
goto failed;
}
- memset(&cp, 0, sizeof(cp));
- memcpy(&cp.lap, lap, 3);
- cp.length = 0x08;
- cp.num_rsp = 0x00;
-
- err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
+ err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
if (err < 0)
mgmt_pending_remove(cmd);
@@ -1657,13 +1673,13 @@ static int stop_discovery(struct sock *sk, u16 index)
hci_dev_lock_bh(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0);
+ cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
if (!cmd) {
err = -ENOMEM;
goto failed;
}
- err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+ err = hci_cancel_inquiry(hdev);
if (err < 0)
mgmt_pending_remove(cmd);
@@ -1678,7 +1694,6 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
struct hci_dev *hdev;
- struct pending_cmd *cmd;
struct mgmt_cp_block_device *cp = (void *) data;
int err;
@@ -1695,23 +1710,13 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
hci_dev_lock_bh(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0);
- if (!cmd) {
- err = -ENOMEM;
- goto failed;
- }
-
err = hci_blacklist_add(hdev, &cp->bdaddr);
-
if (err < 0)
err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err);
else
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
NULL, 0);
- mgmt_pending_remove(cmd);
-
-failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
@@ -1722,7 +1727,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
struct hci_dev *hdev;
- struct pending_cmd *cmd;
struct mgmt_cp_unblock_device *cp = (void *) data;
int err;
@@ -1739,12 +1743,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
hci_dev_lock_bh(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0);
- if (!cmd) {
- err = -ENOMEM;
- goto failed;
- }
-
err = hci_blacklist_del(hdev, &cp->bdaddr);
if (err < 0)
@@ -1753,9 +1751,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
NULL, 0);
- mgmt_pending_remove(cmd);
-
-failed:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
@@ -1883,11 +1878,11 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_SET_SERVICE_CACHE:
err = set_service_cache(sk, index, buf + sizeof(*hdr), len);
break;
- case MGMT_OP_LOAD_KEYS:
- err = load_keys(sk, index, buf + sizeof(*hdr), len);
+ case MGMT_OP_LOAD_LINK_KEYS:
+ err = load_link_keys(sk, index, buf + sizeof(*hdr), len);
break;
- case MGMT_OP_REMOVE_KEY:
- err = remove_key(sk, index, buf + sizeof(*hdr), len);
+ case MGMT_OP_REMOVE_KEYS:
+ err = remove_keys(sk, index, buf + sizeof(*hdr), len);
break;
case MGMT_OP_DISCONNECT:
err = disconnect(sk, index, buf + sizeof(*hdr), len);
@@ -1958,14 +1953,26 @@ done:
return err;
}
-int mgmt_index_added(u16 index)
+static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
{
- return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL);
+ u8 *status = data;
+
+ cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
+ mgmt_pending_remove(cmd);
}
-int mgmt_index_removed(u16 index)
+int mgmt_index_added(struct hci_dev *hdev)
{
- return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL);
+ return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
+}
+
+int mgmt_index_removed(struct hci_dev *hdev)
+{
+ u8 status = ENODEV;
+
+ mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+
+ return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
}
struct cmd_lookup {
@@ -1993,17 +2000,22 @@ static void mode_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_free(cmd);
}
-int mgmt_powered(u16 index, u8 powered)
+int mgmt_powered(struct hci_dev *hdev, u8 powered)
{
struct mgmt_mode ev;
struct cmd_lookup match = { powered, NULL };
int ret;
- mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match);
+ mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, mode_rsp, &match);
+
+ if (!powered) {
+ u8 status = ENETDOWN;
+ mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+ }
ev.val = powered;
- ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk);
+ ret = mgmt_event(MGMT_EV_POWERED, hdev, &ev, sizeof(ev), match.sk);
if (match.sk)
sock_put(match.sk);
@@ -2011,17 +2023,17 @@ int mgmt_powered(u16 index, u8 powered)
return ret;
}
-int mgmt_discoverable(u16 index, u8 discoverable)
+int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
{
struct mgmt_mode ev;
struct cmd_lookup match = { discoverable, NULL };
int ret;
- mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, mode_rsp, &match);
+ mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, mode_rsp, &match);
ev.val = discoverable;
- ret = mgmt_event(MGMT_EV_DISCOVERABLE, index, &ev, sizeof(ev),
+ ret = mgmt_event(MGMT_EV_DISCOVERABLE, hdev, &ev, sizeof(ev),
match.sk);
if (match.sk)
@@ -2030,17 +2042,17 @@ int mgmt_discoverable(u16 index, u8 discoverable)
return ret;
}
-int mgmt_connectable(u16 index, u8 connectable)
+int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
{
struct mgmt_mode ev;
struct cmd_lookup match = { connectable, NULL };
int ret;
- mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match);
+ mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, mode_rsp, &match);
ev.val = connectable;
- ret = mgmt_event(MGMT_EV_CONNECTABLE, index, &ev, sizeof(ev), match.sk);
+ ret = mgmt_event(MGMT_EV_CONNECTABLE, hdev, &ev, sizeof(ev), match.sk);
if (match.sk)
sock_put(match.sk);
@@ -2048,9 +2060,23 @@ int mgmt_connectable(u16 index, u8 connectable)
return ret;
}
-int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
+int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
+{
+ if (scan & SCAN_PAGE)
+ mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
+ cmd_status_rsp, &status);
+
+ if (scan & SCAN_INQUIRY)
+ mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
+ cmd_status_rsp, &status);
+
+ return 0;
+}
+
+int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+ u8 persistent)
{
- struct mgmt_ev_new_key ev;
+ struct mgmt_ev_new_link_key ev;
memset(&ev, 0, sizeof(ev));
@@ -2060,17 +2086,17 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
memcpy(ev.key.val, key->val, 16);
ev.key.pin_len = key->pin_len;
- return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
+ return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type)
+int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type)
{
- struct mgmt_ev_connected ev;
+ struct mgmt_addr_info ev;
bacpy(&ev.bdaddr, bdaddr);
- ev.link_type = link_type;
+ ev.type = link_to_mgmt(link_type);
- return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL);
+ return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL);
}
static void disconnect_rsp(struct pending_cmd *cmd, void *data)
@@ -2089,17 +2115,18 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_remove(cmd);
}
-int mgmt_disconnected(u16 index, bdaddr_t *bdaddr)
+int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
- struct mgmt_ev_disconnected ev;
+ struct mgmt_addr_info ev;
struct sock *sk = NULL;
int err;
- mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk);
+ mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
bacpy(&ev.bdaddr, bdaddr);
+ ev.type = link_to_mgmt(type);
- err = mgmt_event(MGMT_EV_DISCONNECTED, index, &ev, sizeof(ev), sk);
+ err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
if (sk)
sock_put(sk);
@@ -2107,57 +2134,60 @@ int mgmt_disconnected(u16 index, bdaddr_t *bdaddr)
return err;
}
-int mgmt_disconnect_failed(u16 index)
+int mgmt_disconnect_failed(struct hci_dev *hdev)
{
struct pending_cmd *cmd;
int err;
- cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index);
+ cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
if (!cmd)
return -ENOENT;
- err = cmd_status(cmd->sk, index, MGMT_OP_DISCONNECT, EIO);
+ err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT, EIO);
mgmt_pending_remove(cmd);
return err;
}
-int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
+ u8 status)
{
struct mgmt_ev_connect_failed ev;
- bacpy(&ev.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = link_to_mgmt(type);
ev.status = status;
- return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL);
+ return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure)
+int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
{
struct mgmt_ev_pin_code_request ev;
bacpy(&ev.bdaddr, bdaddr);
ev.secure = secure;
- return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev),
+ return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
NULL);
}
-int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
int err;
- cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index);
+ cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
if (!cmd)
return -ENOENT;
bacpy(&rp.bdaddr, bdaddr);
rp.status = status;
- err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_REPLY, &rp,
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
sizeof(rp));
mgmt_pending_remove(cmd);
@@ -2165,20 +2195,21 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
return err;
}
-int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
int err;
- cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index);
+ cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
if (!cmd)
return -ENOENT;
bacpy(&rp.bdaddr, bdaddr);
rp.status = status;
- err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
sizeof(rp));
mgmt_pending_remove(cmd);
@@ -2186,97 +2217,93 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
return err;
}
-int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value,
- u8 confirm_hint)
+int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ __le32 value, u8 confirm_hint)
{
struct mgmt_ev_user_confirm_request ev;
- BT_DBG("hci%u", index);
+ BT_DBG("%s", hdev->name);
bacpy(&ev.bdaddr, bdaddr);
ev.confirm_hint = confirm_hint;
put_unaligned_le32(value, &ev.value);
- return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev),
+ return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
NULL);
}
-static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status,
- u8 opcode)
+static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status, u8 opcode)
{
struct pending_cmd *cmd;
struct mgmt_rp_user_confirm_reply rp;
int err;
- cmd = mgmt_pending_find(opcode, index);
+ cmd = mgmt_pending_find(opcode, hdev);
if (!cmd)
return -ENOENT;
bacpy(&rp.bdaddr, bdaddr);
rp.status = status;
- err = cmd_complete(cmd->sk, index, opcode, &rp, sizeof(rp));
+ err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
mgmt_pending_remove(cmd);
return err;
}
-int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status)
{
- return confirm_reply_complete(index, bdaddr, status,
+ return confirm_reply_complete(hdev, bdaddr, status,
MGMT_OP_USER_CONFIRM_REPLY);
}
-int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 status)
{
- return confirm_reply_complete(index, bdaddr, status,
+ return confirm_reply_complete(hdev, bdaddr, status,
MGMT_OP_USER_CONFIRM_NEG_REPLY);
}
-int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status)
+int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)
{
struct mgmt_ev_auth_failed ev;
bacpy(&ev.bdaddr, bdaddr);
ev.status = status;
- return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL);
+ return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status)
+int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
{
struct pending_cmd *cmd;
- struct hci_dev *hdev;
struct mgmt_cp_set_local_name ev;
int err;
memset(&ev, 0, sizeof(ev));
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
- cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, index);
+ cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
if (!cmd)
goto send_event;
if (status) {
- err = cmd_status(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, EIO);
+ err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
+ EIO);
goto failed;
}
- hdev = hci_dev_get(index);
- if (hdev) {
- hci_dev_lock_bh(hdev);
- update_eir(hdev);
- hci_dev_unlock_bh(hdev);
- hci_dev_put(hdev);
- }
+ update_eir(hdev);
- err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev,
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev,
sizeof(ev));
if (err < 0)
goto failed;
send_event:
- err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, index, &ev, sizeof(ev),
+ err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
cmd ? cmd->sk : NULL);
failed:
@@ -2285,29 +2312,30 @@ failed:
return err;
}
-int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
- u8 status)
+int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+ u8 *randomizer, u8 status)
{
struct pending_cmd *cmd;
int err;
- BT_DBG("hci%u status %u", index, status);
+ BT_DBG("%s status %u", hdev->name, status);
- cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index);
+ cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
if (!cmd)
return -ENOENT;
if (status) {
- err = cmd_status(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- EIO);
+ err = cmd_status(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_DATA, EIO);
} else {
struct mgmt_rp_read_local_oob_data rp;
memcpy(rp.hash, hash, sizeof(rp.hash));
memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
- err = cmd_complete(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- &rp, sizeof(rp));
+ err = cmd_complete(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_DATA,
+ &rp, sizeof(rp));
}
mgmt_pending_remove(cmd);
@@ -2315,14 +2343,15 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
return err;
}
-int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
- u8 *eir)
+int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
+ u8 *dev_class, s8 rssi, u8 *eir)
{
struct mgmt_ev_device_found ev;
memset(&ev, 0, sizeof(ev));
- bacpy(&ev.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = link_to_mgmt(type);
ev.rssi = rssi;
if (eir)
@@ -2331,10 +2360,10 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
if (dev_class)
memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
- return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL);
+ return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name)
+int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name)
{
struct mgmt_ev_remote_name ev;
@@ -2343,37 +2372,64 @@ int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name)
bacpy(&ev.bdaddr, bdaddr);
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
- return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL);
+ return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_discovering(u16 index, u8 discovering)
+int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status)
{
- return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering,
+ struct pending_cmd *cmd;
+ int err;
+
+ cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+ if (!cmd)
+ return -ENOENT;
+
+ err = cmd_status(cmd->sk, hdev->id, cmd->opcode, status);
+ mgmt_pending_remove(cmd);
+
+ return err;
+}
+
+int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
+{
+ struct pending_cmd *cmd;
+
+ if (discovering)
+ cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+ else
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
+
+ if (cmd != NULL) {
+ cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0);
+ mgmt_pending_remove(cmd);
+ }
+
+ return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering,
sizeof(discovering), NULL);
}
-int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr)
+int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct pending_cmd *cmd;
struct mgmt_ev_device_blocked ev;
- cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index);
+ cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
bacpy(&ev.bdaddr, bdaddr);
- return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
+ cmd ? cmd->sk : NULL);
}
-int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr)
+int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct pending_cmd *cmd;
struct mgmt_ev_device_unblocked ev;
- cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index);
+ cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
bacpy(&ev.bdaddr, bdaddr);
- return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
+ cmd ? cmd->sk : NULL);
}
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 4e32e18211f9..8743f369ed3f 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -65,7 +65,8 @@ static DEFINE_MUTEX(rfcomm_mutex);
static LIST_HEAD(session_list);
-static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
+static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
+ u32 priority);
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
static int rfcomm_queue_disc(struct rfcomm_dlc *d);
@@ -377,13 +378,11 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
{
struct rfcomm_dlc *d;
- struct list_head *p;
- list_for_each(p, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
+ list_for_each_entry(d, &s->dlcs, list)
if (d->dlci == dlci)
return d;
- }
+
return NULL;
}
@@ -749,19 +748,34 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *d
}
/* ---- RFCOMM frame sending ---- */
-static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
+static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
+ u32 priority)
{
struct socket *sock = s->sock;
+ struct sock *sk = sock->sk;
struct kvec iv = { data, len };
struct msghdr msg;
- BT_DBG("session %p len %d", s, len);
+ BT_DBG("session %p len %d priority %u", s, len, priority);
+
+ if (sk->sk_priority != priority) {
+ lock_sock(sk);
+ sk->sk_priority = priority;
+ release_sock(sk);
+ }
memset(&msg, 0, sizeof(msg));
return kernel_sendmsg(sock, &msg, &iv, 1, len);
}
+static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd)
+{
+ BT_DBG("%p cmd %u", s, cmd->ctrl);
+
+ return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd), HCI_PRIO_MAX);
+}
+
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
{
struct rfcomm_cmd cmd;
@@ -773,7 +787,7 @@ static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd);
- return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+ return rfcomm_send_cmd(s, &cmd);
}
static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
@@ -787,7 +801,7 @@ static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd);
- return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+ return rfcomm_send_cmd(s, &cmd);
}
static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
@@ -801,7 +815,7 @@ static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd);
- return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+ return rfcomm_send_cmd(s, &cmd);
}
static int rfcomm_queue_disc(struct rfcomm_dlc *d)
@@ -815,6 +829,8 @@ static int rfcomm_queue_disc(struct rfcomm_dlc *d)
if (!skb)
return -ENOMEM;
+ skb->priority = HCI_PRIO_MAX;
+
cmd = (void *) __skb_put(skb, sizeof(*cmd));
cmd->addr = d->addr;
cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
@@ -837,7 +853,7 @@ static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd);
- return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+ return rfcomm_send_cmd(s, &cmd);
}
static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
@@ -862,7 +878,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
*ptr = __fcs(buf); ptr++;
- return rfcomm_send_frame(s, buf, ptr - buf);
+ return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
}
static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
@@ -904,7 +920,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
*ptr = __fcs(buf); ptr++;
- return rfcomm_send_frame(s, buf, ptr - buf);
+ return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
}
int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
@@ -942,7 +958,7 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
*ptr = __fcs(buf); ptr++;
- return rfcomm_send_frame(s, buf, ptr - buf);
+ return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
}
static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
@@ -969,7 +985,7 @@ static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
*ptr = __fcs(buf); ptr++;
- return rfcomm_send_frame(s, buf, ptr - buf);
+ return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
}
static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
@@ -996,7 +1012,7 @@ static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig
*ptr = __fcs(buf); ptr++;
- return rfcomm_send_frame(s, buf, ptr - buf);
+ return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
}
static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
@@ -1018,7 +1034,7 @@ static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
*ptr = __fcs(buf); ptr++;
- return rfcomm_send_frame(s, buf, ptr - buf);
+ return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
}
static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
@@ -1040,7 +1056,7 @@ static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
*ptr = __fcs(buf); ptr++;
- return rfcomm_send_frame(s, buf, ptr - buf);
+ return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
}
static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
@@ -1091,7 +1107,7 @@ static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
*ptr = __fcs(buf); ptr++;
- return rfcomm_send_frame(s, buf, ptr - buf);
+ return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
}
static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
@@ -1769,7 +1785,8 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
return skb_queue_len(&d->tx_queue);
while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
- err = rfcomm_send_frame(d->session, skb->data, skb->len);
+ err = rfcomm_send_frame(d->session, skb->data, skb->len,
+ skb->priority);
if (err < 0) {
skb_queue_head(&d->tx_queue, skb);
break;
@@ -2120,15 +2137,13 @@ static struct hci_cb rfcomm_cb = {
static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
{
struct rfcomm_session *s;
- struct list_head *pp, *p;
rfcomm_lock();
- list_for_each(p, &session_list) {
- s = list_entry(p, struct rfcomm_session, list);
- list_for_each(pp, &s->dlcs) {
+ list_for_each_entry(s, &session_list, list) {
+ struct rfcomm_dlc *d;
+ list_for_each_entry(d, &s->dlcs, list) {
struct sock *sk = s->sock->sk;
- struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list);
seq_printf(f, "%s %s %ld %d %d %d %d\n",
batostr(&bt_sk(sk)->src),
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 5417f6127323..aea2bdd1510f 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -600,6 +600,8 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
break;
}
+ skb->priority = sk->sk_priority;
+
err = rfcomm_dlc_send(d, skb);
if (err < 0) {
kfree_skb(skb);
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index c258796313e0..fa8f4de53b99 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -34,6 +34,7 @@
#include <linux/capability.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
+#include <linux/workqueue.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -65,7 +66,7 @@ struct rfcomm_dev {
struct rfcomm_dlc *dlc;
struct tty_struct *tty;
wait_queue_head_t wait;
- struct tasklet_struct wakeup_task;
+ struct work_struct wakeup_task;
struct device *tty_dev;
@@ -81,7 +82,7 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
-static void rfcomm_tty_wakeup(unsigned long arg);
+static void rfcomm_tty_wakeup(struct work_struct *work);
/* ---- Device functions ---- */
static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
@@ -133,13 +134,10 @@ static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
static struct rfcomm_dev *__rfcomm_dev_get(int id)
{
struct rfcomm_dev *dev;
- struct list_head *p;
- list_for_each(p, &rfcomm_dev_list) {
- dev = list_entry(p, struct rfcomm_dev, list);
+ list_for_each_entry(dev, &rfcomm_dev_list, list)
if (dev->id == id)
return dev;
- }
return NULL;
}
@@ -197,7 +195,7 @@ static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
{
- struct rfcomm_dev *dev;
+ struct rfcomm_dev *dev, *entry;
struct list_head *head = &rfcomm_dev_list, *p;
int err = 0;
@@ -212,8 +210,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
if (req->dev_id < 0) {
dev->id = 0;
- list_for_each(p, &rfcomm_dev_list) {
- if (list_entry(p, struct rfcomm_dev, list)->id != dev->id)
+ list_for_each_entry(entry, &rfcomm_dev_list, list) {
+ if (entry->id != dev->id)
break;
dev->id++;
@@ -222,9 +220,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
} else {
dev->id = req->dev_id;
- list_for_each(p, &rfcomm_dev_list) {
- struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
-
+ list_for_each_entry(entry, &rfcomm_dev_list, list) {
if (entry->id == dev->id) {
err = -EADDRINUSE;
goto out;
@@ -257,7 +253,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
atomic_set(&dev->opened, 0);
init_waitqueue_head(&dev->wait);
- tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
+ INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup);
skb_queue_head_init(&dev->pending);
@@ -351,7 +347,7 @@ static void rfcomm_wfree(struct sk_buff *skb)
struct rfcomm_dev *dev = (void *) skb->sk;
atomic_sub(skb->truesize, &dev->wmem_alloc);
if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
- tasklet_schedule(&dev->wakeup_task);
+ queue_work(system_nrt_wq, &dev->wakeup_task);
rfcomm_dev_put(dev);
}
@@ -455,9 +451,9 @@ static int rfcomm_release_dev(void __user *arg)
static int rfcomm_get_dev_list(void __user *arg)
{
+ struct rfcomm_dev *dev;
struct rfcomm_dev_list_req *dl;
struct rfcomm_dev_info *di;
- struct list_head *p;
int n = 0, size, err;
u16 dev_num;
@@ -479,8 +475,7 @@ static int rfcomm_get_dev_list(void __user *arg)
read_lock_bh(&rfcomm_dev_lock);
- list_for_each(p, &rfcomm_dev_list) {
- struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
+ list_for_each_entry(dev, &rfcomm_dev_list, list) {
if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
continue;
(di + n)->id = dev->id;
@@ -635,9 +630,10 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
}
/* ---- TTY functions ---- */
-static void rfcomm_tty_wakeup(unsigned long arg)
+static void rfcomm_tty_wakeup(struct work_struct *work)
{
- struct rfcomm_dev *dev = (void *) arg;
+ struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev,
+ wakeup_task);
struct tty_struct *tty = dev->tty;
if (!tty)
return;
@@ -762,7 +758,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
rfcomm_dlc_close(dev->dlc, 0);
clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
- tasklet_kill(&dev->wakeup_task);
+ cancel_work_sync(&dev->wakeup_task);
rfcomm_dlc_lock(dev->dlc);
tty->driver_data = NULL;
@@ -1155,9 +1151,11 @@ static const struct tty_operations rfcomm_ops = {
int __init rfcomm_init_ttys(void)
{
+ int error;
+
rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS);
if (!rfcomm_tty_driver)
- return -1;
+ return -ENOMEM;
rfcomm_tty_driver->owner = THIS_MODULE;
rfcomm_tty_driver->driver_name = "rfcomm";
@@ -1172,10 +1170,11 @@ int __init rfcomm_init_ttys(void)
rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
- if (tty_register_driver(rfcomm_tty_driver)) {
+ error = tty_register_driver(rfcomm_tty_driver);
+ if (error) {
BT_ERR("Can't register RFCOMM TTY driver");
put_tty_driver(rfcomm_tty_driver);
- return -1;
+ return error;
}
BT_INFO("RFCOMM TTY layer initialized");
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 759b63572641..94e94ca35384 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -181,7 +181,8 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
if (!skb)
return;
- hci_send_acl(conn->hcon, skb, 0);
+ skb->priority = HCI_PRIO_MAX;
+ hci_send_acl(conn->hchan, skb, 0);
mod_timer(&conn->security_timer, jiffies +
msecs_to_jiffies(SMP_TIMEOUT));
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 18a3cebb753d..06438f926022 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3169,6 +3169,26 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
}
EXPORT_SYMBOL_GPL(skb_tstamp_tx);
+void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
+{
+ struct sock *sk = skb->sk;
+ struct sock_exterr_skb *serr;
+ int err;
+
+ skb->wifi_acked_valid = 1;
+ skb->wifi_acked = acked;
+
+ serr = SKB_EXT_ERR(skb);
+ memset(serr, 0, sizeof(*serr));
+ serr->ee.ee_errno = ENOMSG;
+ serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
+
+ err = sock_queue_err_skb(sk, skb);
+ if (err)
+ kfree_skb(skb);
+}
+EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
+
/**
* skb_partial_csum_set - set up and verify partial csum values for packet
diff --git a/net/core/sock.c b/net/core/sock.c
index 4ed7b1d12f5e..cbdf51c0d5ac 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -740,6 +740,11 @@ set_rcvbuf:
case SO_RXQ_OVFL:
sock_valbool_flag(sk, SOCK_RXQ_OVFL, valbool);
break;
+
+ case SO_WIFI_STATUS:
+ sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool);
+ break;
+
default:
ret = -ENOPROTOOPT;
break;
@@ -961,6 +966,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
v.val = !!sock_flag(sk, SOCK_RXQ_OVFL);
break;
+ case SO_WIFI_STATUS:
+ v.val = !!sock_flag(sk, SOCK_WIFI_STATUS);
+ break;
+
default:
return -ENOPROTOOPT;
}
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 93b243422659..476b106c0b1c 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -177,7 +177,8 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
if (sdata->vif.type == NL80211_IFTYPE_AP ||
- sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index b3f65520e7a7..39d72ccaffb3 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -78,7 +78,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
if (sdata->vif.type == NL80211_IFTYPE_AP ||
- sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
@@ -372,13 +373,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
pubsta->addr, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- /*
- * The aggregation code is not prepared to handle
- * anything but STA/AP due to the BSSID handling.
- * IBSS could work in the code but isn't supported
- * by drivers or the standard.
- */
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_AP)
return -EINVAL;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index d06c65fa5526..2577c45069e5 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -411,7 +411,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP) |
- BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_TDLS_PEER);
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
@@ -422,6 +423,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
if (test_sta_flag(sta, WLAN_STA_AUTH))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+ sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
}
@@ -488,6 +491,31 @@ static void ieee80211_config_ap_ssid(struct ieee80211_sub_if_data *sdata,
(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
}
+static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
+ u8 *resp, size_t resp_len)
+{
+ struct sk_buff *new, *old;
+
+ if (!resp || !resp_len)
+ return -EINVAL;
+
+ old = sdata->u.ap.probe_resp;
+
+ new = dev_alloc_skb(resp_len);
+ if (!new)
+ return -ENOMEM;
+
+ memcpy(skb_put(new, resp_len), resp, resp_len);
+
+ rcu_assign_pointer(sdata->u.ap.probe_resp, new);
+ synchronize_rcu();
+
+ if (old)
+ dev_kfree_skb(old);
+
+ return 0;
+}
+
/*
* This handles both adding a beacon and setting new beacon info
*/
@@ -498,6 +526,7 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
int new_head_len, new_tail_len;
int size;
int err = -EINVAL;
+ u32 changed = 0;
old = rtnl_dereference(sdata->u.ap.beacon);
@@ -581,11 +610,17 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
kfree(old);
+ err = ieee80211_set_probe_resp(sdata, params->probe_resp,
+ params->probe_resp_len);
+ if (!err)
+ changed |= BSS_CHANGED_AP_PROBE_RESP;
+
ieee80211_config_ap_ssid(sdata, params);
+ changed |= BSS_CHANGED_BEACON_ENABLED |
+ BSS_CHANGED_BEACON |
+ BSS_CHANGED_SSID;
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
- BSS_CHANGED_BEACON |
- BSS_CHANGED_SSID);
+ ieee80211_bss_info_change_notify(sdata, changed);
return 0;
}
@@ -594,6 +629,8 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata;
struct beacon_data *old;
+ struct ieee80211_sub_if_data *vlan;
+ int ret;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -601,7 +638,24 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
if (old)
return -EALREADY;
- return ieee80211_config_beacon(sdata, params);
+ ret = ieee80211_config_beacon(sdata, params);
+ if (ret)
+ return ret;
+
+ /*
+ * Apply control port protocol, this allows us to
+ * not encrypt dynamic WEP control frames.
+ */
+ sdata->control_port_protocol = params->crypto.control_port_ethertype;
+ sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
+ vlan->control_port_protocol =
+ params->crypto.control_port_ethertype;
+ vlan->control_port_no_encrypt =
+ params->crypto.control_port_no_encrypt;
+ }
+
+ return 0;
}
static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
@@ -778,7 +832,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,
}
if (params->ht_capa)
- ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
params->ht_capa,
&sta->sta.ht_cap);
@@ -847,7 +901,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
sta_apply_parameters(local, sta, params);
- rate_control_rate_init(sta);
+ /*
+ * for TDLS, rate control should be initialized only when supported
+ * rates are known.
+ */
+ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+ rate_control_rate_init(sta);
layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
sdata->vif.type == NL80211_IFTYPE_AP;
@@ -931,6 +990,9 @@ static int ieee80211_change_station(struct wiphy *wiphy,
sta_apply_parameters(local, sta, params);
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates)
+ rate_control_rate_init(sta);
+
rcu_read_unlock();
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
@@ -1394,7 +1456,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
(old_oper_type != local->_oper_channel_type))
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
- if ((sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR) &&
+ if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR &&
old_vif_oper_type != sdata->vif.bss_conf.channel_type)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT);
@@ -1917,7 +1979,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
- u64 *cookie)
+ bool dont_wait_for_ack, u64 *cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
@@ -1925,10 +1987,15 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta;
struct ieee80211_work *wk;
const struct ieee80211_mgmt *mgmt = (void *)buf;
- u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
- IEEE80211_TX_CTL_REQ_TX_STATUS;
+ u32 flags;
bool is_offchan = false;
+ if (dont_wait_for_ack)
+ flags = IEEE80211_TX_CTL_NO_ACK;
+ else
+ flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+ IEEE80211_TX_CTL_REQ_TX_STATUS;
+
/* Check that we are on the requested channel for transmission */
if (chan != local->tmp_channel &&
chan != local->oper_channel)
@@ -2488,6 +2555,82 @@ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
+static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u64 *cookie)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_qos_hdr *nullfunc;
+ struct sk_buff *skb;
+ int size = sizeof(*nullfunc);
+ __le16 fc;
+ bool qos;
+ struct ieee80211_tx_info *info;
+ struct sta_info *sta;
+
+ rcu_read_lock();
+ sta = sta_info_get(sdata, peer);
+ if (sta) {
+ qos = test_sta_flag(sta, WLAN_STA_WME);
+ rcu_read_unlock();
+ } else {
+ rcu_read_unlock();
+ return -ENOLINK;
+ }
+
+ if (qos) {
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_NULLFUNC |
+ IEEE80211_FCTL_FROMDS);
+ } else {
+ size -= 2;
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_NULLFUNC |
+ IEEE80211_FCTL_FROMDS);
+ }
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
+ if (!skb)
+ return -ENOMEM;
+
+ skb->dev = dev;
+
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ nullfunc = (void *) skb_put(skb, size);
+ nullfunc->frame_control = fc;
+ nullfunc->duration_id = 0;
+ memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
+ memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
+ memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
+ nullfunc->seq_ctrl = 0;
+
+ info = IEEE80211_SKB_CB(skb);
+
+ info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
+ IEEE80211_TX_INTFL_NL80211_FRAME_TX;
+
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+ skb->priority = 7;
+ if (qos)
+ nullfunc->qos_ctrl = cpu_to_le16(7);
+
+ local_bh_disable();
+ ieee80211_xmit(sdata, skb);
+ local_bh_enable();
+
+ *cookie = (unsigned long) skb;
+ return 0;
+}
+
+static struct ieee80211_channel *
+ieee80211_wiphy_get_channel(struct wiphy *wiphy)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ return local->oper_channel;
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -2553,4 +2696,6 @@ struct cfg80211_ops mac80211_config_ops = {
.set_rekey_data = ieee80211_set_rekey_data,
.tdls_oper = ieee80211_tdls_oper,
.tdls_mgmt = ieee80211_tdls_mgmt,
+ .probe_client = ieee80211_probe_client,
+ .get_channel = ieee80211_wiphy_get_channel,
};
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 883996b2f99f..00cefcb493eb 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -190,7 +190,7 @@ static ssize_t uapsd_max_sp_len_write(struct file *file,
return -EFAULT;
buf[len] = '\0';
- ret = strict_strtoul(buf, 0, &val);
+ ret = kstrtoul(buf, 0, &val);
if (ret)
return -EINVAL;
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index c5f341798c16..edfdd74d4351 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -63,10 +63,10 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
int res = scnprintf(buf, sizeof(buf),
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
- TEST(SHORT_PREAMBLE), TEST(ASSOC_AP),
+ TEST(SHORT_PREAMBLE),
TEST(WME), TEST(WDS), TEST(CLEAR_PS_FILT),
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 5f165d7eb2db..49cc5e0e8a6a 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -5,11 +5,24 @@
#include "ieee80211_i.h"
#include "driver-trace.h"
+static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)
+{
+ WARN_ON(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER));
+}
+
static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb)
{
local->ops->tx(&local->hw, skb);
}
+static inline void drv_tx_frags(struct ieee80211_local *local,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct sk_buff_head *skbs)
+{
+ local->ops->tx_frags(&local->hw, vif, sta, skbs);
+}
+
static inline int drv_start(struct ieee80211_local *local)
{
int ret;
@@ -69,15 +82,23 @@ static inline int drv_resume(struct ieee80211_local *local)
#endif
static inline int drv_add_interface(struct ieee80211_local *local,
- struct ieee80211_vif *vif)
+ struct ieee80211_sub_if_data *sdata)
{
int ret;
might_sleep();
- trace_drv_add_interface(local, vif_to_sdata(vif));
- ret = local->ops->add_interface(&local->hw, vif);
+ if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_MONITOR))
+ return -EINVAL;
+
+ trace_drv_add_interface(local, sdata);
+ ret = local->ops->add_interface(&local->hw, &sdata->vif);
trace_drv_return_int(local, ret);
+
+ if (ret == 0)
+ sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
+
return ret;
}
@@ -89,6 +110,8 @@ static inline int drv_change_interface(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_change_interface(local, sdata, type, p2p);
ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
trace_drv_return_int(local, ret);
@@ -96,12 +119,15 @@ static inline int drv_change_interface(struct ieee80211_local *local,
}
static inline void drv_remove_interface(struct ieee80211_local *local,
- struct ieee80211_vif *vif)
+ struct ieee80211_sub_if_data *sdata)
{
might_sleep();
- trace_drv_remove_interface(local, vif_to_sdata(vif));
- local->ops->remove_interface(&local->hw, vif);
+ check_sdata_in_driver(sdata);
+
+ trace_drv_remove_interface(local, sdata);
+ local->ops->remove_interface(&local->hw, &sdata->vif);
+ sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
trace_drv_return_void(local);
}
@@ -124,6 +150,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
{
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_bss_info_changed(local, sdata, info, changed);
if (local->ops->bss_info_changed)
local->ops->bss_info_changed(&local->hw, &sdata->vif, info, changed);
@@ -139,6 +167,8 @@ static inline int drv_tx_sync(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_tx_sync(local, sdata, bssid, type);
if (local->ops->tx_sync)
ret = local->ops->tx_sync(&local->hw, &sdata->vif,
@@ -154,6 +184,8 @@ static inline void drv_finish_tx_sync(struct ieee80211_local *local,
{
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_finish_tx_sync(local, sdata, bssid, type);
if (local->ops->finish_tx_sync)
local->ops->finish_tx_sync(&local->hw, &sdata->vif,
@@ -211,6 +243,8 @@ static inline int drv_set_key(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_set_key(local, cmd, sdata, sta, key);
ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
trace_drv_return_int(local, ret);
@@ -228,6 +262,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
if (sta)
ista = &sta->sta;
+ check_sdata_in_driver(sdata);
+
trace_drv_update_tkip_key(local, sdata, conf, ista, iv32);
if (local->ops->update_tkip_key)
local->ops->update_tkip_key(&local->hw, &sdata->vif, conf,
@@ -243,6 +279,8 @@ static inline int drv_hw_scan(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_hw_scan(local, sdata);
ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);
trace_drv_return_int(local, ret);
@@ -254,6 +292,8 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local,
{
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_cancel_hw_scan(local, sdata);
local->ops->cancel_hw_scan(&local->hw, &sdata->vif);
trace_drv_return_void(local);
@@ -269,6 +309,8 @@ drv_sched_scan_start(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_sched_scan_start(local, sdata);
ret = local->ops->sched_scan_start(&local->hw, &sdata->vif,
req, ies);
@@ -281,6 +323,8 @@ static inline void drv_sched_scan_stop(struct ieee80211_local *local,
{
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_sched_scan_stop(local, sdata);
local->ops->sched_scan_stop(&local->hw, &sdata->vif);
trace_drv_return_void(local);
@@ -377,6 +421,8 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
enum sta_notify_cmd cmd,
struct ieee80211_sta *sta)
{
+ check_sdata_in_driver(sdata);
+
trace_drv_sta_notify(local, sdata, cmd, sta);
if (local->ops->sta_notify)
local->ops->sta_notify(&local->hw, &sdata->vif, cmd, sta);
@@ -391,6 +437,8 @@ static inline int drv_sta_add(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_sta_add(local, sdata, sta);
if (local->ops->sta_add)
ret = local->ops->sta_add(&local->hw, &sdata->vif, sta);
@@ -406,6 +454,8 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
{
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_sta_remove(local, sdata, sta);
if (local->ops->sta_remove)
local->ops->sta_remove(&local->hw, &sdata->vif, sta);
@@ -421,6 +471,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_conf_tx(local, sdata, queue, params);
if (local->ops->conf_tx)
ret = local->ops->conf_tx(&local->hw, &sdata->vif,
@@ -436,6 +488,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_get_tsf(local, sdata);
if (local->ops->get_tsf)
ret = local->ops->get_tsf(&local->hw, &sdata->vif);
@@ -449,6 +503,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local,
{
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_set_tsf(local, sdata, tsf);
if (local->ops->set_tsf)
local->ops->set_tsf(&local->hw, &sdata->vif, tsf);
@@ -460,6 +516,8 @@ static inline void drv_reset_tsf(struct ieee80211_local *local,
{
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_reset_tsf(local, sdata);
if (local->ops->reset_tsf)
local->ops->reset_tsf(&local->hw, &sdata->vif);
@@ -489,6 +547,8 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size);
if (local->ops->ampdu_action)
@@ -644,6 +704,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
might_sleep();
+ check_sdata_in_driver(sdata);
+
trace_drv_set_bitrate_mask(local, sdata, mask);
if (local->ops->set_bitrate_mask)
ret = local->ops->set_bitrate_mask(&local->hw,
@@ -657,6 +719,8 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct cfg80211_gtk_rekey_data *data)
{
+ check_sdata_in_driver(sdata);
+
trace_drv_set_rekey_data(local, sdata, data);
if (local->ops->set_rekey_data)
local->ops->set_rekey_data(&local->hw, &sdata->vif, data);
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index f0fb737efa86..810cfbea6ad1 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -19,7 +19,82 @@
#include "ieee80211_i.h"
#include "rate.h"
-void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
+bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata)
+{
+ const __le16 flg = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ if ((sdata->u.mgd.ht_capa_mask.cap_info & flg) &&
+ !(sdata->u.mgd.ht_capa.cap_info & flg))
+ return true;
+ return false;
+}
+
+void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta_ht_cap *ht_cap,
+ u16 flag)
+{
+ __le16 le_flag = cpu_to_le16(flag);
+ if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) {
+ if (!(sdata->u.mgd.ht_capa.cap_info & le_flag))
+ ht_cap->cap &= ~flag;
+ }
+}
+
+void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta_ht_cap *ht_cap)
+{
+ u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask);
+ u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
+ int i;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION) {
+ WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
+ return;
+ }
+
+ /* NOTE: If you add more over-rides here, update register_hw
+ * ht_capa_mod_msk logic in main.c as well.
+ * And, if this method can ever change ht_cap.ht_supported, fix
+ * the check in ieee80211_add_ht_ie.
+ */
+
+ /* check for HT over-rides, MCS rates first. */
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ u8 m = smask[i];
+ ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */
+ /* Add back rates that are supported */
+ ht_cap->mcs.rx_mask[i] |= (m & scaps[i]);
+ }
+
+ /* Force removal of HT-40 capabilities? */
+ __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
+
+ /* Allow user to disable the max-AMSDU bit. */
+ __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
+
+ /* Allow user to decrease AMPDU factor */
+ if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_FACTOR) {
+ u8 n = sdata->u.mgd.ht_capa.ampdu_params_info
+ & IEEE80211_HT_AMPDU_PARM_FACTOR;
+ if (n < ht_cap->ampdu_factor)
+ ht_cap->ampdu_factor = n;
+ }
+
+ /* Allow the user to increase AMPDU density. */
+ if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_DENSITY) {
+ u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_DENSITY)
+ >> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
+ if (n > ht_cap->ampdu_density)
+ ht_cap->ampdu_density = n;
+ }
+}
+
+
+void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
struct ieee80211_ht_cap *ht_cap_ie,
struct ieee80211_sta_ht_cap *ht_cap)
{
@@ -103,6 +178,12 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
/* handle MCS rate 32 too */
if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
ht_cap->mcs.rx_mask[32/8] |= 1;
+
+ /*
+ * If user has specified capability over-rides, take care
+ * of that here.
+ */
+ ieee80211_apply_htcap_overrides(sdata, ht_cap);
}
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
@@ -196,7 +277,8 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
if (sdata->vif.type == NL80211_IFTYPE_AP ||
- sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index ede9a8b341ac..7d84af70132f 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -97,6 +97,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
/* if merging, indicate to driver that we leave the old IBSS */
if (sdata->vif.bss_conf.ibss_joined) {
sdata->vif.bss_conf.ibss_joined = false;
+ netif_carrier_off(sdata->dev);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS);
}
@@ -207,6 +208,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
bss = cfg80211_inform_bss_frame(local->hw.wiphy, local->hw.conf.channel,
mgmt, skb->len, 0, GFP_KERNEL);
cfg80211_put_bss(bss);
+ netif_carrier_on(sdata->dev);
cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
}
@@ -990,6 +992,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
}
sta_info_flush(sdata->local, sdata);
+ netif_carrier_off(sdata->dev);
/* remove beacon */
kfree(sdata->u.ibss.ie);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index ea10a51babda..762243e469df 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -24,6 +24,7 @@
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
#include <linux/leds.h>
+#include <linux/idr.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
@@ -141,6 +142,7 @@ typedef unsigned __bitwise__ ieee80211_tx_result;
struct ieee80211_tx_data {
struct sk_buff *skb;
+ struct sk_buff_head skbs;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
@@ -184,12 +186,15 @@ enum ieee80211_packet_rx_flags {
* enum ieee80211_rx_flags - RX data flags
*
* @IEEE80211_RX_CMNTR: received on cooked monitor already
+ * @IEEE80211_RX_BEACON_REPORTED: This frame was already reported
+ * to cfg80211_report_obss_beacon().
*
* These flags are used across handling multiple interfaces
* for a single frame.
*/
enum ieee80211_rx_flags {
IEEE80211_RX_CMNTR = BIT(0),
+ IEEE80211_RX_BEACON_REPORTED = BIT(1),
};
struct ieee80211_rx_data {
@@ -228,6 +233,7 @@ struct beacon_data {
struct ieee80211_if_ap {
struct beacon_data __rcu *beacon;
+ struct sk_buff __rcu *probe_resp;
struct list_head vlans;
@@ -443,6 +449,9 @@ struct ieee80211_if_managed {
*/
int rssi_min_thold, rssi_max_thold;
int last_ave_beacon_signal;
+
+ struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
+ struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
};
struct ieee80211_if_ibss {
@@ -543,6 +552,7 @@ struct ieee80211_if_mesh {
* associated stations and deliver multicast frames both
* back to wireless media and to the local net stack.
* @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
+ * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
*/
enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_ALLMULTI = BIT(0),
@@ -550,6 +560,7 @@ enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_OPERATING_GMODE = BIT(2),
IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
+ IEEE80211_SDATA_IN_DRIVER = BIT(5),
};
/**
@@ -722,17 +733,16 @@ enum {
* operating channel
* @SCAN_SET_CHANNEL: Set the next channel to be scanned
* @SCAN_SEND_PROBE: Send probe requests and wait for probe responses
- * @SCAN_LEAVE_OPER_CHANNEL: Leave the operating channel, notify the AP
- * about us leaving the channel and stop all associated STA interfaces
- * @SCAN_ENTER_OPER_CHANNEL: Enter the operating channel again, notify the
- * AP about us being back and restart all associated STA interfaces
+ * @SCAN_SUSPEND: Suspend the scan and go back to operating channel to
+ * send out data
+ * @SCAN_RESUME: Resume the scan and scan the next channel
*/
enum mac80211_scan_state {
SCAN_DECISION,
SCAN_SET_CHANNEL,
SCAN_SEND_PROBE,
- SCAN_LEAVE_OPER_CHANNEL,
- SCAN_ENTER_OPER_CHANNEL,
+ SCAN_SUSPEND,
+ SCAN_RESUME,
};
struct ieee80211_local {
@@ -1012,6 +1022,9 @@ struct ieee80211_local {
u32 hw_roc_cookie;
bool hw_roc_for_tx;
+ struct idr ack_status_frames;
+ spinlock_t ack_status_lock;
+
/* dummy netdev for use w/ NAPI */
struct net_device napi_dev;
@@ -1030,6 +1043,69 @@ struct ieee80211_ra_tid {
u16 tid;
};
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+ u8 *ie_start;
+ size_t total_len;
+
+ /* pointers to IEs */
+ u8 *ssid;
+ u8 *supp_rates;
+ u8 *fh_params;
+ u8 *ds_params;
+ u8 *cf_params;
+ struct ieee80211_tim_ie *tim;
+ u8 *ibss_params;
+ u8 *challenge;
+ u8 *wpa;
+ u8 *rsn;
+ u8 *erp_info;
+ u8 *ext_supp_rates;
+ u8 *wmm_info;
+ u8 *wmm_param;
+ struct ieee80211_ht_cap *ht_cap_elem;
+ struct ieee80211_ht_info *ht_info_elem;
+ struct ieee80211_meshconf_ie *mesh_config;
+ u8 *mesh_id;
+ u8 *peering;
+ u8 *preq;
+ u8 *prep;
+ u8 *perr;
+ struct ieee80211_rann_ie *rann;
+ u8 *ch_switch_elem;
+ u8 *country_elem;
+ u8 *pwr_constr_elem;
+ u8 *quiet_elem; /* first quite element */
+ u8 *timeout_int;
+
+ /* length of them, respectively */
+ u8 ssid_len;
+ u8 supp_rates_len;
+ u8 fh_params_len;
+ u8 ds_params_len;
+ u8 cf_params_len;
+ u8 tim_len;
+ u8 ibss_params_len;
+ u8 challenge_len;
+ u8 wpa_len;
+ u8 rsn_len;
+ u8 erp_info_len;
+ u8 ext_supp_rates_len;
+ u8 wmm_info_len;
+ u8 wmm_param_len;
+ u8 mesh_id_len;
+ u8 peering_len;
+ u8 preq_len;
+ u8 prep_len;
+ u8 perr_len;
+ u8 ch_switch_elem_len;
+ u8 country_elem_len;
+ u8 pwr_constr_elem_len;
+ u8 quiet_elem_len;
+ u8 num_of_quiet_elem; /* can be more the one */
+ u8 timeout_int_len;
+};
+
static inline struct ieee80211_local *hw_to_local(
struct ieee80211_hw *hw)
{
@@ -1179,7 +1255,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev);
/* HT */
-void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
+bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata);
+void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta_ht_cap *ht_cap);
+void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
struct ieee80211_ht_cap *ht_cap_ie,
struct ieee80211_sta_ht_cap *ht_cap);
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
@@ -1334,6 +1414,12 @@ void ieee80211_recalc_smps(struct ieee80211_local *local);
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
const u8 *ids, int n_ids, size_t offset);
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
+u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+ u16 cap);
+u8 *ieee80211_ie_build_ht_info(u8 *pos,
+ struct ieee80211_sta_ht_cap *ht_cap,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type channel_type);
/* internal work items */
void ieee80211_work_init(struct ieee80211_local *local);
@@ -1362,6 +1448,8 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,
bool ieee80211_set_channel_type(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum nl80211_channel_type chantype);
+enum nl80211_channel_type
+ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 30d73552e9ab..b34ca0cbdf6c 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -188,11 +188,22 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
if (!is_valid_ether_addr(sdata->u.wds.remote_addr))
return -ENOLINK;
break;
- case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_AP_VLAN: {
+ struct ieee80211_sub_if_data *master;
+
if (!sdata->bss)
return -ENOLINK;
+
list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
+
+ master = container_of(sdata->bss,
+ struct ieee80211_sub_if_data, u.ap);
+ sdata->control_port_protocol =
+ master->control_port_protocol;
+ sdata->control_port_no_encrypt =
+ master->control_port_no_encrypt;
break;
+ }
case NL80211_IFTYPE_AP:
sdata->bss = &sdata->u.ap;
break;
@@ -265,7 +276,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
break;
default:
if (coming_up) {
- res = drv_add_interface(local, &sdata->vif);
+ res = drv_add_interface(local, sdata);
if (res)
goto err_stop;
}
@@ -282,10 +293,18 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_bss_info_change_notify(sdata, changed);
- if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
netif_carrier_off(dev);
else
netif_carrier_on(dev);
+
+ /*
+ * set default queue parameters so drivers don't
+ * need to initialise the hardware if the hardware
+ * doesn't start up with sane defaults
+ */
+ ieee80211_set_wmm_default(sdata);
}
set_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -329,15 +348,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
if (coming_up)
local->open_count++;
- if (hw_reconf_flags) {
+ if (hw_reconf_flags)
ieee80211_hw_config(local, hw_reconf_flags);
- /*
- * set default queue parameters so drivers don't
- * need to initialise the hardware if the hardware
- * doesn't start up with sane defaults
- */
- ieee80211_set_wmm_default(sdata);
- }
ieee80211_recalc_ps(local, -1);
@@ -345,7 +357,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
return 0;
err_del_interface:
- drv_remove_interface(local, &sdata->vif);
+ drv_remove_interface(local, sdata);
err_stop:
if (!local->open_count)
drv_stop(local);
@@ -450,15 +462,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sub_if_data *vlan, *tmpsdata;
struct beacon_data *old_beacon =
rtnl_dereference(sdata->u.ap.beacon);
+ struct sk_buff *old_probe_resp =
+ rtnl_dereference(sdata->u.ap.probe_resp);
/* sdata_running will return false, so this will disable */
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_BEACON_ENABLED);
- /* remove beacon */
+ /* remove beacon and probe response */
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+ RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
synchronize_rcu();
kfree(old_beacon);
+ kfree_skb(old_probe_resp);
/* down all dependent devices, that is VLANs */
list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
@@ -520,7 +536,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_free_keys(sdata);
if (going_down)
- drv_remove_interface(local, &sdata->vif);
+ drv_remove_interface(local, sdata);
}
sdata->bss = NULL;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index fb02ea52d2c2..87a89741432d 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -134,9 +134,13 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
- (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+ (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) ||
+ (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)))
sdata->crypto_tx_tailroom_needed_cnt--;
+ WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
+ (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV));
+
return 0;
}
@@ -179,7 +183,8 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
sdata = key->sdata;
if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
- (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
+ (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) ||
+ (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)))
increment_tailroom_need_count(sdata);
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index d999bf3b84e1..dddedfad5404 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -100,7 +100,7 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
*/
bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local)
{
- struct ieee80211_channel *chan, *scan_chan;
+ struct ieee80211_channel *chan;
enum nl80211_channel_type channel_type;
/* This logic needs to match logic in ieee80211_hw_config */
@@ -114,7 +114,7 @@ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local)
else
channel_type = NL80211_CHAN_NO_HT;
} else if (local->tmp_channel) {
- chan = scan_chan = local->tmp_channel;
+ chan = local->tmp_channel;
channel_type = local->tmp_channel_type;
} else {
chan = local->oper_channel;
@@ -126,8 +126,8 @@ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local)
return false;
/* Check current hardware-config against oper_channel. */
- if ((local->oper_channel != local->hw.conf.channel) ||
- (local->_oper_channel_type != local->hw.conf.channel_type))
+ if (local->oper_channel != local->hw.conf.channel ||
+ local->_oper_channel_type != local->hw.conf.channel_type)
return false;
return true;
@@ -135,7 +135,7 @@ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local)
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{
- struct ieee80211_channel *chan, *scan_chan;
+ struct ieee80211_channel *chan;
int ret = 0;
int power;
enum nl80211_channel_type channel_type;
@@ -143,14 +143,12 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
might_sleep();
- scan_chan = local->scan_channel;
-
/* If this off-channel logic ever changes, ieee80211_on_oper_channel
* may need to change as well.
*/
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
- if (scan_chan) {
- chan = scan_chan;
+ if (local->scan_channel) {
+ chan = local->scan_channel;
/* If scanning on oper channel, use whatever channel-type
* is currently in use.
*/
@@ -159,7 +157,7 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
else
channel_type = NL80211_CHAN_NO_HT;
} else if (local->tmp_channel) {
- chan = scan_chan = local->tmp_channel;
+ chan = local->tmp_channel;
channel_type = local->tmp_channel_type;
} else {
chan = local->oper_channel;
@@ -560,6 +558,19 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
},
};
+static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
+ .ampdu_params_info = IEEE80211_HT_AMPDU_PARM_FACTOR |
+ IEEE80211_HT_AMPDU_PARM_DENSITY,
+
+ .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_MAX_AMSDU |
+ IEEE80211_HT_CAP_SGI_40),
+ .mcs = {
+ .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, },
+ },
+};
+
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
@@ -595,7 +606,12 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->flags |= WIPHY_FLAG_NETNS_OK |
WIPHY_FLAG_4ADDR_AP |
- WIPHY_FLAG_4ADDR_STATION;
+ WIPHY_FLAG_4ADDR_STATION |
+ WIPHY_FLAG_REPORTS_OBSS |
+ WIPHY_FLAG_OFFCHAN_TX |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+
+ wiphy->features = NL80211_FEATURE_SK_TX_STATUS;
if (!ops->set_key)
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
@@ -608,7 +624,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
- BUG_ON(!ops->tx);
+ BUG_ON(!ops->tx && !ops->tx_frags);
BUG_ON(!ops->start);
BUG_ON(!ops->stop);
BUG_ON(!ops->config);
@@ -628,6 +644,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->user_power_level = -1;
local->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
+ wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
INIT_LIST_HEAD(&local->interfaces);
@@ -670,6 +687,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
INIT_WORK(&local->sched_scan_stopped_work,
ieee80211_sched_scan_stopped_work);
+ spin_lock_init(&local->ack_status_lock);
+ idr_init(&local->ack_status_frames);
+ /* preallocate at least one entry */
+ idr_pre_get(&local->ack_status_frames, GFP_KERNEL);
+
sta_info_init(local);
for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
@@ -1045,6 +1067,13 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(ieee80211_unregister_hw);
+static int ieee80211_free_ack_frame(int id, void *p, void *data)
+{
+ WARN_ONCE(1, "Have pending ack frames!\n");
+ kfree_skb(p);
+ return 0;
+}
+
void ieee80211_free_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -1055,6 +1084,10 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
if (local->wiphy_ciphers_allocated)
kfree(local->hw.wiphy->cipher_suites);
+ idr_for_each(&local->ack_status_frames,
+ ieee80211_free_ack_frame, NULL);
+ idr_destroy(&local->ack_status_frames);
+
wiphy_free(local->hw.wiphy);
}
EXPORT_SYMBOL(ieee80211_free_hw);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index a7078fdba8ca..ee82d2f7f114 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -76,6 +76,7 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_local *local = sdata->local;
/*
* As support for each feature is added, check for matching
@@ -87,15 +88,23 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat
* - MDA enabled
* - Power management control on fc
*/
- if (ifmsh->mesh_id_len == ie->mesh_id_len &&
- memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
- (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) &&
- (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) &&
- (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) &&
- (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) &&
- (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))
- return true;
-
+ if (!(ifmsh->mesh_id_len == ie->mesh_id_len &&
+ memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
+ (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) &&
+ (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) &&
+ (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) &&
+ (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) &&
+ (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
+ goto mismatch;
+
+ /* disallow peering with mismatched channel types for now */
+ if (ie->ht_info_elem &&
+ (local->_oper_channel_type !=
+ ieee80211_ht_info_to_channel_type(ie->ht_info_elem)))
+ goto mismatch;
+
+ return true;
+mismatch:
return false;
}
@@ -341,6 +350,49 @@ int mesh_add_ds_params_ie(struct sk_buff *skb,
return 0;
}
+int mesh_add_ht_cap_ie(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ u8 *pos;
+
+ sband = local->hw.wiphy->bands[local->oper_channel->band];
+ if (!sband->ht_cap.ht_supported ||
+ local->_oper_channel_type == NL80211_CHAN_NO_HT)
+ return 0;
+
+ if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
+ return -ENOMEM;
+
+ pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap));
+ ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap);
+
+ return 0;
+}
+
+int mesh_add_ht_info_ie(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_channel *channel = local->oper_channel;
+ enum nl80211_channel_type channel_type = local->_oper_channel_type;
+ struct ieee80211_supported_band *sband =
+ local->hw.wiphy->bands[channel->band];
+ struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
+ u8 *pos;
+
+ if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT)
+ return 0;
+
+ if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_info))
+ return -ENOMEM;
+
+ pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_info));
+ ieee80211_ie_build_ht_info(pos, ht_cap, channel, channel_type);
+
+ return 0;
+}
static void ieee80211_mesh_path_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 8c00e2d1d636..622cc96eb4de 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -31,6 +31,8 @@
* @MESH_PATH_FIXED: the mesh path has been manually set and should not be
* modified
* @MESH_PATH_RESOLVED: the mesh path can has been resolved
+ * @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination
+ * already queued up, waiting for the discovery process to start.
*
* MESH_PATH_RESOLVED is used by the mesh path timer to
* decide when to stop or cancel the mesh path discovery.
@@ -41,6 +43,7 @@ enum mesh_path_flags {
MESH_PATH_SN_VALID = BIT(2),
MESH_PATH_FIXED = BIT(3),
MESH_PATH_RESOLVED = BIT(4),
+ MESH_PATH_REQ_QUEUED = BIT(5),
};
/**
@@ -212,6 +215,10 @@ int mesh_add_vendor_ies(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_ds_params_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
+int mesh_add_ht_cap_ie(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata);
+int mesh_add_ht_info_ie(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata);
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
void ieee80211s_init(void);
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 174040a42887..ce3db2735d7c 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -113,20 +113,20 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos;
- int ie_len;
+ u8 *pos, ie_len;
+ int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) +
+ sizeof(mgmt->u.action.u.mesh_action);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+ hdr_len +
+ 2 + 37); /* max HWMP IE */
if (!skb)
return -1;
skb_reserve(skb, local->hw.extra_tx_headroom);
- /* 25 is the size of the common mgmt part (24) plus the size of the
- * common action part (1)
- */
- mgmt = (struct ieee80211_mgmt *)
- skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action));
- memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action));
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
+ memset(mgmt, 0, hdr_len);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
@@ -240,20 +240,20 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos;
- int ie_len;
+ u8 *pos, ie_len;
+ int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) +
+ sizeof(mgmt->u.action.u.mesh_action);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+ hdr_len +
+ 2 + 15 /* PERR IE */);
if (!skb)
return -1;
skb_reserve(skb, local->tx_headroom + local->hw.extra_tx_headroom);
- /* 25 is the size of the common mgmt part (24) plus the size of the
- * common action part (1)
- */
- mgmt = (struct ieee80211_mgmt *)
- skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action));
- memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action));
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
+ memset(mgmt, 0, hdr_len);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
@@ -867,9 +867,20 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
return;
}
+ spin_lock(&mpath->state_lock);
+ if (mpath->flags & MESH_PATH_REQ_QUEUED) {
+ spin_unlock(&mpath->state_lock);
+ spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);
+ kfree(preq_node);
+ return;
+ }
+
memcpy(preq_node->dst, mpath->dst, ETH_ALEN);
preq_node->flags = flags;
+ mpath->flags |= MESH_PATH_REQ_QUEUED;
+ spin_unlock(&mpath->state_lock);
+
list_add_tail(&preq_node->list, &ifmsh->preq_queue.list);
++ifmsh->preq_queue_len;
spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);
@@ -921,6 +932,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
goto enddiscovery;
spin_lock_bh(&mpath->state_lock);
+ mpath->flags &= ~MESH_PATH_REQ_QUEUED;
if (preq_node->flags & PREQ_Q_F_START) {
if (mpath->flags & MESH_PATH_RESOLVING) {
spin_unlock_bh(&mpath->state_lock);
@@ -1028,11 +1040,11 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
mesh_queue_preq(mpath, PREQ_Q_F_START);
}
- if (skb_queue_len(&mpath->frame_queue) >=
- MESH_FRAME_QUEUE_LEN)
+ if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN)
skb_to_free = skb_dequeue(&mpath->frame_queue);
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+ ieee80211_set_qos_hdr(sdata, skb);
skb_queue_tail(&mpath->frame_queue, skb);
if (skb_to_free)
mesh_path_discard_frame(skb_to_free, sdata);
@@ -1061,6 +1073,7 @@ void mesh_path_timer(unsigned long data)
} else if (mpath->discovery_retries < max_preq_retries(sdata)) {
++mpath->discovery_retries;
mpath->discovery_timeout *= 2;
+ mpath->flags &= ~MESH_PATH_REQ_QUEUED;
spin_unlock_bh(&mpath->state_lock);
mesh_queue_preq(mpath, 0);
} else {
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 7f54c5042235..7bd2a76aef0e 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -69,8 +69,6 @@ static inline struct mesh_table *resize_dereference_mpp_paths(void)
lockdep_is_held(&pathtbl_resize_lock));
}
-static int mesh_gate_add(struct mesh_table *tbl, struct mesh_path *mpath);
-
/*
* CAREFUL -- "tbl" must not be an expression,
* in particular not an rcu_dereference(), since
@@ -213,7 +211,6 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
struct ieee80211_hdr *hdr;
struct sk_buff_head tmpq;
unsigned long flags;
- struct ieee80211_sub_if_data *sdata = mpath->sdata;
rcu_assign_pointer(mpath->next_hop, sta);
@@ -224,8 +221,6 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) {
hdr = (struct ieee80211_hdr *) skb->data;
memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN);
- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb));
- ieee80211_set_qos_hdr(sdata, skb);
__skb_queue_tail(&tmpq, skb);
}
@@ -423,21 +418,18 @@ static void mesh_gate_node_reclaim(struct rcu_head *rp)
}
/**
- * mesh_gate_add - mark mpath as path to a mesh gate and add to known_gates
- * @mesh_tbl: table which contains known_gates list
- * @mpath: mpath to known mesh gate
- *
- * Returns: 0 on success
- *
+ * mesh_path_add_gate - add the given mpath to a mesh gate to our path table
+ * @mpath: gate path to add to table
*/
-static int mesh_gate_add(struct mesh_table *tbl, struct mesh_path *mpath)
+int mesh_path_add_gate(struct mesh_path *mpath)
{
+ struct mesh_table *tbl;
struct mpath_node *gate, *new_gate;
struct hlist_node *n;
int err;
rcu_read_lock();
- tbl = rcu_dereference(tbl);
+ tbl = rcu_dereference(mesh_paths);
hlist_for_each_entry_rcu(gate, n, tbl->known_gates, list)
if (gate->mpath == mpath) {
@@ -481,8 +473,6 @@ static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath)
struct mpath_node *gate;
struct hlist_node *p, *q;
- tbl = rcu_dereference(tbl);
-
hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list)
if (gate->mpath == mpath) {
spin_lock_bh(&tbl->gates_lock);
@@ -501,16 +491,6 @@ static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath)
}
/**
- *
- * mesh_path_add_gate - add the given mpath to a mesh gate to our path table
- * @mpath: gate path to add to table
- */
-int mesh_path_add_gate(struct mesh_path *mpath)
-{
- return mesh_gate_add(mesh_paths, mpath);
-}
-
-/**
* mesh_gate_num - number of gates known to this interface
* @sdata: subif data
*/
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 7e57f5d07f66..7314372b12ba 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -80,11 +80,15 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
* on it in the lifecycle management section!
*/
static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
- u8 *hw_addr, u32 rates)
+ u8 *hw_addr, u32 rates,
+ struct ieee802_11_elems *elems)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
struct sta_info *sta;
+ sband = local->hw.wiphy->bands[local->oper_channel->band];
+
if (local->num_sta >= MESH_MAX_PLINKS)
return NULL;
@@ -96,6 +100,10 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
set_sta_flag(sta, WLAN_STA_AUTHORIZED);
set_sta_flag(sta, WLAN_STA_WME);
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
+ if (elems->ht_cap_elem)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+ elems->ht_cap_elem,
+ &sta->sta.ht_cap);
rate_control_rate_init(sta);
return sta;
@@ -153,23 +161,31 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum ieee80211_self_protected_actioncode action,
u8 *da, __le16 llid, __le16 plid, __le16 reason) {
struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
- sdata->u.mesh.ie_len);
+ struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
bool include_plid = false;
- int ie_len = 4;
u16 peering_proto = 0;
- u8 *pos;
-
+ u8 *pos, ie_len = 4;
+ int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) +
+ sizeof(mgmt->u.action.u.self_prot);
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+ hdr_len +
+ 2 + /* capability info */
+ 2 + /* AID */
+ 2 + 8 + /* supported rates */
+ 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
+ 2 + sdata->u.mesh.mesh_id_len +
+ 2 + sizeof(struct ieee80211_meshconf_ie) +
+ 2 + sizeof(struct ieee80211_ht_cap) +
+ 2 + sizeof(struct ieee80211_ht_info) +
+ 2 + 8 + /* peering IE */
+ sdata->u.mesh.ie_len);
if (!skb)
return -1;
skb_reserve(skb, local->hw.extra_tx_headroom);
- /* 25 is the size of the common mgmt part (24) plus the size of the
- * common action part (1)
- */
- mgmt = (struct ieee80211_mgmt *)
- skb_put(skb, 25 + sizeof(mgmt->u.action.u.self_prot));
- memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.self_prot));
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
+ memset(mgmt, 0, hdr_len);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, da, ETH_ALEN);
@@ -235,6 +251,13 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
memcpy(pos, &reason, 2);
pos += 2;
}
+
+ if (action != WLAN_SP_MESH_PEERING_CLOSE) {
+ if (mesh_add_ht_cap_ie(skb, sdata) ||
+ mesh_add_ht_info_ie(skb, sdata))
+ return -1;
+ }
+
if (mesh_add_vendor_ies(skb, sdata))
return -1;
@@ -261,7 +284,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates,
elems->ie_start, elems->total_len,
GFP_KERNEL);
else
- sta = mesh_plink_alloc(sdata, hw_addr, rates);
+ sta = mesh_plink_alloc(sdata, hw_addr, rates, elems);
if (!sta)
return;
if (sta_info_insert_rcu(sta)) {
@@ -552,7 +575,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
- sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
+ sta = mesh_plink_alloc(sdata, mgmt->sa, rates, &elems);
if (!sta) {
mpl_dbg("Mesh plink error: plink table full\n");
return;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 72c8bea81a6c..a6ad19757bac 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -209,6 +209,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
channel_type = NL80211_CHAN_HT20;
if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
+ !ieee80111_cfg_override_disables_ht40(sdata) &&
(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
(hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
@@ -1120,6 +1121,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
/* on the next assoc, re-program HT parameters */
sdata->ht_opmode_valid = false;
+ memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
+ memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
local->power_constr_level = 0;
@@ -1359,9 +1362,6 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
ieee80211_set_disassoc(sdata, true, true);
mutex_unlock(&ifmgd->mtx);
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(local);
- mutex_unlock(&local->mtx);
/*
* must be outside lock due to cfg80211,
* but that's not a problem.
@@ -1370,6 +1370,10 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
NULL, true);
+
+ mutex_lock(&local->mtx);
+ ieee80211_recalc_idle(local);
+ mutex_unlock(&local->mtx);
}
void ieee80211_beacon_connection_loss_work(struct work_struct *work)
@@ -1468,6 +1472,47 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
return RX_MGMT_CFG80211_DISASSOC;
}
+static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
+ u8 *supp_rates, unsigned int supp_rates_len,
+ u32 *rates, u32 *basic_rates,
+ bool *have_higher_than_11mbit,
+ int *min_rate, int *min_rate_index)
+{
+ int i, j;
+
+ for (i = 0; i < supp_rates_len; i++) {
+ int rate = (supp_rates[i] & 0x7f) * 5;
+ bool is_basic = !!(supp_rates[i] & 0x80);
+
+ if (rate > 110)
+ *have_higher_than_11mbit = true;
+
+ /*
+ * BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009
+ * 7.3.2.2 as a magic value instead of a rate. Hence, skip it.
+ *
+ * Note: Even through the membership selector and the basic
+ * rate flag share the same bit, they are not exactly
+ * the same.
+ */
+ if (!!(supp_rates[i] & 0x80) &&
+ (supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
+ continue;
+
+ for (j = 0; j < sband->n_bitrates; j++) {
+ if (sband->bitrates[j].bitrate == rate) {
+ *rates |= BIT(j);
+ if (is_basic)
+ *basic_rates |= BIT(j);
+ if (rate < *min_rate) {
+ *min_rate = rate;
+ *min_rate_index = j;
+ }
+ break;
+ }
+ }
+ }
+}
static bool ieee80211_assoc_success(struct ieee80211_work *wk,
struct ieee80211_mgmt *mgmt, size_t len)
@@ -1484,9 +1529,10 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
struct ieee802_11_elems elems;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
u32 changed = 0;
- int i, j, err;
+ int err;
bool have_higher_than_11mbit = false;
u16 ap_ht_cap_flags;
+ int min_rate = INT_MAX, min_rate_index = -1;
/* AssocResp and ReassocResp have identical structure */
@@ -1533,7 +1579,6 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
set_sta_flag(sta, WLAN_STA_AUTH);
set_sta_flag(sta, WLAN_STA_ASSOC);
- set_sta_flag(sta, WLAN_STA_ASSOC_AP);
if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
set_sta_flag(sta, WLAN_STA_AUTHORIZED);
@@ -1541,38 +1586,23 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
basic_rates = 0;
sband = local->hw.wiphy->bands[wk->chan->band];
- for (i = 0; i < elems.supp_rates_len; i++) {
- int rate = (elems.supp_rates[i] & 0x7f) * 5;
- bool is_basic = !!(elems.supp_rates[i] & 0x80);
+ ieee80211_get_rates(sband, elems.supp_rates, elems.supp_rates_len,
+ &rates, &basic_rates, &have_higher_than_11mbit,
+ &min_rate, &min_rate_index);
- if (rate > 110)
- have_higher_than_11mbit = true;
+ ieee80211_get_rates(sband, elems.ext_supp_rates,
+ elems.ext_supp_rates_len, &rates, &basic_rates,
+ &have_higher_than_11mbit,
+ &min_rate, &min_rate_index);
- for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate) {
- rates |= BIT(j);
- if (is_basic)
- basic_rates |= BIT(j);
- break;
- }
- }
- }
-
- for (i = 0; i < elems.ext_supp_rates_len; i++) {
- int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
- bool is_basic = !!(elems.ext_supp_rates[i] & 0x80);
-
- if (rate > 110)
- have_higher_than_11mbit = true;
-
- for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate) {
- rates |= BIT(j);
- if (is_basic)
- basic_rates |= BIT(j);
- break;
- }
- }
+ /*
+ * some buggy APs don't advertise basic_rates. use the lowest
+ * supported rate instead.
+ */
+ if (unlikely(!basic_rates) && min_rate_index >= 0) {
+ printk(KERN_DEBUG "%s: No basic rates in AssocResp. "
+ "Using min supported rate instead.\n", sdata->name);
+ basic_rates = BIT(min_rate_index);
}
sta->sta.supp_rates[wk->chan->band] = rates;
@@ -1586,7 +1616,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
- ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
ap_ht_cap_flags = sta->sta.ht_cap.cap;
@@ -1955,7 +1985,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
ap_ht_cap_flags = sta->sta.ht_cap.cap;
@@ -2109,9 +2139,6 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, true, true);
mutex_unlock(&ifmgd->mtx);
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(local);
- mutex_unlock(&local->mtx);
/*
* must be outside lock due to cfg80211,
* but that's not a problem.
@@ -2119,6 +2146,11 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
ieee80211_send_deauth_disassoc(sdata, bssid,
IEEE80211_STYPE_DEAUTH, reason,
NULL, true);
+
+ mutex_lock(&local->mtx);
+ ieee80211_recalc_idle(local);
+ mutex_unlock(&local->mtx);
+
mutex_lock(&ifmgd->mtx);
}
@@ -2613,6 +2645,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ if (req->flags & ASSOC_REQ_DISABLE_HT)
+ ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+
+ memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
+ memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,
+ sizeof(ifmgd->ht_capa_mask));
+
if (req->ie && req->ie_len) {
memcpy(wk->ie, req->ie, req->ie_len);
wk->ie_len = req->ie_len;
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 3d414411a96e..ebd8cccac8f2 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -212,8 +212,6 @@ static void ieee80211_hw_roc_start(struct work_struct *work)
return;
}
- ieee80211_recalc_idle(local);
-
if (local->hw_roc_skb) {
sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev);
ieee80211_tx_skb(sdata, local->hw_roc_skb);
@@ -227,6 +225,8 @@ static void ieee80211_hw_roc_start(struct work_struct *work)
GFP_KERNEL);
}
+ ieee80211_recalc_idle(local);
+
mutex_unlock(&local->mtx);
}
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 9ee7164b207c..596efaf50e09 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -125,7 +125,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_BEACON_ENABLED);
- drv_remove_interface(local, &sdata->vif);
+ drv_remove_interface(local, sdata);
}
/* stop hardware - this must stop RX */
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 58a89554b788..b39dda523f39 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -334,8 +334,8 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
static void
-calc_rate_durations(struct minstrel_sta_info *mi, struct ieee80211_local *local,
- struct minstrel_rate *d, struct ieee80211_rate *rate)
+calc_rate_durations(struct ieee80211_local *local, struct minstrel_rate *d,
+ struct ieee80211_rate *rate)
{
int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
@@ -402,8 +402,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
mr->rix = i;
mr->bitrate = sband->bitrates[i].bitrate / 5;
- calc_rate_durations(mi, local, mr,
- &sband->bitrates[i]);
+ calc_rate_durations(local, mr, &sband->bitrates[i]);
/* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index cdb28535716b..ff5f7b84e825 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -36,8 +36,17 @@
/* Transmit duration for the raw data part of an average sized packet */
#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
+/*
+ * Define group sort order: HT40 -> SGI -> #streams
+ */
+#define GROUP_IDX(_streams, _sgi, _ht40) \
+ MINSTREL_MAX_STREAMS * 2 * _ht40 + \
+ MINSTREL_MAX_STREAMS * _sgi + \
+ _streams - 1
+
/* MCS rate information for an MCS group */
-#define MCS_GROUP(_streams, _sgi, _ht40) { \
+#define MCS_GROUP(_streams, _sgi, _ht40) \
+ [GROUP_IDX(_streams, _sgi, _ht40)] = { \
.streams = _streams, \
.flags = \
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
@@ -58,6 +67,9 @@
* To enable sufficiently targeted rate sampling, MCS rates are divided into
* groups, based on the number of streams and flags (HT40, SGI) that they
* use.
+ *
+ * Sortorder has to be fixed for GROUP_IDX macro to be applicable:
+ * HT40 -> SGI -> #streams
*/
const struct mcs_group minstrel_mcs_groups[] = {
MCS_GROUP(1, 0, 0),
@@ -102,21 +114,9 @@ minstrel_ewma(int old, int new, int weight)
static int
minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
{
- int streams = (rate->idx / MCS_GROUP_RATES) + 1;
- u32 flags = IEEE80211_TX_RC_SHORT_GI | IEEE80211_TX_RC_40_MHZ_WIDTH;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) {
- if (minstrel_mcs_groups[i].streams != streams)
- continue;
- if (minstrel_mcs_groups[i].flags != (rate->flags & flags))
- continue;
-
- return i;
- }
-
- WARN_ON(1);
- return 0;
+ return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1,
+ !!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
+ !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
}
static inline struct minstrel_rate_stats *
@@ -130,7 +130,7 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)
* Recalculate success probabilities and counters for a rate using EWMA
*/
static void
-minstrel_calc_rate_ewma(struct minstrel_priv *mp, struct minstrel_rate_stats *mr)
+minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr)
{
if (unlikely(mr->attempts > 0)) {
mr->sample_skipped = 0;
@@ -156,8 +156,7 @@ minstrel_calc_rate_ewma(struct minstrel_priv *mp, struct minstrel_rate_stats *mr
* the expected number of retransmissions and their expected length
*/
static void
-minstrel_ht_calc_tp(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
- int group, int rate)
+minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
{
struct minstrel_rate_stats *mr;
unsigned int usecs;
@@ -226,8 +225,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
mr = &mg->rates[i];
mr->retry_updated = false;
index = MCS_GROUP_RATES * group + i;
- minstrel_calc_rate_ewma(mp, mr);
- minstrel_ht_calc_tp(mp, mi, group, i);
+ minstrel_calc_rate_ewma(mr);
+ minstrel_ht_calc_tp(mi, group, i);
if (!mr->cur_tp)
continue;
@@ -300,10 +299,10 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
static bool
minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate)
{
- if (!rate->count)
+ if (rate->idx < 0)
return false;
- if (rate->idx < 0)
+ if (!rate->count)
return false;
return !!(rate->flags & IEEE80211_TX_RC_MCS);
@@ -357,7 +356,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, unsigned int *idx,
}
static void
-minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, struct sk_buff *skb)
+minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
@@ -455,7 +454,7 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
minstrel_ht_update_stats(mp, mi);
if (!(info->flags & IEEE80211_TX_CTL_AMPDU))
- minstrel_aggr_check(mp, sta, skb);
+ minstrel_aggr_check(sta, skb);
}
}
@@ -515,7 +514,6 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
static void
minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
struct ieee80211_tx_rate *rate, int index,
- struct ieee80211_tx_rate_control *txrc,
bool sample, bool rtscts)
{
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
@@ -628,11 +626,11 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
if (sample_idx >= 0) {
sample = true;
minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx,
- txrc, true, false);
+ true, false);
info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
} else {
minstrel_ht_set_rate(mp, mi, &ar[0], mi->max_tp_rate,
- txrc, false, false);
+ false, false);
}
if (mp->hw->max_rates >= 3) {
@@ -643,13 +641,13 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
*/
if (sample_idx >= 0)
minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate,
- txrc, false, false);
+ false, false);
else
minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2,
- txrc, false, true);
+ false, true);
minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate,
- txrc, false, !sample);
+ false, !sample);
ar[3].count = 0;
ar[3].idx = -1;
@@ -660,7 +658,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
* max_tp_rate -> max_prob_rate by default.
*/
minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_prob_rate,
- txrc, false, !sample);
+ false, !sample);
ar[2].count = 0;
ar[2].idx = -1;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index bb53726cb04a..4eafbfd891d5 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -745,10 +745,11 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
struct ieee80211_local *local = rx->local;
struct ieee80211_hw *hw = &local->hw;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct sta_info *sta = rx->sta;
struct tid_ampdu_rx *tid_agg_rx;
u16 sc;
- int tid;
+ u8 tid, ack_policy;
if (!ieee80211_is_data_qos(hdr->frame_control))
goto dont_reorder;
@@ -761,6 +762,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
if (!sta)
goto dont_reorder;
+ ack_policy = *ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CTL_ACK_POLICY_MASK;
tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
@@ -771,6 +774,15 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
goto dont_reorder;
+ /* not part of a BA session */
+ if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
+ ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL)
+ goto dont_reorder;
+
+ /* not actually part of this BA session */
+ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+ goto dont_reorder;
+
/* new, potentially un-ordered, ampdu frame - process it */
/* reset session timer */
@@ -855,6 +867,13 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
rx->sdata->control_port_protocol)
return RX_CONTINUE;
}
+
+ if (rx->sdata->vif.type == NL80211_IFTYPE_AP &&
+ cfg80211_rx_spurious_frame(rx->sdata->dev,
+ hdr->addr2,
+ GFP_ATOMIC))
+ return RX_DROP_UNUSABLE;
+
return RX_DROP_MONITOR;
}
@@ -1324,15 +1343,20 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
/*
* If we receive a 4-addr nullfunc frame from a STA
- * that was not moved to a 4-addr STA vlan yet, drop
- * the frame to the monitor interface, to make sure
- * that hostapd sees it
+ * that was not moved to a 4-addr STA vlan yet send
+ * the event to userspace and for older hostapd drop
+ * the frame to the monitor interface.
*/
if (ieee80211_has_a4(hdr->frame_control) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
(rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
- !rx->sdata->u.vlan.sta)))
+ !rx->sdata->u.vlan.sta))) {
+ if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT))
+ cfg80211_rx_unexpected_4addr_frame(
+ rx->sdata->dev, sta->sta.addr,
+ GFP_ATOMIC);
return RX_DROP_MONITOR;
+ }
/*
* Update counter and free packet here to avoid
* counting this as a dropped packed.
@@ -1930,6 +1954,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
compare_ether_addr(sdata->vif.addr, hdr->addr3) == 0)
return RX_CONTINUE;
+ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb));
mesh_hdr->ttl--;
if (status->rx_flags & IEEE80211_RX_RA_MATCH) {
@@ -1954,12 +1979,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
memset(info, 0, sizeof(*info));
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
info->control.vif = &rx->sdata->vif;
+ info->control.jiffies = jiffies;
if (is_multicast_ether_addr(fwd_hdr->addr1)) {
IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
fwded_mcast);
- skb_set_queue_mapping(fwd_skb,
- ieee80211_select_queue(sdata, fwd_skb));
- ieee80211_set_qos_hdr(sdata, fwd_skb);
} else {
int err;
/*
@@ -2011,12 +2034,17 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
/*
- * Allow the cooked monitor interface of an AP to see 4-addr frames so
- * that a 4-addr station can be detected and moved into a separate VLAN
+ * Send unexpected-4addr-frame event to hostapd. For older versions,
+ * also drop the frame to cooked monitor interfaces.
*/
if (ieee80211_has_a4(hdr->frame_control) &&
- sdata->vif.type == NL80211_IFTYPE_AP)
+ sdata->vif.type == NL80211_IFTYPE_AP) {
+ if (rx->sta &&
+ !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT))
+ cfg80211_rx_unexpected_4addr_frame(
+ rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC);
return RX_DROP_MONITOR;
+ }
err = __ieee80211_data_to_8023(rx, &port_control);
if (unlikely(err))
@@ -2171,6 +2199,18 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
if (!ieee80211_is_mgmt(mgmt->frame_control))
return RX_DROP_MONITOR;
+ if (rx->sdata->vif.type == NL80211_IFTYPE_AP &&
+ ieee80211_is_beacon(mgmt->frame_control) &&
+ !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) {
+ struct ieee80211_rx_status *status;
+
+ status = IEEE80211_SKB_RXCB(rx->skb);
+ cfg80211_report_obss_beacon(rx->local->hw.wiphy,
+ rx->skb->data, rx->skb->len,
+ status->freq, GFP_ATOMIC);
+ rx->flags |= IEEE80211_RX_BEACON_REPORTED;
+ }
+
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_MONITOR;
@@ -2204,13 +2244,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK:
- /*
- * The aggregation code is not prepared to handle
- * anything but STA/AP due to the BSSID handling;
- * IBSS could work in the code but isn't supported
- * by drivers or the standard.
- */
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_AP)
break;
@@ -2490,6 +2525,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
goto out_free_skb;
rx->flags |= IEEE80211_RX_CMNTR;
+ /* If there are no cooked monitor interfaces, just free the SKB */
+ if (!local->cooked_mntrs)
+ goto out_free_skb;
+
if (skb_headroom(skb) < sizeof(*rthdr) &&
pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC))
goto out_free_skb;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 105436dbb90d..81863031e0a3 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -213,12 +213,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
if (bss)
ieee80211_rx_bss_put(sdata->local, bss);
- /* If we are on-operating-channel, and this packet is for the
- * current channel, pass the pkt on up the stack so that
- * the rest of the stack can make use of it.
- */
- if (ieee80211_cfg_on_oper_channel(sdata->local)
- && (channel == sdata->local->oper_channel))
+ if (channel == sdata->local->oper_channel)
return RX_CONTINUE;
dev_kfree_skb(skb);
@@ -264,8 +259,6 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
bool was_hw_scan)
{
struct ieee80211_local *local = hw_to_local(hw);
- bool on_oper_chan;
- bool enable_beacons = false;
lockdep_assert_held(&local->mtx);
@@ -298,25 +291,13 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
local->scanning = 0;
local->scan_channel = NULL;
- on_oper_chan = ieee80211_cfg_on_oper_channel(local);
-
- if (was_hw_scan || !on_oper_chan)
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
- else
- /* Set power back to normal operating levels. */
- ieee80211_hw_config(local, 0);
+ /* Set power back to normal operating levels. */
+ ieee80211_hw_config(local, 0);
if (!was_hw_scan) {
- bool on_oper_chan2;
ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
- on_oper_chan2 = ieee80211_cfg_on_oper_channel(local);
- /* We should always be on-channel at this point. */
- WARN_ON(!on_oper_chan2);
- if (on_oper_chan2 && (on_oper_chan != on_oper_chan2))
- enable_beacons = true;
-
- ieee80211_offchannel_return(local, enable_beacons, true);
+ ieee80211_offchannel_return(local, true, true);
}
ieee80211_recalc_idle(local);
@@ -361,11 +342,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;
- /* We always want to use off-channel PS, even if we
- * are not really leaving oper-channel. Don't
- * tell the AP though, as long as we are on-channel.
- */
- ieee80211_offchannel_enable_all_ps(local, false);
+ ieee80211_offchannel_stop_vifs(local, true);
ieee80211_configure_filter(local);
@@ -373,8 +350,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
ieee80211_hw_config(local, 0);
ieee80211_queue_delayed_work(&local->hw,
- &local->scan_work,
- IEEE80211_CHANNEL_TIME);
+ &local->scan_work, 0);
return 0;
}
@@ -510,96 +486,39 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
next_chan = local->scan_req->channels[local->scan_channel_idx];
- if (ieee80211_cfg_on_oper_channel(local)) {
- /* We're currently on operating channel. */
- if (next_chan == local->oper_channel)
- /* We don't need to move off of operating channel. */
- local->next_scan_state = SCAN_SET_CHANNEL;
- else
- /*
- * We do need to leave operating channel, as next
- * scan is somewhere else.
- */
- local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
- } else {
- /*
- * we're currently scanning a different channel, let's
- * see if we can scan another channel without interfering
- * with the current traffic situation.
- *
- * Since we don't know if the AP has pending frames for us
- * we can only check for our tx queues and use the current
- * pm_qos requirements for rx. Hence, if no tx traffic occurs
- * at all we will scan as many channels in a row as the pm_qos
- * latency allows us to. Additionally we also check for the
- * currently negotiated listen interval to prevent losing
- * frames unnecessarily.
- *
- * Otherwise switch back to the operating channel.
- */
-
- bad_latency = time_after(jiffies +
- ieee80211_scan_get_channel_time(next_chan),
- local->leave_oper_channel_time +
- usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY)));
-
- listen_int_exceeded = time_after(jiffies +
- ieee80211_scan_get_channel_time(next_chan),
- local->leave_oper_channel_time +
- usecs_to_jiffies(min_beacon_int * 1024) *
- local->hw.conf.listen_interval);
-
- if (associated && ( !tx_empty || bad_latency ||
- listen_int_exceeded))
- local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
- else
- local->next_scan_state = SCAN_SET_CHANNEL;
- }
-
- *next_delay = 0;
-}
-
-static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
- unsigned long *next_delay)
-{
- /* PS will already be in off-channel mode,
- * we do that once at the beginning of scanning.
- */
- ieee80211_offchannel_stop_vifs(local, false);
-
/*
- * What if the nullfunc frames didn't arrive?
+ * we're currently scanning a different channel, let's
+ * see if we can scan another channel without interfering
+ * with the current traffic situation.
+ *
+ * Since we don't know if the AP has pending frames for us
+ * we can only check for our tx queues and use the current
+ * pm_qos requirements for rx. Hence, if no tx traffic occurs
+ * at all we will scan as many channels in a row as the pm_qos
+ * latency allows us to. Additionally we also check for the
+ * currently negotiated listen interval to prevent losing
+ * frames unnecessarily.
+ *
+ * Otherwise switch back to the operating channel.
*/
- drv_flush(local, false);
- if (local->ops->flush)
- *next_delay = 0;
- else
- *next_delay = HZ / 10;
- /* remember when we left the operating channel */
- local->leave_oper_channel_time = jiffies;
+ bad_latency = time_after(jiffies +
+ ieee80211_scan_get_channel_time(next_chan),
+ local->leave_oper_channel_time +
+ usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY)));
- /* advance to the next channel to be scanned */
- local->next_scan_state = SCAN_SET_CHANNEL;
-}
-
-static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local,
- unsigned long *next_delay)
-{
- /* switch back to the operating channel */
- local->scan_channel = NULL;
- if (!ieee80211_cfg_on_oper_channel(local))
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ listen_int_exceeded = time_after(jiffies +
+ ieee80211_scan_get_channel_time(next_chan),
+ local->leave_oper_channel_time +
+ usecs_to_jiffies(min_beacon_int * 1024) *
+ local->hw.conf.listen_interval);
- /*
- * Re-enable vifs and beaconing. Leave PS
- * in off-channel state..will put that back
- * on-channel at the end of scanning.
- */
- ieee80211_offchannel_return(local, true, false);
+ if (associated && (!tx_empty || bad_latency || listen_int_exceeded))
+ local->next_scan_state = SCAN_SUSPEND;
+ else
+ local->next_scan_state = SCAN_SET_CHANNEL;
- *next_delay = HZ / 5;
- local->next_scan_state = SCAN_DECISION;
+ *next_delay = 0;
}
static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
@@ -613,10 +532,8 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
local->scan_channel = chan;
- /* Only call hw-config if we really need to change channels. */
- if (chan != local->hw.conf.channel)
- if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
- skip = 1;
+ if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
+ skip = 1;
/* advance state machine to next channel/band */
local->scan_channel_idx++;
@@ -673,6 +590,44 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
local->next_scan_state = SCAN_DECISION;
}
+static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ /* switch back to the operating channel */
+ local->scan_channel = NULL;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+
+ /*
+ * Re-enable vifs and beaconing. Leave PS
+ * in off-channel state..will put that back
+ * on-channel at the end of scanning.
+ */
+ ieee80211_offchannel_return(local, true, false);
+
+ *next_delay = HZ / 5;
+ /* afterwards, resume scan & go to next channel */
+ local->next_scan_state = SCAN_RESUME;
+}
+
+static void ieee80211_scan_state_resume(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ /* PS already is in off-channel mode */
+ ieee80211_offchannel_stop_vifs(local, false);
+
+ if (local->ops->flush) {
+ drv_flush(local, false);
+ *next_delay = 0;
+ } else
+ *next_delay = HZ / 10;
+
+ /* remember when we left the operating channel */
+ local->leave_oper_channel_time = jiffies;
+
+ /* advance to the next channel to be scanned */
+ local->next_scan_state = SCAN_DECISION;
+}
+
void ieee80211_scan_work(struct work_struct *work)
{
struct ieee80211_local *local =
@@ -743,11 +698,11 @@ void ieee80211_scan_work(struct work_struct *work)
case SCAN_SEND_PROBE:
ieee80211_scan_state_send_probe(local, &next_delay);
break;
- case SCAN_LEAVE_OPER_CHANNEL:
- ieee80211_scan_state_leave_oper_channel(local, &next_delay);
+ case SCAN_SUSPEND:
+ ieee80211_scan_state_suspend(local, &next_delay);
break;
- case SCAN_ENTER_OPER_CHANNEL:
- ieee80211_scan_state_enter_oper_channel(local, &next_delay);
+ case SCAN_RESUME:
+ ieee80211_scan_state_resume(local, &next_delay);
break;
}
} while (next_delay == 0);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ce962d2c8782..8eaa746ec7a2 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1354,12 +1354,12 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
* Use MoreData flag to indicate whether there are
* more buffered frames for this STA
*/
- if (!more_data)
- hdr->frame_control &=
- cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
- else
+ if (more_data || !skb_queue_empty(&frames))
hdr->frame_control |=
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ else
+ hdr->frame_control &=
+ cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
if (ieee80211_is_data_qos(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control))
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 8c8ce05ad26f..6280e8bca49d 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -30,7 +30,6 @@
* when virtual port control is not in use.
* @WLAN_STA_SHORT_PREAMBLE: Station is capable of receiving short-preamble
* frames.
- * @WLAN_STA_ASSOC_AP: We're associated to that station, it is an AP.
* @WLAN_STA_WME: Station is a QoS-STA.
* @WLAN_STA_WDS: Station is one of our WDS peers.
* @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the
@@ -52,6 +51,7 @@
* unblocks the station.
* @WLAN_STA_SP: Station is in a service period, so don't try to
* reply to other uAPSD trigger frames or PS-Poll.
+ * @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame.
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH,
@@ -59,7 +59,6 @@ enum ieee80211_sta_info_flags {
WLAN_STA_PS_STA,
WLAN_STA_AUTHORIZED,
WLAN_STA_SHORT_PREAMBLE,
- WLAN_STA_ASSOC_AP,
WLAN_STA_WME,
WLAN_STA_WDS,
WLAN_STA_CLEAR_PS_FILT,
@@ -71,6 +70,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_TDLS_PEER_AUTH,
WLAN_STA_UAPSD,
WLAN_STA_SP,
+ WLAN_STA_4ADDR_EVENT,
};
#define STA_TID_NUM 16
@@ -390,6 +390,12 @@ static inline int test_and_clear_sta_flag(struct sta_info *sta,
return test_and_clear_bit(flag, &sta->_flags);
}
+static inline int test_and_set_sta_flag(struct sta_info *sta,
+ enum ieee80211_sta_info_flags flag)
+{
+ return test_and_set_bit(flag, &sta->_flags);
+}
+
void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
struct tid_ampdu_tx *tid_tx);
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 80de436eae20..a9da6ee69803 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -517,27 +517,54 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
}
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
- struct ieee80211_work *wk;
u64 cookie = (unsigned long)skb;
- rcu_read_lock();
- list_for_each_entry_rcu(wk, &local->work_list, list) {
- if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
- continue;
- if (wk->offchan_tx.frame != skb)
- continue;
- wk->offchan_tx.status = true;
- break;
- }
- rcu_read_unlock();
- if (local->hw_roc_skb_for_status == skb) {
- cookie = local->hw_roc_cookie ^ 2;
- local->hw_roc_skb_for_status = NULL;
+ if (ieee80211_is_nullfunc(hdr->frame_control) ||
+ ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+ bool acked = info->flags & IEEE80211_TX_STAT_ACK;
+ cfg80211_probe_status(skb->dev, hdr->addr1,
+ cookie, acked, GFP_ATOMIC);
+ } else {
+ struct ieee80211_work *wk;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(wk, &local->work_list, list) {
+ if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
+ continue;
+ if (wk->offchan_tx.frame != skb)
+ continue;
+ wk->offchan_tx.status = true;
+ break;
+ }
+ rcu_read_unlock();
+ if (local->hw_roc_skb_for_status == skb) {
+ cookie = local->hw_roc_cookie ^ 2;
+ local->hw_roc_skb_for_status = NULL;
+ }
+
+ cfg80211_mgmt_tx_status(
+ skb->dev, cookie, skb->data, skb->len,
+ !!(info->flags & IEEE80211_TX_STAT_ACK),
+ GFP_ATOMIC);
}
+ }
- cfg80211_mgmt_tx_status(
- skb->dev, cookie, skb->data, skb->len,
- !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
+ if (unlikely(info->ack_frame_id)) {
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ ack_skb = idr_find(&local->ack_status_frames,
+ info->ack_frame_id);
+ if (ack_skb)
+ idr_remove(&local->ack_status_frames,
+ info->ack_frame_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ /* consumes ack_skb */
+ if (ack_skb)
+ skb_complete_wifi_ack(ack_skb,
+ info->flags & IEEE80211_TX_STAT_ACK);
}
/* this was a transmitted frame, but now we want to reuse it */
@@ -610,3 +637,29 @@ void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets)
num_packets, GFP_ATOMIC);
}
EXPORT_SYMBOL(ieee80211_report_low_ack);
+
+void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (unlikely(info->ack_frame_id)) {
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ ack_skb = idr_find(&local->ack_status_frames,
+ info->ack_frame_id);
+ if (ack_skb)
+ idr_remove(&local->ack_status_frames,
+ info->ack_frame_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ /* consumes ack_skb */
+ if (ack_skb)
+ dev_kfree_skb_any(ack_skb);
+ }
+
+ dev_kfree_skb_any(skb);
+}
+EXPORT_SYMBOL(ieee80211_free_txskb);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1f8b120146d1..8d31933abe6a 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -36,7 +36,8 @@
/* misc utils */
-static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
+static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
+ struct sk_buff *skb, int group_addr,
int next_frag_len)
{
int rate, mrate, erp, dur, i;
@@ -44,7 +45,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband;
struct ieee80211_hdr *hdr;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
/* assume HW handles this */
if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
@@ -76,7 +77,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
* at the highest possible rate belonging to the PHY rates in the
* BSSBasicRateSet
*/
- hdr = (struct ieee80211_hdr *)tx->skb->data;
+ hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_ctl(hdr->frame_control)) {
/* TODO: These control frames are not currently sent by
* mac80211, but should they be implemented, this function
@@ -572,8 +573,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
switch (tx->key->conf.cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
- if (ieee80211_is_auth(hdr->frame_control))
- break;
case WLAN_CIPHER_SUITE_TKIP:
if (!ieee80211_is_data_present(hdr->frame_control))
tx->key = NULL;
@@ -844,11 +843,13 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
-static int ieee80211_fragment(struct ieee80211_local *local,
+static int ieee80211_fragment(struct ieee80211_tx_data *tx,
struct sk_buff *skb, int hdrlen,
int frag_threshold)
{
- struct sk_buff *tail = skb, *tmp;
+ struct ieee80211_local *local = tx->local;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *tmp;
int per_fragm = frag_threshold - hdrlen - FCS_LEN;
int pos = hdrlen + per_fragm;
int rem = skb->len - hdrlen - per_fragm;
@@ -856,6 +857,8 @@ static int ieee80211_fragment(struct ieee80211_local *local,
if (WARN_ON(rem < 0))
return -EINVAL;
+ /* first fragment was already added to queue by caller */
+
while (rem) {
int fraglen = per_fragm;
@@ -868,12 +871,21 @@ static int ieee80211_fragment(struct ieee80211_local *local,
IEEE80211_ENCRYPT_TAILROOM);
if (!tmp)
return -ENOMEM;
- tail->next = tmp;
- tail = tmp;
+
+ __skb_queue_tail(&tx->skbs, tmp);
+
skb_reserve(tmp, local->tx_headroom +
IEEE80211_ENCRYPT_HEADROOM);
/* copy control information */
memcpy(tmp->cb, skb->cb, sizeof(tmp->cb));
+
+ info = IEEE80211_SKB_CB(tmp);
+ info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
+ IEEE80211_TX_CTL_FIRST_FRAGMENT);
+
+ if (rem)
+ info->flags |= IEEE80211_TX_CTL_MORE_FRAMES;
+
skb_copy_queue_mapping(tmp, skb);
tmp->priority = skb->priority;
tmp->dev = skb->dev;
@@ -885,6 +897,7 @@ static int ieee80211_fragment(struct ieee80211_local *local,
pos += fraglen;
}
+ /* adjust first fragment's length */
skb->len = hdrlen + per_fragm;
return 0;
}
@@ -899,6 +912,10 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
int hdrlen;
int fragnum;
+ /* no matter what happens, tx->skb moves to tx->skbs */
+ __skb_queue_tail(&tx->skbs, skb);
+ tx->skb = NULL;
+
if (info->flags & IEEE80211_TX_CTL_DONTFRAG)
return TX_CONTINUE;
@@ -927,21 +944,21 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
* of the fragments then we will simply pretend to accept the skb
* but store it away as pending.
*/
- if (ieee80211_fragment(tx->local, skb, hdrlen, frag_threshold))
+ if (ieee80211_fragment(tx, skb, hdrlen, frag_threshold))
return TX_DROP;
/* update duration/seq/flags of fragments */
fragnum = 0;
- do {
+
+ skb_queue_walk(&tx->skbs, skb) {
int next_len;
const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
hdr = (void *)skb->data;
info = IEEE80211_SKB_CB(skb);
- if (skb->next) {
+ if (!skb_queue_is_last(&tx->skbs, skb)) {
hdr->frame_control |= morefrags;
- next_len = skb->next->len;
/*
* No multi-rate retries for fragmented frames, that
* would completely throw off the NAV at other STAs.
@@ -956,10 +973,9 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
hdr->frame_control &= ~morefrags;
next_len = 0;
}
- hdr->duration_id = ieee80211_duration(tx, 0, next_len);
hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG);
fragnum++;
- } while ((skb = skb->next));
+ }
return TX_CONTINUE;
}
@@ -967,16 +983,16 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
{
- struct sk_buff *skb = tx->skb;
+ struct sk_buff *skb;
if (!tx->sta)
return TX_CONTINUE;
tx->sta->tx_packets++;
- do {
+ skb_queue_walk(&tx->skbs, skb) {
tx->sta->tx_fragments++;
tx->sta->tx_bytes += skb->len;
- } while ((skb = skb->next));
+ }
return TX_CONTINUE;
}
@@ -1015,21 +1031,25 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
{
- struct sk_buff *skb = tx->skb;
+ struct sk_buff *skb;
struct ieee80211_hdr *hdr;
int next_len;
bool group_addr;
- do {
+ skb_queue_walk(&tx->skbs, skb) {
hdr = (void *) skb->data;
if (unlikely(ieee80211_is_pspoll(hdr->frame_control)))
break; /* must not overwrite AID */
- next_len = skb->next ? skb->next->len : 0;
+ if (!skb_queue_is_last(&tx->skbs, skb)) {
+ struct sk_buff *next = skb_queue_next(&tx->skbs, skb);
+ next_len = next->len;
+ } else
+ next_len = 0;
group_addr = is_multicast_ether_addr(hdr->addr1);
hdr->duration_id =
- ieee80211_duration(tx, group_addr, next_len);
- } while ((skb = skb->next));
+ ieee80211_duration(tx, skb, group_addr, next_len);
+ }
return TX_CONTINUE;
}
@@ -1108,6 +1128,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
tx->local = local;
tx->sdata = sdata;
tx->channel = local->hw.conf.channel;
+ __skb_queue_head_init(&tx->skbs);
/*
* If this flag is set to true anywhere, and we get here,
@@ -1180,22 +1201,18 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
return TX_CONTINUE;
}
-/*
- * Returns false if the frame couldn't be transmitted but was queued instead.
- */
-static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp,
- struct sta_info *sta, bool txpending)
+static bool ieee80211_tx_frags(struct ieee80211_local *local,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct sk_buff_head *skbs,
+ bool txpending)
{
- struct sk_buff *skb = *skbp, *next;
+ struct sk_buff *skb, *tmp;
struct ieee80211_tx_info *info;
- struct ieee80211_sub_if_data *sdata;
unsigned long flags;
- int len;
- bool fragm = false;
- while (skb) {
+ skb_queue_walk_safe(skbs, skb, tmp) {
int q = skb_get_queue_mapping(skb);
- __le16 fc;
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
if (local->queue_stop_reasons[q] ||
@@ -1205,24 +1222,10 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp,
* transmission from the tx-pending tasklet when the
* queue is woken again.
*/
-
- do {
- next = skb->next;
- skb->next = NULL;
- /*
- * NB: If txpending is true, next must already
- * be NULL since we must've gone through this
- * loop before already; therefore we can just
- * queue the frame to the head without worrying
- * about reordering of fragments.
- */
- if (unlikely(txpending))
- __skb_queue_head(&local->pending[q],
- skb);
- else
- __skb_queue_tail(&local->pending[q],
- skb);
- } while ((skb = next));
+ if (txpending)
+ skb_queue_splice(skbs, &local->pending[q]);
+ else
+ skb_queue_splice_tail(skbs, &local->pending[q]);
spin_unlock_irqrestore(&local->queue_stop_reason_lock,
flags);
@@ -1231,47 +1234,72 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp,
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
info = IEEE80211_SKB_CB(skb);
+ info->control.vif = vif;
+ info->control.sta = sta;
- if (fragm)
- info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
- IEEE80211_TX_CTL_FIRST_FRAGMENT);
-
- next = skb->next;
- len = skb->len;
+ __skb_unlink(skb, skbs);
+ drv_tx(local, skb);
+ }
- if (next)
- info->flags |= IEEE80211_TX_CTL_MORE_FRAMES;
+ return true;
+}
- sdata = vif_to_sdata(info->control.vif);
+/*
+ * Returns false if the frame couldn't be transmitted but was queued instead.
+ */
+static bool __ieee80211_tx(struct ieee80211_local *local,
+ struct sk_buff_head *skbs, int led_len,
+ struct sta_info *sta, bool txpending)
+{
+ struct ieee80211_tx_info *info;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *pubsta;
+ struct sk_buff *skb;
+ bool result = true;
+ __le16 fc;
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_MONITOR:
- info->control.vif = NULL;
- break;
- case NL80211_IFTYPE_AP_VLAN:
- info->control.vif = &container_of(sdata->bss,
- struct ieee80211_sub_if_data, u.ap)->vif;
- break;
- default:
- /* keep */
- break;
- }
+ if (WARN_ON(skb_queue_empty(skbs)))
+ return true;
- if (sta && sta->uploaded)
- info->control.sta = &sta->sta;
- else
- info->control.sta = NULL;
+ skb = skb_peek(skbs);
+ fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
+ info = IEEE80211_SKB_CB(skb);
+ sdata = vif_to_sdata(info->control.vif);
+ if (sta && !sta->uploaded)
+ sta = NULL;
- fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
- drv_tx(local, skb);
+ if (sta)
+ pubsta = &sta->sta;
+ else
+ pubsta = NULL;
- ieee80211_tpt_led_trig_tx(local, fc, len);
- *skbp = skb = next;
- ieee80211_led_tx(local, 1);
- fragm = true;
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_MONITOR:
+ sdata = NULL;
+ vif = NULL;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data, u.ap);
+ /* fall through */
+ default:
+ vif = &sdata->vif;
+ break;
}
- return true;
+ if (local->ops->tx_frags)
+ drv_tx_frags(local, vif, pubsta, skbs);
+ else
+ result = ieee80211_tx_frags(local, vif, pubsta, skbs,
+ txpending);
+
+ ieee80211_tpt_led_trig_tx(local, fc, led_len);
+ ieee80211_led_tx(local, 1);
+
+ WARN_ON(!skb_queue_empty(skbs));
+
+ return result;
}
/*
@@ -1280,8 +1308,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp,
*/
static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
{
- struct sk_buff *skb = tx->skb;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
ieee80211_tx_result res = TX_DROP;
#define CALL_TXH(txh) \
@@ -1315,13 +1342,10 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
txh_done:
if (unlikely(res == TX_DROP)) {
I802_DEBUG_INC(tx->local->tx_handlers_drop);
- while (skb) {
- struct sk_buff *next;
-
- next = skb->next;
- dev_kfree_skb(skb);
- skb = next;
- }
+ if (tx->skb)
+ dev_kfree_skb(tx->skb);
+ else
+ __skb_queue_purge(&tx->skbs);
return -1;
} else if (unlikely(res == TX_QUEUED)) {
I802_DEBUG_INC(tx->local->tx_handlers_queued);
@@ -1342,6 +1366,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_result res_prepare;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
bool result = true;
+ int led_len;
if (unlikely(skb->len < 10)) {
dev_kfree_skb(skb);
@@ -1351,6 +1376,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
/* initialises tx */
+ led_len = skb->len;
res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
if (unlikely(res_prepare == TX_DROP)) {
@@ -1364,7 +1390,8 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
info->band = tx.channel->band;
if (!invoke_tx_handlers(&tx))
- result = __ieee80211_tx(local, &tx.skb, tx.sta, txpending);
+ result = __ieee80211_tx(local, &tx.skbs, led_len,
+ tx.sta, txpending);
out:
rcu_read_unlock();
return result;
@@ -1685,8 +1712,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
int nh_pos, h_pos;
struct sta_info *sta = NULL;
bool wme_sta = false, authorized = false, tdls_auth = false;
- struct sk_buff *tmp_skb;
bool tdls_direct = false;
+ bool multicast;
+ u32 info_flags = 0;
+ u16 info_id = 0;
if (unlikely(skb->len < ETH_HLEN)) {
ret = NETDEV_TX_OK;
@@ -1873,7 +1902,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
* if it is a multicast address (which can only happen
* in AP mode)
*/
- if (!is_multicast_ether_addr(hdr.addr1)) {
+ multicast = is_multicast_ether_addr(hdr.addr1);
+ if (!multicast) {
rcu_read_lock();
sta = sta_info_get(sdata, hdr.addr1);
if (sta) {
@@ -1914,11 +1944,54 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
goto fail;
}
+ if (unlikely(!multicast && skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) {
+ struct sk_buff *orig_skb = skb;
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (skb) {
+ unsigned long flags;
+ int id, r;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ r = idr_get_new_above(&local->ack_status_frames,
+ orig_skb, 1, &id);
+ if (r == -EAGAIN) {
+ idr_pre_get(&local->ack_status_frames,
+ GFP_ATOMIC);
+ r = idr_get_new_above(&local->ack_status_frames,
+ orig_skb, 1, &id);
+ }
+ if (WARN_ON(!id) || id > 0xffff) {
+ idr_remove(&local->ack_status_frames, id);
+ r = -ERANGE;
+ }
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ if (!r) {
+ info_id = id;
+ info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ } else if (skb_shared(skb)) {
+ kfree_skb(orig_skb);
+ } else {
+ kfree_skb(skb);
+ skb = orig_skb;
+ }
+ } else {
+ /* couldn't clone -- lose tx status ... */
+ skb = orig_skb;
+ }
+ }
+
/*
* If the skb is shared we need to obtain our own copy.
*/
if (skb_shared(skb)) {
- tmp_skb = skb;
+ struct sk_buff *tmp_skb = skb;
+
+ /* can't happen -- skb is a clone if info_id != 0 */
+ WARN_ON(info_id);
+
skb = skb_clone(skb, GFP_ATOMIC);
kfree_skb(tmp_skb);
@@ -2019,6 +2092,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memset(info, 0, sizeof(*info));
dev->trans_start = jiffies;
+
+ info->flags = info_flags;
+ info->ack_frame_id = info_id;
+
ieee80211_xmit(sdata, skb);
return NETDEV_TX_OK;
@@ -2062,10 +2139,15 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
result = ieee80211_tx(sdata, skb, true);
} else {
+ struct sk_buff_head skbs;
+
+ __skb_queue_head_init(&skbs);
+ __skb_queue_tail(&skbs, skb);
+
hdr = (struct ieee80211_hdr *)skb->data;
sta = sta_info_get(sdata, hdr->addr1);
- result = __ieee80211_tx(local, &skb, sta, true);
+ result = __ieee80211_tx(local, &skbs, skb->len, sta, true);
}
return result;
@@ -2279,22 +2361,31 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
struct ieee80211_mgmt *mgmt;
u8 *pos;
+ int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
+ sizeof(mgmt->u.beacon);
#ifdef CONFIG_MAC80211_MESH
if (!sdata->u.mesh.mesh_id_len)
goto out;
#endif
- /* headroom, head length, tail length and maximum TIM length */
- skb = dev_alloc_skb(local->tx_headroom + 400 +
- sdata->u.mesh.ie_len);
+ skb = dev_alloc_skb(local->tx_headroom +
+ hdr_len +
+ 2 + /* NULL SSID */
+ 2 + 8 + /* supported rates */
+ 2 + 3 + /* DS params */
+ 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
+ 2 + sizeof(struct ieee80211_ht_cap) +
+ 2 + sizeof(struct ieee80211_ht_info) +
+ 2 + sdata->u.mesh.mesh_id_len +
+ 2 + sizeof(struct ieee80211_meshconf_ie) +
+ sdata->u.mesh.ie_len);
if (!skb)
goto out;
skb_reserve(skb, local->hw.extra_tx_headroom);
- mgmt = (struct ieee80211_mgmt *)
- skb_put(skb, 24 + sizeof(mgmt->u.beacon));
- memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
+ memset(mgmt, 0, hdr_len);
mgmt->frame_control =
cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
memset(mgmt->da, 0xff, ETH_ALEN);
@@ -2313,6 +2404,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
mesh_add_ds_params_ie(skb, sdata) ||
ieee80211_add_ext_srates_ie(&sdata->vif, skb) ||
mesh_add_rsn_ie(skb, sdata) ||
+ mesh_add_ht_cap_ie(skb, sdata) ||
+ mesh_add_ht_info_ie(skb, sdata) ||
mesh_add_meshid_ie(skb, sdata) ||
mesh_add_meshconf_ie(skb, sdata) ||
mesh_add_vendor_ies(skb, sdata)) {
@@ -2355,6 +2448,37 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_beacon_get_tim);
+struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_if_ap *ap = NULL;
+ struct sk_buff *presp = NULL, *skb = NULL;
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ return NULL;
+
+ rcu_read_lock();
+
+ ap = &sdata->u.ap;
+ presp = rcu_dereference(ap->probe_resp);
+ if (!presp)
+ goto out;
+
+ skb = skb_copy(presp, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ memset(hdr->addr1, 0, sizeof(hdr->addr1));
+
+out:
+ rcu_read_unlock();
+ return skb;
+}
+EXPORT_SYMBOL(ieee80211_proberesp_get);
+
struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 51e256c5fb78..c499a16b7200 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -20,6 +20,7 @@
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/bitmap.h>
+#include <linux/crc32.h>
#include <net/net_namespace.h>
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
@@ -96,13 +97,13 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
{
- struct sk_buff *skb = tx->skb;
+ struct sk_buff *skb;
struct ieee80211_hdr *hdr;
- do {
+ skb_queue_walk(&tx->skbs, skb) {
hdr = (struct ieee80211_hdr *) skb->data;
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- } while ((skb = skb->next));
+ }
}
int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
@@ -564,6 +565,172 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_queue_delayed_work);
+u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
+ struct ieee802_11_elems *elems,
+ u64 filter, u32 crc)
+{
+ size_t left = len;
+ u8 *pos = start;
+ bool calc_crc = filter != 0;
+
+ memset(elems, 0, sizeof(*elems));
+ elems->ie_start = start;
+ elems->total_len = len;
+
+ while (left >= 2) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+
+ if (elen > left)
+ break;
+
+ if (calc_crc && id < 64 && (filter & (1ULL << id)))
+ crc = crc32_be(crc, pos - 2, elen + 2);
+
+ switch (id) {
+ case WLAN_EID_SSID:
+ elems->ssid = pos;
+ elems->ssid_len = elen;
+ break;
+ case WLAN_EID_SUPP_RATES:
+ elems->supp_rates = pos;
+ elems->supp_rates_len = elen;
+ break;
+ case WLAN_EID_FH_PARAMS:
+ elems->fh_params = pos;
+ elems->fh_params_len = elen;
+ break;
+ case WLAN_EID_DS_PARAMS:
+ elems->ds_params = pos;
+ elems->ds_params_len = elen;
+ break;
+ case WLAN_EID_CF_PARAMS:
+ elems->cf_params = pos;
+ elems->cf_params_len = elen;
+ break;
+ case WLAN_EID_TIM:
+ if (elen >= sizeof(struct ieee80211_tim_ie)) {
+ elems->tim = (void *)pos;
+ elems->tim_len = elen;
+ }
+ break;
+ case WLAN_EID_IBSS_PARAMS:
+ elems->ibss_params = pos;
+ elems->ibss_params_len = elen;
+ break;
+ case WLAN_EID_CHALLENGE:
+ elems->challenge = pos;
+ elems->challenge_len = elen;
+ break;
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
+ pos[2] == 0xf2) {
+ /* Microsoft OUI (00:50:F2) */
+
+ if (calc_crc)
+ crc = crc32_be(crc, pos - 2, elen + 2);
+
+ if (pos[3] == 1) {
+ /* OUI Type 1 - WPA IE */
+ elems->wpa = pos;
+ elems->wpa_len = elen;
+ } else if (elen >= 5 && pos[3] == 2) {
+ /* OUI Type 2 - WMM IE */
+ if (pos[4] == 0) {
+ elems->wmm_info = pos;
+ elems->wmm_info_len = elen;
+ } else if (pos[4] == 1) {
+ elems->wmm_param = pos;
+ elems->wmm_param_len = elen;
+ }
+ }
+ }
+ break;
+ case WLAN_EID_RSN:
+ elems->rsn = pos;
+ elems->rsn_len = elen;
+ break;
+ case WLAN_EID_ERP_INFO:
+ elems->erp_info = pos;
+ elems->erp_info_len = elen;
+ break;
+ case WLAN_EID_EXT_SUPP_RATES:
+ elems->ext_supp_rates = pos;
+ elems->ext_supp_rates_len = elen;
+ break;
+ case WLAN_EID_HT_CAPABILITY:
+ if (elen >= sizeof(struct ieee80211_ht_cap))
+ elems->ht_cap_elem = (void *)pos;
+ break;
+ case WLAN_EID_HT_INFORMATION:
+ if (elen >= sizeof(struct ieee80211_ht_info))
+ elems->ht_info_elem = (void *)pos;
+ break;
+ case WLAN_EID_MESH_ID:
+ elems->mesh_id = pos;
+ elems->mesh_id_len = elen;
+ break;
+ case WLAN_EID_MESH_CONFIG:
+ if (elen >= sizeof(struct ieee80211_meshconf_ie))
+ elems->mesh_config = (void *)pos;
+ break;
+ case WLAN_EID_PEER_MGMT:
+ elems->peering = pos;
+ elems->peering_len = elen;
+ break;
+ case WLAN_EID_PREQ:
+ elems->preq = pos;
+ elems->preq_len = elen;
+ break;
+ case WLAN_EID_PREP:
+ elems->prep = pos;
+ elems->prep_len = elen;
+ break;
+ case WLAN_EID_PERR:
+ elems->perr = pos;
+ elems->perr_len = elen;
+ break;
+ case WLAN_EID_RANN:
+ if (elen >= sizeof(struct ieee80211_rann_ie))
+ elems->rann = (void *)pos;
+ break;
+ case WLAN_EID_CHANNEL_SWITCH:
+ elems->ch_switch_elem = pos;
+ elems->ch_switch_elem_len = elen;
+ break;
+ case WLAN_EID_QUIET:
+ if (!elems->quiet_elem) {
+ elems->quiet_elem = pos;
+ elems->quiet_elem_len = elen;
+ }
+ elems->num_of_quiet_elem++;
+ break;
+ case WLAN_EID_COUNTRY:
+ elems->country_elem = pos;
+ elems->country_elem_len = elen;
+ break;
+ case WLAN_EID_PWR_CONSTRAINT:
+ elems->pwr_constr_elem = pos;
+ elems->pwr_constr_elem_len = elen;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ elems->timeout_int = pos;
+ elems->timeout_int_len = elen;
+ break;
+ default:
+ break;
+ }
+
+ left -= elen;
+ pos += elen;
+ }
+
+ return crc;
+}
+
void ieee802_11_parse_elems(u8 *start, size_t len,
struct ieee802_11_elems *elems)
{
@@ -812,23 +979,9 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
offset = noffset;
}
- if (sband->ht_cap.ht_supported) {
- u16 cap = sband->ht_cap.cap;
- __le16 tmp;
-
- *pos++ = WLAN_EID_HT_CAPABILITY;
- *pos++ = sizeof(struct ieee80211_ht_cap);
- memset(pos, 0, sizeof(struct ieee80211_ht_cap));
- tmp = cpu_to_le16(cap);
- memcpy(pos, &tmp, sizeof(u16));
- pos += sizeof(u16);
- *pos++ = sband->ht_cap.ampdu_factor |
- (sband->ht_cap.ampdu_density <<
- IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
- memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
- pos += sizeof(sband->ht_cap.mcs);
- pos += 2 + 4 + 1; /* ext info, BF cap, antsel */
- }
+ if (sband->ht_cap.ht_supported)
+ pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
+ sband->ht_cap.cap);
/*
* If adding more here, adjust code in main.c
@@ -1022,7 +1175,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
ieee80211_sdata_running(sdata))
- res = drv_add_interface(local, &sdata->vif);
+ res = drv_add_interface(local, sdata);
}
/* add STAs back */
@@ -1073,7 +1226,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
BSS_CHANGED_BEACON_INT |
BSS_CHANGED_BSSID |
BSS_CHANGED_CQM |
- BSS_CHANGED_QOS;
+ BSS_CHANGED_QOS |
+ BSS_CHANGED_IDLE;
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
@@ -1086,7 +1240,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
changed |= BSS_CHANGED_IBSS;
/* fall through */
case NL80211_IFTYPE_AP:
- changed |= BSS_CHANGED_SSID;
+ changed |= BSS_CHANGED_SSID |
+ BSS_CHANGED_AP_PROBE_RESP;
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
changed |= BSS_CHANGED_BEACON |
@@ -1108,6 +1263,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
}
+ ieee80211_recalc_ps(local, -1);
+
/*
* Clear the WLAN_STA_BLOCK_BA flag so new aggregation
* sessions can be established after a resume.
@@ -1363,6 +1520,103 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif)
}
EXPORT_SYMBOL(ieee80211_disable_rssi_reports);
+u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+ u16 cap)
+{
+ __le16 tmp;
+
+ *pos++ = WLAN_EID_HT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_ht_cap);
+ memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le16(cap);
+ memcpy(pos, &tmp, sizeof(u16));
+ pos += sizeof(u16);
+
+ /* AMPDU parameters */
+ *pos++ = ht_cap->ampdu_factor |
+ (ht_cap->ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+ /* MCS set */
+ memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
+ pos += sizeof(ht_cap->mcs);
+
+ /* extended capabilities */
+ pos += sizeof(__le16);
+
+ /* BF capabilities */
+ pos += sizeof(__le32);
+
+ /* antenna selection */
+ pos += sizeof(u8);
+
+ return pos;
+}
+
+u8 *ieee80211_ie_build_ht_info(u8 *pos,
+ struct ieee80211_sta_ht_cap *ht_cap,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type channel_type)
+{
+ struct ieee80211_ht_info *ht_info;
+ /* Build HT Information */
+ *pos++ = WLAN_EID_HT_INFORMATION;
+ *pos++ = sizeof(struct ieee80211_ht_info);
+ ht_info = (struct ieee80211_ht_info *)pos;
+ ht_info->control_chan =
+ ieee80211_frequency_to_channel(channel->center_freq);
+ switch (channel_type) {
+ case NL80211_CHAN_HT40MINUS:
+ ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ break;
+ case NL80211_CHAN_HT20:
+ default:
+ ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ }
+ if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+ ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+ ht_info->operation_mode = 0x0000;
+ ht_info->stbc_param = 0x0000;
+
+ /* It seems that Basic MCS set and Supported MCS set
+ are identical for the first 10 bytes */
+ memset(&ht_info->basic_set, 0, 16);
+ memcpy(&ht_info->basic_set, &ht_cap->mcs, 10);
+
+ return pos + sizeof(struct ieee80211_ht_info);
+}
+
+enum nl80211_channel_type
+ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info)
+{
+ enum nl80211_channel_type channel_type;
+
+ if (!ht_info)
+ return NL80211_CHAN_NO_HT;
+
+ switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+ channel_type = NL80211_CHAN_HT20;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ default:
+ channel_type = NL80211_CHAN_NO_HT;
+ }
+
+ return channel_type;
+}
+
int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index a1c6bfd55f0f..68ad351479df 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -330,13 +330,12 @@ ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
ieee80211_tx_set_protected(tx);
- skb = tx->skb;
- do {
+ skb_queue_walk(&tx->skbs, skb) {
if (wep_encrypt_skb(tx, skb) < 0) {
I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
return TX_DROP;
}
- } while ((skb = skb->next));
+ }
return TX_CONTINUE;
}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index fd52e695c071..43327115b490 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -83,7 +83,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
- ra = skb->data;
+ qos = true;
break;
#endif
case NL80211_IFTYPE_STATION:
@@ -143,11 +143,15 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
/* Fill in the QoS header if there is one. */
if (ieee80211_is_data_qos(hdr->frame_control)) {
u8 *p = ieee80211_get_qos_ctl(hdr);
- u8 ack_policy = 0, tid;
+ u8 ack_policy, tid;
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
- if (unlikely(sdata->local->wifi_wme_noack_test))
+ /* preserve EOSP bit */
+ ack_policy = *p & IEEE80211_QOS_CTL_EOSP;
+
+ if (unlikely(sdata->local->wifi_wme_noack_test) ||
+ is_multicast_ether_addr(hdr->addr1))
ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
/* qos header is 2 bytes */
*p++ = ack_policy | tid;
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 6c53b6d1002b..6884a2d986dc 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -94,7 +94,8 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
/* frame sending functions */
-static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
+static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *ht_info_ie,
struct ieee80211_supported_band *sband,
struct ieee80211_channel *channel,
enum ieee80211_smps_mode smps)
@@ -102,8 +103,10 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
struct ieee80211_ht_info *ht_info;
u8 *pos;
u32 flags = channel->flags;
- u16 cap = sband->ht_cap.cap;
- __le16 tmp;
+ u16 cap;
+ struct ieee80211_sta_ht_cap ht_cap;
+
+ BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
if (!sband->ht_cap.ht_supported)
return;
@@ -114,9 +117,13 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
return;
+ memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+ ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
/* determine capability flags */
+ cap = ht_cap.cap;
switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
@@ -154,34 +161,8 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
}
/* reserve and fill IE */
-
pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
- *pos++ = WLAN_EID_HT_CAPABILITY;
- *pos++ = sizeof(struct ieee80211_ht_cap);
- memset(pos, 0, sizeof(struct ieee80211_ht_cap));
-
- /* capability flags */
- tmp = cpu_to_le16(cap);
- memcpy(pos, &tmp, sizeof(u16));
- pos += sizeof(u16);
-
- /* AMPDU parameters */
- *pos++ = sband->ht_cap.ampdu_factor |
- (sband->ht_cap.ampdu_density <<
- IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
-
- /* MCS set */
- memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
- pos += sizeof(sband->ht_cap.mcs);
-
- /* extended capabilities */
- pos += sizeof(__le16);
-
- /* BF capabilities */
- pos += sizeof(__le32);
-
- /* antenna selection */
- pos += sizeof(u8);
+ ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
}
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
@@ -356,7 +337,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
if (wk->assoc.use_11n && wk->assoc.wmm_used &&
local->hw.queues >= 4)
- ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie,
+ ieee80211_add_ht_ie(sdata, skb, wk->assoc.ht_information_ie,
sband, wk->chan, wk->assoc.smps);
/* if present, add any custom non-vendor IEs that go after HT */
@@ -969,10 +950,9 @@ static void ieee80211_work_work(struct work_struct *work)
}
if (!started && !local->tmp_channel) {
- bool on_oper_chan;
- bool tmp_chan_changed = false;
- bool on_oper_chan2;
+ bool on_oper_chan, on_oper_chan2;
enum nl80211_channel_type wk_ct;
+
on_oper_chan = ieee80211_cfg_on_oper_channel(local);
/* Work with existing channel type if possible. */
@@ -981,11 +961,6 @@ static void ieee80211_work_work(struct work_struct *work)
wk_ct = ieee80211_calc_ct(wk->chan_type,
local->hw.conf.channel_type);
- if (local->tmp_channel)
- if ((local->tmp_channel != wk->chan) ||
- (local->tmp_channel_type != wk_ct))
- tmp_chan_changed = true;
-
local->tmp_channel = wk->chan;
local->tmp_channel_type = wk_ct;
/*
@@ -1008,12 +983,7 @@ static void ieee80211_work_work(struct work_struct *work)
true,
false);
}
- } else if (tmp_chan_changed)
- /* Still off-channel, but on some other
- * channel, so update hardware.
- * PS should already be off-channel.
- */
- ieee80211_hw_config(local, 0);
+ }
started = true;
wk->timeout = jiffies;
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index f614ce7bb6e3..93aab0715e8a 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -223,14 +223,14 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
ieee80211_tx_result
ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
{
- struct sk_buff *skb = tx->skb;
+ struct sk_buff *skb;
ieee80211_tx_set_protected(tx);
- do {
+ skb_queue_walk(&tx->skbs, skb) {
if (tkip_encrypt_skb(tx, skb) < 0)
return TX_DROP;
- } while ((skb = skb->next));
+ }
return TX_CONTINUE;
}
@@ -390,7 +390,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
u8 scratch[6 * AES_BLOCK_SIZE];
if (info->control.hw_key &&
- !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
+ !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
+ !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
/*
* hwaccel has no need for preallocated room for CCMP
* header or MIC fields
@@ -412,6 +413,12 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
pos = skb_push(skb, CCMP_HDR_LEN);
memmove(pos, pos + CCMP_HDR_LEN, hdrlen);
+
+ /* the HW only needs room for the IV, but not the actual IV */
+ if (info->control.hw_key &&
+ (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
+ return 0;
+
hdr = (struct ieee80211_hdr *) pos;
pos += hdrlen;
@@ -442,14 +449,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
ieee80211_tx_result
ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
{
- struct sk_buff *skb = tx->skb;
+ struct sk_buff *skb;
ieee80211_tx_set_protected(tx);
- do {
+ skb_queue_walk(&tx->skbs, skb) {
if (ccmp_encrypt_skb(tx, skb) < 0)
return TX_DROP;
- } while ((skb = skb->next));
+ }
return TX_CONTINUE;
}
@@ -547,15 +554,22 @@ static inline void bip_ipn_swap(u8 *d, const u8 *s)
ieee80211_tx_result
ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)
{
- struct sk_buff *skb = tx->skb;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
struct ieee80211_key *key = tx->key;
struct ieee80211_mmie *mmie;
u8 aad[20];
u64 pn64;
+ if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
+ return TX_DROP;
+
+ skb = skb_peek(&tx->skbs);
+
+ info = IEEE80211_SKB_CB(skb);
+
if (info->control.hw_key)
- return 0;
+ return TX_CONTINUE;
if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
return TX_DROP;
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 3925c6578767..fe5ca89abfce 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -126,7 +126,10 @@ static inline int nci_request(struct nci_dev *ndev,
static void nci_reset_req(struct nci_dev *ndev, unsigned long opt)
{
- nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 0, NULL);
+ struct nci_core_reset_cmd cmd;
+
+ cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG;
+ nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd);
}
static void nci_init_req(struct nci_dev *ndev, unsigned long opt)
@@ -136,17 +139,11 @@ static void nci_init_req(struct nci_dev *ndev, unsigned long opt)
static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt)
{
- struct nci_core_conn_create_cmd conn_cmd;
struct nci_rf_disc_map_cmd cmd;
struct disc_map_config *cfg = cmd.mapping_configs;
__u8 *num = &cmd.num_mapping_configs;
int i;
- /* create static rf connection */
- conn_cmd.target_handle = 0;
- conn_cmd.num_target_specific_params = 0;
- nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, &conn_cmd);
-
/* set rf mapping configurations */
*num = 0;
@@ -470,7 +467,7 @@ static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx,
ndev->data_exchange_cb = cb;
ndev->data_exchange_cb_context = cb_context;
- rc = nci_send_data(ndev, ndev->conn_id, skb);
+ rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
if (rc)
clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
@@ -726,7 +723,10 @@ static void nci_tx_work(struct work_struct *work)
if (!skb)
return;
- atomic_dec(&ndev->credits_cnt);
+ /* Check if data flow control is used */
+ if (atomic_read(&ndev->credits_cnt) !=
+ NCI_DATA_FLOW_CONTROL_NOT_USED)
+ atomic_dec(&ndev->credits_cnt);
nfc_dbg("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d",
nci_pbf(skb->data),
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index e5ed90fc1a9c..511fb96e21bc 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -95,7 +95,8 @@ static int nci_queue_tx_data_frags(struct nci_dev *ndev,
__skb_queue_head_init(&frags_q);
while (total_len) {
- frag_len = min_t(int, total_len, ndev->max_pkt_payload_size);
+ frag_len =
+ min_t(int, total_len, ndev->max_data_pkt_payload_size);
skb_frag = nci_skb_alloc(ndev,
(NCI_DATA_HDR_SIZE + frag_len),
@@ -151,7 +152,7 @@ int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb)
nfc_dbg("entry, conn_id 0x%x, plen %d", conn_id, skb->len);
/* check if the packet need to be fragmented */
- if (skb->len <= ndev->max_pkt_payload_size) {
+ if (skb->len <= ndev->max_data_pkt_payload_size) {
/* no need to fragment packet */
nci_push_data_hdr(ndev, conn_id, skb, NCI_PBF_LAST);
diff --git a/net/nfc/nci/lib.c b/net/nfc/nci/lib.c
index b19dc2fa90e1..e99adcfb1bcf 100644
--- a/net/nfc/nci/lib.c
+++ b/net/nfc/nci/lib.c
@@ -42,12 +42,9 @@ int nci_to_errno(__u8 code)
case NCI_STATUS_REJECTED:
return -EBUSY;
- case NCI_STATUS_MESSAGE_CORRUPTED:
+ case NCI_STATUS_RF_FRAME_CORRUPTED:
return -EBADMSG;
- case NCI_STATUS_BUFFER_FULL:
- return -ENOBUFS;
-
case NCI_STATUS_NOT_INITIALIZED:
return -EHOSTDOWN;
@@ -80,9 +77,6 @@ int nci_to_errno(__u8 code)
case NCI_STATUS_NFCEE_TIMEOUT_ERROR:
return -ETIMEDOUT;
- case NCI_STATUS_RF_LINK_LOSS_ERROR:
- return -ENOLINK;
-
case NCI_STATUS_MAX_ACTIVE_NFCEE_INTERFACES_REACHED:
return -EDQUOT;
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 96633f5cda4f..c1bf54172c25 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -54,7 +54,7 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev,
ntf->conn_entries[i].conn_id,
ntf->conn_entries[i].credits);
- if (ntf->conn_entries[i].conn_id == ndev->conn_id) {
+ if (ntf->conn_entries[i].conn_id == NCI_STATIC_RF_CONN_ID) {
/* found static rf connection */
atomic_add(ntf->conn_entries[i].credits,
&ndev->credits_cnt);
@@ -66,22 +66,12 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev,
queue_work(ndev->tx_wq, &ndev->tx_work);
}
-static void nci_rf_field_info_ntf_packet(struct nci_dev *ndev,
- struct sk_buff *skb)
-{
- struct nci_rf_field_info_ntf *ntf = (void *) skb->data;
-
- nfc_dbg("entry, rf_field_status %d", ntf->rf_field_status);
-}
-
-static int nci_rf_activate_nfca_passive_poll(struct nci_dev *ndev,
- struct nci_rf_activate_ntf *ntf, __u8 *data)
+static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev,
+ struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
{
struct rf_tech_specific_params_nfca_poll *nfca_poll;
- struct activation_params_nfca_poll_iso_dep *nfca_poll_iso_dep;
nfca_poll = &ntf->rf_tech_specific_params.nfca_poll;
- nfca_poll_iso_dep = &ntf->activation_params.nfca_poll_iso_dep;
nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data));
data += 2;
@@ -100,32 +90,32 @@ static int nci_rf_activate_nfca_passive_poll(struct nci_dev *ndev,
if (nfca_poll->sel_res_len != 0)
nfca_poll->sel_res = *data++;
- ntf->rf_interface_type = *data++;
- ntf->activation_params_len = *data++;
-
- nfc_dbg("sel_res_len %d, sel_res 0x%x, rf_interface_type %d, activation_params_len %d",
+ nfc_dbg("sel_res_len %d, sel_res 0x%x",
nfca_poll->sel_res_len,
- nfca_poll->sel_res,
- ntf->rf_interface_type,
- ntf->activation_params_len);
-
- switch (ntf->rf_interface_type) {
- case NCI_RF_INTERFACE_ISO_DEP:
- nfca_poll_iso_dep->rats_res_len = *data++;
- if (nfca_poll_iso_dep->rats_res_len > 0) {
- memcpy(nfca_poll_iso_dep->rats_res,
+ nfca_poll->sel_res);
+
+ return data;
+}
+
+static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
+ struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
+{
+ struct activation_params_nfca_poll_iso_dep *nfca_poll;
+
+ switch (ntf->activation_rf_tech_and_mode) {
+ case NCI_NFC_A_PASSIVE_POLL_MODE:
+ nfca_poll = &ntf->activation_params.nfca_poll_iso_dep;
+ nfca_poll->rats_res_len = *data++;
+ if (nfca_poll->rats_res_len > 0) {
+ memcpy(nfca_poll->rats_res,
data,
- nfca_poll_iso_dep->rats_res_len);
+ nfca_poll->rats_res_len);
}
break;
- case NCI_RF_INTERFACE_FRAME:
- /* no activation params */
- break;
-
default:
- nfc_err("unsupported rf_interface_type 0x%x",
- ntf->rf_interface_type);
+ nfc_err("unsupported activation_rf_tech_and_mode 0x%x",
+ ntf->activation_rf_tech_and_mode);
return -EPROTO;
}
@@ -133,7 +123,7 @@ static int nci_rf_activate_nfca_passive_poll(struct nci_dev *ndev,
}
static void nci_target_found(struct nci_dev *ndev,
- struct nci_rf_activate_ntf *ntf)
+ struct nci_rf_intf_activated_ntf *ntf)
{
struct nfc_target nfc_tgt;
@@ -141,6 +131,8 @@ static void nci_target_found(struct nci_dev *ndev,
nfc_tgt.supported_protocols = NFC_PROTO_MIFARE_MASK;
else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) /* 4A */
nfc_tgt.supported_protocols = NFC_PROTO_ISO14443_MASK;
+ else
+ nfc_tgt.supported_protocols = 0;
nfc_tgt.sens_res = ntf->rf_tech_specific_params.nfca_poll.sens_res;
nfc_tgt.sel_res = ntf->rf_tech_specific_params.nfca_poll.sel_res;
@@ -158,49 +150,86 @@ static void nci_target_found(struct nci_dev *ndev,
nfc_targets_found(ndev->nfc_dev, &nfc_tgt, 1);
}
-static void nci_rf_activate_ntf_packet(struct nci_dev *ndev,
- struct sk_buff *skb)
+static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
+ struct sk_buff *skb)
{
- struct nci_rf_activate_ntf ntf;
+ struct nci_rf_intf_activated_ntf ntf;
__u8 *data = skb->data;
- int rc = -1;
+ int err = 0;
clear_bit(NCI_DISCOVERY, &ndev->flags);
set_bit(NCI_POLL_ACTIVE, &ndev->flags);
- ntf.target_handle = *data++;
+ ntf.rf_discovery_id = *data++;
+ ntf.rf_interface_type = *data++;
ntf.rf_protocol = *data++;
- ntf.rf_tech_and_mode = *data++;
+ ntf.activation_rf_tech_and_mode = *data++;
ntf.rf_tech_specific_params_len = *data++;
- nfc_dbg("target_handle %d, rf_protocol 0x%x, rf_tech_and_mode 0x%x, rf_tech_specific_params_len %d",
- ntf.target_handle,
- ntf.rf_protocol,
- ntf.rf_tech_and_mode,
+ nfc_dbg("rf_discovery_id %d", ntf.rf_discovery_id);
+ nfc_dbg("rf_interface_type 0x%x", ntf.rf_interface_type);
+ nfc_dbg("rf_protocol 0x%x", ntf.rf_protocol);
+ nfc_dbg("activation_rf_tech_and_mode 0x%x",
+ ntf.activation_rf_tech_and_mode);
+ nfc_dbg("rf_tech_specific_params_len %d",
ntf.rf_tech_specific_params_len);
- switch (ntf.rf_tech_and_mode) {
- case NCI_NFC_A_PASSIVE_POLL_MODE:
- rc = nci_rf_activate_nfca_passive_poll(ndev, &ntf,
- data);
- break;
+ if (ntf.rf_tech_specific_params_len > 0) {
+ switch (ntf.activation_rf_tech_and_mode) {
+ case NCI_NFC_A_PASSIVE_POLL_MODE:
+ data = nci_extract_rf_params_nfca_passive_poll(ndev,
+ &ntf, data);
+ break;
+
+ default:
+ nfc_err("unsupported activation_rf_tech_and_mode 0x%x",
+ ntf.activation_rf_tech_and_mode);
+ return;
+ }
+ }
- default:
- nfc_err("unsupported rf_tech_and_mode 0x%x",
- ntf.rf_tech_and_mode);
- return;
+ ntf.data_exch_rf_tech_and_mode = *data++;
+ ntf.data_exch_tx_bit_rate = *data++;
+ ntf.data_exch_rx_bit_rate = *data++;
+ ntf.activation_params_len = *data++;
+
+ nfc_dbg("data_exch_rf_tech_and_mode 0x%x",
+ ntf.data_exch_rf_tech_and_mode);
+ nfc_dbg("data_exch_tx_bit_rate 0x%x",
+ ntf.data_exch_tx_bit_rate);
+ nfc_dbg("data_exch_rx_bit_rate 0x%x",
+ ntf.data_exch_rx_bit_rate);
+ nfc_dbg("activation_params_len %d",
+ ntf.activation_params_len);
+
+ if (ntf.activation_params_len > 0) {
+ switch (ntf.rf_interface_type) {
+ case NCI_RF_INTERFACE_ISO_DEP:
+ err = nci_extract_activation_params_iso_dep(ndev,
+ &ntf, data);
+ break;
+
+ case NCI_RF_INTERFACE_FRAME:
+ /* no activation params */
+ break;
+
+ default:
+ nfc_err("unsupported rf_interface_type 0x%x",
+ ntf.rf_interface_type);
+ return;
+ }
}
- if (!rc)
+ if (!err)
nci_target_found(ndev, &ntf);
}
static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
struct sk_buff *skb)
{
- __u8 type = skb->data[0];
+ struct nci_rf_deactivate_ntf *ntf = (void *) skb->data;
- nfc_dbg("entry, type 0x%x", type);
+ nfc_dbg("entry, type 0x%x, reason 0x%x", ntf->type, ntf->reason);
clear_bit(NCI_POLL_ACTIVE, &ndev->flags);
ndev->target_active_prot = 0;
@@ -214,6 +243,9 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
ndev->rx_data_reassembly = 0;
}
+ /* set the available credits to initial value */
+ atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
+
/* complete the data exchange transaction, if exists */
if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
nci_data_exchange_complete(ndev, NULL, -EIO);
@@ -237,12 +269,8 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
nci_core_conn_credits_ntf_packet(ndev, skb);
break;
- case NCI_OP_RF_FIELD_INFO_NTF:
- nci_rf_field_info_ntf_packet(ndev, skb);
- break;
-
- case NCI_OP_RF_ACTIVATE_NTF:
- nci_rf_activate_ntf_packet(ndev, skb);
+ case NCI_OP_RF_INTF_ACTIVATED_NTF:
+ nci_rf_intf_activated_ntf_packet(ndev, skb);
break;
case NCI_OP_RF_DEACTIVATE_NTF:
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index 0403d4cd0917..0591f5aff89f 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -42,10 +42,11 @@ static void nci_core_reset_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
nfc_dbg("entry, status 0x%x", rsp->status);
- if (rsp->status == NCI_STATUS_OK)
+ if (rsp->status == NCI_STATUS_OK) {
ndev->nci_ver = rsp->nci_ver;
-
- nfc_dbg("nci_ver 0x%x", ndev->nci_ver);
+ nfc_dbg("nci_ver 0x%x, config_status 0x%x",
+ rsp->nci_ver, rsp->config_status);
+ }
nci_req_complete(ndev, rsp->status);
}
@@ -58,13 +59,13 @@ static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
nfc_dbg("entry, status 0x%x", rsp_1->status);
if (rsp_1->status != NCI_STATUS_OK)
- return;
+ goto exit;
ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features);
ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces;
if (ndev->num_supported_rf_interfaces >
- NCI_MAX_SUPPORTED_RF_INTERFACES) {
+ NCI_MAX_SUPPORTED_RF_INTERFACES) {
ndev->num_supported_rf_interfaces =
NCI_MAX_SUPPORTED_RF_INTERFACES;
}
@@ -73,20 +74,26 @@ static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
rsp_1->supported_rf_interfaces,
ndev->num_supported_rf_interfaces);
- rsp_2 = (void *) (skb->data + 6 + ndev->num_supported_rf_interfaces);
+ rsp_2 = (void *) (skb->data + 6 + rsp_1->num_supported_rf_interfaces);
ndev->max_logical_connections =
rsp_2->max_logical_connections;
ndev->max_routing_table_size =
__le16_to_cpu(rsp_2->max_routing_table_size);
- ndev->max_control_packet_payload_length =
- rsp_2->max_control_packet_payload_length;
- ndev->rf_sending_buffer_size =
- __le16_to_cpu(rsp_2->rf_sending_buffer_size);
- ndev->rf_receiving_buffer_size =
- __le16_to_cpu(rsp_2->rf_receiving_buffer_size);
- ndev->manufacturer_id =
- __le16_to_cpu(rsp_2->manufacturer_id);
+ ndev->max_ctrl_pkt_payload_len =
+ rsp_2->max_ctrl_pkt_payload_len;
+ ndev->max_size_for_large_params =
+ __le16_to_cpu(rsp_2->max_size_for_large_params);
+ ndev->max_data_pkt_payload_size =
+ rsp_2->max_data_pkt_payload_size;
+ ndev->initial_num_credits =
+ rsp_2->initial_num_credits;
+ ndev->manufact_id =
+ rsp_2->manufact_id;
+ ndev->manufact_specific_info =
+ __le32_to_cpu(rsp_2->manufact_specific_info);
+
+ atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
nfc_dbg("nfcc_features 0x%x",
ndev->nfcc_features);
@@ -104,39 +111,23 @@ static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
ndev->max_logical_connections);
nfc_dbg("max_routing_table_size %d",
ndev->max_routing_table_size);
- nfc_dbg("max_control_packet_payload_length %d",
- ndev->max_control_packet_payload_length);
- nfc_dbg("rf_sending_buffer_size %d",
- ndev->rf_sending_buffer_size);
- nfc_dbg("rf_receiving_buffer_size %d",
- ndev->rf_receiving_buffer_size);
- nfc_dbg("manufacturer_id 0x%x",
- ndev->manufacturer_id);
-
+ nfc_dbg("max_ctrl_pkt_payload_len %d",
+ ndev->max_ctrl_pkt_payload_len);
+ nfc_dbg("max_size_for_large_params %d",
+ ndev->max_size_for_large_params);
+ nfc_dbg("max_data_pkt_payload_size %d",
+ ndev->max_data_pkt_payload_size);
+ nfc_dbg("initial_num_credits %d",
+ ndev->initial_num_credits);
+ nfc_dbg("manufact_id 0x%x",
+ ndev->manufact_id);
+ nfc_dbg("manufact_specific_info 0x%x",
+ ndev->manufact_specific_info);
+
+exit:
nci_req_complete(ndev, rsp_1->status);
}
-static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev,
- struct sk_buff *skb)
-{
- struct nci_core_conn_create_rsp *rsp = (void *) skb->data;
-
- nfc_dbg("entry, status 0x%x", rsp->status);
-
- if (rsp->status != NCI_STATUS_OK)
- return;
-
- ndev->max_pkt_payload_size = rsp->max_pkt_payload_size;
- ndev->initial_num_credits = rsp->initial_num_credits;
- ndev->conn_id = rsp->conn_id;
-
- atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
-
- nfc_dbg("max_pkt_payload_size %d", ndev->max_pkt_payload_size);
- nfc_dbg("initial_num_credits %d", ndev->initial_num_credits);
- nfc_dbg("conn_id %d", ndev->conn_id);
-}
-
static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev,
struct sk_buff *skb)
{
@@ -196,10 +187,6 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
nci_core_init_rsp_packet(ndev, skb);
break;
- case NCI_OP_CORE_CONN_CREATE_RSP:
- nci_core_conn_create_rsp_packet(ndev, skb);
- break;
-
case NCI_OP_RF_DISCOVER_MAP_RSP:
nci_rf_disc_map_rsp_packet(ndev, skb);
break;
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 5be19575c340..354760ebbbd2 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -644,7 +644,7 @@ static ssize_t rfkill_soft_store(struct device *dev,
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- err = strict_strtoul(buf, 0, &state);
+ err = kstrtoul(buf, 0, &state);
if (err)
return err;
@@ -688,7 +688,7 @@ static ssize_t rfkill_state_store(struct device *dev,
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- err = strict_strtoul(buf, 0, &state);
+ err = kstrtoul(buf, 0, &state);
if (err)
return err;
diff --git a/net/socket.c b/net/socket.c
index 2877647f347b..425ef4270460 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -538,6 +538,8 @@ int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags)
*tx_flags |= SKBTX_HW_TSTAMP;
if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE))
*tx_flags |= SKBTX_SW_TSTAMP;
+ if (sock_flag(sk, SOCK_WIFI_STATUS))
+ *tx_flags |= SKBTX_WIFI_STATUS;
return 0;
}
EXPORT_SYMBOL(sock_tx_timestamp);
@@ -674,6 +676,22 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
}
EXPORT_SYMBOL_GPL(__sock_recv_timestamp);
+void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
+ struct sk_buff *skb)
+{
+ int ack;
+
+ if (!sock_flag(sk, SOCK_WIFI_STATUS))
+ return;
+ if (!skb->wifi_acked_valid)
+ return;
+
+ ack = skb->wifi_acked;
+
+ put_cmsg(msg, SOL_SOCKET, SCM_WIFI_STATUS, sizeof(ack), &ack);
+}
+EXPORT_SYMBOL_GPL(__sock_recv_wifi_status);
+
static inline void sock_recv_drops(struct msghdr *msg, struct sock *sk,
struct sk_buff *skb)
{
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 220f3bd176f8..ccdfed897651 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -492,6 +492,10 @@ int wiphy_register(struct wiphy *wiphy)
!(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
return -EINVAL;
+ if (WARN_ON(wiphy->ap_sme_capa &&
+ !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
+ return -EINVAL;
+
if (WARN_ON(wiphy->addresses && !wiphy->n_addresses))
return -EINVAL;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b9ec3061ed72..fb08c28fc90a 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -54,6 +54,8 @@ struct cfg80211_registered_device {
int opencount; /* also protected by devlist_mtx */
wait_queue_head_t dev_wait;
+ u32 ap_beacons_nlpid;
+
/* BSSes/scanning */
spinlock_t bss_lock;
struct list_head bss_list;
@@ -339,13 +341,17 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp,
- struct cfg80211_crypto_settings *crypt);
+ struct cfg80211_crypto_settings *crypt,
+ u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
+ struct ieee80211_ht_cap *ht_capa_mask);
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct ieee80211_channel *chan,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp,
- struct cfg80211_crypto_settings *crypt);
+ struct cfg80211_crypto_settings *crypt,
+ u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
+ struct ieee80211_ht_cap *ht_capa_mask);
int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *bssid,
const u8 *ie, int ie_len, u16 reason,
@@ -376,7 +382,9 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
- u64 *cookie);
+ bool dont_wait_for_ack, u64 *cookie);
+void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
+ const struct ieee80211_ht_cap *ht_capa_mask);
/* SME */
int __cfg80211_connect(struct cfg80211_registered_device *rdev,
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 21fc9702f81c..438dfc105b4a 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -501,13 +501,32 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
return err;
}
+/* Do a logical ht_capa &= ht_capa_mask. */
+void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
+ const struct ieee80211_ht_cap *ht_capa_mask)
+{
+ int i;
+ u8 *p1, *p2;
+ if (!ht_capa_mask) {
+ memset(ht_capa, 0, sizeof(*ht_capa));
+ return;
+ }
+
+ p1 = (u8*)(ht_capa);
+ p2 = (u8*)(ht_capa_mask);
+ for (i = 0; i<sizeof(*ht_capa); i++)
+ p1[i] &= p2[i];
+}
+
int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_channel *chan,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp,
- struct cfg80211_crypto_settings *crypt)
+ struct cfg80211_crypto_settings *crypt,
+ u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
+ struct ieee80211_ht_cap *ht_capa_mask)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_assoc_request req;
@@ -537,6 +556,15 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
memcpy(&req.crypto, crypt, sizeof(req.crypto));
req.use_mfp = use_mfp;
req.prev_bssid = prev_bssid;
+ req.flags = assoc_flags;
+ if (ht_capa)
+ memcpy(&req.ht_capa, ht_capa, sizeof(req.ht_capa));
+ if (ht_capa_mask)
+ memcpy(&req.ht_capa_mask, ht_capa_mask,
+ sizeof(req.ht_capa_mask));
+ cfg80211_oper_and_ht_capa(&req.ht_capa_mask,
+ rdev->wiphy.ht_capa_mod_mask);
+
req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
if (!req.bss) {
@@ -574,14 +602,17 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp,
- struct cfg80211_crypto_settings *crypt)
+ struct cfg80211_crypto_settings *crypt,
+ u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
+ struct ieee80211_ht_cap *ht_capa_mask)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
wdev_lock(wdev);
err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
- ssid, ssid_len, ie, ie_len, use_mfp, crypt);
+ ssid, ssid_len, ie, ie_len, use_mfp, crypt,
+ assoc_flags, ht_capa, ht_capa_mask);
wdev_unlock(wdev);
return err;
@@ -879,6 +910,9 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid)
}
spin_unlock_bh(&wdev->mgmt_registrations_lock);
+
+ if (nlpid == wdev->ap_unexpected_nlpid)
+ wdev->ap_unexpected_nlpid = 0;
}
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
@@ -901,7 +935,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
- u64 *cookie)
+ bool dont_wait_for_ack, u64 *cookie)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
const struct ieee80211_mgmt *mgmt;
@@ -992,7 +1026,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
/* Transmit the Action frame as requested by user space */
return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan,
channel_type, channel_type_valid,
- wait, buf, len, no_cck, cookie);
+ wait, buf, len, no_cck, dont_wait_for_ack,
+ cookie);
}
bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf,
@@ -1107,3 +1142,30 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO))
+ return false;
+
+ return nl80211_unexpected_frame(dev, addr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
+
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+ wdev->iftype != NL80211_IFTYPE_AP_VLAN))
+ return false;
+
+ return nl80211_unexpected_4addr_frame(dev, addr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 48260c2d092a..a1cabde7cb5f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -98,7 +98,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
[NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
[NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
- [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
+ [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
[NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
@@ -132,8 +132,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
[NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
- [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
- .len = NL80211_HT_CAPABILITY_LEN },
+ [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN },
[NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
[NL80211_ATTR_IE] = { .type = NLA_BINARY,
@@ -197,6 +196,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
[NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
[NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
+ [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
+ [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
+ [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_HT_CAPABILITY_MASK] = {
+ .len = NL80211_HT_CAPABILITY_LEN
+ },
};
/* policy for the key attributes */
@@ -204,7 +211,7 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
[NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
[NL80211_KEY_IDX] = { .type = NLA_U8 },
[NL80211_KEY_CIPHER] = { .type = NLA_U32 },
- [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
+ [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
[NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
[NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
[NL80211_KEY_TYPE] = { .type = NLA_U32 },
@@ -759,6 +766,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
dev->wiphy.available_antennas_rx);
+ if (dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD)
+ NLA_PUT_U32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
+ dev->wiphy.probe_resp_offload);
+
if ((dev->wiphy.available_antennas_tx ||
dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
u32 tx_ant = 0, rx_ant = 0;
@@ -875,7 +886,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
CMD(set_pmksa, SET_PMKSA);
CMD(del_pmksa, DEL_PMKSA);
CMD(flush_pmksa, FLUSH_PMKSA);
- CMD(remain_on_channel, REMAIN_ON_CHANNEL);
+ if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
+ CMD(remain_on_channel, REMAIN_ON_CHANNEL);
CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
CMD(mgmt_tx, FRAME);
CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
@@ -891,6 +903,15 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
}
if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
CMD(sched_scan_start, START_SCHED_SCAN);
+ CMD(probe_client, PROBE_CLIENT);
+ if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
+ i++;
+ NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS);
+ }
+
+#ifdef CONFIG_NL80211_TESTMODE
+ CMD(testmode_cmd, TESTMODE);
+#endif
#undef CMD
@@ -906,11 +927,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
nla_nest_end(msg, nl_cmds);
- if (dev->ops->remain_on_channel)
+ if (dev->ops->remain_on_channel &&
+ dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
dev->wiphy.max_remain_on_channel_duration);
- if (dev->ops->mgmt_tx_cancel_wait)
+ if (dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)
NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
if (mgmt_stypes) {
@@ -1008,6 +1030,17 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (nl80211_put_iface_combinations(&dev->wiphy, msg))
goto nla_put_failure;
+ if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME)
+ NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME,
+ dev->wiphy.ap_sme_capa);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features);
+
+ if (dev->wiphy.ht_capa_mod_mask)
+ NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
+ sizeof(*dev->wiphy.ht_capa_mod_mask),
+ dev->wiphy.ht_capa_mod_mask);
+
return genlmsg_end(msg, hdr);
nla_put_failure:
@@ -1253,6 +1286,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
goto bad_res;
}
+ if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+ result = -EINVAL;
+ goto bad_res;
+ }
+
nla_for_each_nested(nl_txq_params,
info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
rem_txq_params) {
@@ -2150,6 +2189,13 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
}
+ if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
+ params.probe_resp =
+ nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
+ params.probe_resp_len =
+ nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
+ }
+
err = call(&rdev->wiphy, dev, &params);
if (!err && params.interval)
wdev->beacon_interval = params.interval;
@@ -2448,26 +2494,34 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
/*
* Get vlan interface making sure it is running and on the right wiphy.
*/
-static int get_vlan(struct genl_info *info,
- struct cfg80211_registered_device *rdev,
- struct net_device **vlan)
+static struct net_device *get_vlan(struct genl_info *info,
+ struct cfg80211_registered_device *rdev)
{
struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
- *vlan = NULL;
-
- if (vlanattr) {
- *vlan = dev_get_by_index(genl_info_net(info),
- nla_get_u32(vlanattr));
- if (!*vlan)
- return -ENODEV;
- if (!(*vlan)->ieee80211_ptr)
- return -EINVAL;
- if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
- return -EINVAL;
- if (!netif_running(*vlan))
- return -ENETDOWN;
+ struct net_device *v;
+ int ret;
+
+ if (!vlanattr)
+ return NULL;
+
+ v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
+ if (!v)
+ return ERR_PTR(-ENODEV);
+
+ if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
+ ret = -EINVAL;
+ goto error;
}
- return 0;
+
+ if (!netif_running(v)) {
+ ret = -ENETDOWN;
+ goto error;
+ }
+
+ return v;
+ error:
+ dev_put(v);
+ return ERR_PTR(ret);
}
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
@@ -2517,9 +2571,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.plink_state =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
- err = get_vlan(info, rdev, &params.vlan);
- if (err)
- goto out;
+ params.vlan = get_vlan(info, rdev);
+ if (IS_ERR(params.vlan))
+ return PTR_ERR(params.vlan);
/* validate settings */
err = 0;
@@ -2687,9 +2741,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)))
return -EINVAL;
- err = get_vlan(info, rdev, &params.vlan);
- if (err)
- goto out;
+ params.vlan = get_vlan(info, rdev);
+ if (IS_ERR(params.vlan))
+ return PTR_ERR(params.vlan);
/* validate settings */
err = 0;
@@ -3352,6 +3406,9 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
cfg80211_regdomain->alpha2);
+ if (cfg80211_regdomain->dfs_region)
+ NLA_PUT_U8(msg, NL80211_ATTR_DFS_REGION,
+ cfg80211_regdomain->dfs_region);
nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
if (!nl_reg_rules)
@@ -3410,6 +3467,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
char *alpha2 = NULL;
int rem_reg_rules = 0, r = 0;
u32 num_rules = 0, rule_idx = 0, size_of_regd;
+ u8 dfs_region = 0;
struct ieee80211_regdomain *rd = NULL;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
@@ -3420,6 +3478,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
+ if (info->attrs[NL80211_ATTR_DFS_REGION])
+ dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
+
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
rem_reg_rules) {
num_rules++;
@@ -3447,6 +3508,13 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
rd->alpha2[0] = alpha2[0];
rd->alpha2[1] = alpha2[1];
+ /*
+ * Disable DFS master mode if the DFS region was
+ * not supported or known on this kernel.
+ */
+ if (reg_supported_dfs_region(dfs_region))
+ rd->dfs_region = dfs_region;
+
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
rem_reg_rules) {
nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
@@ -4354,6 +4422,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
int err, ssid_len, ie_len = 0;
bool use_mfp = false;
+ u32 flags = 0;
+ struct ieee80211_ht_cap *ht_capa = NULL;
+ struct ieee80211_ht_cap *ht_capa_mask = NULL;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
return -EINVAL;
@@ -4397,11 +4468,25 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_PREV_BSSID])
prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
+ flags |= ASSOC_REQ_DISABLE_HT;
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ ht_capa_mask =
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+ if (!ht_capa_mask)
+ return -EINVAL;
+ ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+ }
+
err = nl80211_crypto_settings(rdev, info, &crypto, 1);
if (!err)
err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
ssid, ssid_len, ie, ie_len, use_mfp,
- &crypto);
+ &crypto, flags, ht_capa,
+ ht_capa_mask);
return err;
}
@@ -4891,6 +4976,22 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
return PTR_ERR(connkeys);
}
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
+ connect.flags |= ASSOC_REQ_DISABLE_HT;
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ memcpy(&connect.ht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+ sizeof(connect.ht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ return -EINVAL;
+ memcpy(&connect.ht_capa,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+ sizeof(connect.ht_capa));
+ }
+
err = cfg80211_connect(rdev, dev, &connect, connkeys);
if (err)
kfree(connkeys);
@@ -5078,7 +5179,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
duration > rdev->wiphy.max_remain_on_channel_duration)
return -EINVAL;
- if (!rdev->ops->remain_on_channel)
+ if (!rdev->ops->remain_on_channel ||
+ !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
return -EOPNOTSUPP;
if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
@@ -5266,12 +5368,13 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
bool channel_type_valid = false;
u32 freq;
int err;
- void *hdr;
+ void *hdr = NULL;
u64 cookie;
- struct sk_buff *msg;
+ struct sk_buff *msg = NULL;
unsigned int wait = 0;
- bool offchan;
- bool no_cck;
+ bool offchan, no_cck, dont_wait_for_ack;
+
+ dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
if (!info->attrs[NL80211_ATTR_FRAME] ||
!info->attrs[NL80211_ATTR_WIPHY_FREQ])
@@ -5290,7 +5393,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
return -EOPNOTSUPP;
if (info->attrs[NL80211_ATTR_DURATION]) {
- if (!rdev->ops->mgmt_tx_cancel_wait)
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
return -EINVAL;
wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
}
@@ -5308,6 +5411,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
+ if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
+ return -EINVAL;
+
no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
@@ -5315,29 +5421,36 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
if (chan == NULL)
return -EINVAL;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
+ if (!dont_wait_for_ack) {
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
- hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
- NL80211_CMD_FRAME);
+ hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_FRAME);
- if (IS_ERR(hdr)) {
- err = PTR_ERR(hdr);
- goto free_msg;
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto free_msg;
+ }
}
+
err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
channel_type_valid, wait,
nla_data(info->attrs[NL80211_ATTR_FRAME]),
nla_len(info->attrs[NL80211_ATTR_FRAME]),
- no_cck, &cookie);
+ no_cck, dont_wait_for_ack, &cookie);
if (err)
goto free_msg;
- NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
+ if (msg) {
+ NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
- genlmsg_end(msg, hdr);
- return genlmsg_reply(msg, info);
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+ }
+
+ return 0;
nla_put_failure:
err = -ENOBUFS;
@@ -5827,6 +5940,91 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_register_unexpected_frame(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+
+ if (wdev->ap_unexpected_nlpid)
+ return -EBUSY;
+
+ wdev->ap_unexpected_nlpid = info->snd_pid;
+ return 0;
+}
+
+static int nl80211_probe_client(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct sk_buff *msg;
+ void *hdr;
+ const u8 *addr;
+ u64 cookie;
+ int err;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ if (!rdev->ops->probe_client)
+ return -EOPNOTSUPP;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_PROBE_CLIENT);
+
+ if (IS_ERR(hdr)) {
+ err = PTR_ERR(hdr);
+ goto free_msg;
+ }
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
+ if (err)
+ goto free_msg;
+
+ NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
+
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+ err = -ENOBUFS;
+ free_msg:
+ nlmsg_free(msg);
+ return err;
+}
+
+static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
+ return -EOPNOTSUPP;
+
+ if (rdev->ap_beacons_nlpid)
+ return -EBUSY;
+
+ rdev->ap_beacons_nlpid = info->snd_pid;
+
+ return 0;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -6382,6 +6580,30 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_UNEXPECTED_FRAME,
+ .doit = nl80211_register_unexpected_frame,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_PROBE_CLIENT,
+ .doit = nl80211_probe_client,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_REGISTER_BEACONS,
+ .doit = nl80211_register_beacons,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -6634,10 +6856,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
if (wiphy_idx_valid(request->wiphy_idx))
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
rcu_read_lock();
genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
@@ -6673,10 +6892,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -6757,10 +6973,7 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -6816,10 +7029,7 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
if (resp_ie)
NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -6857,10 +7067,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
if (resp_ie)
NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -6898,10 +7105,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
if (ie)
NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, GFP_KERNEL);
@@ -6934,10 +7138,7 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -6972,10 +7173,7 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
if (ie_len && ie)
NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -7014,10 +7212,7 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
if (tsc)
NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -7068,10 +7263,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
goto nla_put_failure;
nla_nest_end(msg, nl_freq);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
rcu_read_lock();
genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
@@ -7114,10 +7306,7 @@ static void nl80211_send_remain_on_chan_event(
if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -7188,10 +7377,7 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -7202,13 +7388,68 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
+ const u8 *addr, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+ u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);
+
+ if (!nlpid)
+ return false;
+
+ msg = nlmsg_new(100, gfp);
+ if (!msg)
+ return true;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return true;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ err = genlmsg_end(msg, hdr);
+ if (err < 0) {
+ nlmsg_free(msg);
+ return true;
+ }
+
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
+ return true;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+ return true;
+}
+
+bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
+{
+ return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
+ addr, gfp);
+}
+
+bool nl80211_unexpected_4addr_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp)
+{
+ return __nl80211_unexpected_frame(dev,
+ NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+ addr, gfp);
+}
+
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u32 nlpid,
int freq, const u8 *buf, size_t len, gfp_t gfp)
{
struct sk_buff *msg;
void *hdr;
- int err;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
@@ -7225,16 +7466,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
- err = genlmsg_end(msg, hdr);
- if (err < 0) {
- nlmsg_free(msg);
- return err;
- }
+ genlmsg_end(msg, hdr);
- err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
- if (err < 0)
- return err;
- return 0;
+ return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
nla_put_failure:
genlmsg_cancel(msg, hdr);
@@ -7267,10 +7501,7 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
if (ack)
NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
return;
@@ -7312,10 +7543,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
nla_nest_end(msg, pinfoattr);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -7357,10 +7585,7 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
nla_nest_end(msg, rekey_attr);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -7403,10 +7628,7 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
nla_nest_end(msg, attr);
- if (genlmsg_end(msg, hdr) < 0) {
- nlmsg_free(msg);
- return;
- }
+ genlmsg_end(msg, hdr);
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
nl80211_mlme_mcgrp.id, gfp);
@@ -7448,7 +7670,45 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
nla_nest_end(msg, pinfoattr);
- if (genlmsg_end(msg, hdr) < 0) {
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
+ u64 cookie, bool acked, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+ NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
+ if (acked)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
+
+ err = genlmsg_end(msg, hdr);
+ if (err < 0) {
nlmsg_free(msg);
return;
}
@@ -7461,6 +7721,45 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
+EXPORT_SYMBOL(cfg80211_probe_status);
+
+void cfg80211_report_obss_beacon(struct wiphy *wiphy,
+ const u8 *frame, size_t len,
+ int freq, gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid);
+
+ if (!nlpid)
+ return;
+
+ msg = nlmsg_new(len + 100, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ if (freq)
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_report_obss_beacon);
static int nl80211_netlink_notify(struct notifier_block * nb,
unsigned long state,
@@ -7475,9 +7774,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
rcu_read_lock();
- list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
+ list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
cfg80211_mlme_unregister_socket(wdev, notify->pid);
+ if (rdev->ap_beacons_nlpid == notify->pid)
+ rdev->ap_beacons_nlpid = 0;
+ }
rcu_read_unlock();
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index f24a1fbeaf19..12bf4d185abe 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -117,4 +117,9 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, int index,
const u8 *bssid, bool preauth, gfp_t gfp);
+bool nl80211_unexpected_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp);
+bool nl80211_unexpected_4addr_frame(struct net_device *dev,
+ const u8 *addr, gfp_t gfp);
+
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 6acba9d18cc8..0e67016ce78f 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1123,6 +1123,8 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,
if (ignore_reg_update(wiphy, initiator))
return;
+ last_request->dfs_region = cfg80211_regdomain->dfs_region;
+
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (wiphy->bands[band])
handle_band(wiphy, band, initiator);
@@ -1948,6 +1950,42 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
}
}
+bool reg_supported_dfs_region(u8 dfs_region)
+{
+ switch (dfs_region) {
+ case NL80211_DFS_UNSET:
+ case NL80211_DFS_FCC:
+ case NL80211_DFS_ETSI:
+ case NL80211_DFS_JP:
+ return true;
+ default:
+ REG_DBG_PRINT("Ignoring uknown DFS master region: %d\n",
+ dfs_region);
+ return false;
+ }
+}
+
+static void print_dfs_region(u8 dfs_region)
+{
+ if (!dfs_region)
+ return;
+
+ switch (dfs_region) {
+ case NL80211_DFS_FCC:
+ pr_info(" DFS Master region FCC");
+ break;
+ case NL80211_DFS_ETSI:
+ pr_info(" DFS Master region ETSI");
+ break;
+ case NL80211_DFS_JP:
+ pr_info(" DFS Master region JP");
+ break;
+ default:
+ pr_info(" DFS Master region Uknown");
+ break;
+ }
+}
+
static void print_regdomain(const struct ieee80211_regdomain *rd)
{
@@ -1975,6 +2013,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
pr_info("Regulatory domain changed to country: %c%c\n",
rd->alpha2[0], rd->alpha2[1]);
}
+ print_dfs_region(rd->dfs_region);
print_rd_rules(rd);
}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 4a56799d868d..786e414afd91 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -5,6 +5,7 @@ extern const struct ieee80211_regdomain *cfg80211_regdomain;
bool is_world_regdom(const char *alpha2);
bool reg_is_valid_request(const char *alpha2);
+bool reg_supported_dfs_region(u8 dfs_region);
int regulatory_hint_user(const char *alpha2);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 0fb142410404..31119e32e092 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -259,17 +259,20 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
{
const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
- int r;
+ /* equal if both missing */
if (!ie1 && !ie2)
return 0;
- if (!ie1 || !ie2)
+ /* sort missing IE before (left of) present IE */
+ if (!ie1)
return -1;
+ if (!ie2)
+ return 1;
- r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1]));
- if (r == 0 && ie1[1] != ie2[1])
+ /* sort by length first, then by contents */
+ if (ie1[1] != ie2[1])
return ie2[1] - ie1[1];
- return r;
+ return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
}
static bool is_bss(struct cfg80211_bss *a,
@@ -352,8 +355,8 @@ static bool is_mesh(struct cfg80211_bss *a,
sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
}
-static int cmp_bss(struct cfg80211_bss *a,
- struct cfg80211_bss *b)
+static int cmp_bss_core(struct cfg80211_bss *a,
+ struct cfg80211_bss *b)
{
int r;
@@ -375,7 +378,15 @@ static int cmp_bss(struct cfg80211_bss *a,
b->len_information_elements);
}
- r = memcmp(a->bssid, b->bssid, ETH_ALEN);
+ return memcmp(a->bssid, b->bssid, ETH_ALEN);
+}
+
+static int cmp_bss(struct cfg80211_bss *a,
+ struct cfg80211_bss *b)
+{
+ int r;
+
+ r = cmp_bss_core(a, b);
if (r)
return r;
@@ -386,6 +397,52 @@ static int cmp_bss(struct cfg80211_bss *a,
b->len_information_elements);
}
+static int cmp_hidden_bss(struct cfg80211_bss *a,
+ struct cfg80211_bss *b)
+{
+ const u8 *ie1;
+ const u8 *ie2;
+ int i;
+ int r;
+
+ r = cmp_bss_core(a, b);
+ if (r)
+ return r;
+
+ ie1 = cfg80211_find_ie(WLAN_EID_SSID,
+ a->information_elements,
+ a->len_information_elements);
+ ie2 = cfg80211_find_ie(WLAN_EID_SSID,
+ b->information_elements,
+ b->len_information_elements);
+
+ /* Key comparator must use same algorithm in any rb-tree
+ * search function (order is important), otherwise ordering
+ * of items in the tree is broken and search gives incorrect
+ * results. This code uses same order as cmp_ies() does. */
+
+ /* sort missing IE before (left of) present IE */
+ if (!ie1)
+ return -1;
+ if (!ie2)
+ return 1;
+
+ /* zero-size SSID is used as an indication of the hidden bss */
+ if (!ie2[1])
+ return 0;
+
+ /* sort by length first, then by contents */
+ if (ie1[1] != ie2[1])
+ return ie2[1] - ie1[1];
+
+ /* zeroed SSID ie is another indication of a hidden bss */
+ for (i = 0; i < ie2[1]; i++)
+ if (ie2[i + 2])
+ return -1;
+
+ return 0;
+}
+
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel,
const u8 *bssid,
@@ -502,6 +559,48 @@ rb_find_bss(struct cfg80211_registered_device *dev,
}
static struct cfg80211_internal_bss *
+rb_find_hidden_bss(struct cfg80211_registered_device *dev,
+ struct cfg80211_internal_bss *res)
+{
+ struct rb_node *n = dev->bss_tree.rb_node;
+ struct cfg80211_internal_bss *bss;
+ int r;
+
+ while (n) {
+ bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
+ r = cmp_hidden_bss(&res->pub, &bss->pub);
+
+ if (r == 0)
+ return bss;
+ else if (r < 0)
+ n = n->rb_left;
+ else
+ n = n->rb_right;
+ }
+
+ return NULL;
+}
+
+static void
+copy_hidden_ies(struct cfg80211_internal_bss *res,
+ struct cfg80211_internal_bss *hidden)
+{
+ if (unlikely(res->pub.beacon_ies))
+ return;
+ if (WARN_ON(!hidden->pub.beacon_ies))
+ return;
+
+ res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC);
+ if (unlikely(!res->pub.beacon_ies))
+ return;
+
+ res->beacon_ies_allocated = true;
+ res->pub.len_beacon_ies = hidden->pub.len_beacon_ies;
+ memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies,
+ res->pub.len_beacon_ies);
+}
+
+static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res)
{
@@ -604,6 +703,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
kref_put(&res->ref, bss_release);
} else {
+ struct cfg80211_internal_bss *hidden;
+
+ /* First check if the beacon is a probe response from
+ * a hidden bss. If so, copy beacon ies (with nullified
+ * ssid) into the probe response bss entry (with real ssid).
+ * It is required basically for PSM implementation
+ * (probe responses do not contain tim ie) */
+
+ /* TODO: The code is not trying to update existing probe
+ * response bss entries when beacon ies are
+ * getting changed. */
+ hidden = rb_find_hidden_bss(dev, res);
+ if (hidden)
+ copy_hidden_ies(res, hidden);
+
/* this "consumes" the reference */
list_add_tail(&res->list, &dev->bss_list);
rb_insert_bss(dev, res);
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 0acfdc9beacf..f0c900ce2fb9 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -190,7 +190,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
prev_bssid,
params->ssid, params->ssid_len,
params->ie, params->ie_len,
- false, &params->crypto);
+ false, &params->crypto,
+ params->flags, &params->ht_capa,
+ &params->ht_capa_mask);
if (err)
__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
NULL, 0,
@@ -774,6 +776,9 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
wdev->connect_keys = NULL;
}
+ cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
+ rdev->wiphy.ht_capa_mod_mask);
+
if (connkeys && connkeys->def >= 0) {
int idx;
u32 cipher;
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 4dde429441d2..9c601d59b77a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -7,7 +7,6 @@
#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <linux/slab.h>
-#include <linux/crc32.h>
#include <net/cfg80211.h>
#include <net/ip.h>
#include "core.h"
@@ -240,17 +239,6 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
return 0;
}
-/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
-/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
-const unsigned char rfc1042_header[] __aligned(2) =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-EXPORT_SYMBOL(rfc1042_header);
-
-/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
-const unsigned char bridge_tunnel_header[] __aligned(2) =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
-EXPORT_SYMBOL(bridge_tunnel_header);
-
unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc)
{
unsigned int hdrlen = 24;
@@ -1051,169 +1039,13 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
return 0;
}
-u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
- struct ieee802_11_elems *elems,
- u64 filter, u32 crc)
-{
- size_t left = len;
- u8 *pos = start;
- bool calc_crc = filter != 0;
-
- memset(elems, 0, sizeof(*elems));
- elems->ie_start = start;
- elems->total_len = len;
-
- while (left >= 2) {
- u8 id, elen;
-
- id = *pos++;
- elen = *pos++;
- left -= 2;
-
- if (elen > left)
- break;
-
- if (calc_crc && id < 64 && (filter & (1ULL << id)))
- crc = crc32_be(crc, pos - 2, elen + 2);
-
- switch (id) {
- case WLAN_EID_SSID:
- elems->ssid = pos;
- elems->ssid_len = elen;
- break;
- case WLAN_EID_SUPP_RATES:
- elems->supp_rates = pos;
- elems->supp_rates_len = elen;
- break;
- case WLAN_EID_FH_PARAMS:
- elems->fh_params = pos;
- elems->fh_params_len = elen;
- break;
- case WLAN_EID_DS_PARAMS:
- elems->ds_params = pos;
- elems->ds_params_len = elen;
- break;
- case WLAN_EID_CF_PARAMS:
- elems->cf_params = pos;
- elems->cf_params_len = elen;
- break;
- case WLAN_EID_TIM:
- if (elen >= sizeof(struct ieee80211_tim_ie)) {
- elems->tim = (void *)pos;
- elems->tim_len = elen;
- }
- break;
- case WLAN_EID_IBSS_PARAMS:
- elems->ibss_params = pos;
- elems->ibss_params_len = elen;
- break;
- case WLAN_EID_CHALLENGE:
- elems->challenge = pos;
- elems->challenge_len = elen;
- break;
- case WLAN_EID_VENDOR_SPECIFIC:
- if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
- pos[2] == 0xf2) {
- /* Microsoft OUI (00:50:F2) */
-
- if (calc_crc)
- crc = crc32_be(crc, pos - 2, elen + 2);
-
- if (pos[3] == 1) {
- /* OUI Type 1 - WPA IE */
- elems->wpa = pos;
- elems->wpa_len = elen;
- } else if (elen >= 5 && pos[3] == 2) {
- /* OUI Type 2 - WMM IE */
- if (pos[4] == 0) {
- elems->wmm_info = pos;
- elems->wmm_info_len = elen;
- } else if (pos[4] == 1) {
- elems->wmm_param = pos;
- elems->wmm_param_len = elen;
- }
- }
- }
- break;
- case WLAN_EID_RSN:
- elems->rsn = pos;
- elems->rsn_len = elen;
- break;
- case WLAN_EID_ERP_INFO:
- elems->erp_info = pos;
- elems->erp_info_len = elen;
- break;
- case WLAN_EID_EXT_SUPP_RATES:
- elems->ext_supp_rates = pos;
- elems->ext_supp_rates_len = elen;
- break;
- case WLAN_EID_HT_CAPABILITY:
- if (elen >= sizeof(struct ieee80211_ht_cap))
- elems->ht_cap_elem = (void *)pos;
- break;
- case WLAN_EID_HT_INFORMATION:
- if (elen >= sizeof(struct ieee80211_ht_info))
- elems->ht_info_elem = (void *)pos;
- break;
- case WLAN_EID_MESH_ID:
- elems->mesh_id = pos;
- elems->mesh_id_len = elen;
- break;
- case WLAN_EID_MESH_CONFIG:
- if (elen >= sizeof(struct ieee80211_meshconf_ie))
- elems->mesh_config = (void *)pos;
- break;
- case WLAN_EID_PEER_MGMT:
- elems->peering = pos;
- elems->peering_len = elen;
- break;
- case WLAN_EID_PREQ:
- elems->preq = pos;
- elems->preq_len = elen;
- break;
- case WLAN_EID_PREP:
- elems->prep = pos;
- elems->prep_len = elen;
- break;
- case WLAN_EID_PERR:
- elems->perr = pos;
- elems->perr_len = elen;
- break;
- case WLAN_EID_RANN:
- if (elen >= sizeof(struct ieee80211_rann_ie))
- elems->rann = (void *)pos;
- break;
- case WLAN_EID_CHANNEL_SWITCH:
- elems->ch_switch_elem = pos;
- elems->ch_switch_elem_len = elen;
- break;
- case WLAN_EID_QUIET:
- if (!elems->quiet_elem) {
- elems->quiet_elem = pos;
- elems->quiet_elem_len = elen;
- }
- elems->num_of_quiet_elem++;
- break;
- case WLAN_EID_COUNTRY:
- elems->country_elem = pos;
- elems->country_elem_len = elen;
- break;
- case WLAN_EID_PWR_CONSTRAINT:
- elems->pwr_constr_elem = pos;
- elems->pwr_constr_elem_len = elen;
- break;
- case WLAN_EID_TIMEOUT_INTERVAL:
- elems->timeout_int = pos;
- elems->timeout_int_len = elen;
- break;
- default:
- break;
- }
-
- left -= elen;
- pos += elen;
- }
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+const unsigned char rfc1042_header[] __aligned(2) =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+EXPORT_SYMBOL(rfc1042_header);
- return crc;
-}
-EXPORT_SYMBOL(ieee802_11_parse_elems_crc);
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+const unsigned char bridge_tunnel_header[] __aligned(2) =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+EXPORT_SYMBOL(bridge_tunnel_header);
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 6897436b1d3f..3c24eb97e9d7 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -819,12 +819,24 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
struct iw_freq *freq, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct ieee80211_channel *chan;
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra);
case NL80211_IFTYPE_ADHOC:
return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
+ case NL80211_IFTYPE_MONITOR:
+ if (!rdev->ops->get_channel)
+ return -EINVAL;
+
+ chan = rdev->ops->get_channel(wdev->wiphy);
+ if (!chan)
+ return -EINVAL;
+ freq->m = chan->center_freq;
+ freq->e = 6;
+ return 0;
default:
if (!wdev->channel)
return -EINVAL;