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