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

Commit b376704b authored by John W. Linville's avatar John W. Linville
Browse files
parents 1159024d 9439064c
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -478,7 +478,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
		DISCONNECT_EVENT_COMPLETE_ID |
		RSSI_SNR_TRIGGER_0_EVENT_ID |
		PSPOLL_DELIVERY_FAILURE_EVENT_ID |
		SOFT_GEMINI_SENSE_EVENT_ID;
		SOFT_GEMINI_SENSE_EVENT_ID |
		PERIODIC_SCAN_REPORT_EVENT_ID |
		PERIODIC_SCAN_COMPLETE_EVENT_ID;

	if (wl->bss_type == BSS_TYPE_AP_BSS)
		wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID;
+21 −0
Original line number Diff line number Diff line
@@ -1147,6 +1147,26 @@ struct conf_scan_settings {

};

struct conf_sched_scan_settings {
	/* minimum time to wait on the channel for active scans (in TUs) */
	u16 min_dwell_time_active;

	/* maximum time to wait on the channel for active scans (in TUs) */
	u16 max_dwell_time_active;

	/* time to wait on the channel for passive scans (in TUs) */
	u32 dwell_time_passive;

	/* number of probe requests to send on each channel in active scans */
	u8 num_probe_reqs;

	/* RSSI threshold to be used for filtering */
	s8 rssi_threshold;

	/* SNR threshold to be used for filtering */
	s8 snr_threshold;
};

/* these are number of channels on the band divided by two, rounded up */
#define CONF_TX_PWR_COMPENSATION_LEN_2 7
#define CONF_TX_PWR_COMPENSATION_LEN_5 18
@@ -1234,6 +1254,7 @@ struct conf_drv_settings {
	struct conf_pm_config_settings pm_config;
	struct conf_roam_trigger_settings roam_trigger;
	struct conf_scan_settings scan;
	struct conf_sched_scan_settings sched_scan;
	struct conf_rf_settings rf;
	struct conf_ht_setting ht;
	struct conf_memory_settings mem_wl127x;
+1 −0
Original line number Diff line number Diff line
@@ -377,6 +377,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
	DRIVER_STATE_PRINT_HEX(platform_quirks);
	DRIVER_STATE_PRINT_HEX(chip.id);
	DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
	DRIVER_STATE_PRINT_INT(sched_scanning);

#undef DRIVER_STATE_PRINT_INT
#undef DRIVER_STATE_PRINT_LONG
+23 −0
Original line number Diff line number Diff line
@@ -135,6 +135,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl,

		/* enable beacon early termination */
		ret = wl1271_acx_bet_enable(wl, true);
		if (ret < 0)
			break;

		if (wl->ps_compl) {
			complete(wl->ps_compl);
			wl->ps_compl = NULL;
		}
		break;
	default:
		break;
@@ -188,6 +195,22 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
		wl1271_scan_stm(wl);
	}

	if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
		wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT "
			     "(status 0x%0x)", mbox->scheduled_scan_status);

		wl1271_scan_sched_scan_results(wl);
	}

	if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) {
		wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT "
			     "(status 0x%0x)", mbox->scheduled_scan_status);
		if (wl->sched_scanning) {
			wl1271_scan_sched_scan_stop(wl);
			ieee80211_sched_scan_stopped(wl->hw);
		}
	}

	/* disable dynamic PS when requested by the firmware */
	if (vector & SOFT_GEMINI_SENSE_EVENT_ID &&
	    wl->bss_type == BSS_TYPE_STA_BSS) {
+247 −13
Original line number Diff line number Diff line
@@ -257,12 +257,16 @@ static struct conf_drv_settings default_conf = {
		.wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
		.listen_interval             = 1,
		.bcn_filt_mode               = CONF_BCN_FILT_MODE_ENABLED,
		.bcn_filt_ie_count           = 1,
		.bcn_filt_ie_count           = 2,
		.bcn_filt_ie = {
			[0] = {
				.ie          = WLAN_EID_CHANNEL_SWITCH,
				.rule        = CONF_BCN_RULE_PASS_ON_APPEARANCE,
			}
			},
			[1] = {
				.ie          = WLAN_EID_HT_INFORMATION,
				.rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
			},
		},
		.synch_fail_thold            = 10,
		.bss_lose_timeout            = 100,
@@ -302,6 +306,15 @@ static struct conf_drv_settings default_conf = {
		.max_dwell_time_passive       = 100000,
		.num_probe_reqs               = 2,
	},
	.sched_scan = {
		/* sched_scan requires dwell times in TU instead of TU/1000 */
		.min_dwell_time_active = 8,
		.max_dwell_time_active = 30,
		.dwell_time_passive    = 100,
		.num_probe_reqs        = 2,
		.rssi_threshold        = -90,
		.snr_threshold         = 0,
	},
	.rf = {
		.tx_per_channel_power_compensation_2 = {
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -975,6 +988,11 @@ static void wl1271_recovery_work(struct work_struct *work)
	/* Prevent spurious TX during FW restart */
	ieee80211_stop_queues(wl->hw);

	if (wl->sched_scanning) {
		ieee80211_sched_scan_stopped(wl->hw);
		wl->sched_scanning = false;
	}

	/* reboot the chipset */
	__wl1271_op_remove_interface(wl, false);
	ieee80211_restart_hw(wl->hw);
@@ -1332,6 +1350,150 @@ static struct notifier_block wl1271_dev_notifier = {
	.notifier_call = wl1271_dev_notify,
};

static int wl1271_configure_suspend(struct wl1271 *wl)
{
	int ret;

	if (wl->bss_type != BSS_TYPE_STA_BSS)
		return 0;

	mutex_lock(&wl->mutex);

	ret = wl1271_ps_elp_wakeup(wl);
	if (ret < 0)
		goto out_unlock;

	/* enter psm if needed*/
	if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
		DECLARE_COMPLETION_ONSTACK(compl);

		wl->ps_compl = &compl;
		ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
				   wl->basic_rate, true);
		if (ret < 0)
			goto out_sleep;

		/* we must unlock here so we will be able to get events */
		wl1271_ps_elp_sleep(wl);
		mutex_unlock(&wl->mutex);

		ret = wait_for_completion_timeout(
			&compl, msecs_to_jiffies(WL1271_PS_COMPLETE_TIMEOUT));
		if (ret <= 0) {
			wl1271_warning("couldn't enter ps mode!");
			ret = -EBUSY;
			goto out;
		}

		/* take mutex again, and wakeup */
		mutex_lock(&wl->mutex);

		ret = wl1271_ps_elp_wakeup(wl);
		if (ret < 0)
			goto out_unlock;
	}
out_sleep:
	wl1271_ps_elp_sleep(wl);
out_unlock:
	mutex_unlock(&wl->mutex);
out:
	return ret;

}

static void wl1271_configure_resume(struct wl1271 *wl)
{
	int ret;

	if (wl->bss_type != BSS_TYPE_STA_BSS)
		return;

	mutex_lock(&wl->mutex);
	ret = wl1271_ps_elp_wakeup(wl);
	if (ret < 0)
		goto out;

	/* exit psm if it wasn't configured */
	if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
		wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
				   wl->basic_rate, true);

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

static int wl1271_op_suspend(struct ieee80211_hw *hw,
			    struct cfg80211_wowlan *wow)
{
	struct wl1271 *wl = hw->priv;
	wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
	wl->wow_enabled = !!wow;
	if (wl->wow_enabled) {
		int ret;
		ret = wl1271_configure_suspend(wl);
		if (ret < 0) {
			wl1271_warning("couldn't prepare device to suspend");
			return ret;
		}
		/* flush any remaining work */
		wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
		flush_delayed_work(&wl->scan_complete_work);

		/*
		 * disable and re-enable interrupts in order to flush
		 * the threaded_irq
		 */
		wl1271_disable_interrupts(wl);

		/*
		 * set suspended flag to avoid triggering a new threaded_irq
		 * work. no need for spinlock as interrupts are disabled.
		 */
		set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);

		wl1271_enable_interrupts(wl);
		flush_work(&wl->tx_work);
		flush_delayed_work(&wl->pspoll_work);
		flush_delayed_work(&wl->elp_work);
	}
	return 0;
}

static int wl1271_op_resume(struct ieee80211_hw *hw)
{
	struct wl1271 *wl = hw->priv;
	wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
		     wl->wow_enabled);

	/*
	 * re-enable irq_work enqueuing, and call irq_work directly if
	 * there is a pending work.
	 */
	if (wl->wow_enabled) {
		struct wl1271 *wl = hw->priv;
		unsigned long flags;
		bool run_irq_work = false;

		spin_lock_irqsave(&wl->wl_lock, flags);
		clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
		if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
			run_irq_work = true;
		spin_unlock_irqrestore(&wl->wl_lock, flags);

		if (run_irq_work) {
			wl1271_debug(DEBUG_MAC80211,
				     "run postponed irq_work directly");
			wl1271_irq(0, wl);
			wl1271_enable_interrupts(wl);
		}

		wl1271_configure_resume(wl);
	}

	return 0;
}

static int wl1271_op_start(struct ieee80211_hw *hw)
{
	wl1271_debug(DEBUG_MAC80211, "mac80211 start");
@@ -1563,6 +1725,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
	memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
	wl->ap_fw_ps_map = 0;
	wl->ap_ps_map = 0;
	wl->sched_scanning = false;

	/*
	 * this is performed after the cancel_work calls and the associated
@@ -1765,6 +1928,13 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
		wl->session_counter++;
		if (wl->session_counter >= SESSION_COUNTER_MAX)
			wl->session_counter = 0;

		/* The current firmware only supports sched_scan in idle */
		if (wl->sched_scanning) {
			wl1271_scan_sched_scan_stop(wl);
			ieee80211_sched_scan_stopped(wl->hw);
		}

		ret = wl1271_dummy_join(wl);
		if (ret < 0)
			goto out;
@@ -2317,6 +2487,60 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
	return ret;
}

static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
				      struct ieee80211_vif *vif,
				      struct cfg80211_sched_scan_request *req,
				      struct ieee80211_sched_scan_ies *ies)
{
	struct wl1271 *wl = hw->priv;
	int ret;

	wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start");

	mutex_lock(&wl->mutex);

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

	ret = wl1271_scan_sched_scan_config(wl, req, ies);
	if (ret < 0)
		goto out_sleep;

	ret = wl1271_scan_sched_scan_start(wl);
	if (ret < 0)
		goto out_sleep;

	wl->sched_scanning = true;

out_sleep:
	wl1271_ps_elp_sleep(wl);
out:
	mutex_unlock(&wl->mutex);
	return ret;
}

static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
				      struct ieee80211_vif *vif)
{
	struct wl1271 *wl = hw->priv;
	int ret;

	wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");

	mutex_lock(&wl->mutex);

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

	wl1271_scan_sched_scan_stop(wl);

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

static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
{
	struct wl1271 *wl = hw->priv;
@@ -2376,20 +2600,24 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
			    int offset)
{
	u8 *ptr = skb->data + offset;
	u8 ssid_len;
	const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
					 skb->len - offset);

	/* find the location of the ssid in the beacon */
	while (ptr < skb->data + skb->len) {
		if (ptr[0] == WLAN_EID_SSID) {
			wl->ssid_len = ptr[1];
			memcpy(wl->ssid, ptr+2, wl->ssid_len);
			return 0;
	if (!ptr) {
		wl1271_error("No SSID in IEs!");
		return -ENOENT;
	}
		ptr += (ptr[1] + 2);

	ssid_len = ptr[1];
	if (ssid_len > IEEE80211_MAX_SSID_LEN) {
		wl1271_error("SSID is too long!");
		return -EINVAL;
	}

	wl1271_error("No SSID in IEs!\n");
	return -ENOENT;
	wl->ssid_len = ssid_len;
	memcpy(wl->ssid, ptr+2, ssid_len);
	return 0;
}

static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
@@ -3422,12 +3650,16 @@ static const struct ieee80211_ops wl1271_ops = {
	.stop = wl1271_op_stop,
	.add_interface = wl1271_op_add_interface,
	.remove_interface = wl1271_op_remove_interface,
	.suspend = wl1271_op_suspend,
	.resume = wl1271_op_resume,
	.config = wl1271_op_config,
	.prepare_multicast = wl1271_op_prepare_multicast,
	.configure_filter = wl1271_op_configure_filter,
	.tx = wl1271_op_tx,
	.set_key = wl1271_op_set_key,
	.hw_scan = wl1271_op_hw_scan,
	.sched_scan_start = wl1271_op_sched_scan_start,
	.sched_scan_stop = wl1271_op_sched_scan_stop,
	.bss_info_changed = wl1271_op_bss_info_changed,
	.set_frag_threshold = wl1271_op_set_frag_threshold,
	.set_rts_threshold = wl1271_op_set_rts_threshold,
@@ -3626,6 +3858,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
		IEEE80211_HW_CONNECTION_MONITOR |
		IEEE80211_HW_SUPPORTS_CQM_RSSI |
		IEEE80211_HW_REPORTS_TX_ACK_STATUS |
		IEEE80211_HW_SPECTRUM_MGMT |
		IEEE80211_HW_AP_LINK_PS;

	wl->hw->wiphy->cipher_suites = cipher_suites;
@@ -3747,6 +3980,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
	wl->ap_fw_ps_map = 0;
	wl->quirks = 0;
	wl->platform_quirks = 0;
	wl->sched_scanning = false;

	memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
	for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
Loading