Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
239281f8 RL |
2 | /* |
3 | * OCB mode implementation | |
4 | * | |
5 | * Copyright: (c) 2014 Czech Technical University in Prague | |
6 | * (c) 2014 Volkswagen Group Research | |
15ddba5f | 7 | * Copyright (C) 2022 - 2023 Intel Corporation |
239281f8 RL |
8 | * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> |
9 | * Funded by: Volkswagen Group Research | |
239281f8 RL |
10 | */ |
11 | ||
12 | #include <linux/delay.h> | |
13 | #include <linux/if_ether.h> | |
14 | #include <linux/skbuff.h> | |
15 | #include <linux/if_arp.h> | |
16 | #include <linux/etherdevice.h> | |
17 | #include <linux/rtnetlink.h> | |
18 | #include <net/mac80211.h> | |
19 | #include <asm/unaligned.h> | |
20 | ||
21 | #include "ieee80211_i.h" | |
22 | #include "driver-ops.h" | |
23 | #include "rate.h" | |
24 | ||
25 | #define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ) | |
26 | #define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ) | |
27 | #define IEEE80211_OCB_MAX_STA_ENTRIES 128 | |
28 | ||
29 | /** | |
30 | * enum ocb_deferred_task_flags - mac80211 OCB deferred tasks | |
31 | * @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks | |
32 | * | |
33 | * These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb | |
34 | */ | |
35 | enum ocb_deferred_task_flags { | |
36 | OCB_WORK_HOUSEKEEPING, | |
37 | }; | |
38 | ||
39 | void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, | |
40 | const u8 *bssid, const u8 *addr, | |
41 | u32 supp_rates) | |
42 | { | |
43 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | |
44 | struct ieee80211_local *local = sdata->local; | |
45 | struct ieee80211_chanctx_conf *chanctx_conf; | |
46 | struct ieee80211_supported_band *sband; | |
239281f8 RL |
47 | struct sta_info *sta; |
48 | int band; | |
49 | ||
50 | /* XXX: Consider removing the least recently used entry and | |
51 | * allow new one to be added. | |
52 | */ | |
53 | if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) { | |
54 | net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n", | |
55 | sdata->name, addr); | |
56 | return; | |
57 | } | |
58 | ||
59 | ocb_dbg(sdata, "Adding new OCB station %pM\n", addr); | |
60 | ||
61 | rcu_read_lock(); | |
d0a9123e | 62 | chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf); |
239281f8 RL |
63 | if (WARN_ON_ONCE(!chanctx_conf)) { |
64 | rcu_read_unlock(); | |
65 | return; | |
66 | } | |
67 | band = chanctx_conf->def.chan->band; | |
239281f8 RL |
68 | rcu_read_unlock(); |
69 | ||
f36fe0a2 | 70 | sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); |
239281f8 RL |
71 | if (!sta) |
72 | return; | |
73 | ||
239281f8 RL |
74 | /* Add only mandatory rates for now */ |
75 | sband = local->hw.wiphy->bands[band]; | |
5add321c | 76 | sta->sta.deflink.supp_rates[band] = ieee80211_mandatory_rates(sband); |
239281f8 RL |
77 | |
78 | spin_lock(&ifocb->incomplete_lock); | |
79 | list_add(&sta->list, &ifocb->incomplete_stations); | |
80 | spin_unlock(&ifocb->incomplete_lock); | |
16114496 | 81 | wiphy_work_queue(local->hw.wiphy, &sdata->work); |
239281f8 RL |
82 | } |
83 | ||
84 | static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta) | |
85 | __acquires(RCU) | |
86 | { | |
87 | struct ieee80211_sub_if_data *sdata = sta->sdata; | |
88 | u8 addr[ETH_ALEN]; | |
89 | ||
90 | memcpy(addr, sta->sta.addr, ETH_ALEN); | |
91 | ||
92 | ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n", | |
93 | addr, sdata->name); | |
94 | ||
95 | sta_info_move_state(sta, IEEE80211_STA_AUTH); | |
96 | sta_info_move_state(sta, IEEE80211_STA_ASSOC); | |
97 | sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); | |
98 | ||
99 | rate_control_rate_init(sta); | |
100 | ||
101 | /* If it fails, maybe we raced another insertion? */ | |
102 | if (sta_info_insert_rcu(sta)) | |
103 | return sta_info_get(sdata, addr); | |
104 | return sta; | |
105 | } | |
106 | ||
107 | static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata) | |
108 | { | |
109 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | |
110 | ||
111 | ocb_dbg(sdata, "Running ocb housekeeping\n"); | |
112 | ||
113 | ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT); | |
114 | ||
115 | mod_timer(&ifocb->housekeeping_timer, | |
116 | round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL)); | |
117 | } | |
118 | ||
119 | void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata) | |
120 | { | |
121 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | |
122 | struct sta_info *sta; | |
123 | ||
076fc877 JB |
124 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
125 | ||
239281f8 RL |
126 | if (ifocb->joined != true) |
127 | return; | |
128 | ||
239281f8 RL |
129 | spin_lock_bh(&ifocb->incomplete_lock); |
130 | while (!list_empty(&ifocb->incomplete_stations)) { | |
131 | sta = list_first_entry(&ifocb->incomplete_stations, | |
132 | struct sta_info, list); | |
133 | list_del(&sta->list); | |
134 | spin_unlock_bh(&ifocb->incomplete_lock); | |
135 | ||
136 | ieee80211_ocb_finish_sta(sta); | |
137 | rcu_read_unlock(); | |
138 | spin_lock_bh(&ifocb->incomplete_lock); | |
139 | } | |
140 | spin_unlock_bh(&ifocb->incomplete_lock); | |
141 | ||
142 | if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags)) | |
143 | ieee80211_ocb_housekeeping(sdata); | |
239281f8 RL |
144 | } |
145 | ||
34f11cd3 | 146 | static void ieee80211_ocb_housekeeping_timer(struct timer_list *t) |
239281f8 | 147 | { |
34f11cd3 KC |
148 | struct ieee80211_sub_if_data *sdata = |
149 | from_timer(sdata, t, u.ocb.housekeeping_timer); | |
239281f8 RL |
150 | struct ieee80211_local *local = sdata->local; |
151 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | |
152 | ||
153 | set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); | |
154 | ||
16114496 | 155 | wiphy_work_queue(local->hw.wiphy, &sdata->work); |
239281f8 RL |
156 | } |
157 | ||
158 | void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata) | |
159 | { | |
160 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | |
161 | ||
34f11cd3 KC |
162 | timer_setup(&ifocb->housekeeping_timer, |
163 | ieee80211_ocb_housekeeping_timer, 0); | |
239281f8 RL |
164 | INIT_LIST_HEAD(&ifocb->incomplete_stations); |
165 | spin_lock_init(&ifocb->incomplete_lock); | |
166 | } | |
167 | ||
168 | int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, | |
169 | struct ocb_setup *setup) | |
170 | { | |
6092077a | 171 | struct ieee80211_chan_req chanreq = { .oper = setup->chandef }; |
239281f8 RL |
172 | struct ieee80211_local *local = sdata->local; |
173 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | |
15ddba5f | 174 | u64 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID; |
239281f8 RL |
175 | int err; |
176 | ||
0cd8080e JB |
177 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
178 | ||
239281f8 RL |
179 | if (ifocb->joined == true) |
180 | return -EINVAL; | |
181 | ||
39eac2de | 182 | sdata->deflink.operating_11g_mode = true; |
bfd8403a JB |
183 | sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; |
184 | sdata->deflink.needed_rx_chains = sdata->local->rx_chains; | |
239281f8 | 185 | |
6092077a | 186 | err = ieee80211_link_use_channel(&sdata->deflink, &chanreq, |
b4f85443 | 187 | IEEE80211_CHANCTX_SHARED); |
239281f8 RL |
188 | if (err) |
189 | return err; | |
190 | ||
191 | ieee80211_bss_info_change_notify(sdata, changed); | |
192 | ||
193 | ifocb->joined = true; | |
194 | ||
195 | set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); | |
16114496 | 196 | wiphy_work_queue(local->hw.wiphy, &sdata->work); |
239281f8 RL |
197 | |
198 | netif_carrier_on(sdata->dev); | |
199 | return 0; | |
200 | } | |
201 | ||
202 | int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) | |
203 | { | |
204 | struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; | |
205 | struct ieee80211_local *local = sdata->local; | |
206 | struct sta_info *sta; | |
207 | ||
0cd8080e JB |
208 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
209 | ||
239281f8 | 210 | ifocb->joined = false; |
ec67d6e0 | 211 | sta_info_flush(sdata, -1); |
239281f8 RL |
212 | |
213 | spin_lock_bh(&ifocb->incomplete_lock); | |
214 | while (!list_empty(&ifocb->incomplete_stations)) { | |
215 | sta = list_first_entry(&ifocb->incomplete_stations, | |
216 | struct sta_info, list); | |
217 | list_del(&sta->list); | |
218 | spin_unlock_bh(&ifocb->incomplete_lock); | |
219 | ||
220 | sta_info_free(local, sta); | |
221 | spin_lock_bh(&ifocb->incomplete_lock); | |
222 | } | |
223 | spin_unlock_bh(&ifocb->incomplete_lock); | |
224 | ||
225 | netif_carrier_off(sdata->dev); | |
226 | clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); | |
227 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB); | |
228 | ||
d8675a63 | 229 | ieee80211_link_release_channel(&sdata->deflink); |
239281f8 RL |
230 | |
231 | skb_queue_purge(&sdata->skb_queue); | |
232 | ||
233 | del_timer_sync(&sdata->u.ocb.housekeeping_timer); | |
234 | /* If the timer fired while we waited for it, it will have | |
235 | * requeued the work. Now the work will be running again | |
236 | * but will not rearm the timer again because it checks | |
237 | * whether we are connected to the network or not -- at this | |
238 | * point we shouldn't be anymore. | |
239 | */ | |
240 | ||
241 | return 0; | |
242 | } |