Commit 393934c6 authored by John W. Linville's avatar John W. Linville

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6

Conflicts:
	drivers/net/wireless/ath/ath9k/ath9k.h
	drivers/net/wireless/ath/ath9k/main.c
	drivers/net/wireless/ath/ath9k/xmit.c
parents 69f4aab1 0a54917c
...@@ -1879,7 +1879,8 @@ ath5k_beacon_send(struct ath5k_softc *sc) ...@@ -1879,7 +1879,8 @@ ath5k_beacon_send(struct ath5k_softc *sc)
sc->bmisscount = 0; sc->bmisscount = 0;
} }
if (sc->opmode == NL80211_IFTYPE_AP && sc->num_ap_vifs > 1) { if ((sc->opmode == NL80211_IFTYPE_AP && sc->num_ap_vifs > 1) ||
sc->opmode == NL80211_IFTYPE_MESH_POINT) {
u64 tsf = ath5k_hw_get_tsf64(ah); u64 tsf = ath5k_hw_get_tsf64(ah);
u32 tsftu = TSF_TO_TU(tsf); u32 tsftu = TSF_TO_TU(tsf);
int slot = ((tsftu % sc->bintval) * ATH_BCBUF) / sc->bintval; int slot = ((tsftu % sc->bintval) * ATH_BCBUF) / sc->bintval;
...@@ -1911,8 +1912,9 @@ ath5k_beacon_send(struct ath5k_softc *sc) ...@@ -1911,8 +1912,9 @@ ath5k_beacon_send(struct ath5k_softc *sc)
/* NB: hw still stops DMA, so proceed */ /* NB: hw still stops DMA, so proceed */
} }
/* refresh the beacon for AP mode */ /* refresh the beacon for AP or MESH mode */
if (sc->opmode == NL80211_IFTYPE_AP) if (sc->opmode == NL80211_IFTYPE_AP ||
sc->opmode == NL80211_IFTYPE_MESH_POINT)
ath5k_beacon_update(sc->hw, vif); ath5k_beacon_update(sc->hw, vif);
ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr); ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
...@@ -2997,7 +2999,8 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, ...@@ -2997,7 +2999,8 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
/* Assign the vap/adhoc to a beacon xmit slot. */ /* Assign the vap/adhoc to a beacon xmit slot. */
if ((avf->opmode == NL80211_IFTYPE_AP) || if ((avf->opmode == NL80211_IFTYPE_AP) ||
(avf->opmode == NL80211_IFTYPE_ADHOC)) { (avf->opmode == NL80211_IFTYPE_ADHOC) ||
(avf->opmode == NL80211_IFTYPE_MESH_POINT)) {
int slot; int slot;
WARN_ON(list_empty(&sc->bcbuf)); WARN_ON(list_empty(&sc->bcbuf));
...@@ -3016,7 +3019,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, ...@@ -3016,7 +3019,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
sc->bslot[avf->bslot] = vif; sc->bslot[avf->bslot] = vif;
if (avf->opmode == NL80211_IFTYPE_AP) if (avf->opmode == NL80211_IFTYPE_AP)
sc->num_ap_vifs++; sc->num_ap_vifs++;
else else if (avf->opmode == NL80211_IFTYPE_ADHOC)
sc->num_adhoc_vifs++; sc->num_adhoc_vifs++;
} }
......
...@@ -311,7 +311,7 @@ void ath_rx_cleanup(struct ath_softc *sc); ...@@ -311,7 +311,7 @@ void ath_rx_cleanup(struct ath_softc *sc);
int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp); int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp);
struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype); struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype);
void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq); void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq);
void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx); bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx);
void ath_draintxq(struct ath_softc *sc, void ath_draintxq(struct ath_softc *sc,
struct ath_txq *txq, bool retry_tx); struct ath_txq *txq, bool retry_tx);
void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an); void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an);
......
...@@ -1063,15 +1063,19 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, ...@@ -1063,15 +1063,19 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah,
case 1: case 1:
break; break;
case 2: case 2:
scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; if (scaledPower > REDUCE_SCALED_POWER_BY_TWO_CHAIN)
scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN;
else
scaledPower = 0;
break; break;
case 3: case 3:
scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; if (scaledPower > REDUCE_SCALED_POWER_BY_THREE_CHAIN)
scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN;
else
scaledPower = 0;
break; break;
} }
scaledPower = max((u16)0, scaledPower);
if (IS_CHAN_2GHZ(chan)) { if (IS_CHAN_2GHZ(chan)) {
numCtlModes = ARRAY_SIZE(ctlModesFor11g) - numCtlModes = ARRAY_SIZE(ctlModesFor11g) -
SUB_NUM_CTL_MODES_AT_2G_40; SUB_NUM_CTL_MODES_AT_2G_40;
......
...@@ -1015,6 +1015,13 @@ static int ath9k_hif_usb_suspend(struct usb_interface *interface, ...@@ -1015,6 +1015,13 @@ static int ath9k_hif_usb_suspend(struct usb_interface *interface,
{ {
struct hif_device_usb *hif_dev = usb_get_intfdata(interface); struct hif_device_usb *hif_dev = usb_get_intfdata(interface);
/*
* The device has to be set to FULLSLEEP mode in case no
* interface is up.
*/
if (!(hif_dev->flags & HIF_USB_START))
ath9k_htc_suspend(hif_dev->htc_handle);
ath9k_hif_usb_dealloc_urbs(hif_dev); ath9k_hif_usb_dealloc_urbs(hif_dev);
return 0; return 0;
......
...@@ -455,6 +455,8 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv); ...@@ -455,6 +455,8 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv);
void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv); void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv);
void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv); void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv);
void ath9k_ps_work(struct work_struct *work); void ath9k_ps_work(struct work_struct *work);
bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
enum ath9k_power_mode mode);
void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv); void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv);
void ath9k_init_leds(struct ath9k_htc_priv *priv); void ath9k_init_leds(struct ath9k_htc_priv *priv);
...@@ -464,6 +466,7 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, ...@@ -464,6 +466,7 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
u16 devid, char *product, u32 drv_info); u16 devid, char *product, u32 drv_info);
void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug); void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug);
#ifdef CONFIG_PM #ifdef CONFIG_PM
void ath9k_htc_suspend(struct htc_target *htc_handle);
int ath9k_htc_resume(struct htc_target *htc_handle); int ath9k_htc_resume(struct htc_target *htc_handle);
#endif #endif
#ifdef CONFIG_ATH9K_HTC_DEBUGFS #ifdef CONFIG_ATH9K_HTC_DEBUGFS
......
...@@ -882,6 +882,12 @@ void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug) ...@@ -882,6 +882,12 @@ void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
void ath9k_htc_suspend(struct htc_target *htc_handle)
{
ath9k_htc_setpower(htc_handle->drv_priv, ATH9K_PM_FULL_SLEEP);
}
int ath9k_htc_resume(struct htc_target *htc_handle) int ath9k_htc_resume(struct htc_target *htc_handle)
{ {
struct ath9k_htc_priv *priv = htc_handle->drv_priv; struct ath9k_htc_priv *priv = htc_handle->drv_priv;
......
...@@ -63,8 +63,8 @@ static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, ...@@ -63,8 +63,8 @@ static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
return mode; return mode;
} }
static bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
enum ath9k_power_mode mode) enum ath9k_power_mode mode)
{ {
bool ret; bool ret;
......
...@@ -698,8 +698,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, ...@@ -698,8 +698,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
rs->rs_phyerr = phyerr; rs->rs_phyerr = phyerr;
} else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
rs->rs_status |= ATH9K_RXERR_DECRYPT; rs->rs_status |= ATH9K_RXERR_DECRYPT;
else if ((ads.ds_rxstatus8 & AR_MichaelErr) && else if (ads.ds_rxstatus8 & AR_MichaelErr)
rs->rs_keyix != ATH9K_RXKEYIX_INVALID)
rs->rs_status |= ATH9K_RXERR_MIC; rs->rs_status |= ATH9K_RXERR_MIC;
else if (ads.ds_rxstatus8 & AR_KeyMiss) else if (ads.ds_rxstatus8 & AR_KeyMiss)
rs->rs_status |= ATH9K_RXERR_DECRYPT; rs->rs_status |= ATH9K_RXERR_DECRYPT;
......
...@@ -246,9 +246,10 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, ...@@ -246,9 +246,10 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
* the relevant bits of the h/w. * the relevant bits of the h/w.
*/ */
ath9k_hw_disable_interrupts(ah); ath9k_hw_disable_interrupts(ah);
ath_drain_all_txq(sc, false); stopped = ath_drain_all_txq(sc, false);
stopped = ath_stoprecv(sc); if (!ath_stoprecv(sc))
stopped = false;
/* XXX: do not flush receive queue here. We don't want /* XXX: do not flush receive queue here. We don't want
* to flush data frames already in queue because of * to flush data frames already in queue because of
...@@ -1434,8 +1435,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, ...@@ -1434,8 +1435,6 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
struct ath_softc *sc = aphy->sc; struct ath_softc *sc = aphy->sc;
struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (void *)vif->drv_priv; struct ath_vif *avp = (void *)vif->drv_priv;
bool bs_valid = false;
int i;
ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n"); ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n");
...@@ -1449,26 +1448,21 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, ...@@ -1449,26 +1448,21 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
(sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) || (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
(sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) { (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) {
/* Disable SWBA interrupt */
sc->sc_ah->imask &= ~ATH9K_INT_SWBA;
ath9k_ps_wakeup(sc); ath9k_ps_wakeup(sc);
ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_ah->imask);
ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
tasklet_kill(&sc->bcon_tasklet);
} }
ath_beacon_return(sc, avp); ath_beacon_return(sc, avp);
sc->sc_flags &= ~SC_OP_BEACONS; sc->sc_flags &= ~SC_OP_BEACONS;
for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { if (sc->nbcnvifs) {
if (sc->beacon.bslot[i] == vif) { /* Re-enable SWBA interrupt */
printk(KERN_DEBUG "%s: vif had allocated beacon " sc->sc_ah->imask |= ATH9K_INT_SWBA;
"slot\n", __func__);
sc->beacon.bslot[i] = NULL;
sc->beacon.bslot_aphy[i] = NULL;
} else if (sc->beacon.bslot[i])
bs_valid = true;
}
if (!bs_valid && (sc->sc_ah->imask & ATH9K_INT_SWBA)) {
/* Disable SWBA interrupt */
sc->sc_ah->imask &= ~ATH9K_INT_SWBA;
ath9k_ps_wakeup(sc); ath9k_ps_wakeup(sc);
ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_ah->imask); ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_ah->imask);
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
......
...@@ -841,6 +841,10 @@ static bool ath9k_rx_accept(struct ath_common *common, ...@@ -841,6 +841,10 @@ static bool ath9k_rx_accept(struct ath_common *common,
struct ath_rx_status *rx_stats, struct ath_rx_status *rx_stats,
bool *decrypt_error) bool *decrypt_error)
{ {
#define is_mc_or_valid_tkip_keyix ((is_mc || \
(rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID && \
test_bit(rx_stats->rs_keyix, common->tkip_keymap))))
struct ath_hw *ah = common->ah; struct ath_hw *ah = common->ah;
__le16 fc; __le16 fc;
u8 rx_status_len = ah->caps.rx_status_len; u8 rx_status_len = ah->caps.rx_status_len;
...@@ -882,15 +886,18 @@ static bool ath9k_rx_accept(struct ath_common *common, ...@@ -882,15 +886,18 @@ static bool ath9k_rx_accept(struct ath_common *common,
if (rx_stats->rs_status & ATH9K_RXERR_DECRYPT) { if (rx_stats->rs_status & ATH9K_RXERR_DECRYPT) {
*decrypt_error = true; *decrypt_error = true;
} else if (rx_stats->rs_status & ATH9K_RXERR_MIC) { } else if (rx_stats->rs_status & ATH9K_RXERR_MIC) {
bool is_mc;
/* /*
* The MIC error bit is only valid if the frame * The MIC error bit is only valid if the frame
* is not a control frame or fragment, and it was * is not a control frame or fragment, and it was
* decrypted using a valid TKIP key. * decrypted using a valid TKIP key.
*/ */
is_mc = !!is_multicast_ether_addr(hdr->addr1);
if (!ieee80211_is_ctl(fc) && if (!ieee80211_is_ctl(fc) &&
!ieee80211_has_morefrags(fc) && !ieee80211_has_morefrags(fc) &&
!(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) && !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
test_bit(rx_stats->rs_keyix, common->tkip_keymap)) is_mc_or_valid_tkip_keyix)
rxs->flag |= RX_FLAG_MMIC_ERROR; rxs->flag |= RX_FLAG_MMIC_ERROR;
else else
rx_stats->rs_status &= ~ATH9K_RXERR_MIC; rx_stats->rs_status &= ~ATH9K_RXERR_MIC;
......
...@@ -1171,7 +1171,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) ...@@ -1171,7 +1171,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
} }
} }
void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
{ {
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_common *common = ath9k_hw_common(sc->sc_ah);
...@@ -1179,7 +1179,7 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) ...@@ -1179,7 +1179,7 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
int i, npend = 0; int i, npend = 0;
if (sc->sc_flags & SC_OP_INVALID) if (sc->sc_flags & SC_OP_INVALID)
return; return true;
/* Stop beacon queue */ /* Stop beacon queue */
ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
...@@ -1193,22 +1193,15 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) ...@@ -1193,22 +1193,15 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
} }
} }
if (npend) { if (npend)
int r; ath_err(common, "Failed to stop TX DMA!\n");
ath_err(common, "Failed to stop TX DMA. Resetting hardware!\n");
r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
if (r)
ath_err(common,
"Unable to reset hardware; reset status %d\n",
r);
}
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i)) if (ATH_TXQ_SETUP(sc, i))
ath_draintxq(sc, &sc->tx.txq[i], retry_tx); ath_draintxq(sc, &sc->tx.txq[i], retry_tx);
} }
return !npend;
} }
void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
......
...@@ -1811,6 +1811,12 @@ static int __orinoco_commit(struct orinoco_private *priv) ...@@ -1811,6 +1811,12 @@ static int __orinoco_commit(struct orinoco_private *priv)
struct net_device *dev = priv->ndev; struct net_device *dev = priv->ndev;
int err = 0; int err = 0;
/* If we've called commit, we are reconfiguring or bringing the
* interface up. Maintaining countermeasures across this would
* be confusing, so note that we've disabled them. The port will
* be enabled later in orinoco_commit or __orinoco_up. */
priv->tkip_cm_active = 0;
err = orinoco_hw_program_rids(priv); err = orinoco_hw_program_rids(priv);
/* FIXME: what about netif_tx_lock */ /* FIXME: what about netif_tx_lock */
......
...@@ -151,20 +151,20 @@ orinoco_cs_config(struct pcmcia_device *link) ...@@ -151,20 +151,20 @@ orinoco_cs_config(struct pcmcia_device *link)
goto failed; goto failed;
} }
ret = pcmcia_request_irq(link, orinoco_interrupt);
if (ret)
goto failed;
/* We initialize the hermes structure before completing PCMCIA
* configuration just in case the interrupt handler gets
* called. */
mem = ioport_map(link->resource[0]->start, mem = ioport_map(link->resource[0]->start,
resource_size(link->resource[0])); resource_size(link->resource[0]));
if (!mem) if (!mem)
goto failed; goto failed;
/* We initialize the hermes structure before completing PCMCIA
* configuration just in case the interrupt handler gets
* called. */
hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
ret = pcmcia_request_irq(link, orinoco_interrupt);
if (ret)
goto failed;
ret = pcmcia_enable_device(link); ret = pcmcia_enable_device(link);
if (ret) if (ret)
goto failed; goto failed;
......
...@@ -214,21 +214,21 @@ spectrum_cs_config(struct pcmcia_device *link) ...@@ -214,21 +214,21 @@ spectrum_cs_config(struct pcmcia_device *link)
goto failed; goto failed;
} }
ret = pcmcia_request_irq(link, orinoco_interrupt);
if (ret)
goto failed;
/* We initialize the hermes structure before completing PCMCIA
* configuration just in case the interrupt handler gets
* called. */
mem = ioport_map(link->resource[0]->start, mem = ioport_map(link->resource[0]->start,
resource_size(link->resource[0])); resource_size(link->resource[0]));
if (!mem) if (!mem)
goto failed; goto failed;
/* We initialize the hermes structure before completing PCMCIA
* configuration just in case the interrupt handler gets
* called. */
hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
hw->eeprom_pda = true; hw->eeprom_pda = true;
ret = pcmcia_request_irq(link, orinoco_interrupt);
if (ret)
goto failed;
ret = pcmcia_enable_device(link); ret = pcmcia_enable_device(link);
if (ret) if (ret)
goto failed; goto failed;
......
...@@ -919,10 +919,10 @@ static int orinoco_ioctl_set_auth(struct net_device *dev, ...@@ -919,10 +919,10 @@ static int orinoco_ioctl_set_auth(struct net_device *dev,
*/ */
if (param->value) { if (param->value) {
priv->tkip_cm_active = 1; priv->tkip_cm_active = 1;
ret = hermes_enable_port(hw, 0); ret = hermes_disable_port(hw, 0);
} else { } else {
priv->tkip_cm_active = 0; priv->tkip_cm_active = 0;
ret = hermes_disable_port(hw, 0); ret = hermes_enable_port(hw, 0);
} }
break; break;
......
...@@ -1746,15 +1746,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ...@@ -1746,15 +1746,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
int nh_pos, h_pos; int nh_pos, h_pos;
struct sta_info *sta = NULL; struct sta_info *sta = NULL;
u32 sta_flags = 0; u32 sta_flags = 0;
struct sk_buff *tmp_skb;
if (unlikely(skb->len < ETH_HLEN)) { if (unlikely(skb->len < ETH_HLEN)) {
ret = NETDEV_TX_OK; ret = NETDEV_TX_OK;
goto fail; goto fail;
} }
nh_pos = skb_network_header(skb) - skb->data;
h_pos = skb_transport_header(skb) - skb->data;
/* convert Ethernet header to proper 802.11 header (based on /* convert Ethernet header to proper 802.11 header (based on
* operation mode) */ * operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13]; ethertype = (skb->data[12] << 8) | skb->data[13];
...@@ -1927,6 +1925,20 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ...@@ -1927,6 +1925,20 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
goto fail; goto fail;
} }
/*
* If the skb is shared we need to obtain our own copy.
*/
if (skb_shared(skb)) {
tmp_skb = skb;
skb = skb_copy(skb, GFP_ATOMIC);
kfree_skb(tmp_skb);
if (!skb) {
ret = NETDEV_TX_OK;
goto fail;
}
}
hdr.frame_control = fc; hdr.frame_control = fc;
hdr.duration_id = 0; hdr.duration_id = 0;
hdr.seq_ctrl = 0; hdr.seq_ctrl = 0;
...@@ -1945,6 +1957,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ...@@ -1945,6 +1957,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
encaps_len = 0; encaps_len = 0;
} }
nh_pos = skb_network_header(skb) - skb->data;
h_pos = skb_transport_header(skb) - skb->data;
skb_pull(skb, skip_header_bytes); skb_pull(skb, skip_header_bytes);
nh_pos -= skip_header_bytes; nh_pos -= skip_header_bytes;
h_pos -= skip_header_bytes; h_pos -= skip_header_bytes;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment