/** * ath5k_hw_set_associd - Set BSSID for association * * @ah: The &struct ath5k_hw * @bssid: BSSID * @assoc_id: Assoc id * * Sets the BSSID which trigers the "SME Join" operation */ void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) { u32 low_id, high_id; u16 tim_offset = 0; /* * Set simple BSSID mask on 5212 */ if (ah->ah_version == AR5K_AR5212) { ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask), AR5K_BSS_IDM0); ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask), AR5K_BSS_IDM1); } /* * Set BSSID which triggers the "SME Join" operation */ low_id = AR5K_LOW_ID(bssid); high_id = AR5K_HIGH_ID(bssid); ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0); ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) << AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); if (assoc_id == 0) { ath5k_hw_disable_pspoll(ah); return; } AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM, tim_offset ? tim_offset + 4 : 0); ath5k_hw_enable_pspoll(ah, NULL, 0); }
/** * ath5k_hw_set_opmode - Set PCU operating mode * * @ah: The &struct ath5k_hw * * Initialize PCU for the various operating modes (AP/STA etc) * * For gPXE we always assume STA mode. */ int ath5k_hw_set_opmode(struct ath5k_hw *ah) { u32 pcu_reg, beacon_reg, low_id, high_id; /* Preserve rest settings */ pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0)); beacon_reg = 0; pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_PWR_SV : 0); /* * Set PCU registers */ low_id = AR5K_LOW_ID(ah->ah_sta_id); high_id = AR5K_HIGH_ID(ah->ah_sta_id); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); /* * Set Beacon Control Register on 5210 */ if (ah->ah_version == AR5K_AR5210) ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR); return 0; }
/** * ath5k_hw_set_opmode - Set PCU operating mode * * @ah: The &struct ath5k_hw * * Initialize PCU for the various operating modes (AP/STA etc) * * NOTE: ah->ah_op_mode must be set before calling this. */ int ath5k_hw_set_opmode(struct ath5k_hw *ah) { u32 pcu_reg, beacon_reg, low_id, high_id; pcu_reg = 0; beacon_reg = 0; ATH5K_TRACE(ah->ah_sc); switch (ah->ah_op_mode) { case NL80211_IFTYPE_ADHOC: pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_NO_PSPOLL : 0); beacon_reg |= AR5K_BCR_ADHOC; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_NO_PSPOLL : 0); beacon_reg |= AR5K_BCR_AP; break; case NL80211_IFTYPE_STATION: pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_PWR_SV : 0); case NL80211_IFTYPE_MONITOR: pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_NO_PSPOLL : 0); break; default: return -EINVAL; } /* * Set PCU registers */ low_id = AR5K_LOW_ID(ah->ah_sta_id); high_id = AR5K_HIGH_ID(ah->ah_sta_id); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); /* * Set Beacon Control Register on 5210 */ if (ah->ah_version == AR5K_AR5210) ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR); return 0; }
/** * ath5k_hw_set_associd - Set BSSID for association * * @ah: The &struct ath5k_hw * @bssid: BSSID * @assoc_id: Assoc id * * Sets the BSSID which trigers the "SME Join" operation */ void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) { u32 low_id, high_id; /* * Set simple BSSID mask on 5212 */ if (ah->ah_version == AR5K_AR5212) { ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask), AR5K_BSS_IDM0); ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask), AR5K_BSS_IDM1); } /* * Set BSSID which triggers the "SME Join" operation */ low_id = AR5K_LOW_ID(bssid); high_id = AR5K_HIGH_ID(bssid); ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0); ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) << AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); }
/** * ath5k_hw_set_lladdr - Set station id * * @ah: The &struct ath5k_hw * @mac: The card's mac address * * Set station id on hw using the provided mac address */ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) { u32 low_id, high_id; ATH5K_TRACE(ah->ah_sc); /* Set new station ID */ memcpy(ah->ah_sta_id, mac, ETH_ALEN); low_id = AR5K_LOW_ID(mac); high_id = AR5K_HIGH_ID(mac); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, high_id, AR5K_STA_ID1); return 0; }
/* * Simple example: on your card you have have two BSSes you have created with * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. * There is another BSSID-03 but you are not part of it. For simplicity's sake, * assuming only 4 bits for a mac address and for BSSIDs you can then have: * * \ * MAC: 0001 | * BSSID-01: 0100 | --> Belongs to us * BSSID-02: 1001 | * / * ------------------- * BSSID-03: 0110 | --> External * ------------------- * * Our bssid_mask would then be: * * On loop iteration for BSSID-01: * ~(0001 ^ 0100) -> ~(0101) * -> 1010 * bssid_mask = 1010 * * On loop iteration for BSSID-02: * bssid_mask &= ~(0001 ^ 1001) * bssid_mask = (1010) & ~(0001 ^ 1001) * bssid_mask = (1010) & ~(1001) * bssid_mask = (1010) & (0110) * bssid_mask = 0010 * * A bssid_mask of 0010 means "only pay attention to the second least * significant bit". This is because its the only bit common * amongst the MAC and all BSSIDs we support. To findout what the real * common bit is we can simply "&" the bssid_mask now with any BSSID we have * or our MAC address (we assume the hardware uses the MAC address). * * Now, suppose there's an incoming frame for BSSID-03: * * IFRAME-01: 0110 * * An easy eye-inspeciton of this already should tell you that this frame * will not pass our check. This is beacuse the bssid_mask tells the * hardware to only look at the second least significant bit and the * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB * as 1, which does not match 0. * * So with IFRAME-01 we *assume* the hardware will do: * * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; * --> allow = (0010) == 0000 ? 1 : 0; * --> allow = 0 * * Lets now test a frame that should work: * * IFRAME-02: 0001 (we should allow) * * allow = (0001 & 1010) == 1010 * * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; * --> allow = (0010) == (0010) * --> allow = 1 * * Other examples: * * IFRAME-03: 0100 --> allowed * IFRAME-04: 1001 --> allowed * IFRAME-05: 1101 --> allowed but its not for us!!! * */ int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) { u32 low_id, high_id; ATH5K_TRACE(ah->ah_sc); if (ah->ah_version == AR5K_AR5212) { low_id = AR5K_LOW_ID(mask); high_id = AR5K_HIGH_ID(mask); ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0); ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1); return 0; } return -EIO; }
/** * ath5k_hw_set_lladdr - Set station id * * @ah: The &struct ath5k_hw * @mac: The card's mac address * * Set station id on hw using the provided mac address */ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) { u32 low_id, high_id; u32 pcu_reg; /* Set new station ID */ memcpy(ah->ah_sta_id, mac, ETH_ALEN); pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; low_id = AR5K_LOW_ID(mac); high_id = AR5K_HIGH_ID(mac); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); return 0; }
/* * Simple example: on your card you have have two BSSes you have created with * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. * There is another BSSID-03 but you are not part of it. For simplicity's sake, * assuming only 4 bits for a mac address and for BSSIDs you can then have: * * \ * MAC: 0001 | * BSSID-01: 0100 | --> Belongs to us * BSSID-02: 1001 | * / * ------------------- * BSSID-03: 0110 | --> External * ------------------- * * Our bssid_mask would then be: * * On loop iteration for BSSID-01: * ~(0001 ^ 0100) -> ~(0101) * -> 1010 * bssid_mask = 1010 * * On loop iteration for BSSID-02: * bssid_mask &= ~(0001 ^ 1001) * bssid_mask = (1010) & ~(0001 ^ 1001) * bssid_mask = (1010) & ~(1001) * bssid_mask = (1010) & (0110) * bssid_mask = 0010 * * A bssid_mask of 0010 means "only pay attention to the second least * significant bit". This is because its the only bit common * amongst the MAC and all BSSIDs we support. To findout what the real * common bit is we can simply "&" the bssid_mask now with any BSSID we have * or our MAC address (we assume the hardware uses the MAC address). * * Now, suppose there's an incoming frame for BSSID-03: * * IFRAME-01: 0110 * * An easy eye-inspeciton of this already should tell you that this frame * will not pass our check. This is beacuse the bssid_mask tells the * hardware to only look at the second least significant bit and the * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB * as 1, which does not match 0. * * So with IFRAME-01 we *assume* the hardware will do: * * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; * --> allow = (0010) == 0000 ? 1 : 0; * --> allow = 0 * * Lets now test a frame that should work: * * IFRAME-02: 0001 (we should allow) * * allow = (0001 & 1010) == 1010 * * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; * --> allow = (0010) == (0010) * --> allow = 1 * * Other examples: * * IFRAME-03: 0100 --> allowed * IFRAME-04: 1001 --> allowed * IFRAME-05: 1101 --> allowed but its not for us!!! * */ int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) { u32 low_id, high_id; /* Cache bssid mask so that we can restore it * on reset */ memcpy(ah->ah_bssid_mask, mask, ETH_ALEN); if (ah->ah_version == AR5K_AR5212) { low_id = AR5K_LOW_ID(mask); high_id = AR5K_HIGH_ID(mask); ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0); ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1); return 0; } return -EIO; }
int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) { u32 low_id, high_id; ATH5K_TRACE(ah->ah_sc); /* Invalid entry (key table overflow) */ AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); /* MAC may be NULL if it's a broadcast key. In this case no need to * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */ if (unlikely(mac == NULL)) { low_id = 0xffffffff; high_id = 0xffff | AR5K_KEYTABLE_VALID; } else { low_id = AR5K_LOW_ID(mac); high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID; } ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry)); ath5k_hw_reg_write(ah, high_id, AR5K_KEYTABLE_MAC1(entry)); return 0; }
/** * ath5k_hw_set_opmode - Set PCU operating mode * * @ah: The &struct ath5k_hw * * Initialize PCU for the various operating modes (AP/STA etc) * * NOTE: ah->ah_op_mode must be set before calling this. */ int ath5k_hw_set_opmode(struct ath5k_hw *ah) { u32 pcu_reg, beacon_reg, low_id, high_id; /* Preserve rest settings */ pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0)); beacon_reg = 0; ATH5K_TRACE(ah->ah_sc); switch (ah->ah_op_mode) { case NL80211_IFTYPE_ADHOC: pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_KEYSRCH_MODE; beacon_reg |= AR5K_BCR_ADHOC; if (ah->ah_version == AR5K_AR5210) pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; else AR5K_REG_ENABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE; beacon_reg |= AR5K_BCR_AP; if (ah->ah_version == AR5K_AR5210) pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; else AR5K_REG_DISABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS); break; case NL80211_IFTYPE_STATION: pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_PWR_SV : 0); case NL80211_IFTYPE_MONITOR: pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_NO_PSPOLL : 0); break; default: return -EINVAL; } /* * Set PCU registers */ low_id = AR5K_LOW_ID(ah->ah_sta_id); high_id = AR5K_HIGH_ID(ah->ah_sta_id); ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); /* * Set Beacon Control Register on 5210 */ if (ah->ah_version == AR5K_AR5210) ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR); return 0; }