Commit | Line | Data |
---|---|---|
2f01a1f5 | 1 | /* |
80301cdc | 2 | * This file is part of wl1251 |
2f01a1f5 KV |
3 | * |
4 | * Copyright (c) 1998-2007 Texas Instruments Incorporated | |
5 | * Copyright (C) 2008 Nokia Corporation | |
6 | * | |
2f01a1f5 KV |
7 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License | |
9 | * version 2 as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
19 | * 02110-1301 USA | |
20 | * | |
21 | */ | |
22 | ||
13674118 | 23 | #include "wl1251.h" |
9bc6772e KV |
24 | #include "reg.h" |
25 | #include "io.h" | |
26 | #include "event.h" | |
27 | #include "ps.h" | |
2f01a1f5 | 28 | |
80301cdc | 29 | static int wl1251_event_scan_complete(struct wl1251 *wl, |
2f01a1f5 KV |
30 | struct event_mailbox *mbox) |
31 | { | |
f1e3e051 JB |
32 | int ret = 0; |
33 | ||
80301cdc | 34 | wl1251_debug(DEBUG_EVENT, "status: 0x%x, channels: %d", |
2f01a1f5 KV |
35 | mbox->scheduled_scan_status, |
36 | mbox->scheduled_scan_channels); | |
37 | ||
38 | if (wl->scanning) { | |
2f01a1f5 | 39 | ieee80211_scan_completed(wl->hw, false); |
2789da9e | 40 | wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan completed"); |
2f01a1f5 | 41 | wl->scanning = false; |
f1e3e051 JB |
42 | if (wl->hw->conf.flags & IEEE80211_CONF_IDLE) |
43 | ret = wl1251_ps_set_mode(wl, STATION_IDLE); | |
2f01a1f5 KV |
44 | } |
45 | ||
f1e3e051 | 46 | return ret; |
2f01a1f5 KV |
47 | } |
48 | ||
f7ad1eed DG |
49 | #define WL1251_PSM_ENTRY_RETRIES 3 |
50 | static int wl1251_event_ps_report(struct wl1251 *wl, | |
51 | struct event_mailbox *mbox) | |
52 | { | |
53 | int ret = 0; | |
54 | ||
55 | wl1251_debug(DEBUG_EVENT, "ps status: %x", mbox->ps_status); | |
56 | ||
57 | switch (mbox->ps_status) { | |
58 | case EVENT_ENTER_POWER_SAVE_FAIL: | |
59 | wl1251_debug(DEBUG_PSM, "PSM entry failed"); | |
60 | ||
61 | if (wl->station_mode != STATION_POWER_SAVE_MODE) { | |
62 | /* remain in active mode */ | |
63 | wl->psm_entry_retry = 0; | |
64 | break; | |
65 | } | |
66 | ||
67 | if (wl->psm_entry_retry < WL1251_PSM_ENTRY_RETRIES) { | |
68 | wl->psm_entry_retry++; | |
69 | ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); | |
70 | } else { | |
71 | wl1251_error("Power save entry failed, giving up"); | |
72 | wl->psm_entry_retry = 0; | |
73 | } | |
74 | break; | |
75 | case EVENT_ENTER_POWER_SAVE_SUCCESS: | |
76 | case EVENT_EXIT_POWER_SAVE_FAIL: | |
77 | case EVENT_EXIT_POWER_SAVE_SUCCESS: | |
78 | default: | |
79 | wl->psm_entry_retry = 0; | |
80 | break; | |
81 | } | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
80301cdc | 86 | static void wl1251_event_mbox_dump(struct event_mailbox *mbox) |
2f01a1f5 | 87 | { |
80301cdc KV |
88 | wl1251_debug(DEBUG_EVENT, "MBOX DUMP:"); |
89 | wl1251_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); | |
90 | wl1251_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); | |
2f01a1f5 KV |
91 | } |
92 | ||
80301cdc | 93 | static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) |
2f01a1f5 KV |
94 | { |
95 | int ret; | |
96 | u32 vector; | |
97 | ||
80301cdc | 98 | wl1251_event_mbox_dump(mbox); |
2f01a1f5 KV |
99 | |
100 | vector = mbox->events_vector & ~(mbox->events_mask); | |
80301cdc | 101 | wl1251_debug(DEBUG_EVENT, "vector: 0x%x", vector); |
2f01a1f5 KV |
102 | |
103 | if (vector & SCAN_COMPLETE_EVENT_ID) { | |
80301cdc | 104 | ret = wl1251_event_scan_complete(wl, mbox); |
2f01a1f5 KV |
105 | if (ret < 0) |
106 | return ret; | |
107 | } | |
108 | ||
109 | if (vector & BSS_LOSE_EVENT_ID) { | |
80301cdc | 110 | wl1251_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); |
2f01a1f5 | 111 | |
a0bbb58b JN |
112 | if (wl->psm_requested && |
113 | wl->station_mode != STATION_ACTIVE_MODE) { | |
80301cdc | 114 | ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); |
2f01a1f5 KV |
115 | if (ret < 0) |
116 | return ret; | |
117 | } | |
118 | } | |
119 | ||
f7ad1eed DG |
120 | if (vector & PS_REPORT_EVENT_ID) { |
121 | wl1251_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); | |
122 | ret = wl1251_event_ps_report(wl, mbox); | |
123 | if (ret < 0) | |
124 | return ret; | |
125 | } | |
126 | ||
e5b02f64 | 127 | if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) { |
287f6f96 JO |
128 | wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT"); |
129 | ||
130 | /* indicate to the stack, that beacons have been lost */ | |
e5b02f64 GI |
131 | if (wl->vif && wl->vif->type == NL80211_IFTYPE_STATION) |
132 | ieee80211_beacon_loss(wl->vif); | |
287f6f96 JO |
133 | } |
134 | ||
33d51fac VG |
135 | if (vector & REGAINED_BSS_EVENT_ID) { |
136 | if (wl->psm_requested) { | |
137 | ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); | |
138 | if (ret < 0) | |
139 | return ret; | |
140 | } | |
141 | } | |
142 | ||
8964e492 DG |
143 | if (wl->vif && wl->rssi_thold) { |
144 | if (vector & ROAMING_TRIGGER_LOW_RSSI_EVENT_ID) { | |
145 | wl1251_debug(DEBUG_EVENT, | |
146 | "ROAMING_TRIGGER_LOW_RSSI_EVENT"); | |
147 | ieee80211_cqm_rssi_notify(wl->vif, | |
148 | NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, | |
149 | GFP_KERNEL); | |
150 | } | |
151 | ||
152 | if (vector & ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID) { | |
153 | wl1251_debug(DEBUG_EVENT, | |
154 | "ROAMING_TRIGGER_REGAINED_RSSI_EVENT"); | |
155 | ieee80211_cqm_rssi_notify(wl->vif, | |
156 | NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, | |
157 | GFP_KERNEL); | |
158 | } | |
159 | } | |
160 | ||
2f01a1f5 KV |
161 | return 0; |
162 | } | |
163 | ||
7273b970 GI |
164 | /* |
165 | * Poll the mailbox event field until any of the bits in the mask is set or a | |
166 | * timeout occurs (WL1251_EVENT_TIMEOUT in msecs) | |
167 | */ | |
168 | int wl1251_event_wait(struct wl1251 *wl, u32 mask, int timeout_ms) | |
169 | { | |
170 | u32 events_vector, event; | |
171 | unsigned long timeout; | |
172 | ||
173 | timeout = jiffies + msecs_to_jiffies(timeout_ms); | |
174 | ||
175 | do { | |
176 | if (time_after(jiffies, timeout)) | |
177 | return -ETIMEDOUT; | |
178 | ||
179 | msleep(1); | |
180 | ||
181 | /* read from both event fields */ | |
182 | wl1251_mem_read(wl, wl->mbox_ptr[0], &events_vector, | |
183 | sizeof(events_vector)); | |
184 | event = events_vector & mask; | |
185 | wl1251_mem_read(wl, wl->mbox_ptr[1], &events_vector, | |
186 | sizeof(events_vector)); | |
187 | event |= events_vector & mask; | |
188 | } while (!event); | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
80301cdc | 193 | int wl1251_event_unmask(struct wl1251 *wl) |
2f01a1f5 KV |
194 | { |
195 | int ret; | |
196 | ||
80301cdc | 197 | ret = wl1251_acx_event_mbox_mask(wl, ~(wl->event_mask)); |
2f01a1f5 KV |
198 | if (ret < 0) |
199 | return ret; | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
80301cdc | 204 | void wl1251_event_mbox_config(struct wl1251 *wl) |
2f01a1f5 | 205 | { |
80301cdc | 206 | wl->mbox_ptr[0] = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR); |
2f01a1f5 KV |
207 | wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); |
208 | ||
80301cdc | 209 | wl1251_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", |
2f01a1f5 KV |
210 | wl->mbox_ptr[0], wl->mbox_ptr[1]); |
211 | } | |
212 | ||
80301cdc | 213 | int wl1251_event_handle(struct wl1251 *wl, u8 mbox_num) |
2f01a1f5 KV |
214 | { |
215 | struct event_mailbox mbox; | |
216 | int ret; | |
217 | ||
80301cdc | 218 | wl1251_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); |
2f01a1f5 KV |
219 | |
220 | if (mbox_num > 1) | |
221 | return -EINVAL; | |
222 | ||
223 | /* first we read the mbox descriptor */ | |
0764de64 | 224 | wl1251_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox, |
2f01a1f5 KV |
225 | sizeof(struct event_mailbox)); |
226 | ||
227 | /* process the descriptor */ | |
80301cdc | 228 | ret = wl1251_event_process(wl, &mbox); |
2f01a1f5 KV |
229 | if (ret < 0) |
230 | return ret; | |
231 | ||
232 | /* then we let the firmware know it can go on...*/ | |
80301cdc | 233 | wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); |
2f01a1f5 KV |
234 | |
235 | return 0; | |
236 | } |