提交 8feceb67 编写于 作者: V Vasanthakumar Thiagarajan 提交者: John W. Linville

ath9k: Re-order functions in main.c

Some of the functions in main.c are re-ordered in such
a way that all local functions are defined before mac80211
and pci callbacks.
Signed-off-by: NVasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: NJohn W. Linville <linville@tuxdriver.com>
上级 f8e77cae
...@@ -325,148 +325,49 @@ static u8 parse_mpdudensity(u8 mpdudensity) ...@@ -325,148 +325,49 @@ static u8 parse_mpdudensity(u8 mpdudensity)
} }
} }
static int ath9k_start(struct ieee80211_hw *hw) static void ath9k_ht_conf(struct ath_softc *sc,
{ struct ieee80211_bss_conf *bss_conf)
struct ath_softc *sc = hw->priv;
struct ieee80211_channel *curchan = hw->conf.channel;
int error = 0, pos;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with "
"initial channel: %d MHz\n", __func__, curchan->center_freq);
/* setup initial channel */
pos = ath_get_channel(sc, curchan);
if (pos == -1) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
return -EINVAL;
}
sc->sc_ah->ah_channels[pos].chanmode =
(curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A;
/* open ath_dev */
error = ath_open(sc, &sc->sc_ah->ah_channels[pos]);
if (error) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to complete ath_open\n", __func__);
return error;
}
ieee80211_wake_queues(hw);
return 0;
}
static int ath9k_tx(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct ath_softc *sc = hw->priv;
int hdrlen, padsize;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
/*
* As a temporary workaround, assign seq# here; this will likely need
* to be cleaned up to work better with Beacon transmission and virtual
* BSSes.
*/
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
sc->seq_no += 0x10;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
hdr->seq_ctrl |= cpu_to_le16(sc->seq_no);
}
/* Add the padding after the header if this is not already done */
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
if (hdrlen & 3) {
padsize = hdrlen % 4;
if (skb_headroom(skb) < padsize)
return -1;
skb_push(skb, padsize);
memmove(skb->data, skb->data + padsize, hdrlen);
}
DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n",
__func__,
skb);
if (ath_tx_start(sc, skb) != 0) {
DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__);
dev_kfree_skb_any(skb);
/* FIXME: Check for proper return value from ATH_DEV */
return 0;
}
return 0;
}
static void ath9k_stop(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
int error;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__);
error = ath_suspend(sc);
if (error)
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Device is no longer present\n", __func__);
ieee80211_stop_queues(hw);
}
static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{ {
struct ath_softc *sc = hw->priv; #define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14)
int error, ic_opmode = 0; struct ath_ht_info *ht_info = &sc->sc_ht_info;
/* Support only vap for now */
if (sc->sc_nvaps) if (bss_conf->assoc_ht) {
return -ENOBUFS; ht_info->ext_chan_offset =
bss_conf->ht_bss_conf->bss_cap &
IEEE80211_HT_IE_CHA_SEC_OFFSET;
switch (conf->type) { if (!(bss_conf->ht_conf->cap &
case IEEE80211_IF_TYPE_STA: IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
ic_opmode = ATH9K_M_STA; (bss_conf->ht_bss_conf->bss_cap &
break; IEEE80211_HT_IE_CHA_WIDTH))
case IEEE80211_IF_TYPE_IBSS: ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040;
ic_opmode = ATH9K_M_IBSS; else
break; ht_info->tx_chan_width = ATH9K_HT_MACMODE_20;
case IEEE80211_IF_TYPE_AP:
ic_opmode = ATH9K_M_HOSTAP;
break;
default:
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Interface type %d not yet supported\n",
__func__, conf->type);
return -EOPNOTSUPP;
}
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a VAP of type: %d\n", ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width);
__func__, ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
ic_opmode); bss_conf->ht_conf->ampdu_factor);
ht_info->mpdudensity =
parse_mpdudensity(bss_conf->ht_conf->ampdu_density);
error = ath_vap_attach(sc, 0, conf->vif, ic_opmode);
if (error) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to attach vap, error: %d\n",
__func__, error);
return error;
} }
return 0; #undef IEEE80211_HT_CAP_40MHZ_INTOLERANT
} }
static void ath9k_remove_interface(struct ieee80211_hw *hw, static void ath9k_bss_assoc_info(struct ath_softc *sc,
struct ieee80211_if_init_conf *conf) struct ieee80211_bss_conf *bss_conf)
{ {
struct ath_softc *sc = hw->priv; struct ieee80211_hw *hw = sc->hw;
struct ieee80211_channel *curchan = hw->conf.channel;
struct ath_vap *avp; struct ath_vap *avp;
int error; int pos;
DECLARE_MAC_BUF(mac);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__); if (bss_conf->assoc) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n",
__func__,
bss_conf->aid);
avp = sc->sc_vaps[0]; avp = sc->sc_vaps[0];
if (avp == NULL) { if (avp == NULL) {
...@@ -475,38 +376,30 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, ...@@ -475,38 +376,30 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
return; return;
} }
#ifdef CONFIG_SLOW_ANT_DIV /* New association, store aid */
ath_slow_ant_div_stop(&sc->sc_antdiv); if (avp->av_opmode == ATH9K_M_STA) {
#endif sc->sc_curaid = bss_conf->aid;
ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
/* Update ratectrl */ sc->sc_curaid);
ath_rate_newstate(sc, avp);
/* Reclaim beacon resources */
if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP ||
sc->sc_ah->ah_opmode == ATH9K_M_IBSS) {
ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
ath_beacon_return(sc, avp);
} }
/* Set interrupt mask */ /* Configure the beacon */
sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); ath_beacon_config(sc, 0);
ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask & ~ATH9K_INT_GLOBAL); sc->sc_flags |= SC_OP_BEACONS;
sc->sc_flags &= ~SC_OP_BEACONS;
error = ath_vap_detach(sc, 0); /* Reset rssi stats */
if (error) sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
DPRINTF(sc, ATH_DBG_FATAL, sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
"%s: Unable to detach vap, error: %d\n", sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
__func__, error); sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
}
static int ath9k_config(struct ieee80211_hw *hw, /* Update chainmask */
struct ieee80211_conf *conf) ath_update_chainmask(sc, bss_conf->assoc_ht);
{
struct ath_softc *sc = hw->priv; DPRINTF(sc, ATH_DBG_CONFIG,
struct ieee80211_channel *curchan = hw->conf.channel; "%s: bssid %s aid 0x%x\n",
int pos; __func__,
print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n", DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
__func__, __func__,
...@@ -514,898 +407,1005 @@ static int ath9k_config(struct ieee80211_hw *hw, ...@@ -514,898 +407,1005 @@ static int ath9k_config(struct ieee80211_hw *hw,
pos = ath_get_channel(sc, curchan); pos = ath_get_channel(sc, curchan);
if (pos == -1) { if (pos == -1) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__); DPRINTF(sc, ATH_DBG_FATAL,
return -EINVAL; "%s: Invalid channel\n", __func__);
return;
} }
if (hw->conf.ht_conf.ht_supported)
sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan);
else
sc->sc_ah->ah_channels[pos].chanmode = sc->sc_ah->ah_channels[pos].chanmode =
(curchan->band == IEEE80211_BAND_2GHZ) ? (curchan->band == IEEE80211_BAND_2GHZ) ?
CHANNEL_G : CHANNEL_A; CHANNEL_G : CHANNEL_A;
if (sc->sc_curaid && hw->conf.ht_conf.ht_supported)
sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan);
sc->sc_config.txpowlimit = 2 * conf->power_level;
/* set h/w channel */ /* set h/w channel */
if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
DPRINTF(sc, ATH_DBG_FATAL, "%s: Unable to set channel\n", DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to set channel\n",
__func__); __func__);
return 0; ath_rate_newstate(sc, avp);
/* Update ratectrl about the new state */
ath_rc_node_update(hw, avp->rc_node);
} else {
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Bss Info DISSOC\n", __func__);
sc->sc_curaid = 0;
}
} }
static int ath9k_config_interface(struct ieee80211_hw *hw, void ath_get_beaconconfig(struct ath_softc *sc,
struct ieee80211_vif *vif, int if_id,
struct ieee80211_if_conf *conf) struct ath_beacon_config *conf)
{ {
struct ath_softc *sc = hw->priv; struct ieee80211_hw *hw = sc->hw;
struct ath_hal *ah = sc->sc_ah;
struct ath_vap *avp;
u32 rfilt = 0;
int error, i;
DECLARE_MAC_BUF(mac);
avp = sc->sc_vaps[0]; /* fill in beacon config data */
if (avp == NULL) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
__func__);
return -EINVAL;
}
/* TODO: Need to decide which hw opmode to use for multi-interface conf->beacon_interval = hw->conf.beacon_int;
* cases */ conf->listen_interval = 100;
if (vif->type == IEEE80211_IF_TYPE_AP && conf->dtim_count = 1;
ah->ah_opmode != ATH9K_M_HOSTAP) { conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
ah->ah_opmode = ATH9K_M_HOSTAP; }
ath9k_hw_setopmode(ah);
ath9k_hw_write_associd(ah, sc->sc_myaddr, 0);
/* Request full reset to get hw opmode changed properly */
sc->sc_flags |= SC_OP_FULL_RESET;
}
if ((conf->changed & IEEE80211_IFCC_BSSID) && void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
!is_zero_ether_addr(conf->bssid)) { struct ath_xmit_status *tx_status, struct ath_node *an)
switch (vif->type) { {
case IEEE80211_IF_TYPE_STA: struct ieee80211_hw *hw = sc->hw;
case IEEE80211_IF_TYPE_IBSS: struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
/* Update ratectrl about the new state */
ath_rate_newstate(sc, avp);
/* Set BSSID */ DPRINTF(sc, ATH_DBG_XMIT,
memcpy(sc->sc_curbssid, conf->bssid, ETH_ALEN); "%s: TX complete: skb: %p\n", __func__, skb);
sc->sc_curaid = 0;
ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
sc->sc_curaid);
/* Set aggregation protection mode parameters */ if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
sc->sc_config.ath_aggr_prot = 0; tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
/* free driver's private data area of tx_info */
if (tx_info->driver_data[0] != NULL)
kfree(tx_info->driver_data[0]);
tx_info->driver_data[0] = NULL;
}
/* if (tx_status->flags & ATH_TX_BAR) {
* Reset our TSF so that its value is lower than the tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
* beacon that we are trying to catch. tx_status->flags &= ~ATH_TX_BAR;
* Only then hw will update its TSF register with the }
* new beacon. Reset the TSF before setting the BSSID
* to avoid allowing in any frames that would update
* our TSF only to have us clear it
* immediately thereafter.
*/
ath9k_hw_reset_tsf(sc->sc_ah);
/* Disable BMISS interrupt when we're not associated */ if (tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY)) {
ath9k_hw_set_interrupts(sc->sc_ah, if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) {
sc->sc_imask & /* Frame was not ACKed, but an ACK was expected */
~(ATH9K_INT_SWBA | ATH9K_INT_BMISS)); tx_info->status.excessive_retries = 1;
sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); }
} else {
/* Frame was ACKed */
tx_info->flags |= IEEE80211_TX_STAT_ACK;
}
DPRINTF(sc, ATH_DBG_CONFIG, tx_info->status.retry_count = tx_status->retries;
"%s: RX filter 0x%x bssid %s aid 0x%x\n",
__func__, rfilt,
print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
/* need to reconfigure the beacon */ ieee80211_tx_status(hw, skb);
sc->sc_flags &= ~SC_OP_BEACONS ; if (an)
ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE);
}
break; int _ath_rx_indicate(struct ath_softc *sc,
default: struct sk_buff *skb,
break; struct ath_recv_status *status,
} u16 keyix)
{
struct ieee80211_hw *hw = sc->hw;
struct ath_node *an = NULL;
struct ieee80211_rx_status rx_status;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
int padsize;
enum ATH_RX_TYPE st;
/* see if any padding is done by the hw and remove it */
if (hdrlen & 3) {
padsize = hdrlen % 4;
memmove(skb->data + padsize, skb->data, hdrlen);
skb_pull(skb, padsize);
} }
if ((conf->changed & IEEE80211_IFCC_BEACON) && /* Prepare rx status */
((vif->type == IEEE80211_IF_TYPE_IBSS) || ath9k_rx_prepare(sc, skb, status, &rx_status);
(vif->type == IEEE80211_IF_TYPE_AP))) {
/*
* Allocate and setup the beacon frame.
*
* Stop any previous beacon DMA. This may be
* necessary, for example, when an ibss merge
* causes reconfiguration; we may be called
* with beacon transmission active.
*/
ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
error = ath_beacon_alloc(sc, 0); if (!(keyix == ATH9K_RXKEYIX_INVALID) &&
if (error != 0) !(status->flags & ATH_RX_DECRYPT_ERROR)) {
return error; rx_status.flag |= RX_FLAG_DECRYPTED;
} else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
&& !(status->flags & ATH_RX_DECRYPT_ERROR)
&& skb->len >= hdrlen + 4) {
keyix = skb->data[hdrlen + 3] >> 6;
ath_beacon_sync(sc, 0); if (test_bit(keyix, sc->sc_keymap))
rx_status.flag |= RX_FLAG_DECRYPTED;
} }
/* Check for WLAN_CAPABILITY_PRIVACY ? */ spin_lock_bh(&sc->node_lock);
if ((avp->av_opmode != IEEE80211_IF_TYPE_STA)) { an = ath_node_find(sc, hdr->addr2);
for (i = 0; i < IEEE80211_WEP_NKID; i++) spin_unlock_bh(&sc->node_lock);
if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i))
ath9k_hw_keysetmac(sc->sc_ah,
(u16)i,
sc->sc_curbssid);
}
/* Only legacy IBSS for now */ if (an) {
if (vif->type == IEEE80211_IF_TYPE_IBSS) ath_rx_input(sc, an,
ath_update_chainmask(sc, 0); hw->conf.ht_conf.ht_supported,
skb, status, &st);
}
if (!an || (st != ATH_RX_CONSUMED))
__ieee80211_rx(hw, skb, &rx_status);
return 0; return 0;
} }
#define SUPPORTED_FILTERS \ int ath_rx_subframe(struct ath_node *an,
(FIF_PROMISC_IN_BSS | \ struct sk_buff *skb,
FIF_ALLMULTI | \ struct ath_recv_status *status)
FIF_CONTROL | \
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC | \
FIF_FCSFAIL)
/* FIXME: sc->sc_full_reset ? */
static void ath9k_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
int mc_count,
struct dev_mc_list *mclist)
{ {
struct ath_softc *sc = hw->priv; struct ath_softc *sc = an->an_sc;
u32 rfilt; struct ieee80211_hw *hw = sc->hw;
struct ieee80211_rx_status rx_status;
changed_flags &= SUPPORTED_FILTERS;
*total_flags &= SUPPORTED_FILTERS;
sc->rx_filter = *total_flags; /* Prepare rx status */
rfilt = ath_calcrxfilter(sc); ath9k_rx_prepare(sc, skb, status, &rx_status);
ath9k_hw_setrxfilter(sc->sc_ah, rfilt); if (!(status->flags & ATH_RX_DECRYPT_ERROR))
rx_status.flag |= RX_FLAG_DECRYPTED;
if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { __ieee80211_rx(hw, skb, &rx_status);
if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
ath9k_hw_write_associd(sc->sc_ah, ath_bcast_mac, 0);
}
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set HW RX filter: 0x%x\n", return 0;
__func__, sc->rx_filter);
} }
static void ath9k_sta_notify(struct ieee80211_hw *hw, /********************************/
struct ieee80211_vif *vif, /* LED functions */
enum sta_notify_cmd cmd, /********************************/
const u8 *addr)
{
struct ath_softc *sc = hw->priv;
struct ath_node *an;
unsigned long flags;
DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&sc->node_lock, flags); static void ath_led_brightness(struct led_classdev *led_cdev,
an = ath_node_find(sc, (u8 *) addr); enum led_brightness brightness)
spin_unlock_irqrestore(&sc->node_lock, flags); {
struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
struct ath_softc *sc = led->sc;
switch (cmd) { switch (brightness) {
case STA_NOTIFY_ADD: case LED_OFF:
spin_lock_irqsave(&sc->node_lock, flags); if (led->led_type == ATH_LED_ASSOC ||
if (!an) { led->led_type == ATH_LED_RADIO)
ath_node_attach(sc, (u8 *)addr, 0); sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %s\n", ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
__func__, (led->led_type == ATH_LED_RADIO) ? 1 :
print_mac(mac, addr)); !!(sc->sc_flags & SC_OP_LED_ASSOCIATED));
} else {
ath_node_get(sc, (u8 *)addr);
}
spin_unlock_irqrestore(&sc->node_lock, flags);
break; break;
case STA_NOTIFY_REMOVE: case LED_FULL:
if (!an) if (led->led_type == ATH_LED_ASSOC)
DPRINTF(sc, ATH_DBG_FATAL, sc->sc_flags |= SC_OP_LED_ASSOCIATED;
"%s: Removal of a non-existent node\n", ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0);
__func__);
else {
ath_node_put(sc, an, ATH9K_BH_STATUS_INTACT);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Put a node: %s\n",
__func__,
print_mac(mac, addr));
}
break; break;
default: default:
break; break;
} }
} }
static int ath9k_conf_tx(struct ieee80211_hw *hw, static int ath_register_led(struct ath_softc *sc, struct ath_led *led,
u16 queue, char *trigger)
const struct ieee80211_tx_queue_params *params)
{ {
struct ath_softc *sc = hw->priv; int ret;
struct ath9k_tx_queue_info qi;
int ret = 0, qnum;
if (queue >= WME_NUM_AC) led->sc = sc;
return 0; led->led_cdev.name = led->name;
led->led_cdev.default_trigger = trigger;
qi.tqi_aifs = params->aifs; led->led_cdev.brightness_set = ath_led_brightness;
qi.tqi_cwmin = params->cw_min;
qi.tqi_cwmax = params->cw_max;
qi.tqi_burstTime = params->txop;
qnum = ath_get_hal_qnum(queue, sc);
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Configure tx [queue/halq] [%d/%d], "
"aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
__func__,
queue,
qnum,
params->aifs,
params->cw_min,
params->cw_max,
params->txop);
ret = ath_txq_update(sc, qnum, &qi); ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev);
if (ret) if (ret)
DPRINTF(sc, ATH_DBG_FATAL, DPRINTF(sc, ATH_DBG_FATAL,
"%s: TXQ Update failed\n", __func__); "Failed to register led:%s", led->name);
else
led->registered = 1;
return ret; return ret;
} }
static int ath9k_set_key(struct ieee80211_hw *hw, static void ath_unregister_led(struct ath_led *led)
enum set_key_cmd cmd,
const u8 *local_addr,
const u8 *addr,
struct ieee80211_key_conf *key)
{ {
struct ath_softc *sc = hw->priv; if (led->registered) {
int ret = 0; led_classdev_unregister(&led->led_cdev);
led->registered = 0;
DPRINTF(sc, ATH_DBG_KEYCACHE, " %s: Set HW Key\n", __func__);
switch (cmd) {
case SET_KEY:
ret = ath_key_config(sc, addr, key);
if (!ret) {
set_bit(key->keyidx, sc->sc_keymap);
key->hw_key_idx = key->keyidx;
/* push IV and Michael MIC generation to stack */
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (key->alg == ALG_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
}
break;
case DISABLE_KEY:
ath_key_delete(sc, key);
clear_bit(key->keyidx, sc->sc_keymap);
sc->sc_keytype = ATH9K_CIPHER_CLR;
break;
default:
ret = -EINVAL;
} }
}
return ret; static void ath_deinit_leds(struct ath_softc *sc)
{
ath_unregister_led(&sc->assoc_led);
sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
ath_unregister_led(&sc->tx_led);
ath_unregister_led(&sc->rx_led);
ath_unregister_led(&sc->radio_led);
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
} }
static void ath9k_ht_conf(struct ath_softc *sc, static void ath_init_leds(struct ath_softc *sc)
struct ieee80211_bss_conf *bss_conf)
{ {
#define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14) char *trigger;
struct ath_ht_info *ht_info = &sc->sc_ht_info; int ret;
if (bss_conf->assoc_ht) { /* Configure gpio 1 for output */
ht_info->ext_chan_offset = ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
bss_conf->ht_bss_conf->bss_cap & AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
IEEE80211_HT_IE_CHA_SEC_OFFSET; /* LED off, active low */
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
if (!(bss_conf->ht_conf->cap & trigger = ieee80211_get_radio_led_name(sc->hw);
IEEE80211_HT_CAP_40MHZ_INTOLERANT) && snprintf(sc->radio_led.name, sizeof(sc->radio_led.name),
(bss_conf->ht_bss_conf->bss_cap & "ath9k-%s:radio", wiphy_name(sc->hw->wiphy));
IEEE80211_HT_IE_CHA_WIDTH)) ret = ath_register_led(sc, &sc->radio_led, trigger);
ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040; sc->radio_led.led_type = ATH_LED_RADIO;
else if (ret)
ht_info->tx_chan_width = ATH9K_HT_MACMODE_20; goto fail;
ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width); trigger = ieee80211_get_assoc_led_name(sc->hw);
ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name),
bss_conf->ht_conf->ampdu_factor); "ath9k-%s:assoc", wiphy_name(sc->hw->wiphy));
ht_info->mpdudensity = ret = ath_register_led(sc, &sc->assoc_led, trigger);
parse_mpdudensity(bss_conf->ht_conf->ampdu_density); sc->assoc_led.led_type = ATH_LED_ASSOC;
if (ret)
goto fail;
} trigger = ieee80211_get_tx_led_name(sc->hw);
snprintf(sc->tx_led.name, sizeof(sc->tx_led.name),
"ath9k-%s:tx", wiphy_name(sc->hw->wiphy));
ret = ath_register_led(sc, &sc->tx_led, trigger);
sc->tx_led.led_type = ATH_LED_TX;
if (ret)
goto fail;
#undef IEEE80211_HT_CAP_40MHZ_INTOLERANT trigger = ieee80211_get_rx_led_name(sc->hw);
snprintf(sc->rx_led.name, sizeof(sc->rx_led.name),
"ath9k-%s:rx", wiphy_name(sc->hw->wiphy));
ret = ath_register_led(sc, &sc->rx_led, trigger);
sc->rx_led.led_type = ATH_LED_RX;
if (ret)
goto fail;
return;
fail:
ath_deinit_leds(sc);
} }
static void ath9k_bss_assoc_info(struct ath_softc *sc, static int ath_detach(struct ath_softc *sc)
struct ieee80211_bss_conf *bss_conf)
{ {
struct ieee80211_hw *hw = sc->hw; struct ieee80211_hw *hw = sc->hw;
struct ieee80211_channel *curchan = hw->conf.channel;
struct ath_vap *avp;
int pos;
DECLARE_MAC_BUF(mac);
if (bss_conf->assoc) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n",
__func__,
bss_conf->aid);
avp = sc->sc_vaps[0];
if (avp == NULL) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
__func__);
return;
}
/* New association, store aid */ DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__);
if (avp->av_opmode == ATH9K_M_STA) {
sc->sc_curaid = bss_conf->aid;
ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
sc->sc_curaid);
}
/* Configure the beacon */ /* Deinit LED control */
ath_beacon_config(sc, 0); ath_deinit_leds(sc);
sc->sc_flags |= SC_OP_BEACONS;
/* Reset rssi stats */ /* Unregister hw */
sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
/* Update chainmask */ ieee80211_unregister_hw(hw);
ath_update_chainmask(sc, bss_conf->assoc_ht);
DPRINTF(sc, ATH_DBG_CONFIG, /* unregister Rate control */
"%s: bssid %s aid 0x%x\n", ath_rate_control_unregister();
__func__,
print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n", /* tx/rx cleanup */
__func__,
curchan->center_freq);
pos = ath_get_channel(sc, curchan); ath_rx_cleanup(sc);
if (pos == -1) { ath_tx_cleanup(sc);
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Invalid channel\n", __func__);
return;
}
if (hw->conf.ht_conf.ht_supported) /* Deinit */
sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan);
else
sc->sc_ah->ah_channels[pos].chanmode =
(curchan->band == IEEE80211_BAND_2GHZ) ?
CHANNEL_G : CHANNEL_A;
/* set h/w channel */ ath_deinit(sc);
if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to set channel\n",
__func__);
ath_rate_newstate(sc, avp); return 0;
/* Update ratectrl about the new state */
ath_rc_node_update(hw, avp->rc_node);
} else {
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Bss Info DISSOC\n", __func__);
sc->sc_curaid = 0;
}
} }
static void ath9k_bss_info_changed(struct ieee80211_hw *hw, static int ath_attach(u16 devid,
struct ieee80211_vif *vif, struct ath_softc *sc)
struct ieee80211_bss_conf *bss_conf,
u32 changed)
{ {
struct ath_softc *sc = hw->priv; struct ieee80211_hw *hw = sc->hw;
int error = 0;
if (changed & BSS_CHANGED_ERP_PREAMBLE) { DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed PREAMBLE %d\n",
__func__,
bss_conf->use_short_preamble);
if (bss_conf->use_short_preamble)
sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
else
sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
}
if (changed & BSS_CHANGED_ERP_CTS_PROT) { error = ath_init(devid, sc);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed CTS PROT %d\n", if (error != 0)
__func__, return error;
bss_conf->use_cts_prot);
if (bss_conf->use_cts_prot &&
hw->conf.channel->band != IEEE80211_BAND_5GHZ)
sc->sc_flags |= SC_OP_PROTECT_ENABLE;
else
sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
}
if (changed & BSS_CHANGED_HT) { /* Init nodes */
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT %d\n",
__func__,
bss_conf->assoc_ht);
ath9k_ht_conf(sc, bss_conf);
}
if (changed & BSS_CHANGED_ASSOC) { INIT_LIST_HEAD(&sc->node_list);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n", spin_lock_init(&sc->node_lock);
__func__,
bss_conf->assoc);
ath9k_bss_assoc_info(sc, bss_conf);
}
}
static u64 ath9k_get_tsf(struct ieee80211_hw *hw) /* get mac address from hardware and set in mac80211 */
{
u64 tsf;
struct ath_softc *sc = hw->priv;
struct ath_hal *ah = sc->sc_ah;
tsf = ath9k_hw_gettsf64(ah); SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr);
return tsf; /* setup channels and rates */
}
static void ath9k_reset_tsf(struct ieee80211_hw *hw) sc->sbands[IEEE80211_BAND_2GHZ].channels =
{ sc->channels[IEEE80211_BAND_2GHZ];
struct ath_softc *sc = hw->priv; sc->sbands[IEEE80211_BAND_2GHZ].bitrates =
struct ath_hal *ah = sc->sc_ah; sc->rates[IEEE80211_BAND_2GHZ];
sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
ath9k_hw_reset_tsf(ah); if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
} /* Setup HT capabilities for 2.4Ghz*/
setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info);
static int ath9k_ampdu_action(struct ieee80211_hw *hw, hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
enum ieee80211_ampdu_mlme_action action, &sc->sbands[IEEE80211_BAND_2GHZ];
const u8 *addr,
u16 tid,
u16 *ssn)
{
struct ath_softc *sc = hw->priv;
int ret = 0;
switch (action) { if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) {
case IEEE80211_AMPDU_RX_START: sc->sbands[IEEE80211_BAND_5GHZ].channels =
ret = ath_rx_aggr_start(sc, addr, tid, ssn); sc->channels[IEEE80211_BAND_5GHZ];
if (ret < 0) sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
DPRINTF(sc, ATH_DBG_FATAL, sc->rates[IEEE80211_BAND_5GHZ];
"%s: Unable to start RX aggregation\n", sc->sbands[IEEE80211_BAND_5GHZ].band =
__func__); IEEE80211_BAND_5GHZ;
break;
case IEEE80211_AMPDU_RX_STOP:
ret = ath_rx_aggr_stop(sc, addr, tid);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to stop RX aggregation\n",
__func__);
break;
case IEEE80211_AMPDU_TX_START:
ret = ath_tx_aggr_start(sc, addr, tid, ssn);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to start TX aggregation\n",
__func__);
else
ieee80211_start_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP:
ret = ath_tx_aggr_stop(sc, addr, tid);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to stop TX aggregation\n",
__func__);
ieee80211_stop_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid); if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
break; /* Setup HT capabilities for 5Ghz*/
default: setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info);
hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
&sc->sbands[IEEE80211_BAND_5GHZ];
}
/* FIXME: Have to figure out proper hw init values later */
hw->queues = 4;
hw->ampdu_queues = 1;
/* Register rate control */
hw->rate_control_algorithm = "ath9k_rate_control";
error = ath_rate_control_register();
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL, DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unknown AMPDU action\n", __func__); "%s: Unable to register rate control "
"algorithm:%d\n", __func__, error);
ath_rate_control_unregister();
goto bad;
} }
return ret; error = ieee80211_register_hw(hw);
} if (error != 0) {
ath_rate_control_unregister();
goto bad;
}
static struct ieee80211_ops ath9k_ops = { /* Initialize LED control */
.tx = ath9k_tx, ath_init_leds(sc);
.start = ath9k_start,
.stop = ath9k_stop,
.add_interface = ath9k_add_interface,
.remove_interface = ath9k_remove_interface,
.config = ath9k_config,
.config_interface = ath9k_config_interface,
.configure_filter = ath9k_configure_filter,
.get_stats = NULL,
.sta_notify = ath9k_sta_notify,
.conf_tx = ath9k_conf_tx,
.get_tx_stats = NULL,
.bss_info_changed = ath9k_bss_info_changed,
.set_tim = NULL,
.set_key = ath9k_set_key,
.hw_scan = NULL,
.get_tkip_seq = NULL,
.set_rts_threshold = NULL,
.set_frag_threshold = NULL,
.set_retry_limit = NULL,
.get_tsf = ath9k_get_tsf,
.reset_tsf = ath9k_reset_tsf,
.tx_last_beacon = NULL,
.ampdu_action = ath9k_ampdu_action
};
void ath_get_beaconconfig(struct ath_softc *sc, /* initialize tx/rx engine */
int if_id,
struct ath_beacon_config *conf)
{
struct ieee80211_hw *hw = sc->hw;
/* fill in beacon config data */ error = ath_tx_init(sc, ATH_TXBUF);
if (error != 0)
goto detach;
conf->beacon_interval = hw->conf.beacon_int; error = ath_rx_init(sc, ATH_RXBUF);
conf->listen_interval = 100; if (error != 0)
conf->dtim_count = 1; goto detach;
conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
return 0;
detach:
ath_detach(sc);
bad:
return error;
} }
void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, static int ath9k_start(struct ieee80211_hw *hw)
struct ath_xmit_status *tx_status, struct ath_node *an)
{ {
struct ieee80211_hw *hw = sc->hw; struct ath_softc *sc = hw->priv;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_channel *curchan = hw->conf.channel;
int error = 0, pos;
DPRINTF(sc, ATH_DBG_XMIT, DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with "
"%s: TX complete: skb: %p\n", __func__, skb); "initial channel: %d MHz\n", __func__, curchan->center_freq);
if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK || /* setup initial channel */
tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
/* free driver's private data area of tx_info */
if (tx_info->driver_data[0] != NULL)
kfree(tx_info->driver_data[0]);
tx_info->driver_data[0] = NULL;
}
if (tx_status->flags & ATH_TX_BAR) { pos = ath_get_channel(sc, curchan);
tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; if (pos == -1) {
tx_status->flags &= ~ATH_TX_BAR; DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
return -EINVAL;
} }
if (tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY)) { sc->sc_ah->ah_channels[pos].chanmode =
if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { (curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A;
/* Frame was not ACKed, but an ACK was expected */
tx_info->status.excessive_retries = 1;
}
} else {
/* Frame was ACKed */
tx_info->flags |= IEEE80211_TX_STAT_ACK;
}
tx_info->status.retry_count = tx_status->retries; /* open ath_dev */
error = ath_open(sc, &sc->sc_ah->ah_channels[pos]);
if (error) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to complete ath_open\n", __func__);
return error;
}
ieee80211_tx_status(hw, skb); ieee80211_wake_queues(hw);
if (an) return 0;
ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE);
} }
int _ath_rx_indicate(struct ath_softc *sc, static int ath9k_tx(struct ieee80211_hw *hw,
struct sk_buff *skb, struct sk_buff *skb)
struct ath_recv_status *status,
u16 keyix)
{ {
struct ieee80211_hw *hw = sc->hw; struct ath_softc *sc = hw->priv;
struct ath_node *an = NULL; int hdrlen, padsize;
struct ieee80211_rx_status rx_status; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
/*
* As a temporary workaround, assign seq# here; this will likely need
* to be cleaned up to work better with Beacon transmission and virtual
* BSSes.
*/
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
int hdrlen = ieee80211_get_hdrlen_from_skb(skb); if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
int padsize; sc->seq_no += 0x10;
enum ATH_RX_TYPE st; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
hdr->seq_ctrl |= cpu_to_le16(sc->seq_no);
}
/* see if any padding is done by the hw and remove it */ /* Add the padding after the header if this is not already done */
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
if (hdrlen & 3) { if (hdrlen & 3) {
padsize = hdrlen % 4; padsize = hdrlen % 4;
memmove(skb->data + padsize, skb->data, hdrlen); if (skb_headroom(skb) < padsize)
skb_pull(skb, padsize); return -1;
skb_push(skb, padsize);
memmove(skb->data, skb->data + padsize, hdrlen);
} }
/* Prepare rx status */ DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n",
ath9k_rx_prepare(sc, skb, status, &rx_status); __func__,
skb);
if (!(keyix == ATH9K_RXKEYIX_INVALID) && if (ath_tx_start(sc, skb) != 0) {
!(status->flags & ATH_RX_DECRYPT_ERROR)) { DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__);
rx_status.flag |= RX_FLAG_DECRYPTED; dev_kfree_skb_any(skb);
} else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) /* FIXME: Check for proper return value from ATH_DEV */
&& !(status->flags & ATH_RX_DECRYPT_ERROR) return 0;
&& skb->len >= hdrlen + 4) {
keyix = skb->data[hdrlen + 3] >> 6;
if (test_bit(keyix, sc->sc_keymap))
rx_status.flag |= RX_FLAG_DECRYPTED;
}
spin_lock_bh(&sc->node_lock);
an = ath_node_find(sc, hdr->addr2);
spin_unlock_bh(&sc->node_lock);
if (an) {
ath_rx_input(sc, an,
hw->conf.ht_conf.ht_supported,
skb, status, &st);
} }
if (!an || (st != ATH_RX_CONSUMED))
__ieee80211_rx(hw, skb, &rx_status);
return 0; return 0;
} }
int ath_rx_subframe(struct ath_node *an, static void ath9k_stop(struct ieee80211_hw *hw)
struct sk_buff *skb,
struct ath_recv_status *status)
{ {
struct ath_softc *sc = an->an_sc; struct ath_softc *sc = hw->priv;
struct ieee80211_hw *hw = sc->hw; int error;
struct ieee80211_rx_status rx_status;
/* Prepare rx status */ DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__);
ath9k_rx_prepare(sc, skb, status, &rx_status);
if (!(status->flags & ATH_RX_DECRYPT_ERROR))
rx_status.flag |= RX_FLAG_DECRYPTED;
__ieee80211_rx(hw, skb, &rx_status); error = ath_suspend(sc);
if (error)
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Device is no longer present\n", __func__);
return 0; ieee80211_stop_queues(hw);
} }
/********************************/ static int ath9k_add_interface(struct ieee80211_hw *hw,
/* LED functions */ struct ieee80211_if_init_conf *conf)
/********************************/
static void ath_led_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{ {
struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); struct ath_softc *sc = hw->priv;
struct ath_softc *sc = led->sc; int error, ic_opmode = 0;
switch (brightness) { /* Support only vap for now */
case LED_OFF:
if (led->led_type == ATH_LED_ASSOC || if (sc->sc_nvaps)
led->led_type == ATH_LED_RADIO) return -ENOBUFS;
sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, switch (conf->type) {
(led->led_type == ATH_LED_RADIO) ? 1 : case IEEE80211_IF_TYPE_STA:
!!(sc->sc_flags & SC_OP_LED_ASSOCIATED)); ic_opmode = ATH9K_M_STA;
break; break;
case LED_FULL: case IEEE80211_IF_TYPE_IBSS:
if (led->led_type == ATH_LED_ASSOC) ic_opmode = ATH9K_M_IBSS;
sc->sc_flags |= SC_OP_LED_ASSOCIATED;
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0);
break; break;
default: case IEEE80211_IF_TYPE_AP:
ic_opmode = ATH9K_M_HOSTAP;
break; break;
default:
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Interface type %d not yet supported\n",
__func__, conf->type);
return -EOPNOTSUPP;
} }
}
static int ath_register_led(struct ath_softc *sc, struct ath_led *led,
char *trigger)
{
int ret;
led->sc = sc; DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a VAP of type: %d\n",
led->led_cdev.name = led->name; __func__,
led->led_cdev.default_trigger = trigger; ic_opmode);
led->led_cdev.brightness_set = ath_led_brightness;
ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); error = ath_vap_attach(sc, 0, conf->vif, ic_opmode);
if (ret) if (error) {
DPRINTF(sc, ATH_DBG_FATAL, DPRINTF(sc, ATH_DBG_FATAL,
"Failed to register led:%s", led->name); "%s: Unable to attach vap, error: %d\n",
else __func__, error);
led->registered = 1; return error;
return ret;
}
static void ath_unregister_led(struct ath_led *led)
{
if (led->registered) {
led_classdev_unregister(&led->led_cdev);
led->registered = 0;
} }
}
static void ath_deinit_leds(struct ath_softc *sc) return 0;
{
ath_unregister_led(&sc->assoc_led);
sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
ath_unregister_led(&sc->tx_led);
ath_unregister_led(&sc->rx_led);
ath_unregister_led(&sc->radio_led);
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
} }
static void ath_init_leds(struct ath_softc *sc) static void ath9k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{ {
char *trigger; struct ath_softc *sc = hw->priv;
int ret; struct ath_vap *avp;
int error;
/* Configure gpio 1 for output */ DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__);
ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
/* LED off, active low */
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
trigger = ieee80211_get_radio_led_name(sc->hw); avp = sc->sc_vaps[0];
snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), if (avp == NULL) {
"ath9k-%s:radio", wiphy_name(sc->hw->wiphy)); DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
ret = ath_register_led(sc, &sc->radio_led, trigger); __func__);
sc->radio_led.led_type = ATH_LED_RADIO; return;
if (ret) }
goto fail;
trigger = ieee80211_get_assoc_led_name(sc->hw); #ifdef CONFIG_SLOW_ANT_DIV
snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), ath_slow_ant_div_stop(&sc->sc_antdiv);
"ath9k-%s:assoc", wiphy_name(sc->hw->wiphy)); #endif
ret = ath_register_led(sc, &sc->assoc_led, trigger);
sc->assoc_led.led_type = ATH_LED_ASSOC;
if (ret)
goto fail;
trigger = ieee80211_get_tx_led_name(sc->hw); /* Update ratectrl */
snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), ath_rate_newstate(sc, avp);
"ath9k-%s:tx", wiphy_name(sc->hw->wiphy));
ret = ath_register_led(sc, &sc->tx_led, trigger);
sc->tx_led.led_type = ATH_LED_TX;
if (ret)
goto fail;
trigger = ieee80211_get_rx_led_name(sc->hw); /* Reclaim beacon resources */
snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP ||
"ath9k-%s:rx", wiphy_name(sc->hw->wiphy)); sc->sc_ah->ah_opmode == ATH9K_M_IBSS) {
ret = ath_register_led(sc, &sc->rx_led, trigger); ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
sc->rx_led.led_type = ATH_LED_RX; ath_beacon_return(sc, avp);
if (ret) }
goto fail;
return; /* Set interrupt mask */
sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask & ~ATH9K_INT_GLOBAL);
sc->sc_flags &= ~SC_OP_BEACONS;
fail: error = ath_vap_detach(sc, 0);
ath_deinit_leds(sc); if (error)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to detach vap, error: %d\n",
__func__, error);
} }
static int ath_detach(struct ath_softc *sc) static int ath9k_config(struct ieee80211_hw *hw,
struct ieee80211_conf *conf)
{ {
struct ieee80211_hw *hw = sc->hw; struct ath_softc *sc = hw->priv;
struct ieee80211_channel *curchan = hw->conf.channel;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__); int pos;
/* Deinit LED control */
ath_deinit_leds(sc);
/* Unregister hw */
ieee80211_unregister_hw(hw); DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
__func__,
curchan->center_freq);
/* unregister Rate control */ pos = ath_get_channel(sc, curchan);
ath_rate_control_unregister(); if (pos == -1) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
return -EINVAL;
}
/* tx/rx cleanup */ sc->sc_ah->ah_channels[pos].chanmode =
(curchan->band == IEEE80211_BAND_2GHZ) ?
CHANNEL_G : CHANNEL_A;
ath_rx_cleanup(sc); if (sc->sc_curaid && hw->conf.ht_conf.ht_supported)
ath_tx_cleanup(sc); sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan);
/* Deinit */ sc->sc_config.txpowlimit = 2 * conf->power_level;
ath_deinit(sc); /* set h/w channel */
if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
DPRINTF(sc, ATH_DBG_FATAL, "%s: Unable to set channel\n",
__func__);
return 0; return 0;
} }
static int ath_attach(u16 devid, static int ath9k_config_interface(struct ieee80211_hw *hw,
struct ath_softc *sc) struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf)
{ {
struct ieee80211_hw *hw = sc->hw; struct ath_softc *sc = hw->priv;
int error = 0; struct ath_hal *ah = sc->sc_ah;
struct ath_vap *avp;
u32 rfilt = 0;
int error, i;
DECLARE_MAC_BUF(mac);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__); avp = sc->sc_vaps[0];
if (avp == NULL) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
__func__);
return -EINVAL;
}
error = ath_init(devid, sc); /* TODO: Need to decide which hw opmode to use for multi-interface
* cases */
if (vif->type == IEEE80211_IF_TYPE_AP &&
ah->ah_opmode != ATH9K_M_HOSTAP) {
ah->ah_opmode = ATH9K_M_HOSTAP;
ath9k_hw_setopmode(ah);
ath9k_hw_write_associd(ah, sc->sc_myaddr, 0);
/* Request full reset to get hw opmode changed properly */
sc->sc_flags |= SC_OP_FULL_RESET;
}
if ((conf->changed & IEEE80211_IFCC_BSSID) &&
!is_zero_ether_addr(conf->bssid)) {
switch (vif->type) {
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
/* Update ratectrl about the new state */
ath_rate_newstate(sc, avp);
/* Set BSSID */
memcpy(sc->sc_curbssid, conf->bssid, ETH_ALEN);
sc->sc_curaid = 0;
ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
sc->sc_curaid);
/* Set aggregation protection mode parameters */
sc->sc_config.ath_aggr_prot = 0;
/*
* Reset our TSF so that its value is lower than the
* beacon that we are trying to catch.
* Only then hw will update its TSF register with the
* new beacon. Reset the TSF before setting the BSSID
* to avoid allowing in any frames that would update
* our TSF only to have us clear it
* immediately thereafter.
*/
ath9k_hw_reset_tsf(sc->sc_ah);
/* Disable BMISS interrupt when we're not associated */
ath9k_hw_set_interrupts(sc->sc_ah,
sc->sc_imask &
~(ATH9K_INT_SWBA | ATH9K_INT_BMISS));
sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: RX filter 0x%x bssid %s aid 0x%x\n",
__func__, rfilt,
print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
/* need to reconfigure the beacon */
sc->sc_flags &= ~SC_OP_BEACONS ;
break;
default:
break;
}
}
if ((conf->changed & IEEE80211_IFCC_BEACON) &&
((vif->type == IEEE80211_IF_TYPE_IBSS) ||
(vif->type == IEEE80211_IF_TYPE_AP))) {
/*
* Allocate and setup the beacon frame.
*
* Stop any previous beacon DMA. This may be
* necessary, for example, when an ibss merge
* causes reconfiguration; we may be called
* with beacon transmission active.
*/
ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
error = ath_beacon_alloc(sc, 0);
if (error != 0) if (error != 0)
return error; return error;
/* Init nodes */ ath_beacon_sync(sc, 0);
}
INIT_LIST_HEAD(&sc->node_list); /* Check for WLAN_CAPABILITY_PRIVACY ? */
spin_lock_init(&sc->node_lock); if ((avp->av_opmode != IEEE80211_IF_TYPE_STA)) {
for (i = 0; i < IEEE80211_WEP_NKID; i++)
if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i))
ath9k_hw_keysetmac(sc->sc_ah,
(u16)i,
sc->sc_curbssid);
}
/* get mac address from hardware and set in mac80211 */ /* Only legacy IBSS for now */
if (vif->type == IEEE80211_IF_TYPE_IBSS)
ath_update_chainmask(sc, 0);
SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr); return 0;
}
/* setup channels and rates */ #define SUPPORTED_FILTERS \
(FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
FIF_CONTROL | \
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC | \
FIF_FCSFAIL)
sc->sbands[IEEE80211_BAND_2GHZ].channels = /* FIXME: sc->sc_full_reset ? */
sc->channels[IEEE80211_BAND_2GHZ]; static void ath9k_configure_filter(struct ieee80211_hw *hw,
sc->sbands[IEEE80211_BAND_2GHZ].bitrates = unsigned int changed_flags,
sc->rates[IEEE80211_BAND_2GHZ]; unsigned int *total_flags,
sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; int mc_count,
struct dev_mc_list *mclist)
{
struct ath_softc *sc = hw->priv;
u32 rfilt;
if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) changed_flags &= SUPPORTED_FILTERS;
/* Setup HT capabilities for 2.4Ghz*/ *total_flags &= SUPPORTED_FILTERS;
setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info);
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sc->rx_filter = *total_flags;
&sc->sbands[IEEE80211_BAND_2GHZ]; rfilt = ath_calcrxfilter(sc);
ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) { if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
sc->sbands[IEEE80211_BAND_5GHZ].channels = if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
sc->channels[IEEE80211_BAND_5GHZ]; ath9k_hw_write_associd(sc->sc_ah, ath_bcast_mac, 0);
sc->sbands[IEEE80211_BAND_5GHZ].bitrates = }
sc->rates[IEEE80211_BAND_5GHZ];
sc->sbands[IEEE80211_BAND_5GHZ].band =
IEEE80211_BAND_5GHZ;
if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set HW RX filter: 0x%x\n",
/* Setup HT capabilities for 5Ghz*/ __func__, sc->rx_filter);
setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info); }
hw->wiphy->bands[IEEE80211_BAND_5GHZ] = static void ath9k_sta_notify(struct ieee80211_hw *hw,
&sc->sbands[IEEE80211_BAND_5GHZ]; struct ieee80211_vif *vif,
enum sta_notify_cmd cmd,
const u8 *addr)
{
struct ath_softc *sc = hw->priv;
struct ath_node *an;
unsigned long flags;
DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&sc->node_lock, flags);
an = ath_node_find(sc, (u8 *) addr);
spin_unlock_irqrestore(&sc->node_lock, flags);
switch (cmd) {
case STA_NOTIFY_ADD:
spin_lock_irqsave(&sc->node_lock, flags);
if (!an) {
ath_node_attach(sc, (u8 *)addr, 0);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %s\n",
__func__,
print_mac(mac, addr));
} else {
ath_node_get(sc, (u8 *)addr);
}
spin_unlock_irqrestore(&sc->node_lock, flags);
break;
case STA_NOTIFY_REMOVE:
if (!an)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Removal of a non-existent node\n",
__func__);
else {
ath_node_put(sc, an, ATH9K_BH_STATUS_INTACT);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Put a node: %s\n",
__func__,
print_mac(mac, addr));
} }
break;
default:
break;
}
}
/* FIXME: Have to figure out proper hw init values later */ static int ath9k_conf_tx(struct ieee80211_hw *hw,
u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct ath_softc *sc = hw->priv;
struct ath9k_tx_queue_info qi;
int ret = 0, qnum;
hw->queues = 4; if (queue >= WME_NUM_AC)
hw->ampdu_queues = 1; return 0;
/* Register rate control */ qi.tqi_aifs = params->aifs;
hw->rate_control_algorithm = "ath9k_rate_control"; qi.tqi_cwmin = params->cw_min;
error = ath_rate_control_register(); qi.tqi_cwmax = params->cw_max;
if (error != 0) { qi.tqi_burstTime = params->txop;
qnum = ath_get_hal_qnum(queue, sc);
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Configure tx [queue/halq] [%d/%d], "
"aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
__func__,
queue,
qnum,
params->aifs,
params->cw_min,
params->cw_max,
params->txop);
ret = ath_txq_update(sc, qnum, &qi);
if (ret)
DPRINTF(sc, ATH_DBG_FATAL, DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to register rate control " "%s: TXQ Update failed\n", __func__);
"algorithm:%d\n", __func__, error);
ath_rate_control_unregister(); return ret;
goto bad; }
static int ath9k_set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
const u8 *local_addr,
const u8 *addr,
struct ieee80211_key_conf *key)
{
struct ath_softc *sc = hw->priv;
int ret = 0;
DPRINTF(sc, ATH_DBG_KEYCACHE, " %s: Set HW Key\n", __func__);
switch (cmd) {
case SET_KEY:
ret = ath_key_config(sc, addr, key);
if (!ret) {
set_bit(key->keyidx, sc->sc_keymap);
key->hw_key_idx = key->keyidx;
/* push IV and Michael MIC generation to stack */
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (key->alg == ALG_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
}
break;
case DISABLE_KEY:
ath_key_delete(sc, key);
clear_bit(key->keyidx, sc->sc_keymap);
sc->sc_keytype = ATH9K_CIPHER_CLR;
break;
default:
ret = -EINVAL;
} }
error = ieee80211_register_hw(hw); return ret;
if (error != 0) { }
ath_rate_control_unregister();
goto bad; static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
u32 changed)
{
struct ath_softc *sc = hw->priv;
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed PREAMBLE %d\n",
__func__,
bss_conf->use_short_preamble);
if (bss_conf->use_short_preamble)
sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
else
sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
} }
/* Initialize LED control */ if (changed & BSS_CHANGED_ERP_CTS_PROT) {
ath_init_leds(sc); DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed CTS PROT %d\n",
__func__,
bss_conf->use_cts_prot);
if (bss_conf->use_cts_prot &&
hw->conf.channel->band != IEEE80211_BAND_5GHZ)
sc->sc_flags |= SC_OP_PROTECT_ENABLE;
else
sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
}
/* initialize tx/rx engine */ if (changed & BSS_CHANGED_HT) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT %d\n",
__func__,
bss_conf->assoc_ht);
ath9k_ht_conf(sc, bss_conf);
}
error = ath_tx_init(sc, ATH_TXBUF); if (changed & BSS_CHANGED_ASSOC) {
if (error != 0) DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n",
goto detach; __func__,
bss_conf->assoc);
ath9k_bss_assoc_info(sc, bss_conf);
}
}
error = ath_rx_init(sc, ATH_RXBUF); static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
if (error != 0) {
goto detach; u64 tsf;
struct ath_softc *sc = hw->priv;
struct ath_hal *ah = sc->sc_ah;
return 0; tsf = ath9k_hw_gettsf64(ah);
detach:
ath_detach(sc); return tsf;
bad:
return error;
} }
static void ath9k_reset_tsf(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
struct ath_hal *ah = sc->sc_ah;
ath9k_hw_reset_tsf(ah);
}
static int ath9k_ampdu_action(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action,
const u8 *addr,
u16 tid,
u16 *ssn)
{
struct ath_softc *sc = hw->priv;
int ret = 0;
switch (action) {
case IEEE80211_AMPDU_RX_START:
ret = ath_rx_aggr_start(sc, addr, tid, ssn);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to start RX aggregation\n",
__func__);
break;
case IEEE80211_AMPDU_RX_STOP:
ret = ath_rx_aggr_stop(sc, addr, tid);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to stop RX aggregation\n",
__func__);
break;
case IEEE80211_AMPDU_TX_START:
ret = ath_tx_aggr_start(sc, addr, tid, ssn);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to start TX aggregation\n",
__func__);
else
ieee80211_start_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP:
ret = ath_tx_aggr_stop(sc, addr, tid);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to stop TX aggregation\n",
__func__);
ieee80211_stop_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
break;
default:
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unknown AMPDU action\n", __func__);
}
return ret;
}
static struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
.stop = ath9k_stop,
.add_interface = ath9k_add_interface,
.remove_interface = ath9k_remove_interface,
.config = ath9k_config,
.config_interface = ath9k_config_interface,
.configure_filter = ath9k_configure_filter,
.get_stats = NULL,
.sta_notify = ath9k_sta_notify,
.conf_tx = ath9k_conf_tx,
.get_tx_stats = NULL,
.bss_info_changed = ath9k_bss_info_changed,
.set_tim = NULL,
.set_key = ath9k_set_key,
.hw_scan = NULL,
.get_tkip_seq = NULL,
.set_rts_threshold = NULL,
.set_frag_threshold = NULL,
.set_retry_limit = NULL,
.get_tsf = ath9k_get_tsf,
.reset_tsf = ath9k_reset_tsf,
.tx_last_beacon = NULL,
.ampdu_action = ath9k_ampdu_action
};
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
void __iomem *mem; void __iomem *mem;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册