diff options
Diffstat (limited to 'drivers/net/wireless/zd1211rw/zd_mac.c')
| -rw-r--r-- | drivers/net/wireless/zd1211rw/zd_mac.c | 573 | 
1 files changed, 455 insertions, 118 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 43307bd42a6..e7af261e919 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -16,8 +16,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/netdevice.h> @@ -138,6 +137,12 @@ static const struct ieee80211_channel zd_channels[] = {  static void housekeeping_init(struct zd_mac *mac);  static void housekeeping_enable(struct zd_mac *mac);  static void housekeeping_disable(struct zd_mac *mac); +static void beacon_init(struct zd_mac *mac); +static void beacon_enable(struct zd_mac *mac); +static void beacon_disable(struct zd_mac *mac); +static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble); +static int zd_mac_config_beacon(struct ieee80211_hw *hw, +				struct sk_buff *beacon, bool in_intr);  static int zd_reg2alpha2(u8 regdomain, char *alpha2)  { @@ -154,6 +159,22 @@ static int zd_reg2alpha2(u8 regdomain, char *alpha2)  	return 1;  } +static int zd_check_signal(struct ieee80211_hw *hw, int signal) +{ +	struct zd_mac *mac = zd_hw_mac(hw); + +	dev_dbg_f_cond(zd_mac_dev(mac), signal < 0 || signal > 100, +			"%s: signal value from device not in range 0..100, " +			"but %d.\n", __func__, signal); + +	if (signal < 0) +		signal = 0; +	else if (signal > 100) +		signal = 100; + +	return signal; +} +  int zd_mac_preinit_hw(struct ieee80211_hw *hw)  {  	int r; @@ -231,6 +252,26 @@ static int set_rx_filter(struct zd_mac *mac)  	return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);  } +static int set_mac_and_bssid(struct zd_mac *mac) +{ +	int r; + +	if (!mac->vif) +		return -1; + +	r = zd_write_mac_addr(&mac->chip, mac->vif->addr); +	if (r) +		return r; + +	/* Vendor driver after setting MAC either sets BSSID for AP or +	 * filter for other modes. +	 */ +	if (mac->type != NL80211_IFTYPE_AP) +		return set_rx_filter(mac); +	else +		return zd_write_bssid(&mac->chip, mac->vif->addr); +} +  static int set_mc_hash(struct zd_mac *mac)  {  	struct zd_mc_hash hash; @@ -238,7 +279,7 @@ static int set_mc_hash(struct zd_mac *mac)  	return zd_chip_set_multicast_hash(&mac->chip, &hash);  } -static int zd_op_start(struct ieee80211_hw *hw) +int zd_op_start(struct ieee80211_hw *hw)  {  	struct zd_mac *mac = zd_hw_mac(hw);  	struct zd_chip *chip = &mac->chip; @@ -264,9 +305,19 @@ static int zd_op_start(struct ieee80211_hw *hw)  	r = set_mc_hash(mac);  	if (r)  		goto disable_int; + +	/* Wait after setting the multicast hash table and powering on +	 * the radio otherwise interface bring up will fail. This matches +	 * what the vendor driver did. +	 */ +	msleep(10); +  	r = zd_chip_switch_radio_on(chip); -	if (r < 0) +	if (r < 0) { +		dev_err(zd_chip_dev(chip), +			"%s: failed to set radio on\n", __func__);  		goto disable_int; +	}  	r = zd_chip_enable_rxtx(chip);  	if (r < 0)  		goto disable_radio; @@ -275,6 +326,8 @@ static int zd_op_start(struct ieee80211_hw *hw)  		goto disable_rxtx;  	housekeeping_enable(mac); +	beacon_enable(mac); +	set_bit(ZD_DEVICE_RUNNING, &mac->flags);  	return 0;  disable_rxtx:  	zd_chip_disable_rxtx(chip); @@ -286,19 +339,22 @@ out:  	return r;  } -static void zd_op_stop(struct ieee80211_hw *hw) +void zd_op_stop(struct ieee80211_hw *hw)  {  	struct zd_mac *mac = zd_hw_mac(hw);  	struct zd_chip *chip = &mac->chip;  	struct sk_buff *skb;  	struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue; +	clear_bit(ZD_DEVICE_RUNNING, &mac->flags); +  	/* The order here deliberately is a little different from the open()  	 * method, since we need to make sure there is no opportunity for RX  	 * frames to be processed by mac80211 after we have stopped it.  	 */  	zd_chip_disable_rxtx(chip); +	beacon_disable(mac);  	housekeeping_disable(mac);  	flush_workqueue(zd_workqueue); @@ -311,6 +367,66 @@ static void zd_op_stop(struct ieee80211_hw *hw)  		dev_kfree_skb_any(skb);  } +int zd_restore_settings(struct zd_mac *mac) +{ +	struct sk_buff *beacon; +	struct zd_mc_hash multicast_hash; +	unsigned int short_preamble; +	int r, beacon_interval, beacon_period; +	u8 channel; + +	dev_dbg_f(zd_mac_dev(mac), "\n"); + +	spin_lock_irq(&mac->lock); +	multicast_hash = mac->multicast_hash; +	short_preamble = mac->short_preamble; +	beacon_interval = mac->beacon.interval; +	beacon_period = mac->beacon.period; +	channel = mac->channel; +	spin_unlock_irq(&mac->lock); + +	r = set_mac_and_bssid(mac); +	if (r < 0) { +		dev_dbg_f(zd_mac_dev(mac), "set_mac_and_bssid failed, %d\n", r); +		return r; +	} + +	r = zd_chip_set_channel(&mac->chip, channel); +	if (r < 0) { +		dev_dbg_f(zd_mac_dev(mac), "zd_chip_set_channel failed, %d\n", +			  r); +		return r; +	} + +	set_rts_cts(mac, short_preamble); + +	r = zd_chip_set_multicast_hash(&mac->chip, &multicast_hash); +	if (r < 0) { +		dev_dbg_f(zd_mac_dev(mac), +			  "zd_chip_set_multicast_hash failed, %d\n", r); +		return r; +	} + +	if (mac->type == NL80211_IFTYPE_MESH_POINT || +	    mac->type == NL80211_IFTYPE_ADHOC || +	    mac->type == NL80211_IFTYPE_AP) { +		if (mac->vif != NULL) { +			beacon = ieee80211_beacon_get(mac->hw, mac->vif); +			if (beacon) +				zd_mac_config_beacon(mac->hw, beacon, false); +		} + +		zd_set_beacon_interval(&mac->chip, beacon_interval, +					beacon_period, mac->type); + +		spin_lock_irq(&mac->lock); +		mac->beacon.last_update = jiffies; +		spin_unlock_irq(&mac->lock); +	} + +	return 0; +} +  /**   * zd_mac_tx_status - reports tx status of a packet if required   * @hw - a &struct ieee80211_hw pointer @@ -368,7 +484,7 @@ static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,  	if (i<IEEE80211_TX_MAX_RATES)  		info->status.rates[i].idx = -1; /* terminate */ -	info->status.ack_signal = ackssi; +	info->status.ack_signal = zd_check_signal(hw, ackssi);  	ieee80211_tx_status_irqsafe(hw, skb);  } @@ -416,9 +532,8 @@ void zd_mac_tx_failed(struct urb *urb)  		tx_hdr = (struct ieee80211_hdr *)skb->data;  		/* we skip all frames not matching the reported destination */ -		if (unlikely(memcmp(tx_hdr->addr1, tx_status->mac, ETH_ALEN))) { +		if (unlikely(!ether_addr_equal(tx_hdr->addr1, tx_status->mac)))  			continue; -		}  		/* we skip all frames not matching the reported final rate */ @@ -571,67 +686,178 @@ static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,  	/* FIXME: Management frame? */  } -static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon) +static bool zd_mac_match_cur_beacon(struct zd_mac *mac, struct sk_buff *beacon) +{ +	if (!mac->beacon.cur_beacon) +		return false; + +	if (mac->beacon.cur_beacon->len != beacon->len) +		return false; + +	return !memcmp(beacon->data, mac->beacon.cur_beacon->data, beacon->len); +} + +static void zd_mac_free_cur_beacon_locked(struct zd_mac *mac) +{ +	ZD_ASSERT(mutex_is_locked(&mac->chip.mutex)); + +	kfree_skb(mac->beacon.cur_beacon); +	mac->beacon.cur_beacon = NULL; +} + +static void zd_mac_free_cur_beacon(struct zd_mac *mac) +{ +	mutex_lock(&mac->chip.mutex); +	zd_mac_free_cur_beacon_locked(mac); +	mutex_unlock(&mac->chip.mutex); +} + +static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon, +				bool in_intr)  {  	struct zd_mac *mac = zd_hw_mac(hw); -	int r; +	int r, ret, num_cmds, req_pos = 0;  	u32 tmp, j = 0;  	/* 4 more bytes for tail CRC */  	u32 full_len = beacon->len + 4; +	unsigned long end_jiffies, message_jiffies; +	struct zd_ioreq32 *ioreqs; + +	mutex_lock(&mac->chip.mutex); + +	/* Check if hw already has this beacon. */ +	if (zd_mac_match_cur_beacon(mac, beacon)) { +		r = 0; +		goto out_nofree; +	} + +	/* Alloc memory for full beacon write at once. */ +	num_cmds = 1 + zd_chip_is_zd1211b(&mac->chip) + full_len; +	ioreqs = kmalloc(num_cmds * sizeof(struct zd_ioreq32), GFP_KERNEL); +	if (!ioreqs) { +		r = -ENOMEM; +		goto out_nofree; +	} -	r = zd_iowrite32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, 0); +	r = zd_iowrite32_locked(&mac->chip, 0, CR_BCN_FIFO_SEMAPHORE);  	if (r < 0) -		return r; -	r = zd_ioread32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, &tmp); +		goto out; +	r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE);  	if (r < 0) -		return r; +		goto release_sema; +	if (in_intr && tmp & 0x2) { +		r = -EBUSY; +		goto release_sema; +	} +	end_jiffies = jiffies + HZ / 2; /*~500ms*/ +	message_jiffies = jiffies + HZ / 10; /*~100ms*/  	while (tmp & 0x2) { -		r = zd_ioread32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, &tmp); +		r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE);  		if (r < 0) -			return r; -		if ((++j % 100) == 0) { -			printk(KERN_ERR "CR_BCN_FIFO_SEMAPHORE not ready\n"); -			if (j >= 500)  { -				printk(KERN_ERR "Giving up beacon config.\n"); -				return -ETIMEDOUT; +			goto release_sema; +		if (time_is_before_eq_jiffies(message_jiffies)) { +			message_jiffies = jiffies + HZ / 10; +			dev_err(zd_mac_dev(mac), +					"CR_BCN_FIFO_SEMAPHORE not ready\n"); +			if (time_is_before_eq_jiffies(end_jiffies))  { +				dev_err(zd_mac_dev(mac), +						"Giving up beacon config.\n"); +				r = -ETIMEDOUT; +				goto reset_device;  			}  		} -		msleep(1); +		msleep(20);  	} -	r = zd_iowrite32(&mac->chip, CR_BCN_FIFO, full_len - 1); -	if (r < 0) -		return r; +	ioreqs[req_pos].addr = CR_BCN_FIFO; +	ioreqs[req_pos].value = full_len - 1; +	req_pos++;  	if (zd_chip_is_zd1211b(&mac->chip)) { -		r = zd_iowrite32(&mac->chip, CR_BCN_LENGTH, full_len - 1); -		if (r < 0) -			return r; +		ioreqs[req_pos].addr = CR_BCN_LENGTH; +		ioreqs[req_pos].value = full_len - 1; +		req_pos++;  	}  	for (j = 0 ; j < beacon->len; j++) { -		r = zd_iowrite32(&mac->chip, CR_BCN_FIFO, -				*((u8 *)(beacon->data + j))); -		if (r < 0) -			return r; +		ioreqs[req_pos].addr = CR_BCN_FIFO; +		ioreqs[req_pos].value = *((u8 *)(beacon->data + j)); +		req_pos++;  	}  	for (j = 0; j < 4; j++) { -		r = zd_iowrite32(&mac->chip, CR_BCN_FIFO, 0x0); -		if (r < 0) -			return r; +		ioreqs[req_pos].addr = CR_BCN_FIFO; +		ioreqs[req_pos].value = 0x0; +		req_pos++;  	} -	r = zd_iowrite32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, 1); -	if (r < 0) -		return r; +	BUG_ON(req_pos != num_cmds); + +	r = zd_iowrite32a_locked(&mac->chip, ioreqs, num_cmds); + +release_sema: +	/* +	 * Try very hard to release device beacon semaphore, as otherwise +	 * device/driver can be left in unusable state. +	 */ +	end_jiffies = jiffies + HZ / 2; /*~500ms*/ +	ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE); +	while (ret < 0) { +		if (in_intr || time_is_before_eq_jiffies(end_jiffies)) { +			ret = -ETIMEDOUT; +			break; +		} + +		msleep(20); +		ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE); +	} + +	if (ret < 0) +		dev_err(zd_mac_dev(mac), "Could not release " +					 "CR_BCN_FIFO_SEMAPHORE!\n"); +	if (r < 0 || ret < 0) { +		if (r >= 0) +			r = ret; + +		/* We don't know if beacon was written successfully or not, +		 * so clear current. */ +		zd_mac_free_cur_beacon_locked(mac); + +		goto out; +	} + +	/* Beacon has now been written successfully, update current. */ +	zd_mac_free_cur_beacon_locked(mac); +	mac->beacon.cur_beacon = beacon; +	beacon = NULL;  	/* 802.11b/g 2.4G CCK 1Mb  	 * 802.11a, not yet implemented, uses different values (see GPL vendor  	 * driver)  	 */ -	return zd_iowrite32(&mac->chip, CR_BCN_PLCP_CFG, 0x00000400 | -			(full_len << 19)); +	r = zd_iowrite32_locked(&mac->chip, 0x00000400 | (full_len << 19), +				CR_BCN_PLCP_CFG); +out: +	kfree(ioreqs); +out_nofree: +	kfree_skb(beacon); +	mutex_unlock(&mac->chip.mutex); + +	return r; + +reset_device: +	zd_mac_free_cur_beacon_locked(mac); +	kfree_skb(beacon); + +	mutex_unlock(&mac->chip.mutex); +	kfree(ioreqs); + +	/* semaphore stuck, reset device to avoid fw freeze later */ +	dev_warn(zd_mac_dev(mac), "CR_BCN_FIFO_SEMAPHORE stuck, " +				  "resetting device..."); +	usb_queue_reset_device(mac->chip.usb.intf); + +	return r;  }  static int fill_ctrlset(struct zd_mac *mac, @@ -648,6 +874,14 @@ static int fill_ctrlset(struct zd_mac *mac,  	ZD_ASSERT(frag_len <= 0xffff); +	/* +	 * Firmware computes the duration itself (for all frames except PSPoll) +	 * and needs the field set to 0 at input, otherwise firmware messes up +	 * duration_id and sets bits 14 and 15 on. +	 */ +	if (!ieee80211_is_pspoll(hdr->frame_control)) +		hdr->duration_id = 0; +  	txrate = ieee80211_get_tx_rate(mac->hw, info);  	cs->modulation = txrate->hw_value; @@ -701,7 +935,9 @@ static int fill_ctrlset(struct zd_mac *mac,   * control block of the skbuff will be initialized. If necessary the incoming   * mac80211 queues will be stopped.   */ -static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void zd_op_tx(struct ieee80211_hw *hw, +		     struct ieee80211_tx_control *control, +		     struct sk_buff *skb)  {  	struct zd_mac *mac = zd_hw_mac(hw);  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -716,11 +952,10 @@ static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)  	r = zd_usb_tx(&mac->chip.usb, skb);  	if (r)  		goto fail; -	return 0; +	return;  fail:  	dev_kfree_skb(skb); -	return 0;  }  /** @@ -761,7 +996,7 @@ static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,  		    continue;  		tx_hdr = (struct ieee80211_hdr *)skb->data; -		if (likely(!memcmp(tx_hdr->addr2, rx_hdr->addr1, ETH_ALEN))) +		if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1)))  		{  			found = 1;  			break; @@ -779,6 +1014,13 @@ static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,  		mac->ack_pending = 1;  		mac->ack_signal = stats->signal; + +		/* Prevent pending tx-packet on AP-mode */ +		if (mac->type == NL80211_IFTYPE_AP) { +			skb = __skb_dequeue(q); +			zd_mac_tx_status(hw, skb, mac->ack_signal, NULL); +			mac->ack_pending = 0; +		}  	}  	spin_unlock_irqrestore(&q->lock, flags); @@ -827,7 +1069,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)  	stats.freq = zd_channels[_zd_chip_get_channel(&mac->chip) - 1].center_freq;  	stats.band = IEEE80211_BAND_2GHZ; -	stats.signal = status->signal_strength; +	stats.signal = zd_check_signal(hw, status->signal_strength);  	rate = zd_rx_rate(buffer, status); @@ -882,13 +1124,16 @@ static int zd_op_add_interface(struct ieee80211_hw *hw,  	case NL80211_IFTYPE_MESH_POINT:  	case NL80211_IFTYPE_STATION:  	case NL80211_IFTYPE_ADHOC: +	case NL80211_IFTYPE_AP:  		mac->type = vif->type;  		break;  	default:  		return -EOPNOTSUPP;  	} -	return zd_write_mac_addr(&mac->chip, vif->addr); +	mac->vif = vif; + +	return set_mac_and_bssid(mac);  }  static void zd_op_remove_interface(struct ieee80211_hw *hw, @@ -896,8 +1141,11 @@ static void zd_op_remove_interface(struct ieee80211_hw *hw,  {  	struct zd_mac *mac = zd_hw_mac(hw);  	mac->type = NL80211_IFTYPE_UNSPECIFIED; -	zd_set_beacon_interval(&mac->chip, 0); +	mac->vif = NULL; +	zd_set_beacon_interval(&mac->chip, 0, 0, NL80211_IFTYPE_UNSPECIFIED);  	zd_write_mac_addr(&mac->chip, NULL); + +	zd_mac_free_cur_beacon(mac);  }  static int zd_op_config(struct ieee80211_hw *hw, u32 changed) @@ -905,49 +1153,65 @@ static int zd_op_config(struct ieee80211_hw *hw, u32 changed)  	struct zd_mac *mac = zd_hw_mac(hw);  	struct ieee80211_conf *conf = &hw->conf; -	return zd_chip_set_channel(&mac->chip, conf->channel->hw_value); +	spin_lock_irq(&mac->lock); +	mac->channel = conf->chandef.chan->hw_value; +	spin_unlock_irq(&mac->lock); + +	return zd_chip_set_channel(&mac->chip, conf->chandef.chan->hw_value);  } -static void zd_process_intr(struct work_struct *work) +static void zd_beacon_done(struct zd_mac *mac)  { -	u16 int_status; -	struct zd_mac *mac = container_of(work, struct zd_mac, process_intr); +	struct sk_buff *skb, *beacon; -	int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer+4)); -	if (int_status & INT_CFG_NEXT_BCN) -		dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n"); -	else -		dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n"); - -	zd_chip_enable_hwint(&mac->chip); -} +	if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) +		return; +	if (!mac->vif || mac->vif->type != NL80211_IFTYPE_AP) +		return; +	/* +	 * Send out buffered broad- and multicast frames. +	 */ +	while (!ieee80211_queue_stopped(mac->hw, 0)) { +		skb = ieee80211_get_buffered_bc(mac->hw, mac->vif); +		if (!skb) +			break; +		zd_op_tx(mac->hw, NULL, skb); +	} -static void set_multicast_hash_handler(struct work_struct *work) -{ -	struct zd_mac *mac = -		container_of(work, struct zd_mac, set_multicast_hash_work); -	struct zd_mc_hash hash; +	/* +	 * Fetch next beacon so that tim_count is updated. +	 */ +	beacon = ieee80211_beacon_get(mac->hw, mac->vif); +	if (beacon) +		zd_mac_config_beacon(mac->hw, beacon, true);  	spin_lock_irq(&mac->lock); -	hash = mac->multicast_hash; +	mac->beacon.last_update = jiffies;  	spin_unlock_irq(&mac->lock); - -	zd_chip_set_multicast_hash(&mac->chip, &hash);  } -static void set_rx_filter_handler(struct work_struct *work) +static void zd_process_intr(struct work_struct *work)  { -	struct zd_mac *mac = -		container_of(work, struct zd_mac, set_rx_filter_work); -	int r; +	u16 int_status; +	unsigned long flags; +	struct zd_mac *mac = container_of(work, struct zd_mac, process_intr); -	dev_dbg_f(zd_mac_dev(mac), "\n"); -	r = set_rx_filter(mac); -	if (r) -		dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r); +	spin_lock_irqsave(&mac->lock, flags); +	int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer + 4)); +	spin_unlock_irqrestore(&mac->lock, flags); + +	if (int_status & INT_CFG_NEXT_BCN) { +		/*dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");*/ +		zd_beacon_done(mac); +	} else { +		dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n"); +	} + +	zd_chip_enable_hwint(&mac->chip);  } +  static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw,  				   struct netdev_hw_addr_list *mc_list)  { @@ -979,6 +1243,7 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,  	};  	struct zd_mac *mac = zd_hw_mac(hw);  	unsigned long flags; +	int r;  	/* Only deal with supported flags */  	changed_flags &= SUPPORTED_FIF_FLAGS; @@ -1000,11 +1265,13 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,  	mac->multicast_hash = hash;  	spin_unlock_irqrestore(&mac->lock, flags); -	/* XXX: these can be called here now, can sleep now! */ -	queue_work(zd_workqueue, &mac->set_multicast_hash_work); +	zd_chip_set_multicast_hash(&mac->chip, &hash); -	if (changed_flags & FIF_CONTROL) -		queue_work(zd_workqueue, &mac->set_rx_filter_work); +	if (changed_flags & FIF_CONTROL) { +		r = set_rx_filter(mac); +		if (r) +			dev_err(zd_mac_dev(mac), "set_rx_filter error %d\n", r); +	}  	/* no handling required for FIF_OTHER_BSS as we don't currently  	 * do BSSID filtering */ @@ -1016,20 +1283,9 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,  	 * time. */  } -static void set_rts_cts_work(struct work_struct *work) +static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble)  { -	struct zd_mac *mac = -		container_of(work, struct zd_mac, set_rts_cts_work); -	unsigned long flags; -	unsigned int short_preamble; -  	mutex_lock(&mac->chip.mutex); - -	spin_lock_irqsave(&mac->lock, flags); -	mac->updating_rts_rate = 0; -	short_preamble = mac->short_preamble; -	spin_unlock_irqrestore(&mac->lock, flags); -  	zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble);  	mutex_unlock(&mac->chip.mutex);  } @@ -1040,33 +1296,41 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,  				   u32 changes)  {  	struct zd_mac *mac = zd_hw_mac(hw); -	unsigned long flags;  	int associated;  	dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes);  	if (mac->type == NL80211_IFTYPE_MESH_POINT || -	    mac->type == NL80211_IFTYPE_ADHOC) { +	    mac->type == NL80211_IFTYPE_ADHOC || +	    mac->type == NL80211_IFTYPE_AP) {  		associated = true;  		if (changes & BSS_CHANGED_BEACON) {  			struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);  			if (beacon) { -				zd_mac_config_beacon(hw, beacon); -				kfree_skb(beacon); +				zd_chip_disable_hwint(&mac->chip); +				zd_mac_config_beacon(hw, beacon, false); +				zd_chip_enable_hwint(&mac->chip);  			}  		}  		if (changes & BSS_CHANGED_BEACON_ENABLED) { -			u32 interval; +			u16 interval = 0; +			u8 period = 0; + +			if (bss_conf->enable_beacon) { +				period = bss_conf->dtim_period; +				interval = bss_conf->beacon_int; +			} -			if (bss_conf->enable_beacon) -				interval = BCN_MODE_IBSS | -						bss_conf->beacon_int; -			else -				interval = 0; +			spin_lock_irq(&mac->lock); +			mac->beacon.period = period; +			mac->beacon.interval = interval; +			mac->beacon.last_update = jiffies; +			spin_unlock_irq(&mac->lock); -			zd_set_beacon_interval(&mac->chip, interval); +			zd_set_beacon_interval(&mac->chip, interval, period, +					       mac->type);  		}  	} else  		associated = is_valid_ether_addr(bss_conf->bssid); @@ -1078,19 +1342,15 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,  	/* TODO: do hardware bssid filtering */  	if (changes & BSS_CHANGED_ERP_PREAMBLE) { -		spin_lock_irqsave(&mac->lock, flags); +		spin_lock_irq(&mac->lock);  		mac->short_preamble = bss_conf->use_short_preamble; -		if (!mac->updating_rts_rate) { -			mac->updating_rts_rate = 1; -			/* FIXME: should disable TX here, until work has -			 * completed and RTS_CTS reg is updated */ -			queue_work(zd_workqueue, &mac->set_rts_cts_work); -		} -		spin_unlock_irqrestore(&mac->lock, flags); +		spin_unlock_irq(&mac->lock); + +		set_rts_cts(mac, bss_conf->use_short_preamble);  	}  } -static u64 zd_op_get_tsf(struct ieee80211_hw *hw) +static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)  {  	struct zd_mac *mac = zd_hw_mac(hw);  	return zd_chip_get_tsf(&mac->chip); @@ -1138,12 +1398,15 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)  	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;  	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | -		    IEEE80211_HW_SIGNAL_UNSPEC; +		    IEEE80211_HW_SIGNAL_UNSPEC | +		    IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | +		    IEEE80211_HW_MFP_CAPABLE;  	hw->wiphy->interface_modes =  		BIT(NL80211_IFTYPE_MESH_POINT) |  		BIT(NL80211_IFTYPE_STATION) | -		BIT(NL80211_IFTYPE_ADHOC); +		BIT(NL80211_IFTYPE_ADHOC) | +		BIT(NL80211_IFTYPE_AP);  	hw->max_signal = 100;  	hw->queues = 1; @@ -1160,15 +1423,86 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)  	zd_chip_init(&mac->chip, hw, intf);  	housekeeping_init(mac); -	INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler); -	INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work); -	INIT_WORK(&mac->set_rx_filter_work, set_rx_filter_handler); +	beacon_init(mac);  	INIT_WORK(&mac->process_intr, zd_process_intr);  	SET_IEEE80211_DEV(hw, &intf->dev);  	return hw;  } +#define BEACON_WATCHDOG_DELAY round_jiffies_relative(HZ) + +static void beacon_watchdog_handler(struct work_struct *work) +{ +	struct zd_mac *mac = +		container_of(work, struct zd_mac, beacon.watchdog_work.work); +	struct sk_buff *beacon; +	unsigned long timeout; +	int interval, period; + +	if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) +		goto rearm; +	if (mac->type != NL80211_IFTYPE_AP || !mac->vif) +		goto rearm; + +	spin_lock_irq(&mac->lock); +	interval = mac->beacon.interval; +	period = mac->beacon.period; +	timeout = mac->beacon.last_update + +			msecs_to_jiffies(interval * 1024 / 1000) * 3; +	spin_unlock_irq(&mac->lock); + +	if (interval > 0 && time_is_before_jiffies(timeout)) { +		dev_dbg_f(zd_mac_dev(mac), "beacon interrupt stalled, " +					   "restarting. " +					   "(interval: %d, dtim: %d)\n", +					   interval, period); + +		zd_chip_disable_hwint(&mac->chip); + +		beacon = ieee80211_beacon_get(mac->hw, mac->vif); +		if (beacon) { +			zd_mac_free_cur_beacon(mac); + +			zd_mac_config_beacon(mac->hw, beacon, false); +		} + +		zd_set_beacon_interval(&mac->chip, interval, period, mac->type); + +		zd_chip_enable_hwint(&mac->chip); + +		spin_lock_irq(&mac->lock); +		mac->beacon.last_update = jiffies; +		spin_unlock_irq(&mac->lock); +	} + +rearm: +	queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work, +			   BEACON_WATCHDOG_DELAY); +} + +static void beacon_init(struct zd_mac *mac) +{ +	INIT_DELAYED_WORK(&mac->beacon.watchdog_work, beacon_watchdog_handler); +} + +static void beacon_enable(struct zd_mac *mac) +{ +	dev_dbg_f(zd_mac_dev(mac), "\n"); + +	mac->beacon.last_update = jiffies; +	queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work, +			   BEACON_WATCHDOG_DELAY); +} + +static void beacon_disable(struct zd_mac *mac) +{ +	dev_dbg_f(zd_mac_dev(mac), "\n"); +	cancel_delayed_work_sync(&mac->beacon.watchdog_work); + +	zd_mac_free_cur_beacon(mac); +} +  #define LINK_LED_WORK_DELAY HZ  static void link_led_handler(struct work_struct *work) @@ -1179,6 +1513,9 @@ static void link_led_handler(struct work_struct *work)  	int is_associated;  	int r; +	if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) +		goto requeue; +  	spin_lock_irq(&mac->lock);  	is_associated = mac->associated;  	spin_unlock_irq(&mac->lock); @@ -1188,6 +1525,7 @@ static void link_led_handler(struct work_struct *work)  	if (r)  		dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r); +requeue:  	queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,  		           LINK_LED_WORK_DELAY);  } @@ -1207,7 +1545,6 @@ static void housekeeping_enable(struct zd_mac *mac)  static void housekeeping_disable(struct zd_mac *mac)  {  	dev_dbg_f(zd_mac_dev(mac), "\n"); -	cancel_rearming_delayed_workqueue(zd_workqueue, -		&mac->housekeeping.link_led_work); +	cancel_delayed_work_sync(&mac->housekeeping.link_led_work);  	zd_chip_control_leds(&mac->chip, ZD_LED_OFF);  }  | 
