linux/drivers/uwb/wlp/eda.c
<<
>>
Prefs
   1/*
   2 * WUSB Wire Adapter: WLP interface
   3 * Ethernet to device address cache
   4 *
   5 * Copyright (C) 2005-2006 Intel Corporation
   6 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License version
  10 * 2 as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20 * 02110-1301, USA.
  21 *
  22 *
  23 * We need to be able to map ethernet addresses to device addresses
  24 * and back because there is not explicit relationship between the eth
  25 * addresses used in the ETH frames and the device addresses (no, it
  26 * would not have been simpler to force as ETH address the MBOA MAC
  27 * address...no, not at all :).
  28 *
  29 * A device has one MBOA MAC address and one device address. It is possible
  30 * for a device to have more than one virtual MAC address (although a
  31 * virtual address can be the same as the MBOA MAC address). The device
  32 * address is guaranteed to be unique among the devices in the extended
  33 * beacon group (see ECMA 17.1.1). We thus use the device address as index
  34 * to this cache. We do allow searching based on virtual address as this
  35 * is how Ethernet frames will be addressed.
  36 *
  37 * We need to support virtual EUI-48. Although, right now the virtual
  38 * EUI-48 will always be the same as the MAC SAP address. The EDA cache
  39 * entry thus contains a MAC SAP address as well as the virtual address
  40 * (used to map the network stack address to a neighbor). When we move
  41 * to support more than one virtual MAC on a host then this organization
  42 * will have to change. Perhaps a neighbor has a list of WSSs, each with a
  43 * tag and virtual EUI-48.
  44 *
  45 * On data transmission
  46 * it is used to determine if the neighbor is connected and what WSS it
  47 * belongs to. With this we know what tag to add to the WLP frame. Storing
  48 * the WSS in the EDA cache may be overkill because we only support one
  49 * WSS. Hopefully we will support more than one WSS at some point.
  50 * On data reception it is used to determine the WSS based on
  51 * the tag and address of the transmitting neighbor.
  52 */
  53
  54#include <linux/netdevice.h>
  55#include <linux/etherdevice.h>
  56#include <linux/wlp.h>
  57#include "wlp-internal.h"
  58
  59
  60/* FIXME: cache is not purged, only on device close */
  61
  62/* FIXME: does not scale, change to dynamic array */
  63
  64/*
  65 * Initialize the EDA cache
  66 *
  67 * @returns 0 if ok, < 0 errno code on error
  68 *
  69 * Call when the interface is being brought up
  70 *
  71 * NOTE: Keep it as a separate function as the implementation will
  72 *       change and be more complex.
  73 */
  74void wlp_eda_init(struct wlp_eda *eda)
  75{
  76        INIT_LIST_HEAD(&eda->cache);
  77        spin_lock_init(&eda->lock);
  78}
  79
  80/*
  81 * Release the EDA cache
  82 *
  83 * @returns 0 if ok, < 0 errno code on error
  84 *
  85 * Called when the interface is brought down
  86 */
  87void wlp_eda_release(struct wlp_eda *eda)
  88{
  89        unsigned long flags;
  90        struct wlp_eda_node *itr, *next;
  91
  92        spin_lock_irqsave(&eda->lock, flags);
  93        list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
  94                list_del(&itr->list_node);
  95                kfree(itr);
  96        }
  97        spin_unlock_irqrestore(&eda->lock, flags);
  98}
  99
 100/*
 101 * Add an address mapping
 102 *
 103 * @returns 0 if ok, < 0 errno code on error
 104 *
 105 * An address mapping is initially created when the neighbor device is seen
 106 * for the first time (it is "onair"). At this time the neighbor is not
 107 * connected or associated with a WSS so we only populate the Ethernet and
 108 * Device address fields.
 109 *
 110 */
 111int wlp_eda_create_node(struct wlp_eda *eda,
 112                        const unsigned char eth_addr[ETH_ALEN],
 113                        const struct uwb_dev_addr *dev_addr)
 114{
 115        int result = 0;
 116        struct wlp_eda_node *itr;
 117        unsigned long flags;
 118
 119        BUG_ON(dev_addr == NULL || eth_addr == NULL);
 120        spin_lock_irqsave(&eda->lock, flags);
 121        list_for_each_entry(itr, &eda->cache, list_node) {
 122                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 123                        printk(KERN_ERR "EDA cache already contains entry "
 124                               "for neighbor %02x:%02x\n",
 125                               dev_addr->data[1], dev_addr->data[0]);
 126                        result = -EEXIST;
 127                        goto out_unlock;
 128                }
 129        }
 130        itr = kzalloc(sizeof(*itr), GFP_ATOMIC);
 131        if (itr != NULL) {
 132                memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr));
 133                itr->dev_addr = *dev_addr;
 134                list_add(&itr->list_node, &eda->cache);
 135        } else
 136                result = -ENOMEM;
 137out_unlock:
 138        spin_unlock_irqrestore(&eda->lock, flags);
 139        return result;
 140}
 141
 142/*
 143 * Remove entry from EDA cache
 144 *
 145 * This is done when the device goes off air.
 146 */
 147void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr)
 148{
 149        struct wlp_eda_node *itr, *next;
 150        unsigned long flags;
 151
 152        spin_lock_irqsave(&eda->lock, flags);
 153        list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
 154                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 155                        list_del(&itr->list_node);
 156                        kfree(itr);
 157                        break;
 158                }
 159        }
 160        spin_unlock_irqrestore(&eda->lock, flags);
 161}
 162
 163/*
 164 * Update an address mapping
 165 *
 166 * @returns 0 if ok, < 0 errno code on error
 167 */
 168int wlp_eda_update_node(struct wlp_eda *eda,
 169                        const struct uwb_dev_addr *dev_addr,
 170                        struct wlp_wss *wss,
 171                        const unsigned char virt_addr[ETH_ALEN],
 172                        const u8 tag, const enum wlp_wss_connect state)
 173{
 174        int result = -ENOENT;
 175        struct wlp_eda_node *itr;
 176        unsigned long flags;
 177
 178        spin_lock_irqsave(&eda->lock, flags);
 179        list_for_each_entry(itr, &eda->cache, list_node) {
 180                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 181                        /* Found it, update it */
 182                        itr->wss = wss;
 183                        memcpy(itr->virt_addr, virt_addr,
 184                               sizeof(itr->virt_addr));
 185                        itr->tag = tag;
 186                        itr->state = state;
 187                        result = 0;
 188                        goto out_unlock;
 189                }
 190        }
 191        /* Not found */
 192out_unlock:
 193        spin_unlock_irqrestore(&eda->lock, flags);
 194        return result;
 195}
 196
 197/*
 198 * Update only state field of an address mapping
 199 *
 200 * @returns 0 if ok, < 0 errno code on error
 201 */
 202int wlp_eda_update_node_state(struct wlp_eda *eda,
 203                              const struct uwb_dev_addr *dev_addr,
 204                              const enum wlp_wss_connect state)
 205{
 206        int result = -ENOENT;
 207        struct wlp_eda_node *itr;
 208        unsigned long flags;
 209
 210        spin_lock_irqsave(&eda->lock, flags);
 211        list_for_each_entry(itr, &eda->cache, list_node) {
 212                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 213                        /* Found it, update it */
 214                        itr->state = state;
 215                        result = 0;
 216                        goto out_unlock;
 217                }
 218        }
 219        /* Not found */
 220out_unlock:
 221        spin_unlock_irqrestore(&eda->lock, flags);
 222        return result;
 223}
 224
 225/*
 226 * Return contents of EDA cache entry
 227 *
 228 * @dev_addr: index to EDA cache
 229 * @eda_entry: pointer to where contents of EDA cache will be copied
 230 */
 231int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr,
 232                      struct wlp_eda_node *eda_entry)
 233{
 234        int result = -ENOENT;
 235        struct wlp_eda_node *itr;
 236        unsigned long flags;
 237
 238        spin_lock_irqsave(&eda->lock, flags);
 239        list_for_each_entry(itr, &eda->cache, list_node) {
 240                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 241                        *eda_entry = *itr;
 242                        result = 0;
 243                        goto out_unlock;
 244                }
 245        }
 246        /* Not found */
 247out_unlock:
 248        spin_unlock_irqrestore(&eda->lock, flags);
 249        return result;
 250}
 251
 252/*
 253 * Execute function for every element in the cache
 254 *
 255 * @function: function to execute on element of cache (must be atomic)
 256 * @priv:     private data of function
 257 * @returns:  result of first function that failed, or last function
 258 *            executed if no function failed.
 259 *
 260 * Stop executing when function returns error for any element in cache.
 261 *
 262 * IMPORTANT: We are using a spinlock here: the function executed on each
 263 * element has to be atomic.
 264 */
 265int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function,
 266                     void *priv)
 267{
 268        int result = 0;
 269        struct wlp *wlp = container_of(eda, struct wlp, eda);
 270        struct wlp_eda_node *entry;
 271        unsigned long flags;
 272
 273        spin_lock_irqsave(&eda->lock, flags);
 274        list_for_each_entry(entry, &eda->cache, list_node) {
 275                result = (*function)(wlp, entry, priv);
 276                if (result < 0)
 277                        break;
 278        }
 279        spin_unlock_irqrestore(&eda->lock, flags);
 280        return result;
 281}
 282
 283/*
 284 * Execute function for single element in the cache (return dev addr)
 285 *
 286 * @virt_addr: index into EDA cache used to determine which element to
 287 *             execute the function on
 288 * @dev_addr: device address of element in cache will be returned using
 289 *            @dev_addr
 290 * @function: function to execute on element of cache (must be atomic)
 291 * @priv:     private data of function
 292 * @returns:  result of function
 293 *
 294 * IMPORTANT: We are using a spinlock here: the function executed on the
 295 * element has to be atomic.
 296 */
 297int wlp_eda_for_virtual(struct wlp_eda *eda,
 298                        const unsigned char virt_addr[ETH_ALEN],
 299                        struct uwb_dev_addr *dev_addr,
 300                        wlp_eda_for_each_f function,
 301                        void *priv)
 302{
 303        int result = 0;
 304        struct wlp *wlp = container_of(eda, struct wlp, eda);
 305        struct wlp_eda_node *itr;
 306        unsigned long flags;
 307        int found = 0;
 308
 309        spin_lock_irqsave(&eda->lock, flags);
 310        list_for_each_entry(itr, &eda->cache, list_node) {
 311                if (!memcmp(itr->virt_addr, virt_addr,
 312                           sizeof(itr->virt_addr))) {
 313                        result = (*function)(wlp, itr, priv);
 314                        *dev_addr = itr->dev_addr;
 315                        found = 1;
 316                        break;
 317                }
 318        }
 319        if (!found)
 320                result = -ENODEV;
 321        spin_unlock_irqrestore(&eda->lock, flags);
 322        return result;
 323}
 324
 325static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED",
 326                                          "WLP_WSS_CONNECTED",
 327                                          "WLP_WSS_CONNECT_FAILED",
 328};
 329
 330static const char *wlp_wss_connect_state_str(unsigned id)
 331{
 332        if (id >= ARRAY_SIZE(__wlp_wss_connect_state))
 333                return "unknown WSS connection state";
 334        return __wlp_wss_connect_state[id];
 335}
 336
 337/*
 338 * View EDA cache from user space
 339 *
 340 * A debugging feature to give user visibility into the EDA cache. Also
 341 * used to display members of WSS to user (called from wlp_wss_members_show())
 342 */
 343ssize_t wlp_eda_show(struct wlp *wlp, char *buf)
 344{
 345        ssize_t result = 0;
 346        struct wlp_eda_node *entry;
 347        unsigned long flags;
 348        struct wlp_eda *eda = &wlp->eda;
 349        spin_lock_irqsave(&eda->lock, flags);
 350        result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr "
 351                           "tag state virt_addr\n");
 352        list_for_each_entry(entry, &eda->cache, list_node) {
 353                result += scnprintf(buf + result, PAGE_SIZE - result,
 354                                    "%pM %02x:%02x %p 0x%02x %s %pM\n",
 355                                    entry->eth_addr,
 356                                    entry->dev_addr.data[1],
 357                                    entry->dev_addr.data[0], entry->wss,
 358                                    entry->tag,
 359                                    wlp_wss_connect_state_str(entry->state),
 360                                    entry->virt_addr);
 361                if (result >= PAGE_SIZE)
 362                        break;
 363        }
 364        spin_unlock_irqrestore(&eda->lock, flags);
 365        return result;
 366}
 367EXPORT_SYMBOL_GPL(wlp_eda_show);
 368
 369/*
 370 * Add new EDA cache entry based on user input in sysfs
 371 *
 372 * Should only be used for debugging.
 373 *
 374 * The WSS is assumed to be the only WSS supported. This needs to be
 375 * redesigned when we support more than one WSS.
 376 */
 377ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size)
 378{
 379        ssize_t result;
 380        struct wlp_eda *eda = &wlp->eda;
 381        u8 eth_addr[6];
 382        struct uwb_dev_addr dev_addr;
 383        u8 tag;
 384        unsigned state;
 385
 386        result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx "
 387                        "%02hhx:%02hhx %02hhx %u\n",
 388                        &eth_addr[0], &eth_addr[1],
 389                        &eth_addr[2], &eth_addr[3],
 390                        &eth_addr[4], &eth_addr[5],
 391                        &dev_addr.data[1], &dev_addr.data[0], &tag, &state);
 392        switch (result) {
 393        case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */
 394                /*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/
 395                result = -ENOSYS;
 396                break;
 397        case 10:
 398                state = state >= 1 ? 1 : 0;
 399                result = wlp_eda_create_node(eda, eth_addr, &dev_addr);
 400                if (result < 0 && result != -EEXIST)
 401                        goto error;
 402                /* Set virtual addr to be same as MAC */
 403                result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss,
 404                                             eth_addr, tag, state);
 405                if (result < 0)
 406                        goto error;
 407                break;
 408        default: /* bad format */
 409                result = -EINVAL;
 410        }
 411error:
 412        return result < 0 ? result : size;
 413}
 414EXPORT_SYMBOL_GPL(wlp_eda_store);
 415