hc
2024-08-16 62c46c9150c4afde7e5b25436263fddf79d66f0b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
 * Copyright (C) 2017 Spreadtrum Communications Inc.
 *
 * Filename : 11h.c
 * Abstract : This file is a general implement of 802.11h
 *
 * Authors  :
 * Jay.Yang <Jay.Yang@spreadtrum.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
 
#include "11h.h"
#include "work.h"
 
int sprdwl_init_dfs_master(struct sprdwl_vif *vif)
{
   vif->dfs_cac_workqueue = alloc_workqueue("SPRDWL_DFS_CAC%s",
           WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1, vif->name);
   if (!vif->dfs_cac_workqueue) {
       wl_err("alloc DFS CAC workqueue failure\n");
       vif->ndev = NULL;
       memset(&vif->wdev, 0, sizeof(vif->wdev));
       return -ENOMEM;
   }
 
   INIT_DELAYED_WORK(&vif->dfs_cac_work, sprdwl_dfs_cac_work_queue);
   vif->dfs_chan_sw_workqueue = alloc_workqueue("SPRDWL_DFS_CHSW%s",
       WQ_HIGHPRI | WQ_UNBOUND | WQ_MEM_RECLAIM, 1, vif->name);
   if (!vif->dfs_chan_sw_workqueue) {
       wl_err("alloc DFS CHSW workqueue failure\n");
       vif->ndev = NULL;
       memset(&vif->wdev, 0, sizeof(vif->wdev));
       return -ENOMEM;
   }
 
   INIT_DELAYED_WORK(&vif->dfs_chan_sw_work,
             sprdwl_dfs_chan_sw_work_queue);
 
   return 0;
}
 
void sprdwl_deinit_dfs_master(struct sprdwl_vif *vif)
{
   if (vif->dfs_cac_workqueue) {
       flush_workqueue(vif->dfs_cac_workqueue);
       destroy_workqueue(vif->dfs_cac_workqueue);
       vif->dfs_cac_workqueue = NULL;
   }
 
   if (vif->dfs_chan_sw_workqueue) {
       flush_workqueue(vif->dfs_chan_sw_workqueue);
       destroy_workqueue(vif->dfs_chan_sw_workqueue);
       vif->dfs_chan_sw_workqueue = NULL;
   }
}
 
/* This is work queue function for channel switch handling.
 * This function takes care of updating new channel definitin to
 * bss config structure, restart AP and indicate channel switch success
 * to cfg80211.
 */
void sprdwl_dfs_chan_sw_work_queue(struct work_struct *work)
{
   struct delayed_work *delayed_work =
       container_of(work, struct delayed_work, work);
   struct sprdwl_vif *vif =
       container_of(delayed_work, struct sprdwl_vif,
                dfs_chan_sw_work);
 
   cfg80211_ch_switch_notify(vif->ndev, &vif->dfs_chandef);
}
 
/*This is delayed work emits CAC finished event for cfg80211 if
 * CAC was started earlier.
 */
void sprdwl_dfs_cac_work_queue(struct work_struct *work)
{
   struct cfg80211_chan_def chandef;
   struct delayed_work *delayed_work =
       container_of(work, struct delayed_work, work);
   struct sprdwl_vif *vif =
       container_of(delayed_work, struct sprdwl_vif,
                dfs_cac_work);
 
   chandef = vif->dfs_chandef;
   if (vif->wdev.cac_started) {
       wl_err("CAC timer finished; No radar detected\n");
       cfg80211_cac_event(vif->ndev, &chandef,
                  NL80211_RADAR_CAC_FINISHED,
                  GFP_KERNEL);
   }
}
 
int sprdwl_cfg80211_start_radar_detection(struct wiphy *wiphy,
                     struct net_device *ndev,
                     struct cfg80211_chan_def *chandef,
                     u32 cac_time_ms)
{
   struct sprdwl_vif *vif = netdev_priv(ndev);
   struct sprdwl_radar_params radar_params;
 
   wl_debug("%s enter:\n", __func__);
   radar_params.chan_num = chandef->chan->hw_value;
   radar_params.chan_width = chandef->width;
   radar_params.cac_time_ms = cac_time_ms;
 
   memcpy(&vif->dfs_chandef, chandef, sizeof(vif->dfs_chandef));
   /*send radar detect CMD*/
   sprdwl_send_dfs_cmd(vif, &radar_params, sizeof(radar_params));
 
   queue_delayed_work(vif->dfs_cac_workqueue, &vif->dfs_cac_work,
              msecs_to_jiffies(cac_time_ms));
 
   return 0;
}
 
int sprdwl_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *ndev,
                  struct cfg80211_csa_settings *params)
{
   struct sprdwl_vif *vif = netdev_priv(ndev);
   struct ieee_types_header *chsw_ie;
   struct ieee80211_channel_sw_ie *channel_sw;
   struct cfg80211_beacon_data *beacon = &params->beacon_csa;
   int chsw_msec = 0;
 
   if (vif->wdev.cac_started)
       return -EBUSY;
 
   if (cfg80211_chandef_identical(&params->chandef, &vif->dfs_chandef))
       return -EINVAL;
 
   chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH,
            params->beacon_csa.tail, params->beacon_csa.tail_len);
 
   if (!chsw_ie) {
       wl_err("Couldn't parse chan switch announcement IE\n");
       return -EINVAL;
   }
 
   channel_sw = (void *)(chsw_ie + 1);
   if (channel_sw->mode) {
       if (netif_carrier_ok(vif->ndev))
           netif_carrier_off(vif->ndev);
       /*stop tx Q*/
       if (!netif_queue_stopped(vif->ndev))
           netif_stop_queue(vif->ndev);
   }
 
   if (beacon->tail_len)
       sprdwl_reset_beacon(vif->priv, vif->ctx_id,
                   beacon->tail, beacon->tail_len);
 
   /*set mgmt ies*/
   if (sprdwl_change_beacon(vif, beacon)) {
       wl_err("set beacon IE failure\n");
       return -EINVAL;
   }
 
   memcpy(&vif->dfs_chandef, &params->chandef, sizeof(vif->dfs_chandef));
   chsw_msec = max(channel_sw->count * vif->priv->beacon_period, 500);
   queue_delayed_work(vif->dfs_chan_sw_workqueue, &vif->dfs_chan_sw_work,
              msecs_to_jiffies(chsw_msec));
   return 0;
}
 
void sprdwl_stop_radar_detection(struct sprdwl_vif *vif,
                struct cfg80211_chan_def *chandef)
{
   struct sprdwl_work *misc_work;
   struct sprdwl_radar_params radar_params;
 
   memset(&radar_params, 0, sizeof(struct sprdwl_radar_params));
   radar_params.chan_num = chandef->chan->hw_value;
   radar_params.chan_width = chandef->width;
   radar_params.cac_time_ms = 0;
 
   /*send stop radar detection cmd*/
   misc_work = sprdwl_alloc_work(sizeof(radar_params));
   misc_work->vif = vif;
   misc_work->id = SPRDWL_WORK_DFS;
   memcpy(misc_work->data, &radar_params, sizeof(radar_params));
 
   sprdwl_queue_work(vif->priv, misc_work);
}
 
/* This function is to abort ongoing CAC upon stopping AP operations
 * or during unload.
 */
void sprdwl_abort_cac(struct sprdwl_vif *vif)
{
   if (vif->wdev.cac_started) {
       sprdwl_stop_radar_detection(vif, &vif->dfs_chandef);
       cancel_delayed_work_sync(&vif->dfs_cac_work);
       cfg80211_cac_event(vif->ndev, &vif->dfs_chandef,
                  NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
   }
}
 
/* Handler for radar detected event from FW.*/
int sprdwl_11h_handle_radar_detected(struct sprdwl_vif *vif,
                    u8 *data, u16 len)
{
   struct sprdwl_radar_event *rdr_event;
 
   rdr_event = (struct sprdwl_radar_event *)data;
 
   wl_debug("radar detected; indicating kernel\n");
   sprdwl_stop_radar_detection(vif, &vif->dfs_chandef);
   cfg80211_radar_event(vif->priv->wiphy, &vif->dfs_chandef,
                GFP_KERNEL);
   /*print radar detect reg & type*/
   wl_debug("regdomain:%d,radar detection type:%d\n",
        rdr_event->reg_domain, rdr_event->det_type);
   return 0;
}
 
void sprdwl_send_dfs_cmd(struct sprdwl_vif *vif, void *data, int len)
{
   struct sprdwl_msg_buf *msg;
 
   wl_debug("%s:enter\n", __func__);
   msg = sprdwl_cmd_getbuf(vif->priv, len, vif->ctx_id,
               SPRDWL_HEAD_RSP, WIFI_CMD_RADAR_DETECT);
   memcpy(msg->data, data, len);
   sprdwl_cmd_send_recv(vif->priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}