diff options
Diffstat (limited to 'net')
265 files changed, 5259 insertions, 2785 deletions
diff --git a/net/802/fc.c b/net/802/fc.c index 1e49f2d4ea9..bd345f3d29f 100644 --- a/net/802/fc.c +++ b/net/802/fc.c @@ -27,6 +27,7 @@ #include <linux/net.h> #include <linux/proc_fs.h> #include <linux/init.h> +#include <linux/export.h> #include <net/arp.h> /* diff --git a/net/802/garp.c b/net/802/garp.c index 070bf4403bf..8e21b6db398 100644 --- a/net/802/garp.c +++ b/net/802/garp.c @@ -15,6 +15,7 @@ #include <linux/rtnetlink.h> #include <linux/llc.h> #include <linux/slab.h> +#include <linux/module.h> #include <net/llc.h> #include <net/llc_pdu.h> #include <net/garp.h> diff --git a/net/802/stp.c b/net/802/stp.c index 0e136ef1e4b..15540b7323c 100644 --- a/net/802/stp.c +++ b/net/802/stp.c @@ -12,6 +12,7 @@ #include <linux/etherdevice.h> #include <linux/llc.h> #include <linux/slab.h> +#include <linux/module.h> #include <net/llc.h> #include <net/llc_pdu.h> #include <net/stp.h> diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 163397f1fd5..f5ffc02729d 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -2,6 +2,7 @@ #include <linux/netdevice.h> #include <linux/if_vlan.h> #include <linux/netpoll.h> +#include <linux/export.h> #include "vlan.h" bool vlan_do_receive(struct sk_buff **skbp, bool last_handler) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index c8cf9391417..bc252862458 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -470,10 +470,12 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change) { struct net_device *real_dev = vlan_dev_info(dev)->real_dev; - if (change & IFF_ALLMULTI) - dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1); - if (change & IFF_PROMISC) - dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1); + if (dev->flags & IFF_UP) { + if (change & IFF_ALLMULTI) + dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1); + if (change & IFF_PROMISC) + dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1); + } } static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index be9a5c19a77..235c2197dbb 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/if_vlan.h> +#include <linux/module.h> #include <net/net_namespace.h> #include <net/netlink.h> #include <net/rtnetlink.h> diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c index 1acc69576df..173a2e82f48 100644 --- a/net/appletalk/aarp.c +++ b/net/appletalk/aarp.c @@ -39,6 +39,7 @@ #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/export.h> int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME; int sysctl_aarp_tick_time = AARP_TICK_TIME; diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c index 6ef0e761e5d..b5b1a221c24 100644 --- a/net/appletalk/atalk_proc.c +++ b/net/appletalk/atalk_proc.c @@ -14,6 +14,7 @@ #include <net/net_namespace.h> #include <net/sock.h> #include <linux/atalk.h> +#include <linux/export.h> static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos) diff --git a/net/atm/pvc.c b/net/atm/pvc.c index 437ee70c5e6..3a734919c36 100644 --- a/net/atm/pvc.c +++ b/net/atm/pvc.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/skbuff.h> #include <linux/bitops.h> +#include <linux/export.h> #include <net/sock.h> /* for sock_no_* */ #include "resources.h" /* devs and vccs */ diff --git a/net/atm/svc.c b/net/atm/svc.c index 754ee4791d9..1281049c135 100644 --- a/net/atm/svc.c +++ b/net/atm/svc.c @@ -20,6 +20,7 @@ #include <linux/bitops.h> #include <net/sock.h> /* for sock_no_* */ #include <linux/uaccess.h> +#include <linux/export.h> #include "resources.h" #include "common.h" /* common for PVCs and SVCs */ diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index a1690845dc6..87fddab22e0 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -38,6 +38,7 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <linux/seq_file.h> +#include <linux/export.h> static ax25_route *ax25_route_list; static DEFINE_RWLOCK(ax25_route_lock); diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c index d349be9578f..4c83137b595 100644 --- a/net/ax25/ax25_uid.c +++ b/net/ax25/ax25_uid.c @@ -37,6 +37,7 @@ #include <linux/stat.h> #include <linux/netfilter.h> #include <linux/sysctl.h> +#include <linux/export.h> #include <net/ip.h> #include <net/arp.h> diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 91bcd3a961e..a6cd856046a 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 7d00ddf9e9d..9e8940b24bb 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 e0af7237cd9..de0b93e4598 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; @@ -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 be84ae33ae3..fb3feeb185d 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, "e))) { - 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, "e))) { + u32 priority = (skb_peek(&chan->data_q))->priority; + while (quote-- && (skb = skb_peek(&chan->data_q))) { + BT_DBG("chan %p skb %p len %d priority %u", chan, skb, + skb->len, skb->priority); - 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, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); + tmp = cnt; + while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { + u32 priority = (skb_peek(&chan->data_q))->priority; + while (quote-- && (skb = skb_peek(&chan->data_q))) { + BT_DBG("chan %p skb %p len %d priority %u", chan, skb, + skb->len, skb->priority); + + /* Stop if priority has changed */ + if (skb->priority < priority) + break; + + skb = skb_dequeue(&chan->data_q); hci_send_frame(skb); hdev->le_last_tx = jiffies; cnt--; - conn->sent++; + chan->sent++; + chan->conn->sent++; } } + if (hdev->le_pkts) hdev->le_cnt = cnt; else hdev->acl_cnt = cnt; + + if (cnt != tmp) + hci_prio_recalculate(hdev, LE_LINK); } static void hci_tx_task(unsigned long arg) @@ -2407,3 +2586,31 @@ static void hci_cmd_task(unsigned long arg) } } } + +int hci_do_inquiry(struct hci_dev *hdev, u8 length) +{ + /* General inquiry access code (GIAC) */ + u8 lap[3] = { 0x33, 0x8b, 0x9e }; + struct hci_cp_inquiry cp; + + BT_DBG("%s", hdev->name); + + if (test_bit(HCI_INQUIRY, &hdev->flags)) + return -EINPROGRESS; + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.lap, lap, sizeof(cp.lap)); + cp.length = length; + + return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); +} + +int hci_cancel_inquiry(struct hci_dev *hdev) +{ + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_INQUIRY, &hdev->flags)) + return -EPERM; + + return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d7d96b6b1f0..a89cf1f24e4 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 22f1a6c8703..c62d254a137 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -5,6 +5,7 @@ #include <linux/init.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/module.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -435,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); @@ -484,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); @@ -542,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; @@ -581,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 075a3e920ca..3c2d888925d 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 5ea94a1eecf..e8a6837996c 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); @@ -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; } @@ -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]; @@ -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; } @@ -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 e8292369cdc..e2e785c7463 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -27,6 +27,7 @@ /* Bluetooth L2CAP sockets. */ #include <linux/security.h> +#include <linux/export.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -333,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)) @@ -358,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)) @@ -448,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; @@ -460,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)) @@ -468,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; @@ -502,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)) { @@ -510,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; } @@ -534,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: @@ -550,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: @@ -657,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: @@ -674,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: @@ -708,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; @@ -930,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 { @@ -963,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 5caff4d4759..94739d3c4f5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -23,6 +23,7 @@ /* Bluetooth HCI Management interface */ #include <linux/uaccess.h> +#include <linux/module.h> #include <asm/unaligned.h> #include <net/bluetooth/bluetooth.h> @@ -32,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); @@ -65,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, @@ -77,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); @@ -96,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) @@ -119,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; @@ -142,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; @@ -175,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); @@ -220,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; @@ -229,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) { @@ -243,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; @@ -322,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; @@ -336,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; @@ -349,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; @@ -373,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; } @@ -385,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; @@ -395,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); @@ -434,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; } @@ -445,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; @@ -467,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; @@ -481,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) @@ -533,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); @@ -586,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); @@ -611,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); @@ -688,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; } @@ -894,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); @@ -901,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); @@ -941,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); @@ -953,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; } @@ -1025,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; } @@ -1039,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; @@ -1059,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; @@ -1081,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; @@ -1091,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: @@ -1112,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; @@ -1173,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; @@ -1264,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; @@ -1309,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) @@ -1369,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); @@ -1431,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; @@ -1468,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; @@ -1514,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; @@ -1606,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; @@ -1620,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); @@ -1656,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); @@ -1677,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; @@ -1694,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); @@ -1721,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; @@ -1738,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) @@ -1752,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); @@ -1882,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); @@ -1957,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 { @@ -1992,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); @@ -2010,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) @@ -2029,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); @@ -2047,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)); @@ -2059,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) @@ -2088,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); @@ -2106,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); @@ -2164,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); @@ -2185,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: @@ -2284,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); @@ -2314,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) @@ -2330,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; @@ -2342,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 4e32e18211f..8743f369ed3 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 5417f612732..aea2bdd1510 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 c258796313e..fa8f4de53b9 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 759b6357264..94e94ca3538 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/bridge/br_input.c b/net/bridge/br_input.c index 6f9f8c01472..5a31731be4d 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -16,6 +16,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/netfilter_bridge.h> +#include <linux/export.h> #include "br_private.h" /* Bridge group multicast address 802.1d (pg 51). */ diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 10eda3cd1d7..19308e305d8 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -12,6 +12,7 @@ */ #include <linux/kernel.h> +#include <linux/kmod.h> #include <linux/etherdevice.h> #include <linux/rtnetlink.h> diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index bf2a333ca7c..5449294bdd5 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -102,16 +102,15 @@ static struct sk_buff *ulog_alloc_skb(unsigned int size) unsigned int n; n = max(size, nlbufsiz); - skb = alloc_skb(n, GFP_ATOMIC); + skb = alloc_skb(n, GFP_ATOMIC | __GFP_NOWARN); if (!skb) { - pr_debug("cannot alloc whole buffer of size %ub!\n", n); if (n > size) { /* try to allocate only as much as we need for * current packet */ skb = alloc_skb(size, GFP_ATOMIC); if (!skb) - pr_debug("cannot even allocate " - "buffer of size %ub\n", size); + pr_debug("cannot even allocate buffer of size %ub\n", + size); } } diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 47fc8f3a47c..f1fa1f6e658 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -16,6 +16,7 @@ #include <linux/net.h> #include <linux/netdevice.h> #include <linux/mutex.h> +#include <linux/module.h> #include <net/netns/generic.h> #include <net/net_namespace.h> #include <net/pkt_sched.h> diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c index 75d4bfae1a7..df08c47183d 100644 --- a/net/caif/cfpkt_skbuff.c +++ b/net/caif/cfpkt_skbuff.c @@ -9,6 +9,7 @@ #include <linux/string.h> #include <linux/skbuff.h> #include <linux/hardirq.h> +#include <linux/export.h> #include <net/caif/cfpkt.h> #define PKT_PREFIX 48 diff --git a/net/ceph/Kconfig b/net/ceph/Kconfig index be683f2d401..cc04dd667a1 100644 --- a/net/ceph/Kconfig +++ b/net/ceph/Kconfig @@ -27,3 +27,17 @@ config CEPH_LIB_PRETTYDEBUG If unsure, say N. +config CEPH_LIB_USE_DNS_RESOLVER + bool "Use in-kernel support for DNS lookup" + depends on CEPH_LIB + select DNS_RESOLVER + default n + help + If you say Y here, hostnames (e.g. monitor addresses) will + be resolved using the CONFIG_DNS_RESOLVER facility. + + For information on how to use CONFIG_DNS_RESOLVER consult + Documentation/networking/dns_resolver.txt + + If unsure, say N. + diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 2883ea01e68..97f70e50ad3 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -432,9 +432,12 @@ EXPORT_SYMBOL(ceph_client_id); /* * create a fresh client instance */ -struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private) +struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, + unsigned supported_features, + unsigned required_features) { struct ceph_client *client; + struct ceph_entity_addr *myaddr = NULL; int err = -ENOMEM; client = kzalloc(sizeof(*client), GFP_KERNEL); @@ -449,15 +452,27 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private) client->auth_err = 0; client->extra_mon_dispatch = NULL; - client->supported_features = CEPH_FEATURE_SUPPORTED_DEFAULT; - client->required_features = CEPH_FEATURE_REQUIRED_DEFAULT; - - client->msgr = NULL; + client->supported_features = CEPH_FEATURE_SUPPORTED_DEFAULT | + supported_features; + client->required_features = CEPH_FEATURE_REQUIRED_DEFAULT | + required_features; + + /* msgr */ + if (ceph_test_opt(client, MYIP)) + myaddr = &client->options->my_addr; + client->msgr = ceph_messenger_create(myaddr, + client->supported_features, + client->required_features); + if (IS_ERR(client->msgr)) { + err = PTR_ERR(client->msgr); + goto fail; + } + client->msgr->nocrc = ceph_test_opt(client, NOCRC); /* subsystems */ err = ceph_monc_init(&client->monc, client); if (err < 0) - goto fail; + goto fail_msgr; err = ceph_osdc_init(&client->osdc, client); if (err < 0) goto fail_monc; @@ -466,6 +481,8 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private) fail_monc: ceph_monc_stop(&client->monc); +fail_msgr: + ceph_messenger_destroy(client->msgr); fail: kfree(client); return ERR_PTR(err); @@ -490,8 +507,7 @@ void ceph_destroy_client(struct ceph_client *client) ceph_debugfs_client_cleanup(client); - if (client->msgr) - ceph_messenger_destroy(client->msgr); + ceph_messenger_destroy(client->msgr); ceph_destroy_options(client->options); @@ -514,24 +530,9 @@ static int have_mon_and_osd_map(struct ceph_client *client) */ int __ceph_open_session(struct ceph_client *client, unsigned long started) { - struct ceph_entity_addr *myaddr = NULL; int err; unsigned long timeout = client->options->mount_timeout * HZ; - /* initialize the messenger */ - if (client->msgr == NULL) { - if (ceph_test_opt(client, MYIP)) - myaddr = &client->options->my_addr; - client->msgr = ceph_messenger_create(myaddr, - client->supported_features, - client->required_features); - if (IS_ERR(client->msgr)) { - client->msgr = NULL; - return PTR_ERR(client->msgr); - } - client->msgr->nocrc = ceph_test_opt(client, NOCRC); - } - /* open session, and wait for mon and osd maps */ err = ceph_monc_open_session(&client->monc); if (err < 0) diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 9918e9eb276..ad5b70801f3 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -11,12 +11,14 @@ #include <linux/string.h> #include <linux/bio.h> #include <linux/blkdev.h> +#include <linux/dns_resolver.h> #include <net/tcp.h> #include <linux/ceph/libceph.h> #include <linux/ceph/messenger.h> #include <linux/ceph/decode.h> #include <linux/ceph/pagelist.h> +#include <linux/export.h> /* * Ceph uses the messenger to exchange ceph_msg messages with other @@ -1078,6 +1080,101 @@ static void addr_set_port(struct sockaddr_storage *ss, int p) } /* + * Unlike other *_pton function semantics, zero indicates success. + */ +static int ceph_pton(const char *str, size_t len, struct sockaddr_storage *ss, + char delim, const char **ipend) +{ + struct sockaddr_in *in4 = (void *)ss; + struct sockaddr_in6 *in6 = (void *)ss; + + memset(ss, 0, sizeof(*ss)); + + if (in4_pton(str, len, (u8 *)&in4->sin_addr.s_addr, delim, ipend)) { + ss->ss_family = AF_INET; + return 0; + } + + if (in6_pton(str, len, (u8 *)&in6->sin6_addr.s6_addr, delim, ipend)) { + ss->ss_family = AF_INET6; + return 0; + } + + return -EINVAL; +} + +/* + * Extract hostname string and resolve using kernel DNS facility. + */ +#ifdef CONFIG_CEPH_LIB_USE_DNS_RESOLVER +static int ceph_dns_resolve_name(const char *name, size_t namelen, + struct sockaddr_storage *ss, char delim, const char **ipend) +{ + const char *end, *delim_p; + char *colon_p, *ip_addr = NULL; + int ip_len, ret; + + /* + * The end of the hostname occurs immediately preceding the delimiter or + * the port marker (':') where the delimiter takes precedence. + */ + delim_p = memchr(name, delim, namelen); + colon_p = memchr(name, ':', namelen); + + if (delim_p && colon_p) + end = delim_p < colon_p ? delim_p : colon_p; + else if (!delim_p && colon_p) + end = colon_p; + else { + end = delim_p; + if (!end) /* case: hostname:/ */ + end = name + namelen; + } + + if (end <= name) + return -EINVAL; + + /* do dns_resolve upcall */ + ip_len = dns_query(NULL, name, end - name, NULL, &ip_addr, NULL); + if (ip_len > 0) + ret = ceph_pton(ip_addr, ip_len, ss, -1, NULL); + else + ret = -ESRCH; + + kfree(ip_addr); + + *ipend = end; + + pr_info("resolve '%.*s' (ret=%d): %s\n", (int)(end - name), name, + ret, ret ? "failed" : ceph_pr_addr(ss)); + + return ret; +} +#else +static inline int ceph_dns_resolve_name(const char *name, size_t namelen, + struct sockaddr_storage *ss, char delim, const char **ipend) +{ + return -EINVAL; +} +#endif + +/* + * Parse a server name (IP or hostname). If a valid IP address is not found + * then try to extract a hostname to resolve using userspace DNS upcall. + */ +static int ceph_parse_server_name(const char *name, size_t namelen, + struct sockaddr_storage *ss, char delim, const char **ipend) +{ + int ret; + + ret = ceph_pton(name, namelen, ss, delim, ipend); + if (ret) + ret = ceph_dns_resolve_name(name, namelen, ss, delim, ipend); + + return ret; +} + +/* * Parse an ip[:port] list into an addr array. Use the default * monitor port if a port isn't specified. */ @@ -1085,15 +1182,13 @@ int ceph_parse_ips(const char *c, const char *end, struct ceph_entity_addr *addr, int max_count, int *count) { - int i; + int i, ret = -EINVAL; const char *p = c; dout("parse_ips on '%.*s'\n", (int)(end-c), c); for (i = 0; i < max_count; i++) { const char *ipend; struct sockaddr_storage *ss = &addr[i].in_addr; - struct sockaddr_in *in4 = (void *)ss; - struct sockaddr_in6 *in6 = (void *)ss; int port; char delim = ','; @@ -1102,15 +1197,11 @@ int ceph_parse_ips(const char *c, const char *end, p++; } - memset(ss, 0, sizeof(*ss)); - if (in4_pton(p, end - p, (u8 *)&in4->sin_addr.s_addr, - delim, &ipend)) - ss->ss_family = AF_INET; - else if (in6_pton(p, end - p, (u8 *)&in6->sin6_addr.s6_addr, - delim, &ipend)) - ss->ss_family = AF_INET6; - else + ret = ceph_parse_server_name(p, end - p, ss, delim, &ipend); + if (ret) goto bad; + ret = -EINVAL; + p = ipend; if (delim == ']') { @@ -1155,7 +1246,7 @@ int ceph_parse_ips(const char *c, const char *end, bad: pr_err("parse_ips bad ip '%.*s'\n", (int)(end - c), c); - return -EINVAL; + return ret; } EXPORT_SYMBOL(ceph_parse_ips); @@ -2281,7 +2372,8 @@ EXPORT_SYMBOL(ceph_con_keepalive); * construct a new message with given type, size * the new msg has a ref count of 1. */ -struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) +struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags, + bool can_fail) { struct ceph_msg *m; @@ -2333,7 +2425,7 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) m->front.iov_base = kmalloc(front_len, flags); } if (m->front.iov_base == NULL) { - pr_err("msg_new can't allocate %d bytes\n", + dout("ceph_msg_new can't allocate %d bytes\n", front_len); goto out2; } @@ -2348,7 +2440,14 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) out2: ceph_msg_put(m); out: - pr_err("msg_new can't create type %d front %d\n", type, front_len); + if (!can_fail) { + pr_err("msg_new can't create type %d front %d\n", type, + front_len); + WARN_ON(1); + } else { + dout("msg_new can't create type %d front %d\n", type, + front_len); + } return NULL; } EXPORT_SYMBOL(ceph_msg_new); @@ -2398,7 +2497,7 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, } if (!msg) { *skip = 0; - msg = ceph_msg_new(type, front_len, GFP_NOFS); + msg = ceph_msg_new(type, front_len, GFP_NOFS, false); if (!msg) { pr_err("unable to allocate msg type %d len %d\n", type, front_len); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index cbe31fa4550..0b62deae42b 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -116,14 +116,12 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) */ static void __close_session(struct ceph_mon_client *monc) { - if (monc->con) { - dout("__close_session closing mon%d\n", monc->cur_mon); - ceph_con_revoke(monc->con, monc->m_auth); - ceph_con_close(monc->con); - monc->cur_mon = -1; - monc->pending_auth = 0; - ceph_auth_reset(monc->auth); - } + dout("__close_session closing mon%d\n", monc->cur_mon); + ceph_con_revoke(monc->con, monc->m_auth); + ceph_con_close(monc->con); + monc->cur_mon = -1; + monc->pending_auth = 0; + ceph_auth_reset(monc->auth); } /* @@ -302,15 +300,6 @@ void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc) */ int ceph_monc_open_session(struct ceph_mon_client *monc) { - if (!monc->con) { - monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); - if (!monc->con) - return -ENOMEM; - ceph_con_init(monc->client->msgr, monc->con); - monc->con->private = monc; - monc->con->ops = &mon_con_ops; - } - mutex_lock(&monc->mutex); __open_session(monc); __schedule_delayed(monc); @@ -528,10 +517,12 @@ int ceph_monc_do_statfs(struct ceph_mon_client *monc, struct ceph_statfs *buf) init_completion(&req->completion); err = -ENOMEM; - req->request = ceph_msg_new(CEPH_MSG_STATFS, sizeof(*h), GFP_NOFS); + req->request = ceph_msg_new(CEPH_MSG_STATFS, sizeof(*h), GFP_NOFS, + true); if (!req->request) goto out; - req->reply = ceph_msg_new(CEPH_MSG_STATFS_REPLY, 1024, GFP_NOFS); + req->reply = ceph_msg_new(CEPH_MSG_STATFS_REPLY, 1024, GFP_NOFS, + true); if (!req->reply) goto out; @@ -626,10 +617,12 @@ int ceph_monc_do_poolop(struct ceph_mon_client *monc, u32 op, init_completion(&req->completion); err = -ENOMEM; - req->request = ceph_msg_new(CEPH_MSG_POOLOP, sizeof(*h), GFP_NOFS); + req->request = ceph_msg_new(CEPH_MSG_POOLOP, sizeof(*h), GFP_NOFS, + true); if (!req->request) goto out; - req->reply = ceph_msg_new(CEPH_MSG_POOLOP_REPLY, 1024, GFP_NOFS); + req->reply = ceph_msg_new(CEPH_MSG_POOLOP_REPLY, 1024, GFP_NOFS, + true); if (!req->reply) goto out; @@ -755,13 +748,21 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) if (err) goto out; - monc->con = NULL; + /* connection */ + monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); + if (!monc->con) + goto out_monmap; + ceph_con_init(monc->client->msgr, monc->con); + monc->con->private = monc; + monc->con->ops = &mon_con_ops; /* authentication */ monc->auth = ceph_auth_init(cl->options->name, cl->options->key); - if (IS_ERR(monc->auth)) - return PTR_ERR(monc->auth); + if (IS_ERR(monc->auth)) { + err = PTR_ERR(monc->auth); + goto out_con; + } monc->auth->want_keys = CEPH_ENTITY_TYPE_AUTH | CEPH_ENTITY_TYPE_MON | CEPH_ENTITY_TYPE_OSD | CEPH_ENTITY_TYPE_MDS; @@ -770,19 +771,21 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) err = -ENOMEM; monc->m_subscribe_ack = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE_ACK, sizeof(struct ceph_mon_subscribe_ack), - GFP_NOFS); + GFP_NOFS, true); if (!monc->m_subscribe_ack) - goto out_monmap; + goto out_auth; - monc->m_subscribe = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE, 96, GFP_NOFS); + monc->m_subscribe = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE, 96, GFP_NOFS, + true); if (!monc->m_subscribe) goto out_subscribe_ack; - monc->m_auth_reply = ceph_msg_new(CEPH_MSG_AUTH_REPLY, 4096, GFP_NOFS); + monc->m_auth_reply = ceph_msg_new(CEPH_MSG_AUTH_REPLY, 4096, GFP_NOFS, + true); if (!monc->m_auth_reply) goto out_subscribe; - monc->m_auth = ceph_msg_new(CEPH_MSG_AUTH, 4096, GFP_NOFS); + monc->m_auth = ceph_msg_new(CEPH_MSG_AUTH, 4096, GFP_NOFS, true); monc->pending_auth = 0; if (!monc->m_auth) goto out_auth_reply; @@ -808,6 +811,10 @@ out_subscribe: ceph_msg_put(monc->m_subscribe); out_subscribe_ack: ceph_msg_put(monc->m_subscribe_ack); +out_auth: + ceph_auth_destroy(monc->auth); +out_con: + monc->con->ops->put(monc->con); out_monmap: kfree(monc->monmap); out: @@ -822,11 +829,11 @@ void ceph_monc_stop(struct ceph_mon_client *monc) mutex_lock(&monc->mutex); __close_session(monc); - if (monc->con) { - monc->con->private = NULL; - monc->con->ops->put(monc->con); - monc->con = NULL; - } + + monc->con->private = NULL; + monc->con->ops->put(monc->con); + monc->con = NULL; + mutex_unlock(&monc->mutex); ceph_auth_destroy(monc->auth); @@ -973,7 +980,7 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, case CEPH_MSG_MON_MAP: case CEPH_MSG_MDS_MAP: case CEPH_MSG_OSD_MAP: - m = ceph_msg_new(type, front_len, GFP_NOFS); + m = ceph_msg_new(type, front_len, GFP_NOFS, false); break; } @@ -1000,7 +1007,7 @@ static void mon_fault(struct ceph_connection *con) if (!con->private) goto out; - if (monc->con && !monc->hunting) + if (!monc->hunting) pr_info("mon%d %s session lost, " "hunting for new mon\n", monc->cur_mon, ceph_pr_addr(&monc->con->peer_addr.in_addr)); diff --git a/net/ceph/msgpool.c b/net/ceph/msgpool.c index 1f4cb30a42c..11d5f4196a7 100644 --- a/net/ceph/msgpool.c +++ b/net/ceph/msgpool.c @@ -12,7 +12,7 @@ static void *msgpool_alloc(gfp_t gfp_mask, void *arg) struct ceph_msgpool *pool = arg; struct ceph_msg *msg; - msg = ceph_msg_new(0, pool->front_len, gfp_mask); + msg = ceph_msg_new(0, pool->front_len, gfp_mask, true); if (!msg) { dout("msgpool_alloc %s failed\n", pool->name); } else { @@ -61,7 +61,7 @@ struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool, WARN_ON(1); /* try to alloc a fresh message */ - return ceph_msg_new(0, front_len, GFP_NOFS); + return ceph_msg_new(0, front_len, GFP_NOFS, false); } msg = mempool_alloc(pool->pool, GFP_NOFS); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 88ad8a2501b..733e46008b8 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -227,7 +227,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, msg = ceph_msgpool_get(&osdc->msgpool_op_reply, 0); else msg = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, - OSD_OPREPLY_FRONT_LEN, gfp_flags); + OSD_OPREPLY_FRONT_LEN, gfp_flags, true); if (!msg) { ceph_osdc_put_request(req); return NULL; @@ -250,7 +250,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, if (use_mempool) msg = ceph_msgpool_get(&osdc->msgpool_op, 0); else - msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, gfp_flags); + msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, gfp_flags, true); if (!msg) { ceph_osdc_put_request(req); return NULL; @@ -943,7 +943,7 @@ EXPORT_SYMBOL(ceph_osdc_set_request_linger); * Caller should hold map_sem for read and request_mutex. */ static int __map_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req) + struct ceph_osd_request *req, int force_resend) { struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; struct ceph_pg pgid; @@ -967,7 +967,8 @@ static int __map_request(struct ceph_osd_client *osdc, num = err; } - if ((req->r_osd && req->r_osd->o_osd == o && + if ((!force_resend && + req->r_osd && req->r_osd->o_osd == o && req->r_sent >= req->r_osd->o_incarnation && req->r_num_pg_osds == num && memcmp(req->r_pg_osds, acting, sizeof(acting[0])*num) == 0) || @@ -1289,18 +1290,18 @@ static void reset_changed_osds(struct ceph_osd_client *osdc) * * Caller should hold map_sem for read and request_mutex. */ -static void kick_requests(struct ceph_osd_client *osdc) +static void kick_requests(struct ceph_osd_client *osdc, int force_resend) { struct ceph_osd_request *req, *nreq; struct rb_node *p; int needmap = 0; int err; - dout("kick_requests\n"); + dout("kick_requests %s\n", force_resend ? " (force resend)" : ""); mutex_lock(&osdc->request_mutex); for (p = rb_first(&osdc->requests); p; p = rb_next(p)) { req = rb_entry(p, struct ceph_osd_request, r_node); - err = __map_request(osdc, req); + err = __map_request(osdc, req, force_resend); if (err < 0) continue; /* error */ if (req->r_osd == NULL) { @@ -1318,7 +1319,7 @@ static void kick_requests(struct ceph_osd_client *osdc) r_linger_item) { dout("linger req=%p req->r_osd=%p\n", req, req->r_osd); - err = __map_request(osdc, req); + err = __map_request(osdc, req, force_resend); if (err == 0) continue; /* no change and no osd was specified */ if (err < 0) @@ -1395,7 +1396,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) ceph_osdmap_destroy(osdc->osdmap); osdc->osdmap = newmap; } - kick_requests(osdc); + kick_requests(osdc, 0); reset_changed_osds(osdc); } else { dout("ignoring incremental map %u len %d\n", @@ -1423,6 +1424,8 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) "older than our %u\n", epoch, maplen, osdc->osdmap->epoch); } else { + int skipped_map = 0; + dout("taking full map %u len %d\n", epoch, maplen); newmap = osdmap_decode(&p, p+maplen); if (IS_ERR(newmap)) { @@ -1432,9 +1435,12 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) BUG_ON(!newmap); oldmap = osdc->osdmap; osdc->osdmap = newmap; - if (oldmap) + if (oldmap) { + if (oldmap->epoch + 1 < newmap->epoch) + skipped_map = 1; ceph_osdmap_destroy(oldmap); - kick_requests(osdc); + } + kick_requests(osdc, skipped_map); } p += maplen; nr_maps--; @@ -1707,7 +1713,7 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, * the request still han't been touched yet. */ if (req->r_sent == 0) { - rc = __map_request(osdc, req); + rc = __map_request(osdc, req, 0); if (rc < 0) { if (nofail) { dout("osdc_start_request failed map, " @@ -2032,7 +2038,7 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, if (front > req->r_reply->front.iov_len) { pr_warning("get_reply front %d > preallocated %d\n", front, (int)req->r_reply->front.iov_len); - m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front, GFP_NOFS); + m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front, GFP_NOFS, false); if (!m) goto out; ceph_msg_put(req->r_reply); @@ -2080,7 +2086,7 @@ static struct ceph_msg *alloc_msg(struct ceph_connection *con, switch (type) { case CEPH_MSG_OSD_MAP: case CEPH_MSG_WATCH_NOTIFY: - return ceph_msg_new(type, front, GFP_NOFS); + return ceph_msg_new(type, front, GFP_NOFS, false); case CEPH_MSG_OSD_OPREPLY: return get_reply(con, hdr, skip); default: diff --git a/net/compat.c b/net/compat.c index c578d9382e1..6def90e0a11 100644 --- a/net/compat.c +++ b/net/compat.c @@ -22,6 +22,7 @@ #include <linux/filter.h> #include <linux/compat.h> #include <linux/security.h> +#include <linux/export.h> #include <net/scm.h> #include <net/sock.h> diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 283d1b86387..277faef9148 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -13,6 +13,7 @@ #include <linux/netdevice.h> #include <linux/rtnetlink.h> +#include <linux/export.h> #include <linux/list.h> #include <linux/proc_fs.h> diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 57e8f95110e..c02e63c908d 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/list.h> +#include <linux/module.h> #include <net/net_namespace.h> #include <net/sock.h> #include <net/fib_rules.h> diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 909ecb3c2a3..039d51e6c28 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -872,12 +872,8 @@ static void neigh_timer_handler(unsigned long arg) now = jiffies; next = now + HZ; - if (!(state & NUD_IN_TIMER)) { -#ifndef CONFIG_SMP - printk(KERN_WARNING "neigh: timer & !nud_in_timer\n"); -#endif + if (!(state & NUD_IN_TIMER)) goto out; - } if (state & NUD_REACHABLE) { if (time_before_eq(now, diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 7604a635376..c71c434a4c0 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -20,6 +20,7 @@ #include <linux/rtnetlink.h> #include <linux/wireless.h> #include <linux/vmalloc.h> +#include <linux/export.h> #include <net/wext.h> #include "net-sysfs.h" diff --git a/net/core/net-traces.c b/net/core/net-traces.c index 52380b1d552..ba3c0120786 100644 --- a/net/core/net-traces.c +++ b/net/core/net-traces.c @@ -11,6 +11,7 @@ #include <linux/inetdevice.h> #include <linux/inet.h> #include <linux/interrupt.h> +#include <linux/export.h> #include <linux/netpoll.h> #include <linux/sched.h> #include <linux/delay.h> diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 5bbdbf0d366..aefcd7acbff 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -10,6 +10,7 @@ #include <linux/nsproxy.h> #include <linux/proc_fs.h> #include <linux/file.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/netns/generic.h> diff --git a/net/core/netevent.c b/net/core/netevent.c index 865f0ceb81f..f17ccd291d3 100644 --- a/net/core/netevent.c +++ b/net/core/netevent.c @@ -15,6 +15,7 @@ #include <linux/rtnetlink.h> #include <linux/notifier.h> +#include <linux/export.h> #include <net/netevent.h> static ATOMIC_NOTIFIER_HEAD(netevent_notif_chain); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index f57d94627a2..cf64c1ffa4c 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/workqueue.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/tcp.h> #include <net/udp.h> #include <asm/unaligned.h> diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ca4db40e75b..06438f92602 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -189,6 +189,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, * aligned memory blocks, unless SLUB/SLAB debug is enabled. * Both skb->head and skb_shared_info are cache line aligned. */ + size = SKB_DATA_ALIGN(size); size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); data = kmalloc_node_track_caller(size, gfp_mask, node); if (!data) @@ -3168,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 4ed7b1d12f5..cbdf51c0d5a 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/core/timestamping.c b/net/core/timestamping.c index 82fb28857b6..661b5a40ec1 100644 --- a/net/core/timestamping.c +++ b/net/core/timestamping.c @@ -21,6 +21,7 @@ #include <linux/phy.h> #include <linux/ptp_classify.h> #include <linux/skbuff.h> +#include <linux/export.h> static struct sock_filter ptp_filter[] = { PTP_FILTER diff --git a/net/core/user_dma.c b/net/core/user_dma.c index 2d7cf3d52b4..1b5fefdb819 100644 --- a/net/core/user_dma.c +++ b/net/core/user_dma.c @@ -27,6 +27,7 @@ #include <linux/dmaengine.h> #include <linux/socket.h> +#include <linux/export.h> #include <net/tcp.h> #include <net/netdma.h> diff --git a/net/dcb/dcbevent.c b/net/dcb/dcbevent.c index 665a8802105..1d9eb7c60a6 100644 --- a/net/dcb/dcbevent.c +++ b/net/dcb/dcbevent.c @@ -19,6 +19,7 @@ #include <linux/rtnetlink.h> #include <linux/notifier.h> +#include <linux/export.h> static ATOMIC_NOTIFIER_HEAD(dcbevent_notif_chain); diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 9bfbc1d1b50..d86053002c1 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -25,6 +25,7 @@ #include <linux/dcbnl.h> #include <net/dcbevent.h> #include <linux/rtnetlink.h> +#include <linux/module.h> #include <net/sock.h> /** diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c index 25b7a8d1ad5..ba07824af4c 100644 --- a/net/dccp/ackvec.c +++ b/net/dccp/ackvec.c @@ -12,6 +12,7 @@ #include "dccp.h" #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/export.h> static struct kmem_cache *dccp_ackvec_slab; static struct kmem_cache *dccp_ackvec_record_slab; diff --git a/net/dccp/ccids/lib/tfrc.c b/net/dccp/ccids/lib/tfrc.c index 4902029854d..1f94b7e01d3 100644 --- a/net/dccp/ccids/lib/tfrc.c +++ b/net/dccp/ccids/lib/tfrc.c @@ -4,6 +4,7 @@ * Copyright (c) 2007 The University of Aberdeen, Scotland, UK * Copyright (c) 2007 Arnaldo Carvalho de Melo <acme@redhat.com> */ +#include <linux/moduleparam.h> #include "tfrc.h" #ifdef CONFIG_IP_DCCP_TFRC_DEBUG diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 332639b56f4..90a919afbed 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -433,6 +433,7 @@ exit: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); return NULL; put_and_exit: + bh_unlock_sock(newsk); sock_put(newsk); goto exit; } diff --git a/net/dccp/timer.c b/net/dccp/timer.c index 7587870b704..16f0b223102 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -12,6 +12,7 @@ #include <linux/dccp.h> #include <linux/skbuff.h> +#include <linux/export.h> #include "dccp.h" diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 43450c10022..a77d16158eb 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -77,6 +77,7 @@ #include <linux/netfilter_decnet.h> #include <linux/rcupdate.h> #include <linux/times.h> +#include <linux/export.h> #include <asm/errno.h> #include <net/net_namespace.h> #include <net/netlink.h> diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index f0efb0ccfec..f65c9ddaee4 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -23,6 +23,7 @@ #include <linux/spinlock.h> #include <linux/list.h> #include <linux/rcupdate.h> +#include <linux/export.h> #include <net/neighbour.h> #include <net/dst.h> #include <net/flow.h> diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 3fb14b7c13c..0dc1589343c 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -12,6 +12,7 @@ #include <linux/netdevice.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/module.h> #include <net/dsa.h> #include "dsa_priv.h" diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index 71ee1108d4f..adaf4621490 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -30,6 +30,7 @@ #include <net/genetlink.h> #include <net/sock.h> #include <linux/nl802154.h> +#include <linux/export.h> #include <net/af_ieee802154.h> #include <net/nl802154.h> #include <net/ieee802154.h> diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index a53bb1b5b11..46339ba7a2d 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -26,6 +26,7 @@ #include <linux/init.h> #include <linux/list.h> #include <linux/rcupdate.h> +#include <linux/export.h> #include <net/ip.h> #include <net/route.h> #include <net/tcp.h> diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 89d6f71a6a9..37b671185c8 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -73,6 +73,7 @@ #include <linux/list.h> #include <linux/slab.h> #include <linux/prefetch.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/ip.h> #include <net/protocol.h> diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 44d65d546e3..89168c6351f 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/kmemcheck.h> #include <linux/slab.h> +#include <linux/module.h> #include <net/inet_hashtables.h> #include <net/inet_timewait_sock.h> #include <net/ip.h> diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index 472a8c4f1dc..0da2afc97f3 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -54,6 +54,7 @@ #include <linux/delay.h> #include <linux/nfs_fs.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/arp.h> #include <net/ip.h> diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 6164e982e0e..76a7f07b38b 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -61,6 +61,7 @@ #include <linux/if_arp.h> #include <linux/netfilter_ipv4.h> #include <linux/compat.h> +#include <linux/export.h> #include <net/ipip.h> #include <net/checksum.h> #include <net/netlink.h> diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 929b27bdeb7..9899619ab9b 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -5,6 +5,7 @@ #include <linux/ip.h> #include <linux/skbuff.h> #include <linux/gfp.h> +#include <linux/export.h> #include <net/route.h> #include <net/xfrm.h> #include <net/ip.h> diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index db8d22db425..a639967eb72 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -395,7 +395,6 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par) config = clusterip_config_init(cipinfo, e->ip.dst.s_addr, dev); if (!config) { - pr_info("cannot allocate config\n"); dev_put(dev); return -ENOMEM; } diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index 446e0f467a1..b5508151e54 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -135,10 +135,8 @@ static struct sk_buff *ulog_alloc_skb(unsigned int size) * due to slab allocator restrictions */ n = max(size, nlbufsiz); - skb = alloc_skb(n, GFP_ATOMIC); + skb = alloc_skb(n, GFP_ATOMIC | __GFP_NOWARN); if (!skb) { - pr_debug("cannot alloc whole buffer %ub!\n", n); - if (n > size) { /* try to allocate only as much as we need for * current packet */ diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 5585980fce2..9682b36df38 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -21,6 +21,7 @@ #include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_acct.h> #include <linux/rculist_nulls.h> +#include <linux/export.h> struct ct_iter_state { struct seq_net_private p; diff --git a/net/ipv4/netfilter/nf_nat_proto_common.c b/net/ipv4/netfilter/nf_nat_proto_common.c index f52d41ea069..a3d99761860 100644 --- a/net/ipv4/netfilter/nf_nat_proto_common.c +++ b/net/ipv4/netfilter/nf_nat_proto_common.c @@ -12,6 +12,7 @@ #include <linux/ip.h> #include <linux/netfilter.h> +#include <linux/export.h> #include <net/secure_seq.h> #include <net/netfilter/nf_nat.h> #include <net/netfilter/nf_nat_core.h> diff --git a/net/ipv4/netfilter/nf_nat_proto_icmp.c b/net/ipv4/netfilter/nf_nat_proto_icmp.c index 5744c3ec847..9f4dc1235dc 100644 --- a/net/ipv4/netfilter/nf_nat_proto_icmp.c +++ b/net/ipv4/netfilter/nf_nat_proto_icmp.c @@ -8,6 +8,7 @@ #include <linux/types.h> #include <linux/init.h> +#include <linux/export.h> #include <linux/ip.h> #include <linux/icmp.h> diff --git a/net/ipv4/netfilter/nf_nat_proto_sctp.c b/net/ipv4/netfilter/nf_nat_proto_sctp.c index 756331d4266..bd5a80a62a5 100644 --- a/net/ipv4/netfilter/nf_nat_proto_sctp.c +++ b/net/ipv4/netfilter/nf_nat_proto_sctp.c @@ -10,6 +10,7 @@ #include <linux/init.h> #include <linux/ip.h> #include <linux/sctp.h> +#include <linux/module.h> #include <net/sctp/checksum.h> #include <net/netfilter/nf_nat_protocol.h> diff --git a/net/ipv4/netfilter/nf_nat_proto_tcp.c b/net/ipv4/netfilter/nf_nat_proto_tcp.c index aa460a595d5..0d67bb80130 100644 --- a/net/ipv4/netfilter/nf_nat_proto_tcp.c +++ b/net/ipv4/netfilter/nf_nat_proto_tcp.c @@ -8,6 +8,7 @@ #include <linux/types.h> #include <linux/init.h> +#include <linux/export.h> #include <linux/ip.h> #include <linux/tcp.h> diff --git a/net/ipv4/netfilter/nf_nat_proto_udp.c b/net/ipv4/netfilter/nf_nat_proto_udp.c index dfe65c7e292..0b1b8601cba 100644 --- a/net/ipv4/netfilter/nf_nat_proto_udp.c +++ b/net/ipv4/netfilter/nf_nat_proto_udp.c @@ -7,6 +7,7 @@ */ #include <linux/types.h> +#include <linux/export.h> #include <linux/init.h> #include <linux/ip.h> #include <linux/udp.h> diff --git a/net/ipv4/netfilter/nf_nat_proto_udplite.c b/net/ipv4/netfilter/nf_nat_proto_udplite.c index 3cc8c8af39e..f83ef23e2ab 100644 --- a/net/ipv4/netfilter/nf_nat_proto_udplite.c +++ b/net/ipv4/netfilter/nf_nat_proto_udplite.c @@ -13,6 +13,7 @@ #include <linux/udp.h> #include <linux/netfilter.h> +#include <linux/module.h> #include <net/netfilter/nf_nat.h> #include <net/netfilter/nf_nat_protocol.h> diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c index d1cb412c18e..2133c30a4a5 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c @@ -400,11 +400,8 @@ static unsigned char asn1_octets_decode(struct asn1_ctx *ctx, *len = 0; *octets = kmalloc(eoc - ctx->pointer, GFP_ATOMIC); - if (*octets == NULL) { - if (net_ratelimit()) - pr_notice("OOM in bsalg (%d)\n", __LINE__); + if (*octets == NULL) return 0; - } ptr = *octets; while (ctx->pointer < eoc) { @@ -451,11 +448,8 @@ static unsigned char asn1_oid_decode(struct asn1_ctx *ctx, return 0; *oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC); - if (*oid == NULL) { - if (net_ratelimit()) - pr_notice("OOM in bsalg (%d)\n", __LINE__); + if (*oid == NULL) return 0; - } optr = *oid; @@ -728,8 +722,6 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx, *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); if (*obj == NULL) { kfree(id); - if (net_ratelimit()) - pr_notice("OOM in bsalg (%d)\n", __LINE__); return 0; } (*obj)->syntax.l[0] = l; @@ -744,8 +736,6 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx, if (*obj == NULL) { kfree(p); kfree(id); - if (net_ratelimit()) - pr_notice("OOM in bsalg (%d)\n", __LINE__); return 0; } memcpy((*obj)->syntax.c, p, len); @@ -759,8 +749,6 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx, *obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC); if (*obj == NULL) { kfree(id); - if (net_ratelimit()) - pr_notice("OOM in bsalg (%d)\n", __LINE__); return 0; } if (!asn1_null_decode(ctx, end)) { @@ -780,8 +768,6 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx, if (*obj == NULL) { kfree(lp); kfree(id); - if (net_ratelimit()) - pr_notice("OOM in bsalg (%d)\n", __LINE__); return 0; } memcpy((*obj)->syntax.ul, lp, len); @@ -801,8 +787,6 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx, if (*obj == NULL) { kfree(p); kfree(id); - if (net_ratelimit()) - pr_notice("OOM in bsalg (%d)\n", __LINE__); return 0; } memcpy((*obj)->syntax.uc, p, len); @@ -819,8 +803,6 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx, *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC); if (*obj == NULL) { kfree(id); - if (net_ratelimit()) - pr_notice("OOM in bsalg (%d)\n", __LINE__); return 0; } (*obj)->syntax.ul[0] = ul; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 39b403f854c..a06f73fdb3c 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -39,6 +39,7 @@ #include <net/protocol.h> #include <linux/skbuff.h> #include <linux/proc_fs.h> +#include <linux/export.h> #include <net/sock.h> #include <net/ping.h> #include <net/udp.h> diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 4bfad5da94f..466ea8bb7a4 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -42,6 +42,7 @@ #include <linux/inetdevice.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/export.h> #include <net/sock.h> #include <net/raw.h> diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 61714bd5292..007e2eb769d 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -48,6 +48,7 @@ #include <linux/errno.h> #include <linux/aio.h> #include <linux/kernel.h> +#include <linux/export.h> #include <linux/spinlock.h> #include <linux/sockios.h> #include <linux/socket.h> diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index d7b89b12f6d..90f6544c13e 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -15,6 +15,7 @@ #include <linux/random.h> #include <linux/cryptohash.h> #include <linux/kernel.h> +#include <linux/export.h> #include <net/tcp.h> #include <net/route.h> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 0ea10eefa60..a7443159c40 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1510,6 +1510,7 @@ exit: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); return NULL; put_and_exit: + bh_unlock_sock(newsk); sock_put(newsk); goto exit; } @@ -2339,7 +2340,7 @@ static void tcp_seq_stop(struct seq_file *seq, void *v) } } -static int tcp_seq_open(struct inode *inode, struct file *file) +int tcp_seq_open(struct inode *inode, struct file *file) { struct tcp_seq_afinfo *afinfo = PDE(inode)->data; struct tcp_iter_state *s; @@ -2355,23 +2356,19 @@ static int tcp_seq_open(struct inode *inode, struct file *file) s->last_pos = 0; return 0; } +EXPORT_SYMBOL(tcp_seq_open); int tcp_proc_register(struct net *net, struct tcp_seq_afinfo *afinfo) { int rc = 0; struct proc_dir_entry *p; - afinfo->seq_fops.open = tcp_seq_open; - afinfo->seq_fops.read = seq_read; - afinfo->seq_fops.llseek = seq_lseek; - afinfo->seq_fops.release = seq_release_net; - afinfo->seq_ops.start = tcp_seq_start; afinfo->seq_ops.next = tcp_seq_next; afinfo->seq_ops.stop = tcp_seq_stop; p = proc_create_data(afinfo->name, S_IRUGO, net->proc_net, - &afinfo->seq_fops, afinfo); + afinfo->seq_fops, afinfo); if (!p) rc = -ENOMEM; return rc; @@ -2520,12 +2517,18 @@ out: return 0; } +static const struct file_operations tcp_afinfo_seq_fops = { + .owner = THIS_MODULE, + .open = tcp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net +}; + static struct tcp_seq_afinfo tcp4_seq_afinfo = { .name = "tcp", .family = AF_INET, - .seq_fops = { - .owner = THIS_MODULE, - }, + .seq_fops = &tcp_afinfo_seq_fops, .seq_ops = { .show = tcp4_seq_show, }, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index ebaa96bd346..ab0966df1e2 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1397,6 +1397,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) nf_reset(skb); if (up->encap_type) { + int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); + /* * This is an encapsulation socket so pass the skb to * the socket's udp_encap_rcv() hook. Otherwise, just @@ -1409,11 +1411,11 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) */ /* if we're overly short, let UDP handle it */ - if (skb->len > sizeof(struct udphdr) && - up->encap_rcv != NULL) { + encap_rcv = ACCESS_ONCE(up->encap_rcv); + if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) { int ret; - ret = (*up->encap_rcv)(sk, skb); + ret = encap_rcv(sk, skb); if (ret <= 0) { UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_INDATAGRAMS, @@ -2037,7 +2039,7 @@ static void udp_seq_stop(struct seq_file *seq, void *v) spin_unlock_bh(&state->udp_table->hash[state->bucket].lock); } -static int udp_seq_open(struct inode *inode, struct file *file) +int udp_seq_open(struct inode *inode, struct file *file) { struct udp_seq_afinfo *afinfo = PDE(inode)->data; struct udp_iter_state *s; @@ -2053,6 +2055,7 @@ static int udp_seq_open(struct inode *inode, struct file *file) s->udp_table = afinfo->udp_table; return err; } +EXPORT_SYMBOL(udp_seq_open); /* ------------------------------------------------------------------------ */ int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo) @@ -2060,17 +2063,12 @@ int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo) struct proc_dir_entry *p; int rc = 0; - afinfo->seq_fops.open = udp_seq_open; - afinfo->seq_fops.read = seq_read; - afinfo->seq_fops.llseek = seq_lseek; - afinfo->seq_fops.release = seq_release_net; - afinfo->seq_ops.start = udp_seq_start; afinfo->seq_ops.next = udp_seq_next; afinfo->seq_ops.stop = udp_seq_stop; p = proc_create_data(afinfo->name, S_IRUGO, net->proc_net, - &afinfo->seq_fops, afinfo); + afinfo->seq_fops, afinfo); if (!p) rc = -ENOMEM; return rc; @@ -2120,14 +2118,20 @@ int udp4_seq_show(struct seq_file *seq, void *v) return 0; } +static const struct file_operations udp_afinfo_seq_fops = { + .owner = THIS_MODULE, + .open = udp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net +}; + /* ------------------------------------------------------------------------ */ static struct udp_seq_afinfo udp4_seq_afinfo = { .name = "udp", .family = AF_INET, .udp_table = &udp_table, - .seq_fops = { - .owner = THIS_MODULE, - }, + .seq_fops = &udp_afinfo_seq_fops, .seq_ops = { .show = udp4_seq_show, }, diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index aee9963f7f5..12e9499a1a6 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -10,6 +10,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include <linux/export.h> #include "udp_impl.h" struct udp_table udplite_table __read_mostly; @@ -71,13 +72,20 @@ static struct inet_protosw udplite4_protosw = { }; #ifdef CONFIG_PROC_FS + +static const struct file_operations udplite_afinfo_seq_fops = { + .owner = THIS_MODULE, + .open = udp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net +}; + static struct udp_seq_afinfo udplite4_seq_afinfo = { .name = "udplite", .family = AF_INET, .udp_table = &udplite_table, - .seq_fops = { - .owner = THIS_MODULE, - }, + .seq_fops = &udplite_afinfo_seq_fops, .seq_ops = { .show = udp4_seq_show, }, diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index d9ac0a0058b..9258e751bab 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c @@ -12,6 +12,7 @@ #include <linux/pfkeyv2.h> #include <linux/ipsec.h> #include <linux/netfilter_ipv4.h> +#include <linux/export.h> static int xfrm4_init_flags(struct xfrm_state *x) { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index d0611a5de45..cf88df82e2c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -87,6 +87,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/export.h> /* Set to 3 to get tracing... */ #define ACONF_DEBUG 2 diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c index 6b03826552e..399287e595d 100644 --- a/net/ipv6/addrconf_core.c +++ b/net/ipv6/addrconf_core.c @@ -3,6 +3,7 @@ * not configured or static. */ +#include <linux/export.h> #include <net/ipv6.h> #define IPV6_ADDR_SCOPE_TYPE(scope) ((scope) << 16) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 1318de4c3e8..bf22a225f42 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -30,6 +30,7 @@ #include <linux/in6.h> #include <linux/icmpv6.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/dst.h> #include <net/sock.h> diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index 14ed0a955b5..37f548b7f6d 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -2,6 +2,7 @@ * IPv6 library code, needed by static components when full IPv6 support is * not configured or static. */ +#include <linux/export.h> #include <net/ipv6.h> /* diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 34d244df907..295571576f8 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -14,6 +14,7 @@ */ #include <linux/netdevice.h> +#include <linux/export.h> #include <net/fib_rules.h> #include <net/ipv6.h> diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 54303945019..4566dbd916d 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -21,6 +21,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/sock.h> diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index def0538e241..449a9185b8f 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -51,6 +51,7 @@ #include <linux/pim.h> #include <net/addrconf.h> #include <linux/netfilter_ipv6.h> +#include <linux/export.h> #include <net/ip6_checksum.h> struct mr6_table { diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 30fcee46544..db31561cc8d 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -3,6 +3,7 @@ #include <linux/ipv6.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv6.h> +#include <linux/export.h> #include <net/dst.h> #include <net/ipv6.h> #include <net/ip6_route.h> @@ -100,9 +101,16 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, .pinet6 = (struct ipv6_pinfo *) &fake_pinfo, }; const void *sk = strict ? &fake_sk : NULL; - - *dst = ip6_route_output(net, sk, &fl->u.ip6); - return (*dst)->error; + struct dst_entry *result; + int err; + + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) + dst_release(result); + else + *dst = result; + return err; } __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index e8762c73b17..38f00b0298d 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -182,7 +182,6 @@ fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst) return container_of(q, struct nf_ct_frag6_queue, q); oom: - pr_debug("Can't alloc new queue\n"); return NULL; } @@ -370,10 +369,10 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) struct sk_buff *clone; int i, plen = 0; - if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL) { - pr_debug("Can't alloc skb\n"); + clone = alloc_skb(0, GFP_ATOMIC); + if (clone == NULL) goto out_oom; - } + clone->next = head->next; head->next = clone; skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 18ff5df7ec0..1008ce94bc3 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -21,6 +21,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/stddef.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/ip.h> #include <net/sock.h> diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 6f7824e1cea..331af3b882a 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -61,6 +61,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/export.h> static struct raw_hashinfo raw_v6_hashinfo = { .lock = __RW_LOCK_UNLOCKED(raw_v6_hashinfo.lock), diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index cc22099ac8b..dfb164e9051 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -42,6 +42,7 @@ #include <linux/jhash.h> #include <linux/skbuff.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/sock.h> #include <net/snmp.h> diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 57b82dc1ae9..8473016bba4 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -26,6 +26,7 @@ #include <linux/capability.h> #include <linux/errno.h> +#include <linux/export.h> #include <linux/types.h> #include <linux/times.h> #include <linux/socket.h> diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 6dcf5e7d661..166a57c47d3 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -10,6 +10,7 @@ #include <linux/in6.h> #include <linux/ipv6.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/ndisc.h> #include <net/ipv6.h> #include <net/addrconf.h> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 10b2b3165a1..36131d122a6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -2161,12 +2161,18 @@ out: return 0; } +static const struct file_operations tcp6_afinfo_seq_fops = { + .owner = THIS_MODULE, + .open = tcp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net +}; + static struct tcp_seq_afinfo tcp6_seq_afinfo = { .name = "tcp6", .family = AF_INET6, - .seq_fops = { - .owner = THIS_MODULE, - }, + .seq_fops = &tcp6_afinfo_seq_fops, .seq_ops = { .show = tcp6_seq_show, }, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f4ca0a5b345..846f4757eb8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1424,13 +1424,19 @@ int udp6_seq_show(struct seq_file *seq, void *v) return 0; } +static const struct file_operations udp6_afinfo_seq_fops = { + .owner = THIS_MODULE, + .open = udp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net +}; + static struct udp_seq_afinfo udp6_seq_afinfo = { .name = "udp6", .family = AF_INET6, .udp_table = &udp_table, - .seq_fops = { - .owner = THIS_MODULE, - }, + .seq_fops = &udp6_afinfo_seq_fops, .seq_ops = { .show = udp6_seq_show, }, diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index 986c4de5292..1d08e21d9f6 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -11,6 +11,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include <linux/export.h> #include "udp_impl.h" static int udplitev6_rcv(struct sk_buff *skb) @@ -93,13 +94,20 @@ void udplitev6_exit(void) } #ifdef CONFIG_PROC_FS + +static const struct file_operations udplite6_afinfo_seq_fops = { + .owner = THIS_MODULE, + .open = udp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net +}; + static struct udp_seq_afinfo udplite6_seq_afinfo = { .name = "udplite6", .family = AF_INET6, .udp_table = &udplite_table, - .seq_fops = { - .owner = THIS_MODULE, - }, + .seq_fops = &udplite6_afinfo_seq_fops, .seq_ops = { .show = udp6_seq_show, }, diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index 248f0b2a7ee..f2d72b8a3fa 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c @@ -15,6 +15,7 @@ #include <linux/pfkeyv2.h> #include <linux/ipsec.h> #include <linux/netfilter_ipv6.h> +#include <linux/export.h> #include <net/dsfield.h> #include <net/ipv6.h> #include <net/addrconf.h> diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c index 26b5bfcf1d0..f8ba30dfeca 100644 --- a/net/ipx/ipx_proc.c +++ b/net/ipx/ipx_proc.c @@ -9,6 +9,7 @@ #include <linux/proc_fs.h> #include <linux/spinlock.h> #include <linux/seq_file.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/tcp_states.h> #include <net/ipx.h> diff --git a/net/irda/discovery.c b/net/irda/discovery.c index 36c3f037f17..b0b56a339a8 100644 --- a/net/irda/discovery.c +++ b/net/irda/discovery.c @@ -35,6 +35,7 @@ #include <linux/fs.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/irda/irda.h> #include <net/irda/irlmp.h> diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index 3eca35faf2a..14653b8d664 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -42,6 +42,7 @@ #include <linux/kmod.h> #include <linux/spinlock.h> #include <linux/slab.h> +#include <linux/export.h> #include <asm/ioctls.h> #include <asm/uaccess.h> diff --git a/net/irda/irttp.c b/net/irda/irttp.c index 285ccd623ae..32e3bb02611 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -29,6 +29,7 @@ #include <linux/fs.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/export.h> #include <asm/byteorder.h> #include <asm/unaligned.h> diff --git a/net/irda/qos.c b/net/irda/qos.c index 4369f7f41bc..798ffd9a705 100644 --- a/net/irda/qos.c +++ b/net/irda/qos.c @@ -30,6 +30,8 @@ * ********************************************************************/ +#include <linux/export.h> + #include <asm/byteorder.h> #include <net/irda/irda.h> diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 34b2ddeacb6..bf8d50c6793 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -397,6 +397,7 @@ static void l2tp_recv_dequeue(struct l2tp_session *session) * expect to send up next, dequeue it and any other * in-sequence packets behind it. */ +start: spin_lock_bh(&session->reorder_q.lock); skb_queue_walk_safe(&session->reorder_q, skb, tmp) { if (time_after(jiffies, L2TP_SKB_CB(skb)->expires)) { @@ -433,7 +434,7 @@ static void l2tp_recv_dequeue(struct l2tp_session *session) */ spin_unlock_bh(&session->reorder_q.lock); l2tp_recv_dequeue_skb(session, skb); - spin_lock_bh(&session->reorder_q.lock); + goto start; } out: diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c index 90324211131..e32cab44ea9 100644 --- a/net/llc/llc_input.c +++ b/net/llc/llc_input.c @@ -13,6 +13,7 @@ */ #include <linux/netdevice.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/llc.h> #include <net/llc_pdu.h> diff --git a/net/llc/llc_output.c b/net/llc/llc_output.c index b38a1079a98..b658cba89fd 100644 --- a/net/llc/llc_output.c +++ b/net/llc/llc_output.c @@ -18,6 +18,7 @@ #include <linux/netdevice.h> #include <linux/trdevice.h> #include <linux/skbuff.h> +#include <linux/export.h> #include <net/llc.h> #include <net/llc_pdu.h> diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index 7af1ff2d1f1..a1839c00435 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -17,6 +17,7 @@ #include <linux/proc_fs.h> #include <linux/errno.h> #include <linux/seq_file.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/sock.h> #include <net/llc.h> diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 7d3b438755f..96ddb72760b 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -247,15 +247,3 @@ config MAC80211_DEBUG_COUNTERS and show them in debugfs. If unsure, say N. - -config MAC80211_DRIVER_API_TRACER - bool "Driver API tracer" - depends on MAC80211_DEBUG_MENU - depends on EVENT_TRACING - help - Say Y here to make mac80211 register with the ftrace - framework for the driver API -- you can then see which - driver methods it is calling and which API functions - drivers are calling by looking at the trace. - - If unsure, say Y. diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index fdb54e61d63..d540c3b160f 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -24,7 +24,8 @@ mac80211-y := \ util.o \ wme.o \ event.o \ - chan.o + chan.o \ + driver-trace.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ @@ -41,7 +42,6 @@ mac80211-$(CONFIG_MAC80211_MESH) += \ mac80211-$(CONFIG_PM) += pm.o -mac80211-$(CONFIG_MAC80211_DRIVER_API_TRACER) += driver-trace.o CFLAGS_driver-trace.o := -I$(src) # objects for PID algorithm diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 97f33588b65..e844e5a3840 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -38,6 +38,7 @@ #include <linux/ieee80211.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "driver-ops.h" @@ -72,8 +73,11 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL); #ifdef CONFIG_MAC80211_HT_DEBUG - printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", - sta->sta.addr, tid); + printk(KERN_DEBUG + "Rx BA session stop requested for %pM tid %u %s reason: %d\n", + sta->sta.addr, tid, + initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", + (int)reason); #endif /* CONFIG_MAC80211_HT_DEBUG */ if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, @@ -84,7 +88,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, /* check if this is a self generated aggregation halt */ if (initiator == WLAN_BACK_RECIPIENT && tx) ieee80211_send_delba(sta->sdata, sta->sta.addr, - tid, 0, reason); + tid, WLAN_BACK_RECIPIENT, reason); del_timer_sync(&tid_rx->session_timer); del_timer_sync(&tid_rx->reorder_timer); @@ -108,7 +112,7 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, int i; rcu_read_lock(); - sta = sta_info_get(sdata, addr); + sta = sta_info_get_bss(sdata, addr); if (!sta) { rcu_read_unlock(); return; @@ -176,7 +180,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 331472ce038..2c2e9519a2e 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -15,6 +15,7 @@ #include <linux/ieee80211.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "driver-ops.h" @@ -77,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); @@ -184,6 +186,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, #endif /* CONFIG_MAC80211_HT_DEBUG */ del_timer_sync(&tid_tx->addba_resp_timer); + del_timer_sync(&tid_tx->session_timer); /* * After this packets are no longer handed right through @@ -367,6 +370,28 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) tid_tx->timeout); } +/* + * After accepting the AddBA Response we activated a timer, + * resetting it after each frame that we send. + */ +static void sta_tx_agg_session_timer_expired(unsigned long data) +{ + /* not an elegant detour, but there is no choice as the timer passes + * only one argument, and various sta_info are needed here, so init + * flow in sta_info_create gives the TID as data, while the timer_to_id + * array gives the sta through container_of */ + u8 *ptid = (u8 *)data; + u8 *timer_to_id = ptid - *ptid; + struct sta_info *sta = container_of(timer_to_id, struct sta_info, + timer_to_tid[0]); + +#ifdef CONFIG_MAC80211_HT_DEBUG + printk(KERN_DEBUG "tx session timer expired on tid %d\n", (u16)*ptid); +#endif + + ieee80211_stop_tx_ba_session(&sta->sta, *ptid); +} + int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, u16 timeout) { @@ -391,13 +416,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; @@ -441,11 +461,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, tid_tx->timeout = timeout; - /* Tx timer */ + /* response timer */ tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; init_timer(&tid_tx->addba_resp_timer); + /* tx timer */ + tid_tx->session_timer.function = sta_tx_agg_session_timer_expired; + tid_tx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; + init_timer(&tid_tx->session_timer); + /* assign a dialog token */ sta->ampdu_mlme.dialog_token_allocator++; tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; @@ -550,7 +575,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) } mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, ra); + sta = sta_info_get_bss(sdata, ra); if (!sta) { mutex_unlock(&local->sta_mtx); #ifdef CONFIG_MAC80211_HT_DEBUG @@ -679,7 +704,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, ra); + sta = sta_info_get_bss(sdata, ra); if (!sta) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Could not find station: %pM\n", ra); @@ -817,6 +842,11 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, ieee80211_agg_tx_operational(local, sta, tid); sta->ampdu_mlme.addba_req_num[tid] = 0; + + if (tid_tx->timeout) + mod_timer(&tid_tx->session_timer, + TU_TO_EXP_TIME(tid_tx->timeout)); + } else { ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, true); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d06c65fa552..393b2a4445b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -102,6 +102,16 @@ static int ieee80211_change_iface(struct wiphy *wiphy, return 0; } +static int ieee80211_set_noack_map(struct wiphy *wiphy, + struct net_device *dev, + u16 noack_map) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + sdata->noack_map = noack_map; + return 0; +} + static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr, struct key_params *params) @@ -411,7 +421,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 +433,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 +501,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 = rtnl_dereference(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 +536,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 +620,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 +639,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 +648,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 +842,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 +911,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 +1000,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 && @@ -1123,6 +1195,8 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, { u8 *new_ie; const u8 *old_ie; + struct ieee80211_sub_if_data *sdata = container_of(ifmsh, + struct ieee80211_sub_if_data, u.mesh); /* allocate information elements */ new_ie = NULL; @@ -1149,6 +1223,10 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, if (setup->is_secure) ifmsh->security |= IEEE80211_MESH_SEC_SECURED; + /* mcast rate setting in Mesh Node */ + memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate, + sizeof(setup->mcast_rate)); + return 0; } @@ -1194,6 +1272,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask)) conf->dot11MeshHWMPpreqMinInterval = nconf->dot11MeshHWMPpreqMinInterval; + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, mask)) + conf->dot11MeshHWMPperrMinInterval = + nconf->dot11MeshHWMPperrMinInterval; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, mask)) conf->dot11MeshHWMPnetDiameterTraversalTime = @@ -1394,7 +1475,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 +1998,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 +2006,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 +2574,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 +2715,7 @@ 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, + .set_noack_map = ieee80211_set_noack_map, }; diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 883996b2f99..90baea53e7c 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -97,40 +97,6 @@ static const struct file_operations reset_ops = { .llseek = noop_llseek, }; -static ssize_t noack_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - - return mac80211_format_buffer(user_buf, count, ppos, "%d\n", - local->wifi_wme_noack_test); -} - -static ssize_t noack_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - char buf[10]; - size_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - local->wifi_wme_noack_test = !!simple_strtoul(buf, NULL, 0); - - return count; -} - -static const struct file_operations noack_ops = { - .read = noack_read, - .write = noack_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - static ssize_t uapsd_queues_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -190,7 +156,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; @@ -398,7 +364,6 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(wep_iv); DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); - DEBUGFS_ADD(noack); DEBUGFS_ADD(uapsd_queues); DEBUGFS_ADD(uapsd_max_sp_len); DEBUGFS_ADD(channel_type); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 9352819a986..8df28910b8e 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -405,6 +405,8 @@ IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout, u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval, u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPperrMinInterval, + u.mesh.mshcfg.dot11MeshHWMPperrMinInterval, DEC); IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime, u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC); IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries, @@ -534,6 +536,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) MESHPARAMS_ADD(dot11MeshMaxPeerLinks); MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout); MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval); + MESHPARAMS_ADD(dot11MeshHWMPperrMinInterval); MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime); MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries); MESHPARAMS_ADD(path_refresh_time); diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 3110cbdc501..2406b3e7393 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 5f165d7eb2d..e8960ae3986 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -5,11 +5,34 @@ #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 struct ieee80211_sub_if_data * +get_bss_sdata(struct ieee80211_sub_if_data *sdata) +{ + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, + u.ap); + + return sdata; +} + 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 +92,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 +120,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 +129,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 +160,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 +177,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 +194,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 +253,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 +272,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 +289,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 +302,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 +319,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 +333,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 +431,9 @@ static inline void drv_sta_notify(struct ieee80211_local *local, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { + sdata = get_bss_sdata(sdata); + 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 +448,9 @@ static inline int drv_sta_add(struct ieee80211_local *local, might_sleep(); + sdata = get_bss_sdata(sdata); + 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 +466,9 @@ static inline void drv_sta_remove(struct ieee80211_local *local, { might_sleep(); + sdata = get_bss_sdata(sdata); + 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 +484,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 +501,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 +516,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 +529,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 +560,9 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, might_sleep(); + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); if (local->ops->ampdu_action) @@ -644,6 +718,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 +733,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/driver-trace.h b/net/mac80211/driver-trace.h index 2af4fca5533..6e9df8fd8fb 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -5,17 +5,6 @@ #include <net/mac80211.h> #include "ieee80211_i.h" -#if !defined(CONFIG_MAC80211_DRIVER_API_TRACER) || defined(__CHECKER__) -#undef TRACE_EVENT -#define TRACE_EVENT(name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#undef DECLARE_EVENT_CLASS -#define DECLARE_EVENT_CLASS(...) -#undef DEFINE_EVENT -#define DEFINE_EVENT(evt_class, name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#endif - #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211 diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index f80a35c0d00..d3eafaefb16 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -14,11 +14,87 @@ */ #include <linux/ieee80211.h> +#include <linux/export.h> #include <net/mac80211.h> #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; +} + +static 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) { @@ -102,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) @@ -195,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 ede9a8b341a..7d84af70132 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 ea10a51babd..bdefa6ba3f4 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 { @@ -505,7 +514,9 @@ struct ieee80211_if_mesh { atomic_t mpaths; /* Timestamp of last SN update */ unsigned long last_sn_update; - /* Timestamp of last SN sent */ + /* Time when it's ok to send next PERR */ + unsigned long next_perr; + /* Timestamp of last PREQ sent */ unsigned long last_preq; struct mesh_rmc *rmc; spinlock_t mesh_preq_queue_lock; @@ -543,6 +554,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 +562,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), }; /** @@ -600,6 +613,9 @@ struct ieee80211_sub_if_data { struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; unsigned int fragment_next; + /* TID bitmap for NoAck policy */ + u16 noack_map; + struct ieee80211_key __rcu *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; struct ieee80211_key __rcu *default_unicast_key; struct ieee80211_key __rcu *default_multicast_key; @@ -722,17 +738,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 { @@ -951,7 +966,6 @@ struct ieee80211_local { int total_ps_buffered; /* total number of all buffered unicast and * multicast packets for power saving stations */ - int wifi_wme_noack_test; unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ /* @@ -1012,6 +1026,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 +1047,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) { @@ -1140,13 +1220,11 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); void ieee80211_sched_scan_stopped_work(struct work_struct *work); /* off-channel helpers */ -bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local); void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local, bool tell_ap); void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, bool offchannel_ps_enable); void ieee80211_offchannel_return(struct ieee80211_local *local, - bool enable_beaconing, bool offchannel_ps_disable); void ieee80211_hw_roc_setup(struct ieee80211_local *local); @@ -1179,7 +1257,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 +1416,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 +1450,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 30d73552e9a..3d3bb5e9d8f 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; @@ -656,7 +672,6 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, struct ieee80211_local *local = sdata->local; struct ieee80211_hdr *hdr; struct ieee80211_radiotap_header *rtap = (void *)skb->data; - u8 *p; if (local->hw.queues < 4) return 0; @@ -667,19 +682,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len)); - if (!ieee80211_is_data(hdr->frame_control)) { - skb->priority = 7; - return ieee802_1d_to_ac[skb->priority]; - } - if (!ieee80211_is_data_qos(hdr->frame_control)) { - skb->priority = 0; - return ieee802_1d_to_ac[skb->priority]; - } - - p = ieee80211_get_qos_ctl(hdr); - skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; - - return ieee80211_downgrade_queue(local, skb); + return ieee80211_select_queue_80211(local, skb, hdr); } static const struct net_device_ops ieee80211_monitorif_ops = { @@ -850,6 +853,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE); sdata->control_port_no_encrypt = false; + sdata->noack_map = 0; + /* only monitor differs */ sdata->dev->type = ARPHRD_ETHER; diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 756b157c2ed..87a89741432 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -15,6 +15,7 @@ #include <linux/rcupdate.h> #include <linux/rtnetlink.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "driver-ops.h" @@ -133,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; } @@ -178,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/led.c b/net/mac80211/led.c index 14590332c81..1bf7903496f 100644 --- a/net/mac80211/led.c +++ b/net/mac80211/led.c @@ -9,6 +9,7 @@ /* just for IFNAMSIZ */ #include <linux/if.h> #include <linux/slab.h> +#include <linux/export.h> #include "led.h" void ieee80211_led_rx(struct ieee80211_local *local) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index cae443563ec..24cc50b963a 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -92,50 +92,9 @@ static void ieee80211_reconfig_filter(struct work_struct *work) ieee80211_configure_filter(local); } -/* - * Returns true if we are logically configured to be on - * the operating channel AND the hardware-conf is currently - * configured on the operating channel. Compares channel-type - * as well. - */ -bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local) -{ - struct ieee80211_channel *chan, *scan_chan; - enum nl80211_channel_type channel_type; - - /* This logic needs to match logic in ieee80211_hw_config */ - if (local->scan_channel) { - chan = local->scan_channel; - /* If scanning on oper channel, use whatever channel-type - * is currently in use. - */ - if (chan == local->oper_channel) - channel_type = local->_oper_channel_type; - else - channel_type = NL80211_CHAN_NO_HT; - } else if (local->tmp_channel) { - chan = scan_chan = local->tmp_channel; - channel_type = local->tmp_channel_type; - } else { - chan = local->oper_channel; - channel_type = local->_oper_channel_type; - } - - if (chan != local->oper_channel || - channel_type != local->_oper_channel_type) - 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)) - return false; - - return true; -} - 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 +102,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 +116,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 +517,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 +565,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 +583,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 +603,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 +646,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++) { @@ -1051,6 +1032,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); @@ -1061,6 +1049,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 a7078fdba8c..c707c8bf6d2 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 = @@ -697,6 +749,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) atomic_set(&ifmsh->mpaths, 0); mesh_rmc_init(sdata); ifmsh->last_preq = jiffies; + ifmsh->next_perr = jiffies; /* Allocate all mesh structures when creating the first mesh interface. */ if (!mesh_allocated) ieee80211s_init(); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 8c00e2d1d63..bd14bd26a2b 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); @@ -226,6 +233,8 @@ void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); /* Mesh paths */ int mesh_nexthop_lookup(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); +int mesh_nexthop_resolve(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 174040a4288..73abb7524b2 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,24 @@ 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_if_mesh *ifmsh = &sdata->u.mesh; 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); + if (time_before(jiffies, ifmsh->next_perr)) + return -EAGAIN; + + 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); @@ -290,6 +294,8 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, /* see note in function header */ prepare_frame_for_deferred_tx(sdata, skb); + ifmsh->next_perr = TU_TO_EXP_TIME( + ifmsh->mshcfg.dot11MeshHWMPperrMinInterval); ieee80211_add_pending_skb(local, skb); return 0; } @@ -393,15 +399,13 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, orig_metric = PREQ_IE_METRIC(hwmp_ie); break; case MPATH_PREP: - /* Originator here refers to the MP that was the destination in - * the Path Request. The draft refers to that MP as the - * destination address, even though usually it is the origin of - * the PREP frame. We divert from the nomenclature in the draft + /* Originator here refers to the MP that was the target in the + * Path Request. We divert from the nomenclature in the draft * so that we can easily use a single function to gather path * information from both PREQ and PREP frames. */ - orig_addr = PREP_IE_ORIG_ADDR(hwmp_ie); - orig_sn = PREP_IE_ORIG_SN(hwmp_ie); + orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie); + orig_sn = PREP_IE_TARGET_SN(hwmp_ie); orig_lifetime = PREP_IE_LIFETIME(hwmp_ie); orig_metric = PREP_IE_METRIC(hwmp_ie); break; @@ -562,9 +566,9 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, ttl = ifmsh->mshcfg.element_ttl; if (ttl != 0) { mhwmp_dbg("replying to the PREQ"); - mesh_path_sel_frame_tx(MPATH_PREP, 0, target_addr, - cpu_to_le32(target_sn), 0, orig_addr, - cpu_to_le32(orig_sn), mgmt->sa, 0, ttl, + mesh_path_sel_frame_tx(MPATH_PREP, 0, orig_addr, + cpu_to_le32(orig_sn), 0, target_addr, + cpu_to_le32(target_sn), mgmt->sa, 0, ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), 0, sdata); } else @@ -618,14 +622,8 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, mhwmp_dbg("received PREP from %pM", PREP_IE_ORIG_ADDR(prep_elem)); - /* Note that we divert from the draft nomenclature and denominate - * destination to what the draft refers to as origininator. So in this - * function destnation refers to the final destination of the PREP, - * which corresponds with the originator of the PREQ which this PREP - * replies - */ - target_addr = PREP_IE_TARGET_ADDR(prep_elem); - if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0) + orig_addr = PREP_IE_ORIG_ADDR(prep_elem); + if (memcmp(orig_addr, sdata->vif.addr, ETH_ALEN) == 0) /* destination, no forwarding required */ return; @@ -636,7 +634,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, } rcu_read_lock(); - mpath = mesh_path_lookup(target_addr, sdata); + mpath = mesh_path_lookup(orig_addr, sdata); if (mpath) spin_lock_bh(&mpath->state_lock); else @@ -651,7 +649,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, flags = PREP_IE_FLAGS(prep_elem); lifetime = PREP_IE_LIFETIME(prep_elem); hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1; - orig_addr = PREP_IE_ORIG_ADDR(prep_elem); + target_addr = PREP_IE_TARGET_ADDR(prep_elem); target_sn = PREP_IE_TARGET_SN(prep_elem); orig_sn = PREP_IE_ORIG_SN(prep_elem); @@ -867,9 +865,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 +930,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); @@ -972,71 +982,97 @@ enddiscovery: kfree(preq_node); } -/** - * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame +/* mesh_nexthop_resolve - lookup next hop for given skb and start path + * discovery if no forwarding information is found. * * @skb: 802.11 frame to be sent * @sdata: network subif the frame will be sent through * - * Returns: 0 if the next hop was found. Nonzero otherwise. If no next hop is - * found, the function will start a path discovery and queue the frame so it is - * sent when the path is resolved. This means the caller must not free the skb - * in this case. + * Returns: 0 if the next hop was found and -ENOENT if the frame was queued. + * skb is freeed here if no mpath could be allocated. */ -int mesh_nexthop_lookup(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +int mesh_nexthop_resolve(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) { - struct sk_buff *skb_to_free = NULL; - struct mesh_path *mpath; - struct sta_info *next_hop; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mesh_path *mpath; + struct sk_buff *skb_to_free = NULL; u8 *target_addr = hdr->addr3; int err = 0; rcu_read_lock(); - mpath = mesh_path_lookup(target_addr, sdata); + err = mesh_nexthop_lookup(skb, sdata); + if (!err) + goto endlookup; + /* no nexthop found, start resolving */ + mpath = mesh_path_lookup(target_addr, sdata); if (!mpath) { mesh_path_add(target_addr, sdata); mpath = mesh_path_lookup(target_addr, sdata); if (!mpath) { - sdata->u.mesh.mshstats.dropped_frames_no_route++; + mesh_path_discard_frame(skb, sdata); err = -ENOSPC; goto endlookup; } } - if (mpath->flags & MESH_PATH_ACTIVE) { - if (time_after(jiffies, - mpath->exp_time - - msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && - !memcmp(sdata->vif.addr, hdr->addr4, ETH_ALEN) && - !(mpath->flags & MESH_PATH_RESOLVING) && - !(mpath->flags & MESH_PATH_FIXED)) { - mesh_queue_preq(mpath, - PREQ_Q_F_START | PREQ_Q_F_REFRESH); - } - next_hop = rcu_dereference(mpath->next_hop); - if (next_hop) - memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); - else - err = -ENOENT; - } else { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - if (!(mpath->flags & MESH_PATH_RESOLVING)) { - /* Start discovery only if it is not running yet */ - mesh_queue_preq(mpath, PREQ_Q_F_START); - } + if (!(mpath->flags & MESH_PATH_RESOLVING)) + mesh_queue_preq(mpath, PREQ_Q_F_START); + + if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN) + skb_to_free = skb_dequeue(&mpath->frame_queue); - 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); + err = -ENOENT; + if (skb_to_free) + mesh_path_discard_frame(skb_to_free, sdata); + +endlookup: + rcu_read_unlock(); + return err; +} +/** + * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame. Calling + * this function is considered "using" the associated mpath, so preempt a path + * refresh if this mpath expires soon. + * + * @skb: 802.11 frame to be sent + * @sdata: network subif the frame will be sent through + * + * Returns: 0 if the next hop was found. Nonzero otherwise. + */ +int mesh_nexthop_lookup(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) +{ + struct mesh_path *mpath; + struct sta_info *next_hop; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u8 *target_addr = hdr->addr3; + int err = -ENOENT; + + rcu_read_lock(); + mpath = mesh_path_lookup(target_addr, sdata); + + if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) + goto endlookup; + + if (time_after(jiffies, + mpath->exp_time - + msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && + !memcmp(sdata->vif.addr, hdr->addr4, ETH_ALEN) && + !(mpath->flags & MESH_PATH_RESOLVING) && + !(mpath->flags & MESH_PATH_FIXED)) + mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); - info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; - skb_queue_tail(&mpath->frame_queue, skb); - if (skb_to_free) - mesh_path_discard_frame(skb_to_free, sdata); - err = -ENOENT; + next_hop = rcu_dereference(mpath->next_hop); + if (next_hop) { + memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); + memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); + err = 0; } endlookup: @@ -1061,6 +1097,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 7f54c504223..edf167e3b8f 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,7 @@ 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); + memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); __skb_queue_tail(&tmpq, skb); } @@ -269,6 +265,7 @@ static void prepare_for_gate(struct sk_buff *skb, char *dst_addr, next_hop = rcu_dereference(gate_mpath->next_hop)->sta.addr; memcpy(hdr->addr1, next_hop, ETH_ALEN); rcu_read_unlock(); + memcpy(hdr->addr2, gate_mpath->sdata->vif.addr, ETH_ALEN); memcpy(hdr->addr3, dst_addr, ETH_ALEN); } @@ -423,21 +420,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 +475,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 +493,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 */ @@ -991,38 +973,11 @@ int mesh_path_send_to_gates(struct mesh_path *mpath) * @skb: frame to discard * @sdata: network subif the frame was to be sent through * - * If the frame was being forwarded from another MP, a PERR frame will be sent - * to the precursor. The precursor's address (i.e. the previous hop) was saved - * in addr1 of the frame-to-be-forwarded, and would only be overwritten once - * the destination is successfully resolved. - * * Locking: the function must me called within a rcu_read_lock region */ void mesh_path_discard_frame(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct mesh_path *mpath; - u32 sn = 0; - __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD); - - if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) { - u8 *ra, *da; - - da = hdr->addr3; - ra = hdr->addr1; - rcu_read_lock(); - mpath = mesh_path_lookup(da, sdata); - if (mpath) { - spin_lock_bh(&mpath->state_lock); - sn = ++mpath->sn; - spin_unlock_bh(&mpath->state_lock); - } - rcu_read_unlock(); - mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, skb->data, - cpu_to_le32(sn), reason, ra, sdata); - } - kfree_skb(skb); sdata->u.mesh.mshstats.dropped_frames_no_route++; } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7e57f5d07f6..7314372b12b 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 40db011da58..09019d13594 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -16,10 +16,12 @@ #include <linux/skbuff.h> #include <linux/if_arp.h> #include <linux/etherdevice.h> +#include <linux/moduleparam.h> #include <linux/rtnetlink.h> #include <linux/pm_qos.h> #include <linux/crc32.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include <asm/unaligned.h> @@ -207,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) { @@ -1118,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; @@ -1357,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. @@ -1368,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) @@ -1466,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) @@ -1482,7 +1529,7 @@ 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; @@ -1532,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); @@ -1540,47 +1586,14 @@ 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); - - 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); - if (rate < min_rate) { - min_rate = rate; - min_rate_index = j; - } - break; - } - } - } + ieee80211_get_rates(sband, elems.supp_rates, elems.supp_rates_len, + &rates, &basic_rates, &have_higher_than_11mbit, + &min_rate, &min_rate_index); - 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); - if (rate < min_rate) { - min_rate = rate; - min_rate_index = j; - } - break; - } - } - } + 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); /* * some buggy APs don't advertise basic_rates. use the lowest @@ -1603,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; @@ -1972,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; @@ -2126,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. @@ -2136,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); } @@ -2630,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 13427b194ce..e4330d84357 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -12,6 +12,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "driver-trace.h" @@ -155,7 +156,6 @@ void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local, } void ieee80211_offchannel_return(struct ieee80211_local *local, - bool enable_beaconing, bool offchannel_ps_disable) { struct ieee80211_sub_if_data *sdata; @@ -187,11 +187,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, netif_tx_wake_all_queues(sdata->dev); } - /* Check to see if we should re-enable beaconing */ - if (enable_beaconing && - (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_ADHOC || - sdata->vif.type == NL80211_IFTYPE_MESH_POINT)) + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_ADHOC || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); } @@ -211,8 +209,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); @@ -226,6 +222,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 9ee7164b207..596efaf50e0 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/rate.c b/net/mac80211/rate.c index ff5c3aa48a1..5a5a7767d54 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/rtnetlink.h> #include <linux/slab.h> +#include <linux/module.h> #include "rate.h" #include "ieee80211_i.h" #include "debugfs.h" diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 58a89554b78..b39dda523f3 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_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index a290ad231d7..d5a56226e67 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -50,6 +50,7 @@ #include <linux/debugfs.h> #include <linux/ieee80211.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "rc80211_minstrel.h" diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index cdb28535716..ff5f7b84e82 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/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index cefcb5d2dae..e788f76a1df 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -10,6 +10,7 @@ #include <linux/skbuff.h> #include <linux/debugfs.h> #include <linux/ieee80211.h> +#include <linux/export.h> #include <net/mac80211.h> #include "rc80211_minstrel.h" #include "rc80211_minstrel_ht.h" diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c index 4851e9e2dae..c97a0657c04 100644 --- a/net/mac80211/rc80211_pid_debugfs.c +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -13,6 +13,7 @@ #include <linux/types.h> #include <linux/skbuff.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "rate.h" diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 097b42d286e..daf5cde65b3 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -16,6 +16,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/rcupdate.h> +#include <linux/export.h> #include <net/mac80211.h> #include <net/ieee80211_radiotap.h> @@ -747,10 +748,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; @@ -763,6 +765,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]); @@ -773,6 +777,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 */ @@ -857,6 +870,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; } @@ -1326,15 +1346,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. @@ -1870,13 +1895,16 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) static ieee80211_rx_result ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) { - struct ieee80211_hdr *hdr; + struct ieee80211_hdr *fwd_hdr, *hdr; + struct ieee80211_tx_info *info; struct ieee80211s_hdr *mesh_hdr; - unsigned int hdrlen; struct sk_buff *skb = rx->skb, *fwd_skb; struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD); + u16 q, hdrlen; hdr = (struct ieee80211_hdr *) skb->data; hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -1892,15 +1920,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (!mesh_hdr->ttl) - /* illegal frame */ return RX_DROP_MONITOR; - if (ieee80211_queue_stopped(&local->hw, skb_get_queue_mapping(skb))) { - IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, - dropped_frames_congestion); - return RX_DROP_MONITOR; - } - if (mesh_hdr->flags & MESH_FLAGS_AE) { struct mesh_path *mppath; char *proxied_addr; @@ -1932,60 +1953,50 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) compare_ether_addr(sdata->vif.addr, hdr->addr3) == 0) return RX_CONTINUE; - mesh_hdr->ttl--; + q = ieee80211_select_queue_80211(local, skb, hdr); + if (ieee80211_queue_stopped(&local->hw, q)) { + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); + return RX_DROP_MONITOR; + } + skb_set_queue_mapping(skb, q); - if (status->rx_flags & IEEE80211_RX_RA_MATCH) { - if (!mesh_hdr->ttl) - IEEE80211_IFSTA_MESH_CTR_INC(&rx->sdata->u.mesh, - dropped_frames_ttl); - else { - struct ieee80211_hdr *fwd_hdr; - struct ieee80211_tx_info *info; - - fwd_skb = skb_copy(skb, GFP_ATOMIC); - - if (!fwd_skb && net_ratelimit()) - printk(KERN_DEBUG "%s: failed to clone mesh frame\n", - sdata->name); - if (!fwd_skb) - goto out; - - fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; - memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); - info = IEEE80211_SKB_CB(fwd_skb); - memset(info, 0, sizeof(*info)); - info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; - info->control.vif = &rx->sdata->vif; - 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; - /* - * Save TA to addr1 to send TA a path error if a - * suitable next hop is not found - */ - memcpy(fwd_hdr->addr1, fwd_hdr->addr2, - ETH_ALEN); - err = mesh_nexthop_lookup(fwd_skb, sdata); - /* Failed to immediately resolve next hop: - * fwded frame was dropped or will be added - * later to the pending skb queue. */ - if (err) - return RX_DROP_MONITOR; - - IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, - fwded_unicast); - } - IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, - fwded_frames); - ieee80211_add_pending_skb(local, fwd_skb); - } + if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) + goto out; + + if (!--mesh_hdr->ttl) { + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); + return RX_DROP_MONITOR; } + fwd_skb = skb_copy(skb, GFP_ATOMIC); + if (!fwd_skb) { + if (net_ratelimit()) + printk(KERN_DEBUG "%s: failed to clone mesh frame\n", + sdata->name); + goto out; + } + + fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; + info = IEEE80211_SKB_CB(fwd_skb); + 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(ifmsh, fwded_mcast); + memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); + } else if (!mesh_nexthop_lookup(fwd_skb, sdata)) { + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); + } else { + /* unable to resolve next hop */ + mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3, + 0, reason, fwd_hdr->addr2, sdata); + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); + return RX_DROP_MONITOR; + } + + IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames); + ieee80211_add_pending_skb(local, fwd_skb); out: if (is_multicast_ether_addr(hdr->addr1) || sdata->dev->flags & IFF_PROMISC) @@ -2013,12 +2024,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)) @@ -2173,6 +2189,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; @@ -2206,13 +2234,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; @@ -2492,6 +2515,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 83a0b050b37..2c5041cc71f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -17,6 +17,7 @@ #include <linux/pm_qos.h> #include <net/sch_generic.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" @@ -212,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); @@ -263,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); @@ -297,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); } ieee80211_recalc_idle(local); @@ -360,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); @@ -372,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; } @@ -509,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, @@ -612,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++; @@ -672,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, 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 = @@ -742,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 8eaa746ec7a..f9823526200 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -351,10 +351,6 @@ static int sta_info_finish_insert(struct sta_info *sta, if (!sta->dummy || dummy_reinsert) { /* notify driver */ - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, - u.ap); err = drv_sta_add(local, sdata, &sta->sta); if (err) { if (!async) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 8c8ce05ad26..1a14fab4bc9 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 @@ -88,6 +88,7 @@ enum ieee80211_sta_info_flags { * struct tid_ampdu_tx - TID aggregation information (Tx). * * @rcu_head: rcu head for freeing structure + * @session_timer: check if we keep Tx-ing on the TID (by timeout value) * @addba_resp_timer: timer for peer's response to addba request * @pending: pending frames queue -- use sta's spinlock to protect * @dialog_token: dialog token for aggregation session @@ -110,6 +111,7 @@ enum ieee80211_sta_info_flags { */ struct tid_ampdu_tx { struct rcu_head rcu_head; + struct timer_list session_timer; struct timer_list addba_resp_timer; struct sk_buff_head pending; unsigned long state; @@ -390,6 +392,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); @@ -491,7 +499,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, */ int sta_info_insert(struct sta_info *sta); int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU); -int sta_info_insert_atomic(struct sta_info *sta); int sta_info_reinsert(struct sta_info *sta); int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 5533a74e9bb..46222ce0e5b 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include <linux/export.h> #include <net/mac80211.h> #include "ieee80211_i.h" #include "rate.h" @@ -516,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 */ @@ -609,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/tkip.c b/net/mac80211/tkip.c index f49d00a4c7f..51077a956a8 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/types.h> #include <linux/netdevice.h> +#include <linux/export.h> #include <asm/unaligned.h> #include <net/mac80211.h> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 48bbb96d8ed..c4cb4a536e2 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -18,6 +18,7 @@ #include <linux/etherdevice.h> #include <linux/bitmap.h> #include <linux/rcupdate.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/ieee80211_radiotap.h> #include <net/cfg80211.h> @@ -35,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; @@ -43,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) @@ -75,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 @@ -149,11 +151,15 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, rate = mrate; } - /* Time needed to transmit ACK - * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up - * to closest integer */ - - dur = ieee80211_frame_duration(local, 10, rate, erp, + /* Don't calculate ACKs for QoS Frames with NoAck Policy set */ + if (ieee80211_is_data_qos(hdr->frame_control) && + *(ieee80211_get_qos_ctl(hdr)) | IEEE80211_QOS_CTL_ACK_POLICY_NOACK) + dur = 0; + else + /* Time needed to transmit ACK + * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up + * to closest integer */ + dur = ieee80211_frame_duration(local, 10, rate, erp, tx->sdata->vif.bss_conf.use_short_preamble); if (next_frag_len) { @@ -571,8 +577,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; @@ -636,6 +640,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) else txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || + tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT || tx->sdata->vif.type == NL80211_IFTYPE_ADHOC); /* set up RTS protection if desired */ @@ -843,11 +848,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; @@ -855,6 +862,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; @@ -867,12 +876,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; @@ -884,6 +902,7 @@ static int ieee80211_fragment(struct ieee80211_local *local, pos += fraglen; } + /* adjust first fragment's length */ skb->len = hdrlen + per_fragm; return 0; } @@ -898,6 +917,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; @@ -926,21 +949,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. @@ -955,10 +978,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; } @@ -966,16 +988,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; } @@ -1014,21 +1036,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; } @@ -1042,9 +1068,11 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, int tid) { bool queued = false; + bool reset_agg_timer = false; if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { info->flags |= IEEE80211_TX_CTL_AMPDU; + reset_agg_timer = true; } else if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { /* * nothing -- this aggregation session is being started @@ -1076,6 +1104,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, /* do nothing, let packet pass through */ } else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { info->flags |= IEEE80211_TX_CTL_AMPDU; + reset_agg_timer = true; } else { queued = true; info->control.vif = &tx->sdata->vif; @@ -1085,6 +1114,11 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, spin_unlock(&tx->sta->lock); } + /* reset session timer */ + if (reset_agg_timer && tid_tx->timeout) + mod_timer(&tid_tx->session_timer, + TU_TO_EXP_TIME(tid_tx->timeout)); + return queued; } @@ -1107,6 +1141,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, @@ -1151,16 +1186,8 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, if (is_multicast_ether_addr(hdr->addr1)) { tx->flags &= ~IEEE80211_TX_UNICAST; info->flags |= IEEE80211_TX_CTL_NO_ACK; - } else { + } else tx->flags |= IEEE80211_TX_UNICAST; - if (unlikely(local->wifi_wme_noack_test)) - info->flags |= IEEE80211_TX_CTL_NO_ACK; - /* - * Flags are initialized to 0. Hence, no need to - * explicitly unset IEEE80211_TX_CTL_NO_ACK since - * it might already be set for injected frames. - */ - } if (!(info->flags & IEEE80211_TX_CTL_DONTFRAG)) { if (!(tx->flags & IEEE80211_TX_UNICAST) || @@ -1179,22 +1206,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] || @@ -1204,24 +1227,11 @@ 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_init(skbs, &local->pending[q]); + else + skb_queue_splice_tail_init(skbs, + &local->pending[q]); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -1230,47 +1240,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_ONCE(!skb_queue_empty(skbs)); + + return result; } /* @@ -1279,8 +1314,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) \ @@ -1314,13 +1348,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); @@ -1341,6 +1372,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); @@ -1350,6 +1382,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)) { @@ -1363,7 +1396,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; @@ -1430,7 +1464,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) if (ieee80211_vif_is_mesh(&sdata->vif) && ieee80211_is_data(hdr->frame_control) && !is_multicast_ether_addr(hdr->addr1)) - if (mesh_nexthop_lookup(skb, sdata)) { + if (mesh_nexthop_resolve(skb, sdata)) { /* skb queued: don't free */ rcu_read_unlock(); return; @@ -1684,8 +1718,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; @@ -1872,7 +1908,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) { @@ -1913,11 +1950,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); @@ -2018,6 +2098,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; @@ -2061,10 +2145,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; @@ -2177,10 +2266,10 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, /* Bitmap control */ *pos++ = n1 | aid0; /* Part Virt Bitmap */ + skb_put(skb, n2 - n1); memcpy(pos, bss->tim + n1, n2 - n1 + 1); tim[1] = n2 - n1 + 4; - skb_put(skb, n2 - n1); } else { *pos++ = aid0; /* Bitmap control */ *pos++ = 0; /* Part Virt Bitmap */ @@ -2278,22 +2367,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); @@ -2312,6 +2410,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)) { @@ -2354,6 +2454,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 6719bce4a08..5243c2cadee 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -13,12 +13,14 @@ #include <net/mac80211.h> #include <linux/netdevice.h> +#include <linux/export.h> #include <linux/types.h> #include <linux/slab.h> #include <linux/skbuff.h> #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> @@ -95,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, @@ -563,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) { @@ -811,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 @@ -1025,7 +1179,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 */ @@ -1075,7 +1229,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: @@ -1089,6 +1244,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* fall through */ case NL80211_IFTYPE_AP: changed |= BSS_CHANGED_SSID; + + if (sdata->vif.type == NL80211_IFTYPE_AP) + changed |= BSS_CHANGED_AP_PROBE_RESP; + /* fall through */ case NL80211_IFTYPE_MESH_POINT: changed |= BSS_CHANGED_BEACON | @@ -1110,6 +1269,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. @@ -1365,6 +1526,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 a1c6bfd55f0..68ad351479d 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 fd52e695c07..89511be3111 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -52,6 +52,30 @@ static int wme_downgrade_ac(struct sk_buff *skb) } } +/* Indicate which queue to use for this fully formed 802.11 frame */ +u16 ieee80211_select_queue_80211(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_hdr *hdr) +{ + u8 *p; + + if (local->hw.queues < 4) + return 0; + + if (!ieee80211_is_data(hdr->frame_control)) { + skb->priority = 7; + return ieee802_1d_to_ac[skb->priority]; + } + if (!ieee80211_is_data_qos(hdr->frame_control)) { + skb->priority = 0; + return ieee802_1d_to_ac[skb->priority]; + } + + p = ieee80211_get_qos_ctl(hdr); + skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; + + return ieee80211_downgrade_queue(local, skb); +} /* Indicate which queue to use. */ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, @@ -83,7 +107,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: @@ -139,16 +163,24 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); /* 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 (is_multicast_ether_addr(hdr->addr1) || + sdata->noack_map & BIT(tid)) { ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; + info->flags |= IEEE80211_TX_CTL_NO_ACK; + } + /* qos header is 2 bytes */ *p++ = ack_policy | tid; *p = ieee80211_vif_is_mesh(&sdata->vif) ? diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index 34e166fbf4d..94edceb617f 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -15,6 +15,9 @@ extern const int ieee802_1d_to_ac[8]; +u16 ieee80211_select_queue_80211(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_hdr *hdr); u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/work.c b/net/mac80211/work.c index 6c53b6d1002..c6dd01a0529 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 */ @@ -881,44 +862,6 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local, kfree_skb(skb); } -static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct, - enum nl80211_channel_type oper_ct) -{ - switch (wk_ct) { - case NL80211_CHAN_NO_HT: - return true; - case NL80211_CHAN_HT20: - if (oper_ct != NL80211_CHAN_NO_HT) - return true; - return false; - case NL80211_CHAN_HT40MINUS: - case NL80211_CHAN_HT40PLUS: - return (wk_ct == oper_ct); - } - WARN_ON(1); /* shouldn't get here */ - return false; -} - -static enum nl80211_channel_type -ieee80211_calc_ct(enum nl80211_channel_type wk_ct, - enum nl80211_channel_type oper_ct) -{ - switch (wk_ct) { - case NL80211_CHAN_NO_HT: - return oper_ct; - case NL80211_CHAN_HT20: - if (oper_ct != NL80211_CHAN_NO_HT) - return oper_ct; - return wk_ct; - case NL80211_CHAN_HT40MINUS: - case NL80211_CHAN_HT40PLUS: - return wk_ct; - } - WARN_ON(1); /* shouldn't get here */ - return wk_ct; -} - - static void ieee80211_work_timer(unsigned long data) { struct ieee80211_local *local = (void *) data; @@ -969,51 +912,12 @@ 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; - enum nl80211_channel_type wk_ct; - on_oper_chan = ieee80211_cfg_on_oper_channel(local); - - /* Work with existing channel type if possible. */ - wk_ct = wk->chan_type; - if (wk->chan == local->hw.conf.channel) - 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; + ieee80211_offchannel_stop_vifs(local, true); local->tmp_channel = wk->chan; - local->tmp_channel_type = wk_ct; - /* - * Leave the station vifs in awake mode if they - * happen to be on the same channel as - * the requested channel. - */ - on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); - if (on_oper_chan != on_oper_chan2) { - if (on_oper_chan2) { - /* going off oper channel, PS too */ - ieee80211_offchannel_stop_vifs(local, - true); - ieee80211_hw_config(local, 0); - } else { - /* going on channel, but leave PS - * off-channel. */ - ieee80211_hw_config(local, 0); - ieee80211_offchannel_return(local, - 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); + local->tmp_channel_type = wk->chan_type; + + ieee80211_hw_config(local, 0); started = true; wk->timeout = jiffies; @@ -1082,34 +986,17 @@ static void ieee80211_work_work(struct work_struct *work) list_for_each_entry(wk, &local->work_list, list) { if (!wk->started) continue; - if (wk->chan != local->tmp_channel) - continue; - if (!ieee80211_work_ct_coexists(wk->chan_type, - local->tmp_channel_type)) + if (wk->chan != local->tmp_channel || + wk->chan_type != local->tmp_channel_type) continue; remain_off_channel = true; } if (!remain_off_channel && local->tmp_channel) { local->tmp_channel = NULL; - /* If tmp_channel wasn't operating channel, then - * we need to go back on-channel. - * NOTE: If we can ever be here while scannning, - * or if the hw_config() channel config logic changes, - * then we may need to do a more thorough check to see if - * we still need to do a hardware config. Currently, - * we cannot be here while scanning, however. - */ - if (!ieee80211_cfg_on_oper_channel(local)) - ieee80211_hw_config(local, 0); + ieee80211_hw_config(local, 0); - /* At the least, we need to disable offchannel_ps, - * so just go ahead and run the entire offchannel - * return logic here. We *could* skip enabling - * beaconing if we were already on-oper-channel - * as a future optimization. - */ - ieee80211_offchannel_return(local, true, true); + ieee80211_offchannel_return(local, true); /* give connection some time to breathe */ run_again(local, jiffies + HZ/2); diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index f614ce7bb6e..93aab0715e8 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/netfilter/core.c b/net/netfilter/core.c index 3346829ea07..afca6c78948 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -180,17 +180,16 @@ next_hook: if (ret == 0) ret = -EPERM; } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { - ret = nf_queue(skb, elem, pf, hook, indev, outdev, okfn, - verdict >> NF_VERDICT_QBITS); - if (ret < 0) { - if (ret == -ECANCELED) + int err = nf_queue(skb, elem, pf, hook, indev, outdev, okfn, + verdict >> NF_VERDICT_QBITS); + if (err < 0) { + if (err == -ECANCELED) goto next_hook; - if (ret == -ESRCH && + if (err == -ESRCH && (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) goto next_hook; kfree_skb(skb); } - ret = 0; } rcu_read_unlock(); return ret; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index d7e86ef9d23..86137b558f4 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1699,10 +1699,8 @@ ip_set_init(void) ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL); - if (!ip_set_list) { - pr_err("ip_set: Unable to create ip_set_list\n"); + if (!ip_set_list) return -ENOMEM; - } ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); if (ret != 0) { diff --git a/net/netfilter/ipset/ip_set_getport.c b/net/netfilter/ipset/ip_set_getport.c index 757143b2240..052579fe389 100644 --- a/net/netfilter/ipset/ip_set_getport.c +++ b/net/netfilter/ipset/ip_set_getport.c @@ -17,6 +17,7 @@ #include <net/ipv6.h> #include <linux/netfilter/ipset/ip_set_getport.h> +#include <linux/export.h> /* We must handle non-linear skbs */ static bool diff --git a/net/netfilter/ipset/pfxlen.c b/net/netfilter/ipset/pfxlen.c index bd13d66220f..4f29fa97044 100644 --- a/net/netfilter/ipset/pfxlen.c +++ b/net/netfilter/ipset/pfxlen.c @@ -1,3 +1,4 @@ +#include <linux/export.h> #include <linux/netfilter/ipset/pfxlen.h> /* diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 4f77bb16d22..093cc327020 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -188,14 +188,13 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) } -static inline int +static inline void ip_vs_set_state(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd) { - if (unlikely(!pd->pp->state_transition)) - return 0; - return pd->pp->state_transition(cp, direction, skb, pd); + if (likely(pd->pp->state_transition)) + pd->pp->state_transition(cp, direction, skb, pd); } static inline int @@ -530,7 +529,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, a cache_bypass connection entry */ ipvs = net_ipvs(net); if (ipvs->sysctl_cache_bypass && svc->fwmark && unicast) { - int ret, cs; + int ret; struct ip_vs_conn *cp; unsigned int flags = (svc->flags & IP_VS_SVC_F_ONEPACKET && iph.protocol == IPPROTO_UDP)? @@ -557,7 +556,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ip_vs_in_stats(cp, skb); /* set state */ - cs = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); /* transmit the first SYN packet */ ret = cp->packet_xmit(skb, cp, pd->pp); @@ -1490,7 +1489,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) struct ip_vs_protocol *pp; struct ip_vs_proto_data *pd; struct ip_vs_conn *cp; - int ret, restart, pkts; + int ret, pkts; struct netns_ipvs *ipvs; /* Already marked as IPVS request or reply? */ @@ -1591,7 +1590,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) } ip_vs_in_stats(cp, skb); - restart = ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); + ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd); if (cp->packet_xmit) ret = cp->packet_xmit(skb, cp, pp); /* do not touch skb anymore */ @@ -1878,10 +1877,9 @@ static int __net_init __ip_vs_init(struct net *net) struct netns_ipvs *ipvs; ipvs = net_generic(net, ip_vs_net_id); - if (ipvs == NULL) { - pr_err("%s(): no memory.\n", __func__); + if (ipvs == NULL) return -ENOMEM; - } + /* Hold the beast until a service is registerd */ ipvs->enable = 0; ipvs->net = net; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index e3be48bf4dc..008bf97cc91 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -856,15 +856,12 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, } dest = kzalloc(sizeof(struct ip_vs_dest), GFP_KERNEL); - if (dest == NULL) { - pr_err("%s(): no memory.\n", __func__); + if (dest == NULL) return -ENOMEM; - } + dest->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!dest->stats.cpustats) { - pr_err("%s() alloc_percpu failed\n", __func__); + if (!dest->stats.cpustats) goto err_alloc; - } dest->af = svc->af; dest->protocol = svc->protocol; @@ -1168,10 +1165,8 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u, goto out_err; } svc->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!svc->stats.cpustats) { - pr_err("%s() alloc_percpu failed\n", __func__); + if (!svc->stats.cpustats) goto out_err; - } /* I'm the first user of the service */ atomic_set(&svc->usecnt, 0); @@ -3326,10 +3321,8 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) int ret = 0, cmd; int need_full_svc = 0, need_full_dest = 0; struct net *net; - struct netns_ipvs *ipvs; net = skb_sknet(skb); - ipvs = net_ipvs(net); cmd = info->genlhdr->cmd; mutex_lock(&__ip_vs_mutex); @@ -3421,10 +3414,8 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) void *reply; int ret, cmd, reply_cmd; struct net *net; - struct netns_ipvs *ipvs; net = skb_sknet(skb); - ipvs = net_ipvs(net); cmd = info->genlhdr->cmd; if (cmd == IPVS_CMD_GET_SERVICE) @@ -3720,10 +3711,9 @@ int __net_init ip_vs_control_net_init(struct net *net) /* procfs stats */ ipvs->tot_stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); - if (!ipvs->tot_stats.cpustats) { - pr_err("%s(): alloc_percpu.\n", __func__); + if (!ipvs->tot_stats.cpustats) return -ENOMEM; - } + spin_lock_init(&ipvs->tot_stats.lock); proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops); diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index 95fd0d14200..1c269e56200 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -150,10 +150,9 @@ static int ip_vs_dh_init_svc(struct ip_vs_service *svc) /* allocate the DH table for this service */ tbl = kmalloc(sizeof(struct ip_vs_dh_bucket)*IP_VS_DH_TAB_SIZE, GFP_ATOMIC); - if (tbl == NULL) { - pr_err("%s(): no memory\n", __func__); + if (tbl == NULL) return -ENOMEM; - } + svc->sched_data = tbl; IP_VS_DBG(6, "DH hash table (memory=%Zdbytes) allocated for " "current service\n", diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 4490a32ad5b..538d74ee4f6 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -52,8 +52,9 @@ * List of ports (up to IP_VS_APP_MAX_PORTS) to be handled by helper * First port is set to the default port. */ +static unsigned int ports_count = 1; static unsigned short ports[IP_VS_APP_MAX_PORTS] = {21, 0}; -module_param_array(ports, ushort, NULL, 0); +module_param_array(ports, ushort, &ports_count, 0444); MODULE_PARM_DESC(ports, "Ports to monitor for FTP control commands"); @@ -449,7 +450,7 @@ static int __net_init __ip_vs_ftp_init(struct net *net) if (ret) goto err_exit; - for (i=0; i<IP_VS_APP_MAX_PORTS; i++) { + for (i = 0; i < ports_count; i++) { if (!ports[i]) continue; ret = register_ip_vs_app_inc(net, app, app->protocol, ports[i]); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 87e40ea77a9..0f16283fd05 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -202,10 +202,8 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr, en = ip_vs_lblc_get(dest->af, tbl, daddr); if (!en) { en = kmalloc(sizeof(*en), GFP_ATOMIC); - if (!en) { - pr_err("%s(): no memory\n", __func__); + if (!en) return NULL; - } en->af = dest->af; ip_vs_addr_copy(dest->af, &en->addr, daddr); @@ -345,10 +343,9 @@ static int ip_vs_lblc_init_svc(struct ip_vs_service *svc) * Allocate the ip_vs_lblc_table for this service */ tbl = kmalloc(sizeof(*tbl), GFP_ATOMIC); - if (tbl == NULL) { - pr_err("%s(): no memory\n", __func__); + if (tbl == NULL) return -ENOMEM; - } + svc->sched_data = tbl; IP_VS_DBG(6, "LBLC hash table (memory=%Zdbytes) allocated for " "current service\n", sizeof(*tbl)); diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 90f618ab6dd..eec797f8cce 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -112,10 +112,8 @@ ip_vs_dest_set_insert(struct ip_vs_dest_set *set, struct ip_vs_dest *dest) } e = kmalloc(sizeof(*e), GFP_ATOMIC); - if (e == NULL) { - pr_err("%s(): no memory\n", __func__); + if (e == NULL) return NULL; - } atomic_inc(&dest->refcnt); e->dest = dest; @@ -373,10 +371,8 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr, en = ip_vs_lblcr_get(dest->af, tbl, daddr); if (!en) { en = kmalloc(sizeof(*en), GFP_ATOMIC); - if (!en) { - pr_err("%s(): no memory\n", __func__); + if (!en) return NULL; - } en->af = dest->af; ip_vs_addr_copy(dest->af, &en->addr, daddr); @@ -516,10 +512,9 @@ static int ip_vs_lblcr_init_svc(struct ip_vs_service *svc) * Allocate the ip_vs_lblcr_table for this service */ tbl = kmalloc(sizeof(*tbl), GFP_ATOMIC); - if (tbl == NULL) { - pr_err("%s(): no memory\n", __func__); + if (tbl == NULL) return -ENOMEM; - } + svc->sched_data = tbl; IP_VS_DBG(6, "LBLCR hash table (memory=%Zdbytes) allocated for " "current service\n", sizeof(*tbl)); diff --git a/net/netfilter/ipvs/ip_vs_nfct.c b/net/netfilter/ipvs/ip_vs_nfct.c index f454c80df0a..022e77e1e76 100644 --- a/net/netfilter/ipvs/ip_vs_nfct.c +++ b/net/netfilter/ipvs/ip_vs_nfct.c @@ -127,7 +127,7 @@ ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp, int outin) nf_conntrack_alter_reply(ct, &new_tuple); } -int ip_vs_confirm_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp) +int ip_vs_confirm_conntrack(struct sk_buff *skb) { return nf_conntrack_confirm(skb); } diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 52d073c105e..85312939695 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -74,10 +74,9 @@ register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp) struct ip_vs_proto_data *pd = kzalloc(sizeof(struct ip_vs_proto_data), GFP_ATOMIC); - if (!pd) { - pr_err("%s(): no memory.\n", __func__); + if (!pd) return -ENOMEM; - } + pd->pp = pp; /* For speed issues */ pd->next = ipvs->proto_data_table[hash]; ipvs->proto_data_table[hash] = pd; diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index d12ed53ec95..1fbf7a2816f 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -906,7 +906,7 @@ static const char *sctp_state_name(int state) return "?"; } -static inline int +static inline void set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, int direction, const struct sk_buff *skb) { @@ -924,7 +924,7 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, sch = skb_header_pointer(skb, ihl + sizeof(sctp_sctphdr_t), sizeof(_sctpch), &_sctpch); if (sch == NULL) - return 0; + return; chunk_type = sch->type; /* @@ -993,21 +993,15 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, cp->timeout = pd->timeout_table[cp->state = next_state]; else /* What to do ? */ cp->timeout = sctp_timeouts[cp->state = next_state]; - - return 1; } -static int +static void sctp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd) { - int ret = 0; - spin_lock(&cp->lock); - ret = set_sctp_state(pd, cp, direction, skb); + set_sctp_state(pd, cp, direction, skb); spin_unlock(&cp->lock); - - return ret; } static inline __u16 sctp_app_hashkey(__be16 port) diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index c0cc341b840..ef8641f7af8 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -546,7 +546,7 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, /* * Handle state transitions */ -static int +static void tcp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd) @@ -561,13 +561,11 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction, th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph); if (th == NULL) - return 0; + return; spin_lock(&cp->lock); set_tcp_state(pd, cp, direction, th); spin_unlock(&cp->lock); - - return 1; } static inline __u16 tcp_app_hashkey(__be16 port) diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index f1282cbe6fe..f4b7262896b 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -454,18 +454,17 @@ static const char * udp_state_name(int state) return udp_state_name_table[state] ? udp_state_name_table[state] : "?"; } -static int +static void udp_state_transition(struct ip_vs_conn *cp, int direction, const struct sk_buff *skb, struct ip_vs_proto_data *pd) { if (unlikely(!pd)) { pr_err("UDP no ns data\n"); - return 0; + return; } cp->timeout = pd->timeout_table[IP_VS_UDP_S_NORMAL]; - return 1; } static void __udp_init(struct net *net, struct ip_vs_proto_data *pd) diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index b5e2556c581..33815f4fb45 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -147,10 +147,9 @@ static int ip_vs_sh_init_svc(struct ip_vs_service *svc) /* allocate the SH table for this service */ tbl = kmalloc(sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE, GFP_ATOMIC); - if (tbl == NULL) { - pr_err("%s(): no memory\n", __func__); + if (tbl == NULL) return -ENOMEM; - } + svc->sched_data = tbl; IP_VS_DBG(6, "SH hash table (memory=%Zdbytes) allocated for " "current service\n", diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index 1ef41f50723..fd0d4e09876 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -85,10 +85,9 @@ static int ip_vs_wrr_init_svc(struct ip_vs_service *svc) * Allocate the mark variable for WRR scheduling */ mark = kmalloc(sizeof(struct ip_vs_wrr_mark), GFP_ATOMIC); - if (mark == NULL) { - pr_err("%s(): no memory\n", __func__); + if (mark == NULL) return -ENOMEM; - } + mark->cl = &svc->destinations; mark->cw = 0; mark->mw = ip_vs_wrr_max_weight(svc); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index ee319a4338b..aa2d7206ee8 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -339,7 +339,7 @@ ip_vs_dst_reset(struct ip_vs_dest *dest) \ (skb)->ipvs_property = 1; \ if (unlikely((cp)->flags & IP_VS_CONN_F_NFCT)) \ - __ret = ip_vs_confirm_conntrack(skb, cp); \ + __ret = ip_vs_confirm_conntrack(skb); \ if (__ret == NF_ACCEPT) { \ nf_reset(skb); \ skb_forward_csum(skb); \ diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c index 5178c691ecb..369df3f08d4 100644 --- a/net/netfilter/nf_conntrack_acct.c +++ b/net/netfilter/nf_conntrack_acct.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/kernel.h> #include <linux/moduleparam.h> +#include <linux/export.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_extend.h> diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 5acfaf59a9c..7202b0631cd 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -661,7 +661,6 @@ __nf_conntrack_alloc(struct net *net, u16 zone, */ ct = kmem_cache_alloc(net->ct.nf_conntrack_cachep, gfp); if (ct == NULL) { - pr_debug("nf_conntrack_alloc: Can't alloc conntrack.\n"); atomic_dec(&net->ct.count); return ERR_PTR(-ENOMEM); } @@ -749,10 +748,8 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, ct = __nf_conntrack_alloc(net, zone, tuple, &repl_tuple, GFP_ATOMIC, hash); - if (IS_ERR(ct)) { - pr_debug("Can't allocate conntrack.\n"); + if (IS_ERR(ct)) return (struct nf_conntrack_tuple_hash *)ct; - } if (!l4proto->new(ct, skb, dataoff)) { nf_conntrack_free(ct); diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 3add9943905..6b368be937c 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_core.h> diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index cd1e8e0970f..340c80d968d 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -20,6 +20,8 @@ #include <linux/percpu.h> #include <linux/kernel.h> #include <linux/jhash.h> +#include <linux/moduleparam.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/netfilter/nf_conntrack.h> diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 2d8158acf6f..66b2c54c544 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -307,17 +307,14 @@ nfulnl_alloc_skb(unsigned int inst_size, unsigned int pkt_size) n = max(inst_size, pkt_size); skb = alloc_skb(n, GFP_ATOMIC); if (!skb) { - pr_notice("nfnetlink_log: can't alloc whole buffer (%u bytes)\n", - inst_size); - if (n > pkt_size) { /* try to allocate only as much as we need for current * packet */ skb = alloc_skb(pkt_size, GFP_ATOMIC); if (!skb) - pr_err("nfnetlink_log: can't even alloc %u " - "bytes\n", pkt_size); + pr_err("nfnetlink_log: can't even alloc %u bytes\n", + pkt_size); } } diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 71441b934ff..8d987c3573f 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -14,6 +14,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> +#include <linux/module.h> #include <linux/socket.h> #include <linux/net.h> #include <linux/proc_fs.h> diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 3bdd443aaf1..f407ebc1348 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -122,14 +122,12 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) info->timer = kmalloc(sizeof(*info->timer), GFP_KERNEL); if (!info->timer) { - pr_debug("couldn't alloc timer\n"); ret = -ENOMEM; goto out; } info->timer->attr.attr.name = kstrdup(info->label, GFP_KERNEL); if (!info->timer->attr.attr.name) { - pr_debug("couldn't alloc attribute name\n"); ret = -ENOMEM; goto out_free_timer; } diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 9228ee0dc11..dfd52bad152 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -176,10 +176,7 @@ dsthash_alloc_init(struct xt_hashlimit_htable *ht, ent = NULL; } else ent = kmem_cache_alloc(hashlimit_cachep, GFP_ATOMIC); - if (!ent) { - if (net_ratelimit()) - pr_err("cannot allocate dsthash_ent\n"); - } else { + if (ent) { memcpy(&ent->dst, dst, sizeof(ent->dst)); spin_lock_init(&ent->lock); diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index 70eb2b4984d..44c8eb4c9d6 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c @@ -9,6 +9,7 @@ #include <linux/netfilter/x_tables.h> #include <linux/netfilter/xt_quota.h> +#include <linux/module.h> struct xt_quota_priv { spinlock_t lock; diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index 42ecb71d445..4fe4fb4276d 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c @@ -16,6 +16,7 @@ #include <linux/netfilter/xt_statistic.h> #include <linux/netfilter/x_tables.h> +#include <linux/module.h> struct xt_statistic_priv { atomic_t count; diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index cd5ddb2ebc4..915a87ba23e 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -37,6 +37,7 @@ #include <linux/spinlock.h> #include <net/netrom.h> #include <linux/seq_file.h> +#include <linux/export.h> static unsigned int nr_neigh_no = 1; diff --git a/net/nfc/af_nfc.c b/net/nfc/af_nfc.c index e982cef8f49..da67756425c 100644 --- a/net/nfc/af_nfc.c +++ b/net/nfc/af_nfc.c @@ -22,6 +22,7 @@ */ #include <linux/nfc.h> +#include <linux/module.h> #include "nfc.h" diff --git a/net/nfc/core.c b/net/nfc/core.c index 47e02c1b8c0..3ebc6b3aaba 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -21,6 +21,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> @@ -33,25 +35,6 @@ int nfc_devlist_generation; DEFINE_MUTEX(nfc_devlist_mutex); -int nfc_printk(const char *level, const char *format, ...) -{ - struct va_format vaf; - va_list args; - int r; - - va_start(args, format); - - vaf.fmt = format; - vaf.va = &args; - - r = printk("%sNFC: %pV\n", level, &vaf); - - va_end(args); - - return r; -} -EXPORT_SYMBOL(nfc_printk); - /** * nfc_dev_up - turn on the NFC device * @@ -63,7 +46,7 @@ int nfc_dev_up(struct nfc_dev *dev) { int rc = 0; - nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + pr_debug("dev_name=%s\n", dev_name(&dev->dev)); device_lock(&dev->dev); @@ -97,7 +80,7 @@ int nfc_dev_down(struct nfc_dev *dev) { int rc = 0; - nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + pr_debug("dev_name=%s\n", dev_name(&dev->dev)); device_lock(&dev->dev); @@ -139,7 +122,8 @@ int nfc_start_poll(struct nfc_dev *dev, u32 protocols) { int rc; - nfc_dbg("dev_name=%s protocols=0x%x", dev_name(&dev->dev), protocols); + pr_debug("dev_name=%s protocols=0x%x\n", + dev_name(&dev->dev), protocols); if (!protocols) return -EINVAL; @@ -174,7 +158,7 @@ int nfc_stop_poll(struct nfc_dev *dev) { int rc = 0; - nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + pr_debug("dev_name=%s\n", dev_name(&dev->dev)); device_lock(&dev->dev); @@ -207,8 +191,8 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol) { int rc; - nfc_dbg("dev_name=%s target_idx=%u protocol=%u", dev_name(&dev->dev), - target_idx, protocol); + pr_debug("dev_name=%s target_idx=%u protocol=%u\n", + dev_name(&dev->dev), target_idx, protocol); device_lock(&dev->dev); @@ -236,7 +220,8 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx) { int rc = 0; - nfc_dbg("dev_name=%s target_idx=%u", dev_name(&dev->dev), target_idx); + pr_debug("dev_name=%s target_idx=%u\n", + dev_name(&dev->dev), target_idx); device_lock(&dev->dev); @@ -271,8 +256,8 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, { int rc; - nfc_dbg("dev_name=%s target_idx=%u skb->len=%u", dev_name(&dev->dev), - target_idx, skb->len); + pr_debug("dev_name=%s target_idx=%u skb->len=%u\n", + dev_name(&dev->dev), target_idx, skb->len); device_lock(&dev->dev); @@ -326,7 +311,7 @@ int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, { int i; - nfc_dbg("dev_name=%s n_targets=%d", dev_name(&dev->dev), n_targets); + pr_debug("dev_name=%s n_targets=%d\n", dev_name(&dev->dev), n_targets); dev->polling = false; @@ -360,7 +345,7 @@ static void nfc_release(struct device *d) { struct nfc_dev *dev = to_nfc_dev(d); - nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + pr_debug("dev_name=%s\n", dev_name(&dev->dev)); nfc_genl_data_exit(&dev->genl_data); kfree(dev->targets); @@ -446,7 +431,7 @@ int nfc_register_device(struct nfc_dev *dev) { int rc; - nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + pr_debug("dev_name=%s\n", dev_name(&dev->dev)); mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; @@ -458,9 +443,8 @@ int nfc_register_device(struct nfc_dev *dev) rc = nfc_genl_device_added(dev); if (rc) - nfc_dbg("The userspace won't be notified that the device %s was" - " added", dev_name(&dev->dev)); - + pr_debug("The userspace won't be notified that the device %s was added\n", + dev_name(&dev->dev)); return 0; } @@ -475,7 +459,7 @@ void nfc_unregister_device(struct nfc_dev *dev) { int rc; - nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + pr_debug("dev_name=%s\n", dev_name(&dev->dev)); mutex_lock(&nfc_devlist_mutex); nfc_devlist_generation++; @@ -490,8 +474,8 @@ void nfc_unregister_device(struct nfc_dev *dev) rc = nfc_genl_device_removed(dev); if (rc) - nfc_dbg("The userspace won't be notified that the device %s" - " was removed", dev_name(&dev->dev)); + pr_debug("The userspace won't be notified that the device %s was removed\n", + dev_name(&dev->dev)); } EXPORT_SYMBOL(nfc_unregister_device); @@ -500,7 +484,7 @@ static int __init nfc_init(void) { int rc; - nfc_info("NFC Core ver %s", VERSION); + pr_info("NFC Core ver %s\n", VERSION); rc = class_register(&nfc_class); if (rc) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 4047e29acb3..37de28e9733 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -25,9 +25,12 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/types.h> #include <linux/workqueue.h> #include <linux/completion.h> +#include <linux/export.h> #include <linux/sched.h> #include <linux/bitops.h> #include <linux/skbuff.h> @@ -78,7 +81,7 @@ static int __nci_request(struct nci_dev *ndev, &ndev->req_completion, timeout); - nfc_dbg("wait_for_completion return %ld", completion_rc); + pr_debug("wait_for_completion return %ld\n", completion_rc); if (completion_rc > 0) { switch (ndev->req_status) { @@ -95,8 +98,8 @@ static int __nci_request(struct nci_dev *ndev, break; } } else { - nfc_err("wait_for_completion_interruptible_timeout failed %ld", - completion_rc); + pr_err("wait_for_completion_interruptible_timeout failed %ld\n", + completion_rc); rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); } @@ -125,7 +128,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) @@ -135,17 +141,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; @@ -325,8 +325,6 @@ static void nci_cmd_timer(unsigned long arg) { struct nci_dev *ndev = (void *) arg; - nfc_dbg("entry"); - atomic_set(&ndev->cmd_cnt, 1); queue_work(ndev->cmd_wq, &ndev->cmd_work); } @@ -335,8 +333,6 @@ static int nci_dev_up(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - nfc_dbg("entry"); - return nci_open_device(ndev); } @@ -344,8 +340,6 @@ static int nci_dev_down(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - nfc_dbg("entry"); - return nci_close_device(ndev); } @@ -354,20 +348,18 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); int rc; - nfc_dbg("entry"); - if (test_bit(NCI_DISCOVERY, &ndev->flags)) { - nfc_err("unable to start poll, since poll is already active"); + pr_err("unable to start poll, since poll is already active\n"); return -EBUSY; } if (ndev->target_active_prot) { - nfc_err("there is an active target"); + pr_err("there is an active target\n"); return -EBUSY; } if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { - nfc_dbg("target is active, implicitly deactivate..."); + pr_debug("target is active, implicitly deactivate...\n"); rc = nci_request(ndev, nci_rf_deactivate_req, 0, msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); @@ -388,10 +380,8 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - nfc_dbg("entry"); - if (!test_bit(NCI_DISCOVERY, &ndev->flags)) { - nfc_err("unable to stop poll, since poll is not active"); + pr_err("unable to stop poll, since poll is not active\n"); return; } @@ -404,21 +394,21 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - nfc_dbg("entry, target_idx %d, protocol 0x%x", target_idx, protocol); + pr_debug("target_idx %d, protocol 0x%x\n", target_idx, protocol); if (!test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { - nfc_err("there is no available target to activate"); + pr_err("there is no available target to activate\n"); return -EINVAL; } if (ndev->target_active_prot) { - nfc_err("there is already an active target"); + pr_err("there is already an active target\n"); return -EBUSY; } if (!(ndev->target_available_prots & (1 << protocol))) { - nfc_err("target does not support the requested protocol 0x%x", - protocol); + pr_err("target does not support the requested protocol 0x%x\n", + protocol); return -EINVAL; } @@ -432,10 +422,10 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); - nfc_dbg("entry, target_idx %d", target_idx); + pr_debug("target_idx %d\n", target_idx); if (!ndev->target_active_prot) { - nfc_err("unable to deactivate target, no active target"); + pr_err("unable to deactivate target, no active target\n"); return; } @@ -455,10 +445,10 @@ static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx, struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); int rc; - nfc_dbg("entry, target_idx %d, len %d", target_idx, skb->len); + pr_debug("target_idx %d, len %d\n", target_idx, skb->len); if (!ndev->target_active_prot) { - nfc_err("unable to exchange data, no active target"); + pr_err("unable to exchange data, no active target\n"); return -EINVAL; } @@ -469,7 +459,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); @@ -501,7 +491,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops, { struct nci_dev *ndev; - nfc_dbg("entry, supported_protocols 0x%x", supported_protocols); + pr_debug("supported_protocols 0x%x\n", supported_protocols); if (!ops->open || !ops->close || !ops->send) return NULL; @@ -541,8 +531,6 @@ EXPORT_SYMBOL(nci_allocate_device); */ void nci_free_device(struct nci_dev *ndev) { - nfc_dbg("entry"); - nfc_free_device(ndev->nfc_dev); kfree(ndev); } @@ -559,8 +547,6 @@ int nci_register_device(struct nci_dev *ndev) struct device *dev = &ndev->nfc_dev->dev; char name[32]; - nfc_dbg("entry"); - rc = nfc_register_device(ndev->nfc_dev); if (rc) goto exit; @@ -623,8 +609,6 @@ EXPORT_SYMBOL(nci_register_device); */ void nci_unregister_device(struct nci_dev *ndev) { - nfc_dbg("entry"); - nci_close_device(ndev); destroy_workqueue(ndev->cmd_wq); @@ -644,7 +628,7 @@ int nci_recv_frame(struct sk_buff *skb) { struct nci_dev *ndev = (struct nci_dev *) skb->dev; - nfc_dbg("entry, len %d", skb->len); + pr_debug("len %d\n", skb->len); if (!ndev || (!test_bit(NCI_UP, &ndev->flags) && !test_bit(NCI_INIT, &ndev->flags))) { @@ -664,7 +648,7 @@ static int nci_send_frame(struct sk_buff *skb) { struct nci_dev *ndev = (struct nci_dev *) skb->dev; - nfc_dbg("entry, len %d", skb->len); + pr_debug("len %d\n", skb->len); if (!ndev) { kfree_skb(skb); @@ -683,11 +667,11 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) struct nci_ctrl_hdr *hdr; struct sk_buff *skb; - nfc_dbg("entry, opcode 0x%x, plen %d", opcode, plen); + pr_debug("opcode 0x%x, plen %d\n", opcode, plen); skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); if (!skb) { - nfc_err("no memory for command"); + pr_err("no memory for command\n"); return -ENOMEM; } @@ -717,7 +701,7 @@ static void nci_tx_work(struct work_struct *work) struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); struct sk_buff *skb; - nfc_dbg("entry, credits_cnt %d", atomic_read(&ndev->credits_cnt)); + pr_debug("credits_cnt %d\n", atomic_read(&ndev->credits_cnt)); /* Send queued tx data */ while (atomic_read(&ndev->credits_cnt)) { @@ -725,12 +709,15 @@ 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), - nci_conn_id(skb->data), - nci_plen(skb->data)); + pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", + nci_pbf(skb->data), + nci_conn_id(skb->data), + nci_plen(skb->data)); nci_send_frame(skb); } @@ -759,7 +746,7 @@ static void nci_rx_work(struct work_struct *work) break; default: - nfc_err("unknown MT 0x%x", nci_mt(skb->data)); + pr_err("unknown MT 0x%x\n", nci_mt(skb->data)); kfree_skb(skb); break; } @@ -773,7 +760,7 @@ static void nci_cmd_work(struct work_struct *work) struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); struct sk_buff *skb; - nfc_dbg("entry, cmd_cnt %d", atomic_read(&ndev->cmd_cnt)); + pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt)); /* Send queued command */ if (atomic_read(&ndev->cmd_cnt)) { @@ -783,11 +770,11 @@ static void nci_cmd_work(struct work_struct *work) atomic_dec(&ndev->cmd_cnt); - nfc_dbg("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", - nci_pbf(skb->data), - nci_opcode_gid(nci_opcode(skb->data)), - nci_opcode_oid(nci_opcode(skb->data)), - nci_plen(skb->data)); + pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", + nci_pbf(skb->data), + nci_opcode_gid(nci_opcode(skb->data)), + nci_opcode_oid(nci_opcode(skb->data)), + nci_plen(skb->data)); nci_send_frame(skb); diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index e5ed90fc1a9..1e040feb2f8 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -21,6 +21,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/types.h> #include <linux/interrupt.h> #include <linux/wait.h> @@ -40,7 +42,7 @@ void nci_data_exchange_complete(struct nci_dev *ndev, data_exchange_cb_t cb = ndev->data_exchange_cb; void *cb_context = ndev->data_exchange_cb_context; - nfc_dbg("entry, len %d, err %d", ((skb) ? (skb->len) : (0)), err); + pr_debug("len %d, err %d\n", skb ? skb->len : 0, err); if (cb) { ndev->data_exchange_cb = NULL; @@ -49,7 +51,7 @@ void nci_data_exchange_complete(struct nci_dev *ndev, /* forward skb to nfc core */ cb(cb_context, skb, err); } else if (skb) { - nfc_err("no rx callback, dropping rx data..."); + pr_err("no rx callback, dropping rx data...\n"); /* no waiting callback, free skb */ kfree_skb(skb); @@ -90,12 +92,13 @@ static int nci_queue_tx_data_frags(struct nci_dev *ndev, int frag_len; int rc = 0; - nfc_dbg("entry, conn_id 0x%x, total_len %d", conn_id, total_len); + pr_debug("conn_id 0x%x, total_len %d\n", conn_id, total_len); __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), @@ -118,8 +121,8 @@ static int nci_queue_tx_data_frags(struct nci_dev *ndev, data += frag_len; total_len -= frag_len; - nfc_dbg("frag_len %d, remaining total_len %d", - frag_len, total_len); + pr_debug("frag_len %d, remaining total_len %d\n", + frag_len, total_len); } /* queue all fragments atomically */ @@ -148,10 +151,10 @@ int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb) { int rc = 0; - nfc_dbg("entry, conn_id 0x%x, plen %d", conn_id, skb->len); + pr_debug("conn_id 0x%x, plen %d\n", 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); @@ -160,7 +163,7 @@ int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb) /* fragment packet and queue the fragments */ rc = nci_queue_tx_data_frags(ndev, conn_id, skb); if (rc) { - nfc_err("failed to fragment tx data packet"); + pr_err("failed to fragment tx data packet\n"); goto free_exit; } } @@ -190,7 +193,7 @@ static void nci_add_rx_data_frag(struct nci_dev *ndev, /* first, make enough room for the already accumulated data */ if (skb_cow_head(skb, reassembly_len)) { - nfc_err("error adding room for accumulated rx data"); + pr_err("error adding room for accumulated rx data\n"); kfree_skb(skb); skb = 0; @@ -227,19 +230,19 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u8 pbf = nci_pbf(skb->data); - nfc_dbg("entry, len %d", skb->len); + pr_debug("len %d\n", skb->len); - nfc_dbg("NCI RX: MT=data, PBF=%d, conn_id=%d, plen=%d", - nci_pbf(skb->data), - nci_conn_id(skb->data), - nci_plen(skb->data)); + pr_debug("NCI RX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", + nci_pbf(skb->data), + nci_conn_id(skb->data), + nci_plen(skb->data)); /* strip the nci data header */ skb_pull(skb, NCI_DATA_HDR_SIZE); if (ndev->target_active_prot == NFC_PROTO_MIFARE) { /* frame I/F => remove the status byte */ - nfc_dbg("NFC_PROTO_MIFARE => remove the status byte"); + pr_debug("NFC_PROTO_MIFARE => remove the status byte\n"); skb_trim(skb, (skb->len - 1)); } diff --git a/net/nfc/nci/lib.c b/net/nfc/nci/lib.c index b19dc2fa90e..e99adcfb1bc 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 96633f5cda4..c36bd4a09ab 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -25,6 +25,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/types.h> #include <linux/interrupt.h> #include <linux/bitops.h> @@ -43,18 +45,18 @@ static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, struct nci_core_conn_credit_ntf *ntf = (void *) skb->data; int i; - nfc_dbg("entry, num_entries %d", ntf->num_entries); + pr_debug("num_entries %d\n", ntf->num_entries); if (ntf->num_entries > NCI_MAX_NUM_CONN) ntf->num_entries = NCI_MAX_NUM_CONN; /* update the credits */ for (i = 0; i < ntf->num_entries; i++) { - nfc_dbg("entry[%d]: conn_id %d, credits %d", i, - ntf->conn_entries[i].conn_id, - ntf->conn_entries[i].credits); + pr_debug("entry[%d]: conn_id %d, credits %d\n", + i, 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,31 +68,20 @@ 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; nfca_poll->nfcid1_len = *data++; - nfc_dbg("sens_res 0x%x, nfcid1_len %d", - nfca_poll->sens_res, - nfca_poll->nfcid1_len); + pr_debug("sens_res 0x%x, nfcid1_len %d\n", + nfca_poll->sens_res, nfca_poll->nfcid1_len); memcpy(nfca_poll->nfcid1, data, nfca_poll->nfcid1_len); data += nfca_poll->nfcid1_len; @@ -100,32 +91,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++; + pr_debug("sel_res_len %d, sel_res 0x%x\n", + nfca_poll->sel_res_len, + nfca_poll->sel_res); - nfc_dbg("sel_res_len %d, sel_res 0x%x, rf_interface_type %d, activation_params_len %d", - nfca_poll->sel_res_len, - nfca_poll->sel_res, - ntf->rf_interface_type, - ntf->activation_params_len); + return data; +} - 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, +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); + pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", + ntf->activation_rf_tech_and_mode); return -EPROTO; } @@ -133,7 +124,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,66 +132,105 @@ 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; if (!(nfc_tgt.supported_protocols & ndev->poll_prots)) { - nfc_dbg("the target found does not have the desired protocol"); + pr_debug("the target found does not have the desired protocol\n"); return; } - nfc_dbg("new target found, supported_protocols 0x%x", - nfc_tgt.supported_protocols); + pr_debug("new target found, supported_protocols 0x%x\n", + nfc_tgt.supported_protocols); ndev->target_available_prots = nfc_tgt.supported_protocols; 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, - 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; + pr_debug("rf_discovery_id %d\n", ntf.rf_discovery_id); + pr_debug("rf_interface_type 0x%x\n", ntf.rf_interface_type); + pr_debug("rf_protocol 0x%x\n", ntf.rf_protocol); + pr_debug("activation_rf_tech_and_mode 0x%x\n", + ntf.activation_rf_tech_and_mode); + pr_debug("rf_tech_specific_params_len %d\n", + ntf.rf_tech_specific_params_len); + + 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: + pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", + 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++; + + pr_debug("data_exch_rf_tech_and_mode 0x%x\n", + ntf.data_exch_rf_tech_and_mode); + pr_debug("data_exch_tx_bit_rate 0x%x\n", + ntf.data_exch_tx_bit_rate); + pr_debug("data_exch_rx_bit_rate 0x%x\n", + ntf.data_exch_rx_bit_rate); + pr_debug("activation_params_len %d\n", + 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: + pr_err("unsupported rf_interface_type 0x%x\n", + 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); + pr_debug("entry, type 0x%x, reason 0x%x\n", ntf->type, ntf->reason); clear_bit(NCI_POLL_ACTIVE, &ndev->flags); ndev->target_active_prot = 0; @@ -214,6 +244,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); @@ -223,11 +256,11 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u16 ntf_opcode = nci_opcode(skb->data); - nfc_dbg("NCI RX: MT=ntf, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", - nci_pbf(skb->data), - nci_opcode_gid(ntf_opcode), - nci_opcode_oid(ntf_opcode), - nci_plen(skb->data)); + pr_debug("NCI RX: MT=ntf, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", + nci_pbf(skb->data), + nci_opcode_gid(ntf_opcode), + nci_opcode_oid(ntf_opcode), + nci_plen(skb->data)); /* strip the nci control header */ skb_pull(skb, NCI_CTRL_HDR_SIZE); @@ -237,12 +270,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: @@ -250,7 +279,7 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) break; default: - nfc_err("unknown ntf opcode 0x%x", ntf_opcode); + pr_err("unknown ntf opcode 0x%x\n", ntf_opcode); break; } diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c index 0403d4cd091..ca611c52aaa 100644 --- a/net/nfc/nci/rsp.c +++ b/net/nfc/nci/rsp.c @@ -25,6 +25,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/types.h> #include <linux/interrupt.h> #include <linux/bitops.h> @@ -40,12 +42,13 @@ static void nci_core_reset_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { struct nci_core_reset_rsp *rsp = (void *) skb->data; - nfc_dbg("entry, status 0x%x", rsp->status); + pr_debug("status 0x%x\n", 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); + pr_debug("nci_ver 0x%x, config_status 0x%x\n", + rsp->nci_ver, rsp->config_status); + } nci_req_complete(ndev, rsp->status); } @@ -55,16 +58,16 @@ static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) struct nci_core_init_rsp_1 *rsp_1 = (void *) skb->data; struct nci_core_init_rsp_2 *rsp_2; - nfc_dbg("entry, status 0x%x", rsp_1->status); + pr_debug("status 0x%x\n", 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,68 +76,58 @@ 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); - - nfc_dbg("nfcc_features 0x%x", - ndev->nfcc_features); - nfc_dbg("num_supported_rf_interfaces %d", - ndev->num_supported_rf_interfaces); - nfc_dbg("supported_rf_interfaces[0] 0x%x", - ndev->supported_rf_interfaces[0]); - nfc_dbg("supported_rf_interfaces[1] 0x%x", - ndev->supported_rf_interfaces[1]); - nfc_dbg("supported_rf_interfaces[2] 0x%x", - ndev->supported_rf_interfaces[2]); - nfc_dbg("supported_rf_interfaces[3] 0x%x", - ndev->supported_rf_interfaces[3]); - nfc_dbg("max_logical_connections %d", - 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); - - 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; + 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("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); + pr_debug("nfcc_features 0x%x\n", + ndev->nfcc_features); + pr_debug("num_supported_rf_interfaces %d\n", + ndev->num_supported_rf_interfaces); + pr_debug("supported_rf_interfaces[0] 0x%x\n", + ndev->supported_rf_interfaces[0]); + pr_debug("supported_rf_interfaces[1] 0x%x\n", + ndev->supported_rf_interfaces[1]); + pr_debug("supported_rf_interfaces[2] 0x%x\n", + ndev->supported_rf_interfaces[2]); + pr_debug("supported_rf_interfaces[3] 0x%x\n", + ndev->supported_rf_interfaces[3]); + pr_debug("max_logical_connections %d\n", + ndev->max_logical_connections); + pr_debug("max_routing_table_size %d\n", + ndev->max_routing_table_size); + pr_debug("max_ctrl_pkt_payload_len %d\n", + ndev->max_ctrl_pkt_payload_len); + pr_debug("max_size_for_large_params %d\n", + ndev->max_size_for_large_params); + pr_debug("max_data_pkt_payload_size %d\n", + ndev->max_data_pkt_payload_size); + pr_debug("initial_num_credits %d\n", + ndev->initial_num_credits); + pr_debug("manufact_id 0x%x\n", + ndev->manufact_id); + pr_debug("manufact_specific_info 0x%x\n", + ndev->manufact_specific_info); + +exit: + nci_req_complete(ndev, rsp_1->status); } static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, @@ -142,7 +135,7 @@ static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, { __u8 status = skb->data[0]; - nfc_dbg("entry, status 0x%x", status); + pr_debug("status 0x%x\n", status); nci_req_complete(ndev, status); } @@ -151,7 +144,7 @@ static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u8 status = skb->data[0]; - nfc_dbg("entry, status 0x%x", status); + pr_debug("status 0x%x\n", status); if (status == NCI_STATUS_OK) set_bit(NCI_DISCOVERY, &ndev->flags); @@ -164,7 +157,7 @@ static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, { __u8 status = skb->data[0]; - nfc_dbg("entry, status 0x%x", status); + pr_debug("status 0x%x\n", status); clear_bit(NCI_DISCOVERY, &ndev->flags); @@ -178,11 +171,11 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) /* we got a rsp, stop the cmd timer */ del_timer(&ndev->cmd_timer); - nfc_dbg("NCI RX: MT=rsp, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", - nci_pbf(skb->data), - nci_opcode_gid(rsp_opcode), - nci_opcode_oid(rsp_opcode), - nci_plen(skb->data)); + pr_debug("NCI RX: MT=rsp, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", + nci_pbf(skb->data), + nci_opcode_gid(rsp_opcode), + nci_opcode_oid(rsp_opcode), + nci_plen(skb->data)); /* strip the nci control header */ skb_pull(skb, NCI_CTRL_HDR_SIZE); @@ -196,10 +189,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; @@ -213,7 +202,7 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) break; default: - nfc_err("unknown rsp opcode 0x%x", rsp_opcode); + pr_err("unknown rsp opcode 0x%x\n", rsp_opcode); break; } diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 03f8818e1f1..c10e9b8c5ad 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -21,6 +21,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <net/genetlink.h> #include <linux/nfc.h> #include <linux/slab.h> @@ -51,8 +53,6 @@ static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, { void *hdr; - nfc_dbg("entry"); - hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, &nfc_genl_family, flags, NFC_CMD_GET_TARGET); if (!hdr) @@ -105,8 +105,6 @@ static int nfc_genl_dump_targets(struct sk_buff *skb, struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; int rc; - nfc_dbg("entry"); - if (!dev) { dev = __get_device_from_cb(cb); if (IS_ERR(dev)) @@ -139,8 +137,6 @@ static int nfc_genl_dump_targets_done(struct netlink_callback *cb) { struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; - nfc_dbg("entry"); - if (dev) nfc_put_device(dev); @@ -152,8 +148,6 @@ int nfc_genl_targets_found(struct nfc_dev *dev) struct sk_buff *msg; void *hdr; - nfc_dbg("entry"); - dev->genl_data.poll_req_pid = 0; msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); @@ -183,8 +177,6 @@ int nfc_genl_device_added(struct nfc_dev *dev) struct sk_buff *msg; void *hdr; - nfc_dbg("entry"); - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -216,8 +208,6 @@ int nfc_genl_device_removed(struct nfc_dev *dev) struct sk_buff *msg; void *hdr; - nfc_dbg("entry"); - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -249,8 +239,6 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, { void *hdr; - nfc_dbg("entry"); - hdr = genlmsg_put(msg, pid, seq, &nfc_genl_family, flags, NFC_CMD_GET_DEVICE); if (!hdr) @@ -277,8 +265,6 @@ static int nfc_genl_dump_devices(struct sk_buff *skb, struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; bool first_call = false; - nfc_dbg("entry"); - if (!iter) { first_call = true; iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL); @@ -319,8 +305,6 @@ static int nfc_genl_dump_devices_done(struct netlink_callback *cb) { struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; - nfc_dbg("entry"); - nfc_device_iter_exit(iter); kfree(iter); @@ -334,8 +318,6 @@ static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info) u32 idx; int rc = -ENOBUFS; - nfc_dbg("entry"); - if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) return -EINVAL; @@ -373,8 +355,6 @@ static int nfc_genl_dev_up(struct sk_buff *skb, struct genl_info *info) int rc; u32 idx; - nfc_dbg("entry"); - if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) return -EINVAL; @@ -396,8 +376,6 @@ static int nfc_genl_dev_down(struct sk_buff *skb, struct genl_info *info) int rc; u32 idx; - nfc_dbg("entry"); - if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) return -EINVAL; @@ -420,8 +398,6 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) u32 idx; u32 protocols; - nfc_dbg("entry"); - if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || !info->attrs[NFC_ATTR_PROTOCOLS]) return -EINVAL; @@ -451,8 +427,6 @@ static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info) int rc; u32 idx; - nfc_dbg("entry"); - if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) return -EINVAL; @@ -524,7 +498,7 @@ static int nfc_genl_rcv_nl_event(struct notifier_block *this, if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC) goto out; - nfc_dbg("NETLINK_URELEASE event from id %d", n->pid); + pr_debug("NETLINK_URELEASE event from id %d\n", n->pid); nfc_device_iter_init(&iter); dev = nfc_device_iter_next(&iter); diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index b6753f45624..67d60501530 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -27,13 +27,6 @@ #include <net/nfc/nfc.h> #include <net/sock.h> -__attribute__((format (printf, 2, 3))) -int nfc_printk(const char *level, const char *fmt, ...); - -#define nfc_info(fmt, arg...) nfc_printk(KERN_INFO, fmt, ##arg) -#define nfc_err(fmt, arg...) nfc_printk(KERN_ERR, fmt, ##arg) -#define nfc_dbg(fmt, arg...) pr_debug(fmt "\n", ##arg) - struct nfc_protocol { int id; struct proto *proto; diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 9fd652a5142..68ecf3fa94e 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -21,14 +21,17 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <net/tcp_states.h> #include <linux/nfc.h> +#include <linux/export.h> #include "nfc.h" static void rawsock_write_queue_purge(struct sock *sk) { - nfc_dbg("sk=%p", sk); + pr_debug("sk=%p\n", sk); spin_lock_bh(&sk->sk_write_queue.lock); __skb_queue_purge(&sk->sk_write_queue); @@ -38,7 +41,7 @@ static void rawsock_write_queue_purge(struct sock *sk) static void rawsock_report_error(struct sock *sk, int err) { - nfc_dbg("sk=%p err=%d", sk, err); + pr_debug("sk=%p err=%d\n", sk, err); sk->sk_shutdown = SHUTDOWN_MASK; sk->sk_err = -err; @@ -51,7 +54,7 @@ static int rawsock_release(struct socket *sock) { struct sock *sk = sock->sk; - nfc_dbg("sock=%p", sock); + pr_debug("sock=%p\n", sock); sock_orphan(sk); sock_put(sk); @@ -67,14 +70,14 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr, struct nfc_dev *dev; int rc = 0; - nfc_dbg("sock=%p sk=%p flags=%d", sock, sk, flags); + pr_debug("sock=%p sk=%p flags=%d\n", sock, sk, flags); if (!addr || len < sizeof(struct sockaddr_nfc) || addr->sa_family != AF_NFC) return -EINVAL; - nfc_dbg("addr dev_idx=%u target_idx=%u protocol=%u", addr->dev_idx, - addr->target_idx, addr->nfc_protocol); + pr_debug("addr dev_idx=%u target_idx=%u protocol=%u\n", + addr->dev_idx, addr->target_idx, addr->nfc_protocol); lock_sock(sk); @@ -135,7 +138,7 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb, BUG_ON(in_irq()); - nfc_dbg("sk=%p err=%d", sk, err); + pr_debug("sk=%p err=%d\n", sk, err); if (err) goto error; @@ -171,7 +174,7 @@ static void rawsock_tx_work(struct work_struct *work) struct sk_buff *skb; int rc; - nfc_dbg("sk=%p target_idx=%u", sk, target_idx); + pr_debug("sk=%p target_idx=%u\n", sk, target_idx); if (sk->sk_shutdown & SEND_SHUTDOWN) { rawsock_write_queue_purge(sk); @@ -197,7 +200,7 @@ static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb; int rc; - nfc_dbg("sock=%p sk=%p len=%zu", sock, sk, len); + pr_debug("sock=%p sk=%p len=%zu\n", sock, sk, len); if (msg->msg_namelen) return -EOPNOTSUPP; @@ -238,7 +241,7 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, int copied; int rc; - nfc_dbg("sock=%p sk=%p len=%zu flags=%d", sock, sk, len, flags); + pr_debug("sock=%p sk=%p len=%zu flags=%d\n", sock, sk, len, flags); skb = skb_recv_datagram(sk, flags, noblock, &rc); if (!skb) @@ -282,7 +285,7 @@ static const struct proto_ops rawsock_ops = { static void rawsock_destruct(struct sock *sk) { - nfc_dbg("sk=%p", sk); + pr_debug("sk=%p\n", sk); if (sk->sk_state == TCP_ESTABLISHED) { nfc_deactivate_target(nfc_rawsock(sk)->dev, @@ -293,7 +296,7 @@ static void rawsock_destruct(struct sock *sk) skb_queue_purge(&sk->sk_receive_queue); if (!sock_flag(sk, SOCK_DEAD)) { - nfc_err("Freeing alive NFC raw socket %p", sk); + pr_err("Freeing alive NFC raw socket %p\n", sk); return; } } @@ -303,7 +306,7 @@ static int rawsock_create(struct net *net, struct socket *sock, { struct sock *sk; - nfc_dbg("sock=%p", sock); + pr_debug("sock=%p\n", sock); if (sock->type != SOCK_SEQPACKET) return -ESOCKTNOSUPPORT; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 03bb45adf2f..82a6f34d39d 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -335,7 +335,7 @@ struct packet_skb_cb { (((x)->kactive_blk_num < ((x)->knum_blocks-1)) ? \ ((x)->kactive_blk_num+1) : 0) -static inline struct packet_sock *pkt_sk(struct sock *sk) +static struct packet_sock *pkt_sk(struct sock *sk) { return (struct packet_sock *)sk; } @@ -477,7 +477,7 @@ static void *packet_lookup_frame(struct packet_sock *po, return h.raw; } -static inline void *packet_current_frame(struct packet_sock *po, +static void *packet_current_frame(struct packet_sock *po, struct packet_ring_buffer *rb, int status) { @@ -715,7 +715,7 @@ out: spin_unlock(&po->sk.sk_receive_queue.lock); } -static inline void prb_flush_block(struct tpacket_kbdq_core *pkc1, +static void prb_flush_block(struct tpacket_kbdq_core *pkc1, struct tpacket_block_desc *pbd1, __u32 status) { /* Flush everything minus the block header */ @@ -793,7 +793,7 @@ static void prb_close_block(struct tpacket_kbdq_core *pkc1, pkc1->kactive_blk_num = GET_NEXT_PRB_BLK_NUM(pkc1); } -static inline void prb_thaw_queue(struct tpacket_kbdq_core *pkc) +static void prb_thaw_queue(struct tpacket_kbdq_core *pkc) { pkc->reset_pending_on_curr_blk = 0; } @@ -869,7 +869,7 @@ static void prb_open_block(struct tpacket_kbdq_core *pkc1, * case and __packet_lookup_frame_in_block will check if block-0 * is free and can now be re-used. */ -static inline void prb_freeze_queue(struct tpacket_kbdq_core *pkc, +static void prb_freeze_queue(struct tpacket_kbdq_core *pkc, struct packet_sock *po) { pkc->reset_pending_on_curr_blk = 1; @@ -940,36 +940,36 @@ static void prb_retire_current_block(struct tpacket_kbdq_core *pkc, BUG(); } -static inline int prb_curr_blk_in_use(struct tpacket_kbdq_core *pkc, +static int prb_curr_blk_in_use(struct tpacket_kbdq_core *pkc, struct tpacket_block_desc *pbd) { return TP_STATUS_USER & BLOCK_STATUS(pbd); } -static inline int prb_queue_frozen(struct tpacket_kbdq_core *pkc) +static int prb_queue_frozen(struct tpacket_kbdq_core *pkc) { return pkc->reset_pending_on_curr_blk; } -static inline void prb_clear_blk_fill_status(struct packet_ring_buffer *rb) +static void prb_clear_blk_fill_status(struct packet_ring_buffer *rb) { struct tpacket_kbdq_core *pkc = GET_PBDQC_FROM_RB(rb); atomic_dec(&pkc->blk_fill_in_prog); } -static inline void prb_fill_rxhash(struct tpacket_kbdq_core *pkc, +static void prb_fill_rxhash(struct tpacket_kbdq_core *pkc, struct tpacket3_hdr *ppd) { ppd->hv1.tp_rxhash = skb_get_rxhash(pkc->skb); } -static inline void prb_clear_rxhash(struct tpacket_kbdq_core *pkc, +static void prb_clear_rxhash(struct tpacket_kbdq_core *pkc, struct tpacket3_hdr *ppd) { ppd->hv1.tp_rxhash = 0; } -static inline void prb_fill_vlan_info(struct tpacket_kbdq_core *pkc, +static void prb_fill_vlan_info(struct tpacket_kbdq_core *pkc, struct tpacket3_hdr *ppd) { if (vlan_tx_tag_present(pkc->skb)) { @@ -991,7 +991,7 @@ static void prb_run_all_ft_ops(struct tpacket_kbdq_core *pkc, prb_clear_rxhash(pkc, ppd); } -static inline void prb_fill_curr_block(char *curr, +static void prb_fill_curr_block(char *curr, struct tpacket_kbdq_core *pkc, struct tpacket_block_desc *pbd, unsigned int len) @@ -1071,7 +1071,7 @@ static void *__packet_lookup_frame_in_block(struct packet_sock *po, return NULL; } -static inline void *packet_current_rx_frame(struct packet_sock *po, +static void *packet_current_rx_frame(struct packet_sock *po, struct sk_buff *skb, int status, unsigned int len) { @@ -1091,7 +1091,7 @@ static inline void *packet_current_rx_frame(struct packet_sock *po, } } -static inline void *prb_lookup_block(struct packet_sock *po, +static void *prb_lookup_block(struct packet_sock *po, struct packet_ring_buffer *rb, unsigned int previous, int status) @@ -1104,7 +1104,7 @@ static inline void *prb_lookup_block(struct packet_sock *po, return pbd; } -static inline int prb_previous_blk_num(struct packet_ring_buffer *rb) +static int prb_previous_blk_num(struct packet_ring_buffer *rb) { unsigned int prev; if (rb->prb_bdqc.kactive_blk_num) @@ -1115,7 +1115,7 @@ static inline int prb_previous_blk_num(struct packet_ring_buffer *rb) } /* Assumes caller has held the rx_queue.lock */ -static inline void *__prb_previous_block(struct packet_sock *po, +static void *__prb_previous_block(struct packet_sock *po, struct packet_ring_buffer *rb, int status) { @@ -1123,7 +1123,7 @@ static inline void *__prb_previous_block(struct packet_sock *po, return prb_lookup_block(po, rb, previous, status); } -static inline void *packet_previous_rx_frame(struct packet_sock *po, +static void *packet_previous_rx_frame(struct packet_sock *po, struct packet_ring_buffer *rb, int status) { @@ -1133,7 +1133,7 @@ static inline void *packet_previous_rx_frame(struct packet_sock *po, return __prb_previous_block(po, rb, status); } -static inline void packet_increment_rx_head(struct packet_sock *po, +static void packet_increment_rx_head(struct packet_sock *po, struct packet_ring_buffer *rb) { switch (po->tp_version) { @@ -1148,7 +1148,7 @@ static inline void packet_increment_rx_head(struct packet_sock *po, } } -static inline void *packet_previous_frame(struct packet_sock *po, +static void *packet_previous_frame(struct packet_sock *po, struct packet_ring_buffer *rb, int status) { @@ -1156,7 +1156,7 @@ static inline void *packet_previous_frame(struct packet_sock *po, return packet_lookup_frame(po, rb, previous, status); } -static inline void packet_increment_head(struct packet_ring_buffer *buff) +static void packet_increment_head(struct packet_ring_buffer *buff) { buff->head = buff->head != buff->frame_max ? buff->head+1 : 0; } @@ -1558,7 +1558,7 @@ out_free: return err; } -static inline unsigned int run_filter(const struct sk_buff *skb, +static unsigned int run_filter(const struct sk_buff *skb, const struct sock *sk, unsigned int res) { @@ -2167,10 +2167,10 @@ out: return err; } -static inline struct sk_buff *packet_alloc_skb(struct sock *sk, size_t prepad, - size_t reserve, size_t len, - size_t linear, int noblock, - int *err) +static struct sk_buff *packet_alloc_skb(struct sock *sk, size_t prepad, + size_t reserve, size_t len, + size_t linear, int noblock, + int *err) { struct sk_buff *skb; @@ -3494,7 +3494,7 @@ static void free_pg_vec(struct pgv *pg_vec, unsigned int order, kfree(pg_vec); } -static inline char *alloc_one_pg_vec_page(unsigned long order) +static char *alloc_one_pg_vec_page(unsigned long order) { char *buffer = NULL; gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c index 2f032381bd4..bf35b4e1a14 100644 --- a/net/phonet/datagram.c +++ b/net/phonet/datagram.c @@ -30,6 +30,7 @@ #include <net/sock.h> #include <linux/phonet.h> +#include <linux/export.h> #include <net/phonet/phonet.h> static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb); diff --git a/net/phonet/pep.c b/net/phonet/pep.c index f17fd841f94..2ba6e9fb4cb 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -30,6 +30,7 @@ #include <asm/ioctls.h> #include <linux/phonet.h> +#include <linux/module.h> #include <net/phonet/phonet.h> #include <net/phonet/pep.h> #include <net/phonet/gprs.h> diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 676d18dc75b..3f8d0b1603b 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -31,6 +31,7 @@ #include <net/tcp_states.h> #include <linux/phonet.h> +#include <linux/export.h> #include <net/phonet/phonet.h> #include <net/phonet/pep.h> #include <net/phonet/pn_dev.h> diff --git a/net/rds/cong.c b/net/rds/cong.c index 6daaa49d133..e5b65acd650 100644 --- a/net/rds/cong.c +++ b/net/rds/cong.c @@ -34,6 +34,7 @@ #include <linux/types.h> #include <linux/rbtree.h> #include <linux/bitops.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/connection.c b/net/rds/connection.c index 9334d892366..9e07c756d1f 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -33,6 +33,7 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/inet_hashtables.h> #include "rds.h" diff --git a/net/rds/ib.c b/net/rds/ib.c index 3b83086bcc3..b4c8b0022fe 100644 --- a/net/rds/ib.c +++ b/net/rds/ib.c @@ -38,6 +38,7 @@ #include <linux/if_arp.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/module.h> #include "rds.h" #include "ib.h" diff --git a/net/rds/info.c b/net/rds/info.c index 4fdf1b6e84f..f1c016c4146 100644 --- a/net/rds/info.c +++ b/net/rds/info.c @@ -34,6 +34,7 @@ #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/proc_fs.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/iw.c b/net/rds/iw.c index f7474844f09..7826d46baa7 100644 --- a/net/rds/iw.c +++ b/net/rds/iw.c @@ -38,6 +38,7 @@ #include <linux/if_arp.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/module.h> #include "rds.h" #include "iw.h" diff --git a/net/rds/message.c b/net/rds/message.c index 1fd3d29023d..f0a4658f327 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -32,6 +32,7 @@ */ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/page.c b/net/rds/page.c index b82d63e77b0..2499cd10842 100644 --- a/net/rds/page.c +++ b/net/rds/page.c @@ -33,6 +33,7 @@ #include <linux/highmem.h> #include <linux/gfp.h> #include <linux/cpu.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index f8760e1b668..c2be901d19e 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -30,6 +30,7 @@ * SOFTWARE. * */ +#include <linux/module.h> #include <rdma/rdma_cm.h> #include "rdma_transport.h" diff --git a/net/rds/rds.h b/net/rds/rds.h index da8adac2bf0..7eaba1831f0 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -36,8 +36,8 @@ #define rdsdebug(fmt, args...) pr_debug("%s(): " fmt, __func__ , ##args) #else /* sigh, pr_debug() causes unused variable warnings */ -static inline void __attribute__ ((format (printf, 1, 2))) -rdsdebug(char *fmt, ...) +static inline __printf(1, 2) +void rdsdebug(char *fmt, ...) { } #endif @@ -625,8 +625,8 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len, struct rds_info_lengths *lens, int (*visitor)(struct rds_connection *, void *), size_t item_len); -void __rds_conn_error(struct rds_connection *conn, const char *, ...) - __attribute__ ((format (printf, 2, 3))); +__printf(2, 3) +void __rds_conn_error(struct rds_connection *conn, const char *, ...); #define rds_conn_error(conn, fmt...) \ __rds_conn_error(conn, KERN_WARNING "RDS: " fmt) diff --git a/net/rds/recv.c b/net/rds/recv.c index 596689e5927..bc3f8cd6d07 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -34,6 +34,7 @@ #include <linux/slab.h> #include <net/sock.h> #include <linux/in.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/send.c b/net/rds/send.c index aa57e22539e..e2d63c59e7c 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -31,11 +31,13 @@ * */ #include <linux/kernel.h> +#include <linux/moduleparam.h> #include <linux/gfp.h> #include <net/sock.h> #include <linux/in.h> #include <linux/list.h> #include <linux/ratelimit.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/stats.c b/net/rds/stats.c index 10c759ccac0..7be790d60b9 100644 --- a/net/rds/stats.c +++ b/net/rds/stats.c @@ -33,6 +33,7 @@ #include <linux/percpu.h> #include <linux/seq_file.h> #include <linux/proc_fs.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 8e0a32001c9..edac9ef2bc8 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -33,6 +33,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/in.h> +#include <linux/module.h> #include <net/tcp.h> #include "rds.h" diff --git a/net/rds/threads.c b/net/rds/threads.c index 0fd90f8c5f5..65eaefcab24 100644 --- a/net/rds/threads.c +++ b/net/rds/threads.c @@ -32,6 +32,7 @@ */ #include <linux/kernel.h> #include <linux/random.h> +#include <linux/export.h> #include "rds.h" diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 5be19575c34..354760ebbbd 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/rfkill/input.c b/net/rfkill/input.c index 1bca6d49ec9..24c55c53e6a 100644 --- a/net/rfkill/input.c +++ b/net/rfkill/input.c @@ -15,6 +15,7 @@ #include <linux/input.h> #include <linux/slab.h> +#include <linux/moduleparam.h> #include <linux/workqueue.h> #include <linux/init.h> #include <linux/rfkill.h> diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 128677d6905..ca355e7e58b 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -220,18 +220,7 @@ static struct platform_driver rfkill_gpio_driver = { }, }; -static int __init rfkill_gpio_init(void) -{ - return platform_driver_register(&rfkill_gpio_driver); -} - -static void __exit rfkill_gpio_exit(void) -{ - platform_driver_unregister(&rfkill_gpio_driver); -} - -module_init(rfkill_gpio_init); -module_exit(rfkill_gpio_exit); +module_platform_driver(rfkill_gpio_driver); MODULE_DESCRIPTION("gpio rfkill"); MODULE_AUTHOR("NVIDIA"); diff --git a/net/rfkill/rfkill-regulator.c b/net/rfkill/rfkill-regulator.c index 3ca7277a3c3..2ebfe8d0e87 100644 --- a/net/rfkill/rfkill-regulator.c +++ b/net/rfkill/rfkill-regulator.c @@ -144,17 +144,7 @@ static struct platform_driver rfkill_regulator_driver = { }, }; -static int __init rfkill_regulator_init(void) -{ - return platform_driver_register(&rfkill_regulator_driver); -} -module_init(rfkill_regulator_init); - -static void __exit rfkill_regulator_exit(void) -{ - platform_driver_unregister(&rfkill_regulator_driver); -} -module_exit(rfkill_regulator_exit); +module_platform_driver(rfkill_regulator_driver); MODULE_AUTHOR("Guiming Zhuo <gmzhuo@gmail.com>"); MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index d389de19708..cd9b7ee60f3 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -36,6 +36,7 @@ #include <linux/init.h> #include <net/rose.h> #include <linux/seq_file.h> +#include <linux/export.h> static unsigned int rose_neigh_no = 1; diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c index 5f22e263eda..338d793c711 100644 --- a/net/rxrpc/ar-output.c +++ b/net/rxrpc/ar-output.c @@ -13,6 +13,7 @@ #include <linux/gfp.h> #include <linux/skbuff.h> #include <linux/circ_buf.h> +#include <linux/export.h> #include <net/sock.h> #include <net/af_rxrpc.h> #include "ar-internal.h" diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c index 0c65013e3bf..4b48687c389 100644 --- a/net/rxrpc/ar-recvmsg.c +++ b/net/rxrpc/ar-recvmsg.c @@ -11,6 +11,7 @@ #include <linux/net.h> #include <linux/skbuff.h> +#include <linux/export.h> #include <net/sock.h> #include <net/af_rxrpc.h> #include "ar-internal.h" diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f2fb67e701a..93fdf131bd7 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -20,6 +20,7 @@ #include <linux/init.h> #include <linux/kmod.h> #include <linux/err.h> +#include <linux/module.h> #include <net/net_namespace.h> #include <net/sock.h> #include <net/sch_generic.h> diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 9e087d88567..7b582300d05 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -21,6 +21,7 @@ #include <linux/ipv6.h> #include <linux/if_vlan.h> #include <linux/slab.h> +#include <linux/module.h> #include <net/pkt_cls.h> #include <net/ip.h> diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index ec5cbc84896..0a4b2f9a009 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -11,6 +11,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/export.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/skbuff.h> diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index ea17cbed29e..f88256cbacb 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -14,6 +14,7 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/skbuff.h> +#include <linux/module.h> #include <net/netlink.h> #include <net/pkt_sched.h> #include <net/sch_generic.h> diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 05a6ce21471..1e2eee88c3e 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -37,6 +37,7 @@ #include <linux/types.h> #include <linux/seq_file.h> #include <linux/init.h> +#include <linux/export.h> #include <net/sctp/sctp.h> #include <net/ip.h> /* for snmp_fold_field */ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 836aa63ee12..13bf5fcdbff 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -78,6 +78,7 @@ #include <net/inet_common.h> #include <linux/socket.h> /* for sa_family_t */ +#include <linux/export.h> #include <net/sock.h> #include <net/sctp/sctp.h> #include <net/sctp/sm.h> diff --git a/net/socket.c b/net/socket.c index 2877647f347..425ef427046 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/sunrpc/addr.c b/net/sunrpc/addr.c index 4548757c987..67a655ee82a 100644 --- a/net/sunrpc/addr.c +++ b/net/sunrpc/addr.c @@ -19,6 +19,7 @@ #include <net/ipv6.h> #include <linux/sunrpc/clnt.h> #include <linux/slab.h> +#include <linux/export.h> #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 4cb70dc6e7a..e50502d8ceb 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -129,6 +129,9 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) for (i = 0; i < groups ; i++) if (cred->uc_gids[i] != GROUP_AT(acred->group_info, i)) return 0; + if (groups < NFS_NGROUPS && + cred->uc_gids[groups] != NOGROUP) + return 0; return 1; } diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 91eaa26e4c4..3ad435a14ad 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <linux/tcp.h> #include <linux/slab.h> #include <linux/sunrpc/xprt.h> +#include <linux/export.h> #ifdef RPC_DEBUG #define RPCDBG_FACILITY RPCDBG_TRANS diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index f588b852d41..8761bf8e36f 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -114,6 +114,9 @@ static struct rpc_program rpcb_program; static struct rpc_clnt * rpcb_local_clnt; static struct rpc_clnt * rpcb_local_clnt4; +DEFINE_SPINLOCK(rpcb_clnt_lock); +unsigned int rpcb_users; + struct rpcbind_args { struct rpc_xprt * r_xprt; @@ -161,6 +164,56 @@ static void rpcb_map_release(void *data) kfree(map); } +static int rpcb_get_local(void) +{ + int cnt; + + spin_lock(&rpcb_clnt_lock); + if (rpcb_users) + rpcb_users++; + cnt = rpcb_users; + spin_unlock(&rpcb_clnt_lock); + + return cnt; +} + +void rpcb_put_local(void) +{ + struct rpc_clnt *clnt = rpcb_local_clnt; + struct rpc_clnt *clnt4 = rpcb_local_clnt4; + int shutdown; + + spin_lock(&rpcb_clnt_lock); + if (--rpcb_users == 0) { + rpcb_local_clnt = NULL; + rpcb_local_clnt4 = NULL; + } + shutdown = !rpcb_users; + spin_unlock(&rpcb_clnt_lock); + + if (shutdown) { + /* + * cleanup_rpcb_clnt - remove xprtsock's sysctls, unregister + */ + if (clnt4) + rpc_shutdown_client(clnt4); + if (clnt) + rpc_shutdown_client(clnt); + } +} + +static void rpcb_set_local(struct rpc_clnt *clnt, struct rpc_clnt *clnt4) +{ + /* Protected by rpcb_create_local_mutex */ + rpcb_local_clnt = clnt; + rpcb_local_clnt4 = clnt4; + smp_wmb(); + rpcb_users = 1; + dprintk("RPC: created new rpcb local clients (rpcb_local_clnt: " + "%p, rpcb_local_clnt4: %p)\n", rpcb_local_clnt, + rpcb_local_clnt4); +} + /* * Returns zero on success, otherwise a negative errno value * is returned. @@ -205,9 +258,7 @@ static int rpcb_create_local_unix(void) clnt4 = NULL; } - /* Protected by rpcb_create_local_mutex */ - rpcb_local_clnt = clnt; - rpcb_local_clnt4 = clnt4; + rpcb_set_local(clnt, clnt4); out: return result; @@ -259,9 +310,7 @@ static int rpcb_create_local_net(void) clnt4 = NULL; } - /* Protected by rpcb_create_local_mutex */ - rpcb_local_clnt = clnt; - rpcb_local_clnt4 = clnt4; + rpcb_set_local(clnt, clnt4); out: return result; @@ -271,16 +320,16 @@ out: * Returns zero on success, otherwise a negative errno value * is returned. */ -static int rpcb_create_local(void) +int rpcb_create_local(void) { static DEFINE_MUTEX(rpcb_create_local_mutex); int result = 0; - if (rpcb_local_clnt) + if (rpcb_get_local()) return result; mutex_lock(&rpcb_create_local_mutex); - if (rpcb_local_clnt) + if (rpcb_get_local()) goto out; if (rpcb_create_local_unix() != 0) @@ -382,11 +431,6 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port) struct rpc_message msg = { .rpc_argp = &map, }; - int error; - - error = rpcb_create_local(); - if (error) - return error; dprintk("RPC: %sregistering (%u, %u, %d, %u) with local " "rpcbind\n", (port ? "" : "un"), @@ -522,11 +566,7 @@ int rpcb_v4_register(const u32 program, const u32 version, struct rpc_message msg = { .rpc_argp = &map, }; - int error; - error = rpcb_create_local(); - if (error) - return error; if (rpcb_local_clnt4 == NULL) return -EPROTONOSUPPORT; @@ -1060,15 +1100,3 @@ static struct rpc_program rpcb_program = { .version = rpcb_version, .stats = &rpcb_stats, }; - -/** - * cleanup_rpcb_clnt - remove xprtsock's sysctls, unregister - * - */ -void cleanup_rpcb_clnt(void) -{ - if (rpcb_local_clnt4) - rpc_shutdown_client(rpcb_local_clnt4); - if (rpcb_local_clnt) - rpc_shutdown_client(rpcb_local_clnt); -} diff --git a/net/sunrpc/socklib.c b/net/sunrpc/socklib.c index 10b4319ebbc..145e6784f50 100644 --- a/net/sunrpc/socklib.c +++ b/net/sunrpc/socklib.c @@ -14,6 +14,7 @@ #include <linux/pagemap.h> #include <linux/udp.h> #include <linux/sunrpc/xdr.h> +#include <linux/export.h> /** diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 9d080916099..8ec9778c3f4 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -61,8 +61,6 @@ static struct pernet_operations sunrpc_net_ops = { extern struct cache_detail unix_gid_cache; -extern void cleanup_rpcb_clnt(void); - static int __init init_sunrpc(void) { @@ -102,7 +100,6 @@ out: static void __exit cleanup_sunrpc(void) { - cleanup_rpcb_clnt(); rpcauth_remove_module(); cleanup_socket_xprt(); svc_cleanup_xprt_sock(); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 30d70abb4e2..6e038884ae0 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -366,6 +366,42 @@ svc_pool_for_cpu(struct svc_serv *serv, int cpu) return &serv->sv_pools[pidx % serv->sv_nrpools]; } +static int svc_rpcb_setup(struct svc_serv *serv) +{ + int err; + + err = rpcb_create_local(); + if (err) + return err; + + /* Remove any stale portmap registrations */ + svc_unregister(serv); + return 0; +} + +void svc_rpcb_cleanup(struct svc_serv *serv) +{ + svc_unregister(serv); + rpcb_put_local(); +} +EXPORT_SYMBOL_GPL(svc_rpcb_cleanup); + +static int svc_uses_rpcbind(struct svc_serv *serv) +{ + struct svc_program *progp; + unsigned int i; + + for (progp = serv->sv_program; progp; progp = progp->pg_next) { + for (i = 0; i < progp->pg_nvers; i++) { + if (progp->pg_vers[i] == NULL) + continue; + if (progp->pg_vers[i]->vs_hidden == 0) + return 1; + } + } + + return 0; +} /* * Create an RPC service @@ -431,8 +467,15 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, spin_lock_init(&pool->sp_lock); } - /* Remove any stale portmap registrations */ - svc_unregister(serv); + if (svc_uses_rpcbind(serv)) { + if (svc_rpcb_setup(serv) < 0) { + kfree(serv->sv_pools); + kfree(serv); + return NULL; + } + if (!serv->sv_shutdown) + serv->sv_shutdown = svc_rpcb_cleanup; + } return serv; } @@ -500,7 +543,6 @@ svc_destroy(struct svc_serv *serv) if (svc_serv_is_pooled(serv)) svc_pool_map_put(); - svc_unregister(serv); kfree(serv->sv_pools); kfree(serv); } @@ -971,9 +1013,8 @@ static void svc_unregister(const struct svc_serv *serv) /* * Printk the given error with the address of the client that caused it. */ -static int -__attribute__ ((format (printf, 2, 3))) -svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) +static __printf(2, 3) +int svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) { va_list args; int r; diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index d86bb673e1f..447cd0eb415 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -14,6 +14,7 @@ #include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/xprt.h> +#include <linux/module.h> #define RPCDBG_FACILITY RPCDBG_SVCXPRT diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index dfd686eb0b7..71bed1c1c77 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -21,6 +21,7 @@ #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/module.h> #include <linux/errno.h> #include <linux/fcntl.h> #include <linux/net.h> diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index a385430c722..ba1296d88de 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -50,6 +50,7 @@ #include <rdma/ib_verbs.h> #include <rdma/rdma_cm.h> #include <linux/sunrpc/svc_rdma.h> +#include <linux/export.h> #define RPCDBG_FACILITY RPCDBG_SVCXPRT diff --git a/net/sysctl_net.c b/net/sysctl_net.c index ca84212cfbf..e75813904f2 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -12,6 +12,7 @@ */ #include <linux/mm.h> +#include <linux/export.h> #include <linux/sysctl.h> #include <linux/nsproxy.h> diff --git a/net/tipc/core.c b/net/tipc/core.c index 943b6af8426..c21331d58fd 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -34,6 +34,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <linux/module.h> + #include "core.h" #include "ref.h" #include "name_table.h" diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 9440a3d48ca..42b8324ff2e 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -34,6 +34,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <linux/export.h> #include <net/sock.h> #include "core.h" diff --git a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c index f346395314b..c43612ee96b 100644 --- a/net/wanrouter/wanproc.c +++ b/net/wanrouter/wanproc.c @@ -81,7 +81,6 @@ static struct proc_dir_entry *proc_router; * Iterator */ static void *r_start(struct seq_file *m, loff_t *pos) - __acquires(kernel_lock) { struct wan_device *wandev; loff_t l = *pos; @@ -103,7 +102,6 @@ static void *r_next(struct seq_file *m, void *v, loff_t *pos) } static void r_stop(struct seq_file *m, void *v) - __releases(kernel_lock) { mutex_unlock(&config_mutex); } diff --git a/net/wimax/op-msg.c b/net/wimax/op-msg.c index d5b7c3779c4..0694d62e4db 100644 --- a/net/wimax/op-msg.c +++ b/net/wimax/op-msg.c @@ -77,6 +77,7 @@ #include <linux/netdevice.h> #include <linux/wimax.h> #include <linux/security.h> +#include <linux/export.h> #include "wimax-internal.h" diff --git a/net/wimax/op-reset.c b/net/wimax/op-reset.c index 68bedf3e544..7ceffe39d70 100644 --- a/net/wimax/op-reset.c +++ b/net/wimax/op-reset.c @@ -32,6 +32,7 @@ #include <net/genetlink.h> #include <linux/wimax.h> #include <linux/security.h> +#include <linux/export.h> #include "wimax-internal.h" #define D_SUBMODULE op_reset diff --git a/net/wimax/op-rfkill.c b/net/wimax/op-rfkill.c index 2609e445fe7..7ab60babdd2 100644 --- a/net/wimax/op-rfkill.c +++ b/net/wimax/op-rfkill.c @@ -65,6 +65,7 @@ #include <linux/wimax.h> #include <linux/security.h> #include <linux/rfkill.h> +#include <linux/export.h> #include "wimax-internal.h" #define D_SUBMODULE op_rfkill diff --git a/net/wimax/stack.c b/net/wimax/stack.c index ee99e7dfcdb..3c65eae701c 100644 --- a/net/wimax/stack.c +++ b/net/wimax/stack.c @@ -55,6 +55,7 @@ #include <net/genetlink.h> #include <linux/netdevice.h> #include <linux/wimax.h> +#include <linux/module.h> #include "wimax-internal.h" diff --git a/net/wireless/core.c b/net/wireless/core.c index 220f3bd176f..ccdfed89765 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 b9ec3061ed7..fb08c28fc90 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/ibss.c b/net/wireless/ibss.c index f33fbb79437..30f20fe4a5f 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -7,6 +7,7 @@ #include <linux/etherdevice.h> #include <linux/if_arp.h> #include <linux/slab.h> +#include <linux/export.h> #include <net/cfg80211.h> #include "wext-compat.h" #include "nl80211.h" diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 4423e64c7d9..8c550df1303 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -1,4 +1,5 @@ #include <linux/ieee80211.h> +#include <linux/export.h> #include <net/cfg80211.h> #include "nl80211.h" #include "core.h" @@ -19,6 +20,7 @@ * interface */ #define MESH_PREQ_MIN_INT 10 +#define MESH_PERR_MIN_INT 100 #define MESH_DIAM_TRAVERSAL_TIME 50 /* @@ -46,6 +48,7 @@ const struct mesh_config default_mesh_config = { .dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS, .dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT, .dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT, + .dot11MeshHWMPperrMinInterval = MESH_PERR_MIN_INT, .dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME, .dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES, .path_refresh_time = MESH_PATH_REFRESH_TIME, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 21fc9702f81..438dfc105b4 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 ffafda5022c..8a9b4d817ae 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 }, @@ -196,6 +196,15 @@ 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 + }, + [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, }; /* policy for the key attributes */ @@ -203,7 +212,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 }, @@ -758,6 +767,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; @@ -874,7 +887,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); @@ -890,6 +904,16 @@ 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); + CMD(set_noack_map, SET_NOACK_MAP); + 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 @@ -905,11 +929,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) { @@ -1007,6 +1032,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: @@ -1725,6 +1761,23 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) return rdev->ops->del_virtual_intf(&rdev->wiphy, dev); } +static int nl80211_set_noack_map(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]; + u16 noack_map; + + if (!info->attrs[NL80211_ATTR_NOACK_MAP]) + return -EINVAL; + + if (!rdev->ops->set_noack_map) + return -EOPNOTSUPP; + + noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]); + + return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map); +} + struct get_key_cookie { struct sk_buff *msg; int error; @@ -2155,6 +2208,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, ¶ms); if (!err && params.interval) wdev->beacon_interval = params.interval; @@ -2453,26 +2513,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) @@ -2522,9 +2590,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, ¶ms.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; @@ -2692,9 +2760,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, ¶ms.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; @@ -3127,6 +3195,8 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, cur_params.dot11MeshHWMPactivePathTimeout); NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, cur_params.dot11MeshHWMPpreqMinInterval); + NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + cur_params.dot11MeshHWMPperrMinInterval); NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, cur_params.dot11MeshHWMPnetDiameterTraversalTime); NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, @@ -3161,6 +3231,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 }, + [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 }, [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 }, @@ -3235,6 +3306,9 @@ do {\ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, + mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPnetDiameterTraversalTime, mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, @@ -3357,6 +3431,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) @@ -3415,6 +3492,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]) @@ -3425,6 +3503,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++; @@ -3452,6 +3533,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, @@ -4359,6 +4447,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; @@ -4402,11 +4493,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; } @@ -4896,6 +5001,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); @@ -5083,7 +5204,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]) { @@ -5271,12 +5393,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]) @@ -5295,7 +5418,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]); } @@ -5313,6 +5436,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]); @@ -5320,29 +5446,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; @@ -5540,6 +5673,11 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + if (info->attrs[NL80211_ATTR_MCAST_RATE] && + !nl80211_parse_mcast_rate(rdev, setup.mcast_rate, + nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) + return -EINVAL; + if (info->attrs[NL80211_ATTR_MESH_SETUP]) { /* parse additional setup parameters if given */ err = nl80211_parse_mesh_setup(info, &setup); @@ -5832,6 +5970,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 @@ -6387,6 +6610,39 @@ 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, + }, + { + .cmd = NL80211_CMD_SET_NOACK_MAP, + .doit = nl80211_set_noack_map, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -6639,10 +6895,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, @@ -6678,10 +6931,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); @@ -6762,10 +7012,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); @@ -6821,10 +7068,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); @@ -6862,10 +7106,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); @@ -6903,10 +7144,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); @@ -6939,10 +7177,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); @@ -6977,10 +7212,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); @@ -7019,10 +7251,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); @@ -7073,10 +7302,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, @@ -7119,10 +7345,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); @@ -7193,10 +7416,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); @@ -7207,13 +7427,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) @@ -7230,16 +7505,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); @@ -7272,10 +7540,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; @@ -7317,10 +7582,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); @@ -7362,10 +7624,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); @@ -7408,10 +7667,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); @@ -7453,7 +7709,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; } @@ -7466,6 +7760,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, @@ -7480,9 +7813,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 f24a1fbeaf1..12bf4d185ab 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/radiotap.c b/net/wireless/radiotap.c index dbe35e138e9..c4ad7958af5 100644 --- a/net/wireless/radiotap.c +++ b/net/wireless/radiotap.c @@ -15,6 +15,7 @@ */ #include <linux/kernel.h> +#include <linux/export.h> #include <net/cfg80211.h> #include <net/ieee80211_radiotap.h> #include <asm/unaligned.h> diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 074c1122ce4..70b171a52ae 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -36,12 +36,14 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> +#include <linux/export.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/random.h> #include <linux/ctype.h> #include <linux/nl80211.h> #include <linux/platform_device.h> +#include <linux/moduleparam.h> #include <net/cfg80211.h> #include "core.h" #include "reg.h" @@ -1137,6 +1139,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); @@ -1960,6 +1964,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) { @@ -1987,6 +2027,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 4a56799d868..786e414afd9 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 dc23b31594e..31119e32e09 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -355,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; @@ -378,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; @@ -389,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, @@ -505,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) { @@ -607,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 6e86d5acf14..f0c900ce2fb 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -10,6 +10,7 @@ #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/wireless.h> +#include <linux/export.h> #include <net/iw_handler.h> #include <net/cfg80211.h> #include <net/rtnetlink.h> @@ -189,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, ¶ms->crypto); + false, ¶ms->crypto, + params->flags, ¶ms->ht_capa, + ¶ms->ht_capa_mask); if (err) __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, @@ -773,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 2f178f73943..9c601d59b77 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -3,10 +3,10 @@ * * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net> */ +#include <linux/export.h> #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" @@ -239,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; @@ -1050,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 62f121d1d9c..3c24eb97e9d 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -8,6 +8,7 @@ * Copyright 2008-2009 Johannes Berg <johannes@sipsolutions.net> */ +#include <linux/export.h> #include <linux/wireless.h> #include <linux/nl80211.h> #include <linux/if_arp.h> @@ -818,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; diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index fdbc23c10d8..0af7f54e4f6 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #include <linux/wireless.h> #include <linux/uaccess.h> +#include <linux/export.h> #include <net/cfg80211.h> #include <net/iw_handler.h> #include <net/netlink.h> diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 0d4b8c3033f..326750b9915 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -5,6 +5,7 @@ * Copyright (C) 2009 Intel Corporation. All rights reserved. */ +#include <linux/export.h> #include <linux/etherdevice.h> #include <linux/if_arp.h> #include <linux/slab.h> diff --git a/net/wireless/wext-spy.c b/net/wireless/wext-spy.c index 6dcfe65a2d1..5d643a548fe 100644 --- a/net/wireless/wext-spy.c +++ b/net/wireless/wext-spy.c @@ -10,6 +10,7 @@ #include <linux/wireless.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/export.h> #include <net/iw_handler.h> #include <net/arp.h> #include <net/wext.h> diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 5f03e4ea65b..3e16c6abde4 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1261,14 +1261,19 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, struct x25_sock *x25 = x25_sk(sk); struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name; size_t copied; - int qbit, header_len = x25->neighbour->extended ? - X25_EXT_MIN_LEN : X25_STD_MIN_LEN; - + int qbit, header_len; struct sk_buff *skb; unsigned char *asmptr; int rc = -ENOTCONN; lock_sock(sk); + + if (x25->neighbour == NULL) + goto out; + + header_len = x25->neighbour->extended ? + X25_EXT_MIN_LEN : X25_STD_MIN_LEN; + /* * This works for seqpacket too. The receiver has ordered the queue for * us! We do one quick check first though diff --git a/net/x25/x25_proc.c b/net/x25/x25_proc.c index 7ff37379232..2ffde4631ae 100644 --- a/net/x25/x25_proc.c +++ b/net/x25/x25_proc.c @@ -20,6 +20,7 @@ #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/export.h> #include <net/net_namespace.h> #include <net/sock.h> #include <net/x25.h> diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index 58d9ae00559..d0a1af8ed58 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -12,6 +12,7 @@ */ #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/export.h> #include <net/snmp.h> #include <net/xfrm.h> diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 6ca357406ea..39e02c54ed2 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -18,6 +18,7 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. */ +#include <linux/export.h> #include <net/xfrm.h> u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq) |