diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index cb3d4b70bcbc39c641f9efd49451ef9a6419e2b5..98328b7f83324e7a975138f8c26fd0054d2db5c0 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -7,6 +7,7 @@ * Copyright (c) 2004 Balint Seeber * Copyright (c) 2007 Guido Guenther * Copyright (c) 2007 Kalle Valo + * Copyright (c) 2010 Sebastian Smolorz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -1685,6 +1686,22 @@ static int at76_join(struct at76_priv *priv) return 0; } +static void at76_work_join_bssid(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_join_bssid); + + if (priv->device_unplugged) + return; + + mutex_lock(&priv->mtx); + + if (is_valid_ether_addr(priv->bssid)) + at76_join(priv); + + mutex_unlock(&priv->mtx); +} + static void at76_mac80211_tx_callback(struct urb *urb) { struct at76_priv *priv = urb->context; @@ -1722,6 +1739,7 @@ static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) struct at76_priv *priv = hw->priv; struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; int padding, submit_len, ret; at76_dbg(DBG_MAC80211, "%s()", __func__); @@ -1732,6 +1750,21 @@ static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) return NETDEV_TX_BUSY; } + /* The following code lines are important when the device is going to + * authenticate with a new bssid. The driver must send CMD_JOIN before + * an authentication frame is transmitted. For this to succeed, the + * correct bssid of the AP must be known. As mac80211 does not inform + * drivers about the bssid prior to the authentication process the + * following workaround is necessary. If the TX frame is an + * authentication frame extract the bssid and send the CMD_JOIN. */ + if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { + if (compare_ether_addr(priv->bssid, mgmt->bssid)) { + memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); + ieee80211_queue_work(hw, &priv->work_join_bssid); + return NETDEV_TX_BUSY; + } + } + ieee80211_stop_queues(hw); at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ @@ -1806,6 +1839,7 @@ static void at76_mac80211_stop(struct ieee80211_hw *hw) at76_dbg(DBG_MAC80211, "%s()", __func__); cancel_delayed_work(&priv->dwork_hw_scan); + cancel_work_sync(&priv->work_join_bssid); cancel_work_sync(&priv->work_set_promisc); mutex_lock(&priv->mtx); @@ -2107,6 +2141,7 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) mutex_init(&priv->mtx); INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); + INIT_WORK(&priv->work_join_bssid, at76_work_join_bssid); INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan); tasklet_init(&priv->rx_tasklet, at76_rx_tasklet, 0); @@ -2508,5 +2543,6 @@ MODULE_AUTHOR("Balint Seeber "); MODULE_AUTHOR("Pavel Roskin "); MODULE_AUTHOR("Guido Guenther "); MODULE_AUTHOR("Kalle Valo "); +MODULE_AUTHOR("Sebastian Smolorz "); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/at76c50x-usb.h index 1ec5ccffdbc03b54f87c13193fd2c1f7492d733f..db89d72df4f3f6b47586a2b660a062ed5c92b949 100644 --- a/drivers/net/wireless/at76c50x-usb.h +++ b/drivers/net/wireless/at76c50x-usb.h @@ -387,6 +387,7 @@ struct at76_priv { /* work queues */ struct work_struct work_set_promisc; struct work_struct work_submit_rx; + struct work_struct work_join_bssid; struct delayed_work dwork_hw_scan; struct tasklet_struct rx_tasklet;