Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / net / wireless / wext-spy.c
diff --git a/kernel/net/wireless/wext-spy.c b/kernel/net/wireless/wext-spy.c
new file mode 100644 (file)
index 0000000..33bef22
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * This file implement the Wireless Extensions spy API.
+ *
+ * Authors :   Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+#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>
+
+static inline struct iw_spy_data *get_spydata(struct net_device *dev)
+{
+       /* This is the new way */
+       if (dev->wireless_data)
+               return dev->wireless_data->spy_data;
+       return NULL;
+}
+
+int iw_handler_set_spy(struct net_device *     dev,
+                      struct iw_request_info * info,
+                      union iwreq_data *       wrqu,
+                      char *                   extra)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       struct sockaddr *       address = (struct sockaddr *) extra;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return -EOPNOTSUPP;
+
+       /* Disable spy collection while we copy the addresses.
+        * While we copy addresses, any call to wireless_spy_update()
+        * will NOP. This is OK, as anyway the addresses are changing. */
+       spydata->spy_number = 0;
+
+       /* We want to operate without locking, because wireless_spy_update()
+        * most likely will happen in the interrupt handler, and therefore
+        * have its own locking constraints and needs performance.
+        * The rtnl_lock() make sure we don't race with the other iw_handlers.
+        * This make sure wireless_spy_update() "see" that the spy list
+        * is temporarily disabled. */
+       smp_wmb();
+
+       /* Are there are addresses to copy? */
+       if (wrqu->data.length > 0) {
+               int i;
+
+               /* Copy addresses */
+               for (i = 0; i < wrqu->data.length; i++)
+                       memcpy(spydata->spy_address[i], address[i].sa_data,
+                              ETH_ALEN);
+               /* Reset stats */
+               memset(spydata->spy_stat, 0,
+                      sizeof(struct iw_quality) * IW_MAX_SPY);
+       }
+
+       /* Make sure above is updated before re-enabling */
+       smp_wmb();
+
+       /* Enable addresses */
+       spydata->spy_number = wrqu->data.length;
+
+       return 0;
+}
+EXPORT_SYMBOL(iw_handler_set_spy);
+
+int iw_handler_get_spy(struct net_device *     dev,
+                      struct iw_request_info * info,
+                      union iwreq_data *       wrqu,
+                      char *                   extra)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       struct sockaddr *       address = (struct sockaddr *) extra;
+       int                     i;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return -EOPNOTSUPP;
+
+       wrqu->data.length = spydata->spy_number;
+
+       /* Copy addresses. */
+       for (i = 0; i < spydata->spy_number; i++)       {
+               memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
+               address[i].sa_family = AF_UNIX;
+       }
+       /* Copy stats to the user buffer (just after). */
+       if (spydata->spy_number > 0)
+               memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
+                      spydata->spy_stat,
+                      sizeof(struct iw_quality) * spydata->spy_number);
+       /* Reset updated flags. */
+       for (i = 0; i < spydata->spy_number; i++)
+               spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
+       return 0;
+}
+EXPORT_SYMBOL(iw_handler_get_spy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set spy threshold
+ */
+int iw_handler_set_thrspy(struct net_device *  dev,
+                         struct iw_request_info *info,
+                         union iwreq_data *    wrqu,
+                         char *                extra)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return -EOPNOTSUPP;
+
+       /* Just do it */
+       memcpy(&(spydata->spy_thr_low), &(threshold->low),
+              2 * sizeof(struct iw_quality));
+
+       /* Clear flag */
+       memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
+
+       return 0;
+}
+EXPORT_SYMBOL(iw_handler_set_thrspy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get spy threshold
+ */
+int iw_handler_get_thrspy(struct net_device *  dev,
+                         struct iw_request_info *info,
+                         union iwreq_data *    wrqu,
+                         char *                extra)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return -EOPNOTSUPP;
+
+       /* Just do it */
+       memcpy(&(threshold->low), &(spydata->spy_thr_low),
+              2 * sizeof(struct iw_quality));
+
+       return 0;
+}
+EXPORT_SYMBOL(iw_handler_get_thrspy);
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare and send a Spy Threshold event
+ */
+static void iw_send_thrspy_event(struct net_device *   dev,
+                                struct iw_spy_data *   spydata,
+                                unsigned char *        address,
+                                struct iw_quality *    wstats)
+{
+       union iwreq_data        wrqu;
+       struct iw_thrspy        threshold;
+
+       /* Init */
+       wrqu.data.length = 1;
+       wrqu.data.flags = 0;
+       /* Copy address */
+       memcpy(threshold.addr.sa_data, address, ETH_ALEN);
+       threshold.addr.sa_family = ARPHRD_ETHER;
+       /* Copy stats */
+       memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
+       /* Copy also thresholds */
+       memcpy(&(threshold.low), &(spydata->spy_thr_low),
+              2 * sizeof(struct iw_quality));
+
+       /* Send event to user space */
+       wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call for the driver to update the spy data.
+ * For now, the spy data is a simple array. As the size of the array is
+ * small, this is good enough. If we wanted to support larger number of
+ * spy addresses, we should use something more efficient...
+ */
+void wireless_spy_update(struct net_device *   dev,
+                        unsigned char *        address,
+                        struct iw_quality *    wstats)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       int                     i;
+       int                     match = -1;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return;
+
+       /* Update all records that match */
+       for (i = 0; i < spydata->spy_number; i++)
+               if (ether_addr_equal(address, spydata->spy_address[i])) {
+                       memcpy(&(spydata->spy_stat[i]), wstats,
+                              sizeof(struct iw_quality));
+                       match = i;
+               }
+
+       /* Generate an event if we cross the spy threshold.
+        * To avoid event storms, we have a simple hysteresis : we generate
+        * event only when we go under the low threshold or above the
+        * high threshold. */
+       if (match >= 0) {
+               if (spydata->spy_thr_under[match]) {
+                       if (wstats->level > spydata->spy_thr_high.level) {
+                               spydata->spy_thr_under[match] = 0;
+                               iw_send_thrspy_event(dev, spydata,
+                                                    address, wstats);
+                       }
+               } else {
+                       if (wstats->level < spydata->spy_thr_low.level) {
+                               spydata->spy_thr_under[match] = 1;
+                               iw_send_thrspy_event(dev, spydata,
+                                                    address, wstats);
+                       }
+               }
+       }
+}
+EXPORT_SYMBOL(wireless_spy_update);