~ubuntu-branches/ubuntu/saucy/linux-n900/saucy

« back to all changes in this revision

Viewing changes to drivers/uwb/wlp/eda.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathieu Poirier
  • Date: 2011-02-18 09:43:31 UTC
  • Revision ID: james.westby@ubuntu.com-20110218094331-eyubsja4f9k0yhmq
Tags: 2.6.35-1.1
Initial release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/slab.h>
 
57
#include <linux/wlp.h>
 
58
#include "wlp-internal.h"
 
59
 
 
60
 
 
61
/* FIXME: cache is not purged, only on device close */
 
62
 
 
63
/* FIXME: does not scale, change to dynamic array */
 
64
 
 
65
/*
 
66
 * Initialize the EDA cache
 
67
 *
 
68
 * @returns 0 if ok, < 0 errno code on error
 
69
 *
 
70
 * Call when the interface is being brought up
 
71
 *
 
72
 * NOTE: Keep it as a separate function as the implementation will
 
73
 *       change and be more complex.
 
74
 */
 
75
void wlp_eda_init(struct wlp_eda *eda)
 
76
{
 
77
        INIT_LIST_HEAD(&eda->cache);
 
78
        spin_lock_init(&eda->lock);
 
79
}
 
80
 
 
81
/*
 
82
 * Release the EDA cache
 
83
 *
 
84
 * @returns 0 if ok, < 0 errno code on error
 
85
 *
 
86
 * Called when the interface is brought down
 
87
 */
 
88
void wlp_eda_release(struct wlp_eda *eda)
 
89
{
 
90
        unsigned long flags;
 
91
        struct wlp_eda_node *itr, *next;
 
92
 
 
93
        spin_lock_irqsave(&eda->lock, flags);
 
94
        list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
 
95
                list_del(&itr->list_node);
 
96
                kfree(itr);
 
97
        }
 
98
        spin_unlock_irqrestore(&eda->lock, flags);
 
99
}
 
100
 
 
101
/*
 
102
 * Add an address mapping
 
103
 *
 
104
 * @returns 0 if ok, < 0 errno code on error
 
105
 *
 
106
 * An address mapping is initially created when the neighbor device is seen
 
107
 * for the first time (it is "onair"). At this time the neighbor is not
 
108
 * connected or associated with a WSS so we only populate the Ethernet and
 
109
 * Device address fields.
 
110
 *
 
111
 */
 
112
int wlp_eda_create_node(struct wlp_eda *eda,
 
113
                        const unsigned char eth_addr[ETH_ALEN],
 
114
                        const struct uwb_dev_addr *dev_addr)
 
115
{
 
116
        int result = 0;
 
117
        struct wlp_eda_node *itr;
 
118
        unsigned long flags;
 
119
 
 
120
        BUG_ON(dev_addr == NULL || eth_addr == NULL);
 
121
        spin_lock_irqsave(&eda->lock, flags);
 
122
        list_for_each_entry(itr, &eda->cache, list_node) {
 
123
                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 
124
                        printk(KERN_ERR "EDA cache already contains entry "
 
125
                               "for neighbor %02x:%02x\n",
 
126
                               dev_addr->data[1], dev_addr->data[0]);
 
127
                        result = -EEXIST;
 
128
                        goto out_unlock;
 
129
                }
 
130
        }
 
131
        itr = kzalloc(sizeof(*itr), GFP_ATOMIC);
 
132
        if (itr != NULL) {
 
133
                memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr));
 
134
                itr->dev_addr = *dev_addr;
 
135
                list_add(&itr->list_node, &eda->cache);
 
136
        } else
 
137
                result = -ENOMEM;
 
138
out_unlock:
 
139
        spin_unlock_irqrestore(&eda->lock, flags);
 
140
        return result;
 
141
}
 
142
 
 
143
/*
 
144
 * Remove entry from EDA cache
 
145
 *
 
146
 * This is done when the device goes off air.
 
147
 */
 
148
void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr)
 
149
{
 
150
        struct wlp_eda_node *itr, *next;
 
151
        unsigned long flags;
 
152
 
 
153
        spin_lock_irqsave(&eda->lock, flags);
 
154
        list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
 
155
                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 
156
                        list_del(&itr->list_node);
 
157
                        kfree(itr);
 
158
                        break;
 
159
                }
 
160
        }
 
161
        spin_unlock_irqrestore(&eda->lock, flags);
 
162
}
 
163
 
 
164
/*
 
165
 * Update an address mapping
 
166
 *
 
167
 * @returns 0 if ok, < 0 errno code on error
 
168
 */
 
169
int wlp_eda_update_node(struct wlp_eda *eda,
 
170
                        const struct uwb_dev_addr *dev_addr,
 
171
                        struct wlp_wss *wss,
 
172
                        const unsigned char virt_addr[ETH_ALEN],
 
173
                        const u8 tag, const enum wlp_wss_connect state)
 
174
{
 
175
        int result = -ENOENT;
 
176
        struct wlp_eda_node *itr;
 
177
        unsigned long flags;
 
178
 
 
179
        spin_lock_irqsave(&eda->lock, flags);
 
180
        list_for_each_entry(itr, &eda->cache, list_node) {
 
181
                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 
182
                        /* Found it, update it */
 
183
                        itr->wss = wss;
 
184
                        memcpy(itr->virt_addr, virt_addr,
 
185
                               sizeof(itr->virt_addr));
 
186
                        itr->tag = tag;
 
187
                        itr->state = state;
 
188
                        result = 0;
 
189
                        goto out_unlock;
 
190
                }
 
191
        }
 
192
        /* Not found */
 
193
out_unlock:
 
194
        spin_unlock_irqrestore(&eda->lock, flags);
 
195
        return result;
 
196
}
 
197
 
 
198
/*
 
199
 * Update only state field of an address mapping
 
200
 *
 
201
 * @returns 0 if ok, < 0 errno code on error
 
202
 */
 
203
int wlp_eda_update_node_state(struct wlp_eda *eda,
 
204
                              const struct uwb_dev_addr *dev_addr,
 
205
                              const enum wlp_wss_connect state)
 
206
{
 
207
        int result = -ENOENT;
 
208
        struct wlp_eda_node *itr;
 
209
        unsigned long flags;
 
210
 
 
211
        spin_lock_irqsave(&eda->lock, flags);
 
212
        list_for_each_entry(itr, &eda->cache, list_node) {
 
213
                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 
214
                        /* Found it, update it */
 
215
                        itr->state = state;
 
216
                        result = 0;
 
217
                        goto out_unlock;
 
218
                }
 
219
        }
 
220
        /* Not found */
 
221
out_unlock:
 
222
        spin_unlock_irqrestore(&eda->lock, flags);
 
223
        return result;
 
224
}
 
225
 
 
226
/*
 
227
 * Return contents of EDA cache entry
 
228
 *
 
229
 * @dev_addr: index to EDA cache
 
230
 * @eda_entry: pointer to where contents of EDA cache will be copied
 
231
 */
 
232
int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr,
 
233
                      struct wlp_eda_node *eda_entry)
 
234
{
 
235
        int result = -ENOENT;
 
236
        struct wlp_eda_node *itr;
 
237
        unsigned long flags;
 
238
 
 
239
        spin_lock_irqsave(&eda->lock, flags);
 
240
        list_for_each_entry(itr, &eda->cache, list_node) {
 
241
                if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
 
242
                        *eda_entry = *itr;
 
243
                        result = 0;
 
244
                        goto out_unlock;
 
245
                }
 
246
        }
 
247
        /* Not found */
 
248
out_unlock:
 
249
        spin_unlock_irqrestore(&eda->lock, flags);
 
250
        return result;
 
251
}
 
252
 
 
253
/*
 
254
 * Execute function for every element in the cache
 
255
 *
 
256
 * @function: function to execute on element of cache (must be atomic)
 
257
 * @priv:     private data of function
 
258
 * @returns:  result of first function that failed, or last function
 
259
 *            executed if no function failed.
 
260
 *
 
261
 * Stop executing when function returns error for any element in cache.
 
262
 *
 
263
 * IMPORTANT: We are using a spinlock here: the function executed on each
 
264
 * element has to be atomic.
 
265
 */
 
266
int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function,
 
267
                     void *priv)
 
268
{
 
269
        int result = 0;
 
270
        struct wlp *wlp = container_of(eda, struct wlp, eda);
 
271
        struct wlp_eda_node *entry;
 
272
        unsigned long flags;
 
273
 
 
274
        spin_lock_irqsave(&eda->lock, flags);
 
275
        list_for_each_entry(entry, &eda->cache, list_node) {
 
276
                result = (*function)(wlp, entry, priv);
 
277
                if (result < 0)
 
278
                        break;
 
279
        }
 
280
        spin_unlock_irqrestore(&eda->lock, flags);
 
281
        return result;
 
282
}
 
283
 
 
284
/*
 
285
 * Execute function for single element in the cache (return dev addr)
 
286
 *
 
287
 * @virt_addr: index into EDA cache used to determine which element to
 
288
 *             execute the function on
 
289
 * @dev_addr: device address of element in cache will be returned using
 
290
 *            @dev_addr
 
291
 * @function: function to execute on element of cache (must be atomic)
 
292
 * @priv:     private data of function
 
293
 * @returns:  result of function
 
294
 *
 
295
 * IMPORTANT: We are using a spinlock here: the function executed on the
 
296
 * element has to be atomic.
 
297
 */
 
298
int wlp_eda_for_virtual(struct wlp_eda *eda,
 
299
                        const unsigned char virt_addr[ETH_ALEN],
 
300
                        struct uwb_dev_addr *dev_addr,
 
301
                        wlp_eda_for_each_f function,
 
302
                        void *priv)
 
303
{
 
304
        int result = 0;
 
305
        struct wlp *wlp = container_of(eda, struct wlp, eda);
 
306
        struct wlp_eda_node *itr;
 
307
        unsigned long flags;
 
308
        int found = 0;
 
309
 
 
310
        spin_lock_irqsave(&eda->lock, flags);
 
311
        list_for_each_entry(itr, &eda->cache, list_node) {
 
312
                if (!memcmp(itr->virt_addr, virt_addr,
 
313
                           sizeof(itr->virt_addr))) {
 
314
                        result = (*function)(wlp, itr, priv);
 
315
                        *dev_addr = itr->dev_addr;
 
316
                        found = 1;
 
317
                        break;
 
318
                }
 
319
        }
 
320
        if (!found)
 
321
                result = -ENODEV;
 
322
        spin_unlock_irqrestore(&eda->lock, flags);
 
323
        return result;
 
324
}
 
325
 
 
326
static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED",
 
327
                                          "WLP_WSS_CONNECTED",
 
328
                                          "WLP_WSS_CONNECT_FAILED",
 
329
};
 
330
 
 
331
static const char *wlp_wss_connect_state_str(unsigned id)
 
332
{
 
333
        if (id >= ARRAY_SIZE(__wlp_wss_connect_state))
 
334
                return "unknown WSS connection state";
 
335
        return __wlp_wss_connect_state[id];
 
336
}
 
337
 
 
338
/*
 
339
 * View EDA cache from user space
 
340
 *
 
341
 * A debugging feature to give user visibility into the EDA cache. Also
 
342
 * used to display members of WSS to user (called from wlp_wss_members_show())
 
343
 */
 
344
ssize_t wlp_eda_show(struct wlp *wlp, char *buf)
 
345
{
 
346
        ssize_t result = 0;
 
347
        struct wlp_eda_node *entry;
 
348
        unsigned long flags;
 
349
        struct wlp_eda *eda = &wlp->eda;
 
350
        spin_lock_irqsave(&eda->lock, flags);
 
351
        result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr "
 
352
                           "tag state virt_addr\n");
 
353
        list_for_each_entry(entry, &eda->cache, list_node) {
 
354
                result += scnprintf(buf + result, PAGE_SIZE - result,
 
355
                                    "%pM %02x:%02x %p 0x%02x %s %pM\n",
 
356
                                    entry->eth_addr,
 
357
                                    entry->dev_addr.data[1],
 
358
                                    entry->dev_addr.data[0], entry->wss,
 
359
                                    entry->tag,
 
360
                                    wlp_wss_connect_state_str(entry->state),
 
361
                                    entry->virt_addr);
 
362
                if (result >= PAGE_SIZE)
 
363
                        break;
 
364
        }
 
365
        spin_unlock_irqrestore(&eda->lock, flags);
 
366
        return result;
 
367
}
 
368
EXPORT_SYMBOL_GPL(wlp_eda_show);
 
369
 
 
370
/*
 
371
 * Add new EDA cache entry based on user input in sysfs
 
372
 *
 
373
 * Should only be used for debugging.
 
374
 *
 
375
 * The WSS is assumed to be the only WSS supported. This needs to be
 
376
 * redesigned when we support more than one WSS.
 
377
 */
 
378
ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size)
 
379
{
 
380
        ssize_t result;
 
381
        struct wlp_eda *eda = &wlp->eda;
 
382
        u8 eth_addr[6];
 
383
        struct uwb_dev_addr dev_addr;
 
384
        u8 tag;
 
385
        unsigned state;
 
386
 
 
387
        result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx "
 
388
                        "%02hhx:%02hhx %02hhx %u\n",
 
389
                        &eth_addr[0], &eth_addr[1],
 
390
                        &eth_addr[2], &eth_addr[3],
 
391
                        &eth_addr[4], &eth_addr[5],
 
392
                        &dev_addr.data[1], &dev_addr.data[0], &tag, &state);
 
393
        switch (result) {
 
394
        case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */
 
395
                /*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/
 
396
                result = -ENOSYS;
 
397
                break;
 
398
        case 10:
 
399
                state = state >= 1 ? 1 : 0;
 
400
                result = wlp_eda_create_node(eda, eth_addr, &dev_addr);
 
401
                if (result < 0 && result != -EEXIST)
 
402
                        goto error;
 
403
                /* Set virtual addr to be same as MAC */
 
404
                result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss,
 
405
                                             eth_addr, tag, state);
 
406
                if (result < 0)
 
407
                        goto error;
 
408
                break;
 
409
        default: /* bad format */
 
410
                result = -EINVAL;
 
411
        }
 
412
error:
 
413
        return result < 0 ? result : size;
 
414
}
 
415
EXPORT_SYMBOL_GPL(wlp_eda_store);