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