13d23e349SJohannes Berg /*
23d23e349SJohannes Berg * This file implement the Wireless Extensions spy API.
33d23e349SJohannes Berg *
43d23e349SJohannes Berg * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
53d23e349SJohannes Berg * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
63d23e349SJohannes Berg *
73d23e349SJohannes Berg * (As all part of the Linux kernel, this file is GPL)
83d23e349SJohannes Berg */
93d23e349SJohannes Berg
103d23e349SJohannes Berg #include <linux/wireless.h>
113d23e349SJohannes Berg #include <linux/netdevice.h>
123d23e349SJohannes Berg #include <linux/etherdevice.h>
13bc3b2d7fSPaul Gortmaker #include <linux/export.h>
143d23e349SJohannes Berg #include <net/iw_handler.h>
153d23e349SJohannes Berg #include <net/arp.h>
163d23e349SJohannes Berg #include <net/wext.h>
173d23e349SJohannes Berg
get_spydata(struct net_device * dev)183d23e349SJohannes Berg static inline struct iw_spy_data *get_spydata(struct net_device *dev)
193d23e349SJohannes Berg {
203d23e349SJohannes Berg /* This is the new way */
213d23e349SJohannes Berg if (dev->wireless_data)
223d23e349SJohannes Berg return dev->wireless_data->spy_data;
233d23e349SJohannes Berg return NULL;
243d23e349SJohannes Berg }
253d23e349SJohannes Berg
iw_handler_set_spy(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)263d23e349SJohannes Berg int iw_handler_set_spy(struct net_device * dev,
273d23e349SJohannes Berg struct iw_request_info * info,
283d23e349SJohannes Berg union iwreq_data * wrqu,
293d23e349SJohannes Berg char * extra)
303d23e349SJohannes Berg {
313d23e349SJohannes Berg struct iw_spy_data * spydata = get_spydata(dev);
323d23e349SJohannes Berg struct sockaddr * address = (struct sockaddr *) extra;
333d23e349SJohannes Berg
343d23e349SJohannes Berg /* Make sure driver is not buggy or using the old API */
353d23e349SJohannes Berg if (!spydata)
363d23e349SJohannes Berg return -EOPNOTSUPP;
373d23e349SJohannes Berg
383d23e349SJohannes Berg /* Disable spy collection while we copy the addresses.
393d23e349SJohannes Berg * While we copy addresses, any call to wireless_spy_update()
403d23e349SJohannes Berg * will NOP. This is OK, as anyway the addresses are changing. */
413d23e349SJohannes Berg spydata->spy_number = 0;
423d23e349SJohannes Berg
433d23e349SJohannes Berg /* We want to operate without locking, because wireless_spy_update()
443d23e349SJohannes Berg * most likely will happen in the interrupt handler, and therefore
453d23e349SJohannes Berg * have its own locking constraints and needs performance.
463d23e349SJohannes Berg * The rtnl_lock() make sure we don't race with the other iw_handlers.
473d23e349SJohannes Berg * This make sure wireless_spy_update() "see" that the spy list
483d23e349SJohannes Berg * is temporarily disabled. */
493d23e349SJohannes Berg smp_wmb();
503d23e349SJohannes Berg
513d23e349SJohannes Berg /* Are there are addresses to copy? */
523d23e349SJohannes Berg if (wrqu->data.length > 0) {
533d23e349SJohannes Berg int i;
543d23e349SJohannes Berg
553d23e349SJohannes Berg /* Copy addresses */
563d23e349SJohannes Berg for (i = 0; i < wrqu->data.length; i++)
573d23e349SJohannes Berg memcpy(spydata->spy_address[i], address[i].sa_data,
583d23e349SJohannes Berg ETH_ALEN);
593d23e349SJohannes Berg /* Reset stats */
603d23e349SJohannes Berg memset(spydata->spy_stat, 0,
613d23e349SJohannes Berg sizeof(struct iw_quality) * IW_MAX_SPY);
623d23e349SJohannes Berg }
633d23e349SJohannes Berg
643d23e349SJohannes Berg /* Make sure above is updated before re-enabling */
653d23e349SJohannes Berg smp_wmb();
663d23e349SJohannes Berg
673d23e349SJohannes Berg /* Enable addresses */
683d23e349SJohannes Berg spydata->spy_number = wrqu->data.length;
693d23e349SJohannes Berg
703d23e349SJohannes Berg return 0;
713d23e349SJohannes Berg }
723d23e349SJohannes Berg EXPORT_SYMBOL(iw_handler_set_spy);
733d23e349SJohannes Berg
iw_handler_get_spy(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)743d23e349SJohannes Berg int iw_handler_get_spy(struct net_device * dev,
753d23e349SJohannes Berg struct iw_request_info * info,
763d23e349SJohannes Berg union iwreq_data * wrqu,
773d23e349SJohannes Berg char * extra)
783d23e349SJohannes Berg {
793d23e349SJohannes Berg struct iw_spy_data * spydata = get_spydata(dev);
803d23e349SJohannes Berg struct sockaddr * address = (struct sockaddr *) extra;
813d23e349SJohannes Berg int i;
823d23e349SJohannes Berg
833d23e349SJohannes Berg /* Make sure driver is not buggy or using the old API */
843d23e349SJohannes Berg if (!spydata)
853d23e349SJohannes Berg return -EOPNOTSUPP;
863d23e349SJohannes Berg
873d23e349SJohannes Berg wrqu->data.length = spydata->spy_number;
883d23e349SJohannes Berg
893d23e349SJohannes Berg /* Copy addresses. */
903d23e349SJohannes Berg for (i = 0; i < spydata->spy_number; i++) {
913d23e349SJohannes Berg memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
923d23e349SJohannes Berg address[i].sa_family = AF_UNIX;
933d23e349SJohannes Berg }
943d23e349SJohannes Berg /* Copy stats to the user buffer (just after). */
953d23e349SJohannes Berg if (spydata->spy_number > 0)
963d23e349SJohannes Berg memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number),
973d23e349SJohannes Berg spydata->spy_stat,
983d23e349SJohannes Berg sizeof(struct iw_quality) * spydata->spy_number);
993d23e349SJohannes Berg /* Reset updated flags. */
1003d23e349SJohannes Berg for (i = 0; i < spydata->spy_number; i++)
1013d23e349SJohannes Berg spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
1023d23e349SJohannes Berg return 0;
1033d23e349SJohannes Berg }
1043d23e349SJohannes Berg EXPORT_SYMBOL(iw_handler_get_spy);
1053d23e349SJohannes Berg
1063d23e349SJohannes Berg /*------------------------------------------------------------------*/
1073d23e349SJohannes Berg /*
1083d23e349SJohannes Berg * Standard Wireless Handler : set spy threshold
1093d23e349SJohannes Berg */
iw_handler_set_thrspy(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1103d23e349SJohannes Berg int iw_handler_set_thrspy(struct net_device * dev,
1113d23e349SJohannes Berg struct iw_request_info *info,
1123d23e349SJohannes Berg union iwreq_data * wrqu,
1133d23e349SJohannes Berg char * extra)
1143d23e349SJohannes Berg {
1153d23e349SJohannes Berg struct iw_spy_data * spydata = get_spydata(dev);
1163d23e349SJohannes Berg struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
1173d23e349SJohannes Berg
1183d23e349SJohannes Berg /* Make sure driver is not buggy or using the old API */
1193d23e349SJohannes Berg if (!spydata)
1203d23e349SJohannes Berg return -EOPNOTSUPP;
1213d23e349SJohannes Berg
1223d23e349SJohannes Berg /* Just do it */
123*e93bdd78SGustavo A. R. Silva spydata->spy_thr_low = threshold->low;
124*e93bdd78SGustavo A. R. Silva spydata->spy_thr_high = threshold->high;
1253d23e349SJohannes Berg
1263d23e349SJohannes Berg /* Clear flag */
1273d23e349SJohannes Berg memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
1283d23e349SJohannes Berg
1293d23e349SJohannes Berg return 0;
1303d23e349SJohannes Berg }
1313d23e349SJohannes Berg EXPORT_SYMBOL(iw_handler_set_thrspy);
1323d23e349SJohannes Berg
1333d23e349SJohannes Berg /*------------------------------------------------------------------*/
1343d23e349SJohannes Berg /*
1353d23e349SJohannes Berg * Standard Wireless Handler : get spy threshold
1363d23e349SJohannes Berg */
iw_handler_get_thrspy(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1373d23e349SJohannes Berg int iw_handler_get_thrspy(struct net_device * dev,
1383d23e349SJohannes Berg struct iw_request_info *info,
1393d23e349SJohannes Berg union iwreq_data * wrqu,
1403d23e349SJohannes Berg char * extra)
1413d23e349SJohannes Berg {
1423d23e349SJohannes Berg struct iw_spy_data * spydata = get_spydata(dev);
1433d23e349SJohannes Berg struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
1443d23e349SJohannes Berg
1453d23e349SJohannes Berg /* Make sure driver is not buggy or using the old API */
1463d23e349SJohannes Berg if (!spydata)
1473d23e349SJohannes Berg return -EOPNOTSUPP;
1483d23e349SJohannes Berg
1493d23e349SJohannes Berg /* Just do it */
150*e93bdd78SGustavo A. R. Silva threshold->low = spydata->spy_thr_low;
151*e93bdd78SGustavo A. R. Silva threshold->high = spydata->spy_thr_high;
1523d23e349SJohannes Berg
1533d23e349SJohannes Berg return 0;
1543d23e349SJohannes Berg }
1553d23e349SJohannes Berg EXPORT_SYMBOL(iw_handler_get_thrspy);
1563d23e349SJohannes Berg
1573d23e349SJohannes Berg /*------------------------------------------------------------------*/
1583d23e349SJohannes Berg /*
1593d23e349SJohannes Berg * Prepare and send a Spy Threshold event
1603d23e349SJohannes Berg */
iw_send_thrspy_event(struct net_device * dev,struct iw_spy_data * spydata,unsigned char * address,struct iw_quality * wstats)1613d23e349SJohannes Berg static void iw_send_thrspy_event(struct net_device * dev,
1623d23e349SJohannes Berg struct iw_spy_data * spydata,
1633d23e349SJohannes Berg unsigned char * address,
1643d23e349SJohannes Berg struct iw_quality * wstats)
1653d23e349SJohannes Berg {
1663d23e349SJohannes Berg union iwreq_data wrqu;
1673d23e349SJohannes Berg struct iw_thrspy threshold;
1683d23e349SJohannes Berg
1693d23e349SJohannes Berg /* Init */
1703d23e349SJohannes Berg wrqu.data.length = 1;
1713d23e349SJohannes Berg wrqu.data.flags = 0;
1723d23e349SJohannes Berg /* Copy address */
1733d23e349SJohannes Berg memcpy(threshold.addr.sa_data, address, ETH_ALEN);
1743d23e349SJohannes Berg threshold.addr.sa_family = ARPHRD_ETHER;
1753d23e349SJohannes Berg /* Copy stats */
176*e93bdd78SGustavo A. R. Silva threshold.qual = *wstats;
1773d23e349SJohannes Berg /* Copy also thresholds */
178*e93bdd78SGustavo A. R. Silva threshold.low = spydata->spy_thr_low;
179*e93bdd78SGustavo A. R. Silva threshold.high = spydata->spy_thr_high;
1803d23e349SJohannes Berg
1813d23e349SJohannes Berg /* Send event to user space */
1823d23e349SJohannes Berg wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
1833d23e349SJohannes Berg }
1843d23e349SJohannes Berg
1853d23e349SJohannes Berg /* ---------------------------------------------------------------- */
1863d23e349SJohannes Berg /*
1873d23e349SJohannes Berg * Call for the driver to update the spy data.
1883d23e349SJohannes Berg * For now, the spy data is a simple array. As the size of the array is
1893d23e349SJohannes Berg * small, this is good enough. If we wanted to support larger number of
1903d23e349SJohannes Berg * spy addresses, we should use something more efficient...
1913d23e349SJohannes Berg */
wireless_spy_update(struct net_device * dev,unsigned char * address,struct iw_quality * wstats)1923d23e349SJohannes Berg void wireless_spy_update(struct net_device * dev,
1933d23e349SJohannes Berg unsigned char * address,
1943d23e349SJohannes Berg struct iw_quality * wstats)
1953d23e349SJohannes Berg {
1963d23e349SJohannes Berg struct iw_spy_data * spydata = get_spydata(dev);
1973d23e349SJohannes Berg int i;
1983d23e349SJohannes Berg int match = -1;
1993d23e349SJohannes Berg
2003d23e349SJohannes Berg /* Make sure driver is not buggy or using the old API */
2013d23e349SJohannes Berg if (!spydata)
2023d23e349SJohannes Berg return;
2033d23e349SJohannes Berg
2043d23e349SJohannes Berg /* Update all records that match */
2053d23e349SJohannes Berg for (i = 0; i < spydata->spy_number; i++)
206ac422d3cSJoe Perches if (ether_addr_equal(address, spydata->spy_address[i])) {
2073d23e349SJohannes Berg memcpy(&(spydata->spy_stat[i]), wstats,
2083d23e349SJohannes Berg sizeof(struct iw_quality));
2093d23e349SJohannes Berg match = i;
2103d23e349SJohannes Berg }
2113d23e349SJohannes Berg
2123d23e349SJohannes Berg /* Generate an event if we cross the spy threshold.
2133d23e349SJohannes Berg * To avoid event storms, we have a simple hysteresis : we generate
2143d23e349SJohannes Berg * event only when we go under the low threshold or above the
2153d23e349SJohannes Berg * high threshold. */
2163d23e349SJohannes Berg if (match >= 0) {
2173d23e349SJohannes Berg if (spydata->spy_thr_under[match]) {
2183d23e349SJohannes Berg if (wstats->level > spydata->spy_thr_high.level) {
2193d23e349SJohannes Berg spydata->spy_thr_under[match] = 0;
2203d23e349SJohannes Berg iw_send_thrspy_event(dev, spydata,
2213d23e349SJohannes Berg address, wstats);
2223d23e349SJohannes Berg }
2233d23e349SJohannes Berg } else {
2243d23e349SJohannes Berg if (wstats->level < spydata->spy_thr_low.level) {
2253d23e349SJohannes Berg spydata->spy_thr_under[match] = 1;
2263d23e349SJohannes Berg iw_send_thrspy_event(dev, spydata,
2273d23e349SJohannes Berg address, wstats);
2283d23e349SJohannes Berg }
2293d23e349SJohannes Berg }
2303d23e349SJohannes Berg }
2313d23e349SJohannes Berg }
2323d23e349SJohannes Berg EXPORT_SYMBOL(wireless_spy_update);
233