GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / net / wireless / ti / wlcore / ps.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * This file is part of wl1271
4  *
5  * Copyright (C) 2008-2009 Nokia Corporation
6  *
7  * Contact: Luciano Coelho <luciano.coelho@nokia.com>
8  */
9
10 #include "ps.h"
11 #include "io.h"
12 #include "tx.h"
13 #include "debug.h"
14
15 int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
16                        enum wl1271_cmd_ps_mode mode)
17 {
18         int ret;
19         u16 timeout = wl->conf.conn.dynamic_ps_timeout;
20
21         switch (mode) {
22         case STATION_AUTO_PS_MODE:
23         case STATION_POWER_SAVE_MODE:
24                 wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)",
25                              mode, timeout);
26
27                 ret = wl1271_acx_wake_up_conditions(wl, wlvif,
28                                             wl->conf.conn.wake_up_event,
29                                             wl->conf.conn.listen_interval);
30                 if (ret < 0) {
31                         wl1271_error("couldn't set wake up conditions");
32                         return ret;
33                 }
34
35                 ret = wl1271_cmd_ps_mode(wl, wlvif, mode, timeout);
36                 if (ret < 0)
37                         return ret;
38
39                 set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
40
41                 /*
42                  * enable beacon early termination.
43                  * Not relevant for 5GHz and for high rates.
44                  */
45                 if ((wlvif->band == NL80211_BAND_2GHZ) &&
46                     (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
47                         ret = wl1271_acx_bet_enable(wl, wlvif, true);
48                         if (ret < 0)
49                                 return ret;
50                 }
51                 break;
52         case STATION_ACTIVE_MODE:
53                 wl1271_debug(DEBUG_PSM, "leaving psm");
54
55                 /* disable beacon early termination */
56                 if ((wlvif->band == NL80211_BAND_2GHZ) &&
57                     (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
58                         ret = wl1271_acx_bet_enable(wl, wlvif, false);
59                         if (ret < 0)
60                                 return ret;
61                 }
62
63                 ret = wl1271_cmd_ps_mode(wl, wlvif, mode, 0);
64                 if (ret < 0)
65                         return ret;
66
67                 clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
68                 break;
69         default:
70                 wl1271_warning("trying to set ps to unsupported mode %d", mode);
71                 ret = -EINVAL;
72         }
73
74         return ret;
75 }
76
77 static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
78 {
79         int i;
80         struct sk_buff *skb;
81         struct ieee80211_tx_info *info;
82         unsigned long flags;
83         int filtered[NUM_TX_QUEUES];
84         struct wl1271_link *lnk = &wl->links[hlid];
85
86         /* filter all frames currently in the low level queues for this hlid */
87         for (i = 0; i < NUM_TX_QUEUES; i++) {
88                 filtered[i] = 0;
89                 while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
90                         filtered[i]++;
91
92                         if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
93                                 continue;
94
95                         info = IEEE80211_SKB_CB(skb);
96                         info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
97                         info->status.rates[0].idx = -1;
98                         ieee80211_tx_status_ni(wl->hw, skb);
99                 }
100         }
101
102         spin_lock_irqsave(&wl->wl_lock, flags);
103         for (i = 0; i < NUM_TX_QUEUES; i++) {
104                 wl->tx_queue_count[i] -= filtered[i];
105                 if (lnk->wlvif)
106                         lnk->wlvif->tx_queue_count[i] -= filtered[i];
107         }
108         spin_unlock_irqrestore(&wl->wl_lock, flags);
109
110         wl1271_handle_tx_low_watermark(wl);
111 }
112
113 void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
114                           u8 hlid, bool clean_queues)
115 {
116         struct ieee80211_sta *sta;
117         struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
118
119         if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
120                 return;
121
122         if (!test_bit(hlid, wlvif->ap.sta_hlid_map) ||
123             test_bit(hlid, &wl->ap_ps_map))
124                 return;
125
126         wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d "
127                      "clean_queues %d", hlid, wl->links[hlid].allocated_pkts,
128                      clean_queues);
129
130         rcu_read_lock();
131         sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
132         if (!sta) {
133                 wl1271_error("could not find sta %pM for starting ps",
134                              wl->links[hlid].addr);
135                 rcu_read_unlock();
136                 return;
137         }
138
139         ieee80211_sta_ps_transition_ni(sta, true);
140         rcu_read_unlock();
141
142         /* do we want to filter all frames from this link's queues? */
143         if (clean_queues)
144                 wl1271_ps_filter_frames(wl, hlid);
145
146         __set_bit(hlid, &wl->ap_ps_map);
147 }
148
149 void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
150 {
151         struct ieee80211_sta *sta;
152         struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
153
154         if (!test_bit(hlid, &wl->ap_ps_map))
155                 return;
156
157         wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid);
158
159         __clear_bit(hlid, &wl->ap_ps_map);
160
161         rcu_read_lock();
162         sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
163         if (!sta) {
164                 wl1271_error("could not find sta %pM for ending ps",
165                              wl->links[hlid].addr);
166                 goto end;
167         }
168
169         ieee80211_sta_ps_transition_ni(sta, false);
170 end:
171         rcu_read_unlock();
172 }