wl1251: enable beacon early termination while in power-saving mode
[pandora-wifi.git] / drivers / net / wireless / wl12xx / wl1271_event.c
1 /*
2  * This file is part of wl1271
3  *
4  * Copyright (C) 2008-2009 Nokia Corporation
5  *
6  * Contact: Luciano Coelho <luciano.coelho@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * version 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  *
22  */
23
24 #include "wl1271.h"
25 #include "wl1271_reg.h"
26 #include "wl1271_spi.h"
27 #include "wl1271_io.h"
28 #include "wl1271_event.h"
29 #include "wl1271_ps.h"
30 #include "wl12xx_80211.h"
31
32 static int wl1271_event_scan_complete(struct wl1271 *wl,
33                                       struct event_mailbox *mbox)
34 {
35         int size = sizeof(struct wl12xx_probe_req_template);
36         wl1271_debug(DEBUG_EVENT, "status: 0x%x",
37                      mbox->scheduled_scan_status);
38
39         if (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) {
40                 if (wl->scan.state == WL1271_SCAN_BAND_DUAL) {
41                         wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
42                                                 NULL, size);
43                         /* 2.4 GHz band scanned, scan 5 GHz band, pretend
44                          * to the wl1271_cmd_scan function that we are not
45                          * scanning as it checks that.
46                          */
47                         clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
48                         wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
49                                                 wl->scan.active,
50                                                 wl->scan.high_prio,
51                                                 WL1271_SCAN_BAND_5_GHZ,
52                                                 wl->scan.probe_requests);
53                 } else {
54                         if (wl->scan.state == WL1271_SCAN_BAND_2_4_GHZ)
55                                 wl1271_cmd_template_set(wl,
56                                                 CMD_TEMPL_CFG_PROBE_REQ_2_4,
57                                                 NULL, size);
58                         else
59                                 wl1271_cmd_template_set(wl,
60                                                 CMD_TEMPL_CFG_PROBE_REQ_5,
61                                                 NULL, size);
62
63                         mutex_unlock(&wl->mutex);
64                         ieee80211_scan_completed(wl->hw, false);
65                         mutex_lock(&wl->mutex);
66                         clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
67                 }
68         }
69         return 0;
70 }
71
72 static int wl1271_event_ps_report(struct wl1271 *wl,
73                                   struct event_mailbox *mbox,
74                                   bool *beacon_loss)
75 {
76         int ret = 0;
77
78         wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status);
79
80         switch (mbox->ps_status) {
81         case EVENT_ENTER_POWER_SAVE_FAIL:
82                 wl1271_debug(DEBUG_PSM, "PSM entry failed");
83
84                 if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
85                         /* remain in active mode */
86                         wl->psm_entry_retry = 0;
87                         break;
88                 }
89
90                 if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) {
91                         wl->psm_entry_retry++;
92                         ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
93                                                  true);
94                 } else {
95                         wl1271_error("PSM entry failed, giving up.\n");
96                         /* FIXME: this may need to be reconsidered. for now it
97                            is not possible to indicate to the mac80211
98                            afterwards that PSM entry failed. To maximize
99                            functionality (receiving data and remaining
100                            associated) make sure that we are in sync with the
101                            AP in regard of PSM mode. */
102                         ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
103                                                  false);
104                         wl->psm_entry_retry = 0;
105                 }
106                 break;
107         case EVENT_ENTER_POWER_SAVE_SUCCESS:
108                 wl->psm_entry_retry = 0;
109
110                 /* enable beacon filtering */
111                 ret = wl1271_acx_beacon_filter_opt(wl, true);
112                 if (ret < 0)
113                         break;
114
115                 /* enable beacon early termination */
116                 ret = wl1271_acx_bet_enable(wl, true);
117                 if (ret < 0)
118                         break;
119
120                 /* go to extremely low power mode */
121                 wl1271_ps_elp_sleep(wl);
122                 if (ret < 0)
123                         break;
124                 break;
125         case EVENT_EXIT_POWER_SAVE_FAIL:
126                 wl1271_debug(DEBUG_PSM, "PSM exit failed");
127
128                 if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
129                         wl->psm_entry_retry = 0;
130                         break;
131                 }
132
133                 /* make sure the firmware goes to active mode - the frame to
134                    be sent next will indicate to the AP, that we are active. */
135                 ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
136                                          false);
137                 break;
138         case EVENT_EXIT_POWER_SAVE_SUCCESS:
139         default:
140                 break;
141         }
142
143         return ret;
144 }
145
146 static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
147 {
148         wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
149         wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
150         wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
151 }
152
153 static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
154 {
155         int ret;
156         u32 vector;
157         bool beacon_loss = false;
158
159         wl1271_event_mbox_dump(mbox);
160
161         vector = le32_to_cpu(mbox->events_vector);
162         vector &= ~(le32_to_cpu(mbox->events_mask));
163         wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
164
165         if (vector & SCAN_COMPLETE_EVENT_ID) {
166                 ret = wl1271_event_scan_complete(wl, mbox);
167                 if (ret < 0)
168                         return ret;
169         }
170
171         /*
172          * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon
173          * filtering) is enabled. Without PSM, the stack will receive all
174          * beacons and can detect beacon loss by itself.
175          */
176         if (vector & BSS_LOSE_EVENT_ID &&
177             test_bit(WL1271_FLAG_PSM, &wl->flags)) {
178                 wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
179
180                 /* indicate to the stack, that beacons have been lost */
181                 beacon_loss = true;
182         }
183
184         if (vector & PS_REPORT_EVENT_ID) {
185                 wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
186                 ret = wl1271_event_ps_report(wl, mbox, &beacon_loss);
187                 if (ret < 0)
188                         return ret;
189         }
190
191         if (wl->vif && beacon_loss) {
192                 /* Obviously, it's dangerous to release the mutex while
193                    we are holding many of the variables in the wl struct.
194                    That's why it's done last in the function, and care must
195                    be taken that nothing more is done after this function
196                    returns. */
197                 mutex_unlock(&wl->mutex);
198                 ieee80211_beacon_loss(wl->vif);
199                 mutex_lock(&wl->mutex);
200         }
201
202         return 0;
203 }
204
205 int wl1271_event_unmask(struct wl1271 *wl)
206 {
207         int ret;
208
209         ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask));
210         if (ret < 0)
211                 return ret;
212
213         return 0;
214 }
215
216 void wl1271_event_mbox_config(struct wl1271 *wl)
217 {
218         wl->mbox_ptr[0] = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR);
219         wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
220
221         wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x",
222                      wl->mbox_ptr[0], wl->mbox_ptr[1]);
223 }
224
225 int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
226 {
227         struct event_mailbox mbox;
228         int ret;
229
230         wl1271_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num);
231
232         if (mbox_num > 1)
233                 return -EINVAL;
234
235         /* first we read the mbox descriptor */
236         wl1271_read(wl, wl->mbox_ptr[mbox_num], &mbox,
237                     sizeof(struct event_mailbox), false);
238
239         /* process the descriptor */
240         ret = wl1271_event_process(wl, &mbox);
241         if (ret < 0)
242                 return ret;
243
244         /* then we let the firmware know it can go on...*/
245         wl1271_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK);
246
247         return 0;
248 }