| /* |
| * 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); |