Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b54853f1 authored by Juuso Oikarinen's avatar Juuso Oikarinen Committed by John W. Linville
Browse files

wl1271: Fix filter configuration



Fix the filter configuration to properly work with the two phase filter
configuration (prepare_multicast/configure_filter.)

Signed-off-by: default avatarJuuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: default avatarLuciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: default avatarLuciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent bd5ea18f
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -363,9 +363,6 @@ struct wl1271 {

	struct work_struct tx_work;

	struct work_struct filter_work;
	struct wl1271_filter_params *filter_params;

	/* Pending TX frames */
	struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS];

+51 −106
Original line number Diff line number Diff line
@@ -675,74 +675,6 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
	return ret;
}

struct wl1271_filter_params {
	unsigned int filters;
	unsigned int changed;
	int mc_list_length;
	u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
};

#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
				  FIF_ALLMULTI | \
				  FIF_FCSFAIL | \
				  FIF_BCN_PRBRESP_PROMISC | \
				  FIF_CONTROL | \
				  FIF_OTHER_BSS)

static void wl1271_filter_work(struct work_struct *work)
{
	struct wl1271 *wl =
		container_of(work, struct wl1271, filter_work);
	struct wl1271_filter_params *fp;
	unsigned long flags;
	bool enabled = true;
	int ret;

	/* first, get the filter parameters */
	spin_lock_irqsave(&wl->wl_lock, flags);
	fp = wl->filter_params;
	wl->filter_params = NULL;
	spin_unlock_irqrestore(&wl->wl_lock, flags);

	if (!fp)
		return;

	/* then, lock the mutex without risk of lock-up */
	mutex_lock(&wl->mutex);

	if (wl->state == WL1271_STATE_OFF)
		goto out;

	ret = wl1271_ps_elp_wakeup(wl, false);
	if (ret < 0)
		goto out;

	/* configure the mc filter regardless of the changed flags */
	if (fp->filters & FIF_ALLMULTI)
		enabled = false;

	ret = wl1271_acx_group_address_tbl(wl, enabled,
					   fp->mc_list, fp->mc_list_length);
	if (ret < 0)
		goto out_sleep;

	/* determine, whether supported filter values have changed */
	if (fp->changed == 0)
		goto out;

	/* apply configured filters */
	ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
	if (ret < 0)
		goto out_sleep;

out_sleep:
	wl1271_ps_elp_sleep(wl);

out:
	mutex_unlock(&wl->mutex);
	kfree(fp);
}

int wl1271_plt_start(struct wl1271 *wl)
{
	int ret;
@@ -993,20 +925,12 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
static void wl1271_op_stop(struct ieee80211_hw *hw)
{
	struct wl1271 *wl = hw->priv;
	unsigned long flags;
	int i;

	wl1271_info("down");

	wl1271_debug(DEBUG_MAC80211, "mac80211 stop");

	/* complete/cancel ongoing work */
	cancel_work_sync(&wl->filter_work);
	spin_lock_irqsave(&wl->wl_lock, flags);
	kfree(wl->filter_params);
	wl->filter_params = NULL;
	spin_unlock_irqrestore(&wl->wl_lock, flags);

	unregister_inetaddr_notifier(&wl1271_dev_notifier);
	list_del(&wl->list);

@@ -1029,7 +953,6 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)

	cancel_work_sync(&wl->irq_work);
	cancel_work_sync(&wl->tx_work);
	cancel_work_sync(&wl->filter_work);

	mutex_lock(&wl->mutex);

@@ -1252,19 +1175,18 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
	return ret;
}

struct wl1271_filter_params {
	bool enabled;
	int mc_list_length;
	u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
};

static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
				       struct dev_addr_list *mc_list)
{
	struct wl1271 *wl = hw->priv;
	struct wl1271_filter_params *fp;
	unsigned long flags;
	int i;

	/*
	 * FIXME: we should return a hash that will be passed to
	 * configure_filter() instead of saving everything in the context.
	 */

	fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
	if (!fp) {
		wl1271_error("Out of memory setting filters.");
@@ -1272,9 +1194,10 @@ static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
	}

	/* update multicast filtering parameters */
	fp->enabled = true;
	if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) {
		mc_count = 0;
		fp->filters |= FIF_ALLMULTI;
		fp->enabled = false;
	}

	fp->mc_list_length = 0;
@@ -1288,42 +1211,65 @@ static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
		mc_list = mc_list->next;
	}

	/* FIXME: We still need to set our filters properly */

	spin_lock_irqsave(&wl->wl_lock, flags);
	kfree(wl->filter_params);
	wl->filter_params = fp;
	spin_unlock_irqrestore(&wl->wl_lock, flags);

	return 1;
	return (u64)(unsigned long)fp;
}

#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
				  FIF_ALLMULTI | \
				  FIF_FCSFAIL | \
				  FIF_BCN_PRBRESP_PROMISC | \
				  FIF_CONTROL | \
				  FIF_OTHER_BSS)

static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
				       unsigned int changed,
				       unsigned int *total, u64 multicast)
{
	struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
	struct wl1271 *wl = hw->priv;
	int ret;

	wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");

	mutex_lock(&wl->mutex);

	if (wl->state == WL1271_STATE_OFF)
		goto out;

	ret = wl1271_ps_elp_wakeup(wl, false);
	if (ret < 0)
		goto out;

	*total &= WL1271_SUPPORTED_FILTERS;
	changed &= WL1271_SUPPORTED_FILTERS;

	if (!multicast)
		return;
	if (*total & FIF_ALLMULTI)
		ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
	else if (fp)
		ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
						   fp->mc_list,
						   fp->mc_list_length);
	if (ret < 0)
		goto out_sleep;

	/*
	 * FIXME: for now we are still using a workqueue for filter
	 * configuration, but with the new mac80211, this is not needed,
	 * since configure_filter can now sleep.  We now have
	 * prepare_multicast, which needs to be atomic instead.
	 */
	kfree(fp);

	/* FIXME: We still need to set our filters properly */

	/* store current filter config */
	wl->filter_params->filters = *total;
	wl->filter_params->changed = changed;
	/* determine, whether supported filter values have changed */
	if (changed == 0)
		goto out_sleep;

	ieee80211_queue_work(wl->hw, &wl->filter_work);
	/* apply configured filters */
	ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
	if (ret < 0)
		goto out_sleep;

out_sleep:
	wl1271_ps_elp_sleep(wl);

out:
	mutex_unlock(&wl->mutex);
}

static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -1866,7 +1812,6 @@ static int __devinit wl1271_probe(struct spi_device *spi)

	skb_queue_head_init(&wl->tx_queue);

	INIT_WORK(&wl->filter_work, wl1271_filter_work);
	INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
	wl->channel = WL1271_DEFAULT_CHANNEL;
	wl->scanning = false;