1 /*
2 * Driver interaction with extended Linux Wireless Extensions
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * Alternatively, this software may be distributed under the terms of BSD
9 * license.
10 *
11 */
12
13 #include "includes.h"
14 #include <sys/ioctl.h>
15 #include <net/if_arp.h>
16 #include <net/if.h>
17
18 #include "linux_wext.h"
19 #include "common.h"
20 #include "driver.h"
21 #include "eloop.h"
22 #include "priv_netlink.h"
23 #include "driver_wext.h"
24 #include "ieee802_11_defs.h"
25 #include "wpa_common.h"
26 #include "wpa_ctrl.h"
27 #include "wpa_supplicant_i.h"
28 #include "config.h"
29 #include "linux_ioctl.h"
30 #include "scan.h"
31
32 #include "driver_cmd_wext.h"
33 #ifdef ANDROID
34 #include "android_drv.h"
35 #endif /* ANDROID */
36
37 /**
38 * wpa_driver_wext_set_scan_timeout - Set scan timeout to report scan completion
39 * @priv: Pointer to private wext data from wpa_driver_wext_init()
40 *
41 * This function can be used to set registered timeout when starting a scan to
42 * generate a scan completed event if the driver does not report this.
43 */
wpa_driver_wext_set_scan_timeout(void * priv)44 static void wpa_driver_wext_set_scan_timeout(void *priv)
45 {
46 struct wpa_driver_wext_data *drv = priv;
47 int timeout = 10; /* In case scan A and B bands it can be long */
48
49 /* Not all drivers generate "scan completed" wireless event, so try to
50 * read results after a timeout. */
51 if (drv->scan_complete_events) {
52 /*
53 * The driver seems to deliver SIOCGIWSCAN events to notify
54 * when scan is complete, so use longer timeout to avoid race
55 * conditions with scanning and following association request.
56 */
57 timeout = 30;
58 }
59 wpa_printf(MSG_DEBUG, "Scan requested - scan timeout %d seconds",
60 timeout);
61 eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
62 eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
63 drv->ctx);
64 }
65
66 /**
67 * wpa_driver_wext_combo_scan - Request the driver to initiate combo scan
68 * @priv: Pointer to private wext data from wpa_driver_wext_init()
69 * @params: Scan parameters
70 * Returns: 0 on success, -1 on failure
71 */
wpa_driver_wext_combo_scan(void * priv,struct wpa_driver_scan_params * params)72 int wpa_driver_wext_combo_scan(void *priv, struct wpa_driver_scan_params *params)
73 {
74 char buf[WEXT_CSCAN_BUF_LEN];
75 struct wpa_driver_wext_data *drv = priv;
76 struct iwreq iwr;
77 int ret, bp;
78 unsigned i;
79
80 if (!drv->driver_is_started) {
81 wpa_printf(MSG_DEBUG, "%s: Driver stopped", __func__);
82 return 0;
83 }
84
85 wpa_printf(MSG_DEBUG, "%s: Start", __func__);
86
87 /* Set list of SSIDs */
88 bp = WEXT_CSCAN_HEADER_SIZE;
89 os_memcpy(buf, WEXT_CSCAN_HEADER, bp);
90 for(i=0; i < params->num_ssids; i++) {
91 if ((bp + IW_ESSID_MAX_SIZE + 10) >= (int)sizeof(buf))
92 break;
93 wpa_printf(MSG_DEBUG, "For Scan: %s", params->ssids[i].ssid);
94 buf[bp++] = WEXT_CSCAN_SSID_SECTION;
95 buf[bp++] = params->ssids[i].ssid_len;
96 os_memcpy(&buf[bp], params->ssids[i].ssid, params->ssids[i].ssid_len);
97 bp += params->ssids[i].ssid_len;
98 }
99
100 /* Set list of channels */
101 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION;
102 buf[bp++] = 0;
103
104 /* Set passive dwell time (default is 250) */
105 buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION;
106 buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME;
107 buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME >> 8);
108
109 /* Set home dwell time (default is 40) */
110 buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION;
111 buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME;
112 buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8);
113
114 os_memset(&iwr, 0, sizeof(iwr));
115 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
116 iwr.u.data.pointer = buf;
117 iwr.u.data.length = bp;
118
119 if ((ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr)) < 0) {
120 if (!drv->bgscan_enabled)
121 wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (cscan): %d", ret);
122 else
123 ret = 0; /* Hide error in case of bg scan */
124 }
125 return ret;
126 }
127
wpa_driver_wext_set_cscan_params(char * buf,size_t buf_len,char * cmd)128 static int wpa_driver_wext_set_cscan_params(char *buf, size_t buf_len, char *cmd)
129 {
130 char *pasv_ptr;
131 int bp, i;
132 u16 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF;
133 u8 channel;
134
135 wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
136
137 /* Get command parameters */
138 pasv_ptr = os_strstr(cmd, ",TIME=");
139 if (pasv_ptr) {
140 *pasv_ptr = '\0';
141 pasv_ptr += 6;
142 pasv_dwell = (u16)atoi(pasv_ptr);
143 if (pasv_dwell == 0)
144 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF;
145 }
146 channel = (u8)atoi(cmd + 5);
147
148 bp = WEXT_CSCAN_HEADER_SIZE;
149 os_memcpy(buf, WEXT_CSCAN_HEADER, bp);
150
151 /* Set list of channels */
152 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION;
153 buf[bp++] = channel;
154 if (channel != 0) {
155 i = (pasv_dwell - 1) / WEXT_CSCAN_PASV_DWELL_TIME_DEF;
156 for (; i > 0; i--) {
157 if ((size_t)(bp + 12) >= buf_len)
158 break;
159 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION;
160 buf[bp++] = channel;
161 }
162 } else {
163 if (pasv_dwell > WEXT_CSCAN_PASV_DWELL_TIME_MAX)
164 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_MAX;
165 }
166
167 /* Set passive dwell time (default is 250) */
168 buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION;
169 if (channel != 0) {
170 buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME_DEF;
171 buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME_DEF >> 8);
172 } else {
173 buf[bp++] = (u8)pasv_dwell;
174 buf[bp++] = (u8)(pasv_dwell >> 8);
175 }
176
177 /* Set home dwell time (default is 40) */
178 buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION;
179 buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME;
180 buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8);
181
182 /* Set cscan type */
183 buf[bp++] = WEXT_CSCAN_TYPE_SECTION;
184 buf[bp++] = WEXT_CSCAN_TYPE_PASSIVE;
185 return bp;
186 }
187
wpa_driver_get_country_code(int channels)188 static char *wpa_driver_get_country_code(int channels)
189 {
190 char *country = "US"; /* WEXT_NUMBER_SCAN_CHANNELS_FCC */
191
192 if (channels == WEXT_NUMBER_SCAN_CHANNELS_ETSI)
193 country = "EU";
194 else if( channels == WEXT_NUMBER_SCAN_CHANNELS_MKK1)
195 country = "JP";
196 return country;
197 }
198
wpa_driver_set_backgroundscan_params(void * priv)199 static int wpa_driver_set_backgroundscan_params(void *priv)
200 {
201 struct wpa_driver_wext_data *drv = priv;
202 struct wpa_supplicant *wpa_s;
203 struct iwreq iwr;
204 int ret = 0, i = 0, bp;
205 char buf[WEXT_PNO_MAX_COMMAND_SIZE];
206 struct wpa_ssid *ssid_conf;
207
208 if (drv == NULL) {
209 wpa_printf(MSG_ERROR, "%s: drv is NULL. Exiting", __func__);
210 return -1;
211 }
212 if (drv->ctx == NULL) {
213 wpa_printf(MSG_ERROR, "%s: drv->ctx is NULL. Exiting", __func__);
214 return -1;
215 }
216 wpa_s = (struct wpa_supplicant *)(drv->ctx);
217 if (wpa_s->conf == NULL) {
218 wpa_printf(MSG_ERROR, "%s: wpa_s->conf is NULL. Exiting", __func__);
219 return -1;
220 }
221 ssid_conf = wpa_s->conf->ssid;
222
223 bp = WEXT_PNOSETUP_HEADER_SIZE;
224 os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
225 buf[bp++] = WEXT_PNO_TLV_PREFIX;
226 buf[bp++] = WEXT_PNO_TLV_VERSION;
227 buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
228 buf[bp++] = WEXT_PNO_TLV_RESERVED;
229
230 while ((i < WEXT_PNO_AMOUNT) && (ssid_conf != NULL)) {
231 /* Check that there is enough space needed for 1 more SSID, the other sections and null termination */
232 if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int)sizeof(buf))
233 break;
234 if ((!ssid_conf->disabled) && (ssid_conf->ssid_len <= IW_ESSID_MAX_SIZE)){
235 wpa_printf(MSG_DEBUG, "For PNO Scan: %s", ssid_conf->ssid);
236 buf[bp++] = WEXT_PNO_SSID_SECTION;
237 buf[bp++] = ssid_conf->ssid_len;
238 os_memcpy(&buf[bp], ssid_conf->ssid, ssid_conf->ssid_len);
239 bp += ssid_conf->ssid_len;
240 i++;
241 }
242 ssid_conf = ssid_conf->next;
243 }
244
245 buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
246 os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", WEXT_PNO_SCAN_INTERVAL);
247 bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
248
249 buf[bp++] = WEXT_PNO_REPEAT_SECTION;
250 os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", WEXT_PNO_REPEAT);
251 bp += WEXT_PNO_REPEAT_LENGTH;
252
253 buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
254 os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", WEXT_PNO_MAX_REPEAT);
255 bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
256
257 os_memset(&iwr, 0, sizeof(iwr));
258 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
259 iwr.u.data.pointer = buf;
260 iwr.u.data.length = bp;
261
262 ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
263
264 if (ret < 0) {
265 wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", ret);
266 drv->errors++;
267 if (drv->errors > WEXT_NUMBER_SEQUENTIAL_ERRORS) {
268 drv->errors = 0;
269 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
270 }
271 } else {
272 drv->errors = 0;
273 }
274 return ret;
275
276 }
277
wpa_driver_wext_driver_cmd(void * priv,char * cmd,char * buf,size_t buf_len)278 int wpa_driver_wext_driver_cmd( void *priv, char *cmd, char *buf, size_t buf_len )
279 {
280 struct wpa_driver_wext_data *drv = priv;
281 struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx);
282 struct iwreq iwr;
283 int ret = 0, flags;
284
285 wpa_printf(MSG_DEBUG, "%s %s len = %d", __func__, cmd, buf_len);
286
287 if (!drv->driver_is_started && (os_strcasecmp(cmd, "START") != 0)) {
288 wpa_printf(MSG_ERROR,"WEXT: Driver not initialized yet");
289 return -1;
290 }
291
292 if (os_strcasecmp(cmd, "RSSI-APPROX") == 0) {
293 os_strncpy(cmd, RSSI_CMD, MAX_DRV_CMD_SIZE);
294 } else if( os_strncasecmp(cmd, "SCAN-CHANNELS", 13) == 0 ) {
295 int no_of_chan;
296
297 no_of_chan = atoi(cmd + 13);
298 os_snprintf(cmd, MAX_DRV_CMD_SIZE, "COUNTRY %s",
299 wpa_driver_get_country_code(no_of_chan));
300 } else if (os_strcasecmp(cmd, "STOP") == 0) {
301 linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
302 } else if( os_strcasecmp(cmd, "RELOAD") == 0 ) {
303 wpa_printf(MSG_DEBUG,"Reload command");
304 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
305 return ret;
306 } else if( os_strcasecmp(cmd, "BGSCAN-START") == 0 ) {
307 ret = wpa_driver_set_backgroundscan_params(priv);
308 if (ret < 0) {
309 return ret;
310 }
311 os_strncpy(cmd, "PNOFORCE 1", MAX_DRV_CMD_SIZE);
312 drv->bgscan_enabled = 1;
313 } else if( os_strcasecmp(cmd, "BGSCAN-STOP") == 0 ) {
314 os_strncpy(cmd, "PNOFORCE 0", MAX_DRV_CMD_SIZE);
315 drv->bgscan_enabled = 0;
316 }
317
318 os_memset(&iwr, 0, sizeof(iwr));
319 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
320 os_memcpy(buf, cmd, strlen(cmd) + 1);
321 iwr.u.data.pointer = buf;
322 iwr.u.data.length = buf_len;
323
324 if( os_strncasecmp(cmd, "CSCAN", 5) == 0 ) {
325 if (!wpa_s->scanning && ((wpa_s->wpa_state <= WPA_SCANNING) ||
326 (wpa_s->wpa_state >= WPA_COMPLETED))) {
327 iwr.u.data.length = wpa_driver_wext_set_cscan_params(buf, buf_len, cmd);
328 } else {
329 wpa_printf(MSG_ERROR, "Ongoing Scan action...");
330 return ret;
331 }
332 }
333
334 ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
335
336 if (ret < 0) {
337 wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret, cmd);
338 drv->errors++;
339 if (drv->errors > WEXT_NUMBER_SEQUENTIAL_ERRORS) {
340 drv->errors = 0;
341 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
342 }
343 } else {
344 drv->errors = 0;
345 ret = 0;
346 if ((os_strcasecmp(cmd, RSSI_CMD) == 0) ||
347 (os_strcasecmp(cmd, LINKSPEED_CMD) == 0) ||
348 (os_strcasecmp(cmd, "MACADDR") == 0) ||
349 (os_strcasecmp(cmd, "GETPOWER") == 0) ||
350 (os_strcasecmp(cmd, "GETBAND") == 0)) {
351 ret = strlen(buf);
352 } else if (os_strcasecmp(cmd, "START") == 0) {
353 drv->driver_is_started = TRUE;
354 linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
355 /* os_sleep(0, WPA_DRIVER_WEXT_WAIT_US);
356 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); */
357 } else if (os_strcasecmp(cmd, "STOP") == 0) {
358 drv->driver_is_started = FALSE;
359 /* wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); */
360 } else if (os_strncasecmp(cmd, "CSCAN", 5) == 0) {
361 wpa_driver_wext_set_scan_timeout(priv);
362 wpa_supplicant_notify_scanning(wpa_s, 1);
363 }
364 wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf));
365 }
366 return ret;
367 }
368
wpa_driver_signal_poll(void * priv,struct wpa_signal_info * si)369 int wpa_driver_signal_poll(void *priv, struct wpa_signal_info *si)
370 {
371 char buf[MAX_DRV_CMD_SIZE];
372 struct wpa_driver_wext_data *drv = priv;
373 char *prssi;
374 int res;
375
376 os_memset(si, 0, sizeof(*si));
377 res = wpa_driver_wext_driver_cmd(priv, RSSI_CMD, buf, sizeof(buf));
378 /* Answer: SSID rssi -Val */
379 if (res < 0)
380 return res;
381 prssi = strcasestr(buf, RSSI_CMD);
382 if (!prssi)
383 return -1;
384 si->current_signal = atoi(prssi + strlen(RSSI_CMD) + 1);
385
386 res = wpa_driver_wext_driver_cmd(priv, LINKSPEED_CMD, buf, sizeof(buf));
387 /* Answer: LinkSpeed Val */
388 if (res < 0)
389 return res;
390 si->current_txrate = atoi(buf + strlen(LINKSPEED_CMD) + 1) * 1000;
391
392 return 0;
393 }
394