diff options
Diffstat (limited to 'drivers/net/wireless/ath/hw.c')
-rw-r--r-- | drivers/net/wireless/ath/hw.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c new file mode 100644 index 000000000000..ecc9eb01f4fa --- /dev/null +++ b/drivers/net/wireless/ath/hw.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2009 Atheros Communications Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <asm/unaligned.h> | ||
18 | |||
19 | #include "ath.h" | ||
20 | #include "reg.h" | ||
21 | |||
22 | #define REG_READ common->ops->read | ||
23 | #define REG_WRITE common->ops->write | ||
24 | |||
25 | /** | ||
26 | * ath_hw_set_bssid_mask - filter out bssids we listen | ||
27 | * | ||
28 | * @common: the ath_common struct for the device. | ||
29 | * | ||
30 | * BSSID masking is a method used by AR5212 and newer hardware to inform PCU | ||
31 | * which bits of the interface's MAC address should be looked at when trying | ||
32 | * to decide which packets to ACK. In station mode and AP mode with a single | ||
33 | * BSS every bit matters since we lock to only one BSS. In AP mode with | ||
34 | * multiple BSSes (virtual interfaces) not every bit matters because hw must | ||
35 | * accept frames for all BSSes and so we tweak some bits of our mac address | ||
36 | * in order to have multiple BSSes. | ||
37 | * | ||
38 | * NOTE: This is a simple filter and does *not* filter out all | ||
39 | * relevant frames. Some frames that are not for us might get ACKed from us | ||
40 | * by PCU because they just match the mask. | ||
41 | * | ||
42 | * When handling multiple BSSes you can get the BSSID mask by computing the | ||
43 | * set of ~ ( MAC XOR BSSID ) for all bssids we handle. | ||
44 | * | ||
45 | * When you do this you are essentially computing the common bits of all your | ||
46 | * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with | ||
47 | * the MAC address to obtain the relevant bits and compare the result with | ||
48 | * (frame's BSSID & mask) to see if they match. | ||
49 | * | ||
50 | * Simple example: on your card you have have two BSSes you have created with | ||
51 | * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. | ||
52 | * There is another BSSID-03 but you are not part of it. For simplicity's sake, | ||
53 | * assuming only 4 bits for a mac address and for BSSIDs you can then have: | ||
54 | * | ||
55 | * \ | ||
56 | * MAC: 0001 | | ||
57 | * BSSID-01: 0100 | --> Belongs to us | ||
58 | * BSSID-02: 1001 | | ||
59 | * / | ||
60 | * ------------------- | ||
61 | * BSSID-03: 0110 | --> External | ||
62 | * ------------------- | ||
63 | * | ||
64 | * Our bssid_mask would then be: | ||
65 | * | ||
66 | * On loop iteration for BSSID-01: | ||
67 | * ~(0001 ^ 0100) -> ~(0101) | ||
68 | * -> 1010 | ||
69 | * bssid_mask = 1010 | ||
70 | * | ||
71 | * On loop iteration for BSSID-02: | ||
72 | * bssid_mask &= ~(0001 ^ 1001) | ||
73 | * bssid_mask = (1010) & ~(0001 ^ 1001) | ||
74 | * bssid_mask = (1010) & ~(1001) | ||
75 | * bssid_mask = (1010) & (0110) | ||
76 | * bssid_mask = 0010 | ||
77 | * | ||
78 | * A bssid_mask of 0010 means "only pay attention to the second least | ||
79 | * significant bit". This is because its the only bit common | ||
80 | * amongst the MAC and all BSSIDs we support. To findout what the real | ||
81 | * common bit is we can simply "&" the bssid_mask now with any BSSID we have | ||
82 | * or our MAC address (we assume the hardware uses the MAC address). | ||
83 | * | ||
84 | * Now, suppose there's an incoming frame for BSSID-03: | ||
85 | * | ||
86 | * IFRAME-01: 0110 | ||
87 | * | ||
88 | * An easy eye-inspeciton of this already should tell you that this frame | ||
89 | * will not pass our check. This is beacuse the bssid_mask tells the | ||
90 | * hardware to only look at the second least significant bit and the | ||
91 | * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB | ||
92 | * as 1, which does not match 0. | ||
93 | * | ||
94 | * So with IFRAME-01 we *assume* the hardware will do: | ||
95 | * | ||
96 | * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; | ||
97 | * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; | ||
98 | * --> allow = (0010) == 0000 ? 1 : 0; | ||
99 | * --> allow = 0 | ||
100 | * | ||
101 | * Lets now test a frame that should work: | ||
102 | * | ||
103 | * IFRAME-02: 0001 (we should allow) | ||
104 | * | ||
105 | * allow = (0001 & 1010) == 1010 | ||
106 | * | ||
107 | * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; | ||
108 | * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; | ||
109 | * --> allow = (0010) == (0010) | ||
110 | * --> allow = 1 | ||
111 | * | ||
112 | * Other examples: | ||
113 | * | ||
114 | * IFRAME-03: 0100 --> allowed | ||
115 | * IFRAME-04: 1001 --> allowed | ||
116 | * IFRAME-05: 1101 --> allowed but its not for us!!! | ||
117 | * | ||
118 | */ | ||
119 | void ath_hw_setbssidmask(struct ath_common *common) | ||
120 | { | ||
121 | void *ah = common->ah; | ||
122 | |||
123 | REG_WRITE(ah, get_unaligned_le32(common->bssidmask), AR_BSSMSKL); | ||
124 | REG_WRITE(ah, get_unaligned_le16(common->bssidmask + 4), AR_BSSMSKU); | ||
125 | } | ||
126 | EXPORT_SYMBOL(ath_hw_setbssidmask); | ||