Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * This file implement the Wireless Extensions spy API.
0003  *
0004  * Authors :    Jean Tourrilhes - HPL - <jt@hpl.hp.com>
0005  * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
0006  *
0007  * (As all part of the Linux kernel, this file is GPL)
0008  */
0009 
0010 #include <linux/wireless.h>
0011 #include <linux/netdevice.h>
0012 #include <linux/etherdevice.h>
0013 #include <linux/export.h>
0014 #include <net/iw_handler.h>
0015 #include <net/arp.h>
0016 #include <net/wext.h>
0017 
0018 static inline struct iw_spy_data *get_spydata(struct net_device *dev)
0019 {
0020     /* This is the new way */
0021     if (dev->wireless_data)
0022         return dev->wireless_data->spy_data;
0023     return NULL;
0024 }
0025 
0026 int iw_handler_set_spy(struct net_device *  dev,
0027                struct iw_request_info * info,
0028                union iwreq_data *   wrqu,
0029                char *           extra)
0030 {
0031     struct iw_spy_data *    spydata = get_spydata(dev);
0032     struct sockaddr *   address = (struct sockaddr *) extra;
0033 
0034     /* Make sure driver is not buggy or using the old API */
0035     if (!spydata)
0036         return -EOPNOTSUPP;
0037 
0038     /* Disable spy collection while we copy the addresses.
0039      * While we copy addresses, any call to wireless_spy_update()
0040      * will NOP. This is OK, as anyway the addresses are changing. */
0041     spydata->spy_number = 0;
0042 
0043     /* We want to operate without locking, because wireless_spy_update()
0044      * most likely will happen in the interrupt handler, and therefore
0045      * have its own locking constraints and needs performance.
0046      * The rtnl_lock() make sure we don't race with the other iw_handlers.
0047      * This make sure wireless_spy_update() "see" that the spy list
0048      * is temporarily disabled. */
0049     smp_wmb();
0050 
0051     /* Are there are addresses to copy? */
0052     if (wrqu->data.length > 0) {
0053         int i;
0054 
0055         /* Copy addresses */
0056         for (i = 0; i < wrqu->data.length; i++)
0057             memcpy(spydata->spy_address[i], address[i].sa_data,
0058                    ETH_ALEN);
0059         /* Reset stats */
0060         memset(spydata->spy_stat, 0,
0061                sizeof(struct iw_quality) * IW_MAX_SPY);
0062     }
0063 
0064     /* Make sure above is updated before re-enabling */
0065     smp_wmb();
0066 
0067     /* Enable addresses */
0068     spydata->spy_number = wrqu->data.length;
0069 
0070     return 0;
0071 }
0072 EXPORT_SYMBOL(iw_handler_set_spy);
0073 
0074 int iw_handler_get_spy(struct net_device *  dev,
0075                struct iw_request_info * info,
0076                union iwreq_data *   wrqu,
0077                char *           extra)
0078 {
0079     struct iw_spy_data *    spydata = get_spydata(dev);
0080     struct sockaddr *   address = (struct sockaddr *) extra;
0081     int         i;
0082 
0083     /* Make sure driver is not buggy or using the old API */
0084     if (!spydata)
0085         return -EOPNOTSUPP;
0086 
0087     wrqu->data.length = spydata->spy_number;
0088 
0089     /* Copy addresses. */
0090     for (i = 0; i < spydata->spy_number; i++)   {
0091         memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
0092         address[i].sa_family = AF_UNIX;
0093     }
0094     /* Copy stats to the user buffer (just after). */
0095     if (spydata->spy_number > 0)
0096         memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
0097                spydata->spy_stat,
0098                sizeof(struct iw_quality) * spydata->spy_number);
0099     /* Reset updated flags. */
0100     for (i = 0; i < spydata->spy_number; i++)
0101         spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
0102     return 0;
0103 }
0104 EXPORT_SYMBOL(iw_handler_get_spy);
0105 
0106 /*------------------------------------------------------------------*/
0107 /*
0108  * Standard Wireless Handler : set spy threshold
0109  */
0110 int iw_handler_set_thrspy(struct net_device *   dev,
0111               struct iw_request_info *info,
0112               union iwreq_data *    wrqu,
0113               char *        extra)
0114 {
0115     struct iw_spy_data *    spydata = get_spydata(dev);
0116     struct iw_thrspy *  threshold = (struct iw_thrspy *) extra;
0117 
0118     /* Make sure driver is not buggy or using the old API */
0119     if (!spydata)
0120         return -EOPNOTSUPP;
0121 
0122     /* Just do it */
0123     spydata->spy_thr_low = threshold->low;
0124     spydata->spy_thr_high = threshold->high;
0125 
0126     /* Clear flag */
0127     memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
0128 
0129     return 0;
0130 }
0131 EXPORT_SYMBOL(iw_handler_set_thrspy);
0132 
0133 /*------------------------------------------------------------------*/
0134 /*
0135  * Standard Wireless Handler : get spy threshold
0136  */
0137 int iw_handler_get_thrspy(struct net_device *   dev,
0138               struct iw_request_info *info,
0139               union iwreq_data *    wrqu,
0140               char *        extra)
0141 {
0142     struct iw_spy_data *    spydata = get_spydata(dev);
0143     struct iw_thrspy *  threshold = (struct iw_thrspy *) extra;
0144 
0145     /* Make sure driver is not buggy or using the old API */
0146     if (!spydata)
0147         return -EOPNOTSUPP;
0148 
0149     /* Just do it */
0150     threshold->low = spydata->spy_thr_low;
0151     threshold->high = spydata->spy_thr_high;
0152 
0153     return 0;
0154 }
0155 EXPORT_SYMBOL(iw_handler_get_thrspy);
0156 
0157 /*------------------------------------------------------------------*/
0158 /*
0159  * Prepare and send a Spy Threshold event
0160  */
0161 static void iw_send_thrspy_event(struct net_device *    dev,
0162                  struct iw_spy_data *   spydata,
0163                  unsigned char *    address,
0164                  struct iw_quality *    wstats)
0165 {
0166     union iwreq_data    wrqu;
0167     struct iw_thrspy    threshold;
0168 
0169     /* Init */
0170     wrqu.data.length = 1;
0171     wrqu.data.flags = 0;
0172     /* Copy address */
0173     memcpy(threshold.addr.sa_data, address, ETH_ALEN);
0174     threshold.addr.sa_family = ARPHRD_ETHER;
0175     /* Copy stats */
0176     threshold.qual = *wstats;
0177     /* Copy also thresholds */
0178     threshold.low = spydata->spy_thr_low;
0179     threshold.high = spydata->spy_thr_high;
0180 
0181     /* Send event to user space */
0182     wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
0183 }
0184 
0185 /* ---------------------------------------------------------------- */
0186 /*
0187  * Call for the driver to update the spy data.
0188  * For now, the spy data is a simple array. As the size of the array is
0189  * small, this is good enough. If we wanted to support larger number of
0190  * spy addresses, we should use something more efficient...
0191  */
0192 void wireless_spy_update(struct net_device *    dev,
0193              unsigned char *    address,
0194              struct iw_quality *    wstats)
0195 {
0196     struct iw_spy_data *    spydata = get_spydata(dev);
0197     int         i;
0198     int         match = -1;
0199 
0200     /* Make sure driver is not buggy or using the old API */
0201     if (!spydata)
0202         return;
0203 
0204     /* Update all records that match */
0205     for (i = 0; i < spydata->spy_number; i++)
0206         if (ether_addr_equal(address, spydata->spy_address[i])) {
0207             memcpy(&(spydata->spy_stat[i]), wstats,
0208                    sizeof(struct iw_quality));
0209             match = i;
0210         }
0211 
0212     /* Generate an event if we cross the spy threshold.
0213      * To avoid event storms, we have a simple hysteresis : we generate
0214      * event only when we go under the low threshold or above the
0215      * high threshold. */
0216     if (match >= 0) {
0217         if (spydata->spy_thr_under[match]) {
0218             if (wstats->level > spydata->spy_thr_high.level) {
0219                 spydata->spy_thr_under[match] = 0;
0220                 iw_send_thrspy_event(dev, spydata,
0221                              address, wstats);
0222             }
0223         } else {
0224             if (wstats->level < spydata->spy_thr_low.level) {
0225                 spydata->spy_thr_under[match] = 1;
0226                 iw_send_thrspy_event(dev, spydata,
0227                              address, wstats);
0228             }
0229         }
0230     }
0231 }
0232 EXPORT_SYMBOL(wireless_spy_update);