diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/event.c')
| -rw-r--r-- | drivers/net/wireless/wl12xx/event.c | 64 | 
1 files changed, 59 insertions, 5 deletions
diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 1b170c5cc59..d7be3aec6fc 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -33,6 +33,7 @@ void wl1271_pspoll_work(struct work_struct *work)  {  	struct delayed_work *dwork;  	struct wl1271 *wl; +	int ret;  	dwork = container_of(work, struct delayed_work, work);  	wl = container_of(dwork, struct wl1271, pspoll_work); @@ -55,8 +56,13 @@ void wl1271_pspoll_work(struct work_struct *work)  	 * delivery failure occurred, and no-one changed state since, so  	 * we should go back to powersave.  	 */ +	ret = wl1271_ps_elp_wakeup(wl); +	if (ret < 0) +		goto out; +  	wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true); +	wl1271_ps_elp_sleep(wl);  out:  	mutex_unlock(&wl->mutex);  }; @@ -129,11 +135,6 @@ static int wl1271_event_ps_report(struct wl1271 *wl,  		/* enable beacon early termination */  		ret = wl1271_acx_bet_enable(wl, true); -		if (ret < 0) -			break; - -		/* go to extremely low power mode */ -		wl1271_ps_elp_sleep(wl);  		break;  	default:  		break; @@ -173,6 +174,8 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)  	u32 vector;  	bool beacon_loss = false;  	bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); +	bool disconnect_sta = false; +	unsigned long sta_bitmap = 0;  	wl1271_event_mbox_dump(mbox); @@ -228,9 +231,60 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)  			wl1271_event_rssi_trigger(wl, mbox);  	} +	if ((vector & DUMMY_PACKET_EVENT_ID) && !is_ap) { +		wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); +		if (wl->vif) +			wl1271_tx_dummy_packet(wl); +	} + +	/* +	 * "TX retries exceeded" has a different meaning according to mode. +	 * In AP mode the offending station is disconnected. In STA mode we +	 * report connection loss. +	 */ +	if (vector & MAX_TX_RETRY_EVENT_ID) { +		wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); +		if (is_ap) { +			sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); +			disconnect_sta = true; +		} else { +			beacon_loss = true; +		} +	} + +	if ((vector & INACTIVE_STA_EVENT_ID) && is_ap) { +		wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); +		sta_bitmap |= le16_to_cpu(mbox->sta_aging_status); +		disconnect_sta = true; +	} +  	if (wl->vif && beacon_loss)  		ieee80211_connection_loss(wl->vif); +	if (is_ap && disconnect_sta) { +		u32 num_packets = wl->conf.tx.max_tx_retries; +		struct ieee80211_sta *sta; +		const u8 *addr; +		int h; + +		for (h = find_first_bit(&sta_bitmap, AP_MAX_LINKS); +		     h < AP_MAX_LINKS; +		     h = find_next_bit(&sta_bitmap, AP_MAX_LINKS, h+1)) { +			if (!wl1271_is_active_sta(wl, h)) +				continue; + +			addr = wl->links[h].addr; + +			rcu_read_lock(); +			sta = ieee80211_find_sta(wl->vif, addr); +			if (sta) { +				wl1271_debug(DEBUG_EVENT, "remove sta %d", h); +				ieee80211_report_low_ack(sta, num_packets); +			} +			rcu_read_unlock(); +		} +	} +  	return 0;  }  |