Commit | Line | Data |
---|---|---|
708cf3f0 JA |
1 | /* |
2 | * Driver interaction with extended Linux CFG8021 | |
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/types.h> | |
15 | #include <fcntl.h> | |
16 | #include <net/if.h> | |
17 | ||
18 | #include "common.h" | |
19 | #include "linux_ioctl.h" | |
20 | #include "driver_nl80211.h" | |
21 | #include "wpa_supplicant_i.h" | |
22 | #include "config.h" | |
23 | #ifdef ANDROID | |
24 | #include "android_drv.h" | |
25 | #endif | |
26 | ||
27 | typedef struct android_wifi_priv_cmd { | |
28 | char *buf; | |
29 | int used_len; | |
30 | int total_len; | |
31 | } android_wifi_priv_cmd; | |
32 | ||
33 | static int drv_errors = 0; | |
34 | ||
35 | static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) | |
36 | { | |
37 | drv_errors++; | |
38 | if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { | |
39 | drv_errors = 0; | |
40 | wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); | |
41 | } | |
42 | } | |
43 | ||
44 | static void wpa_driver_notify_country_change(void *ctx, char *cmd) | |
45 | { | |
46 | if ((os_strncasecmp(cmd, "COUNTRY", 7) == 0) || | |
47 | (os_strncasecmp(cmd, "SETBAND", 7) == 0)) { | |
48 | union wpa_event_data event; | |
49 | ||
50 | os_memset(&event, 0, sizeof(event)); | |
51 | event.channel_list_changed.initiator = REGDOM_SET_BY_USER; | |
52 | if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) { | |
53 | event.channel_list_changed.type = REGDOM_TYPE_COUNTRY; | |
54 | if (os_strlen(cmd) > 9) { | |
55 | event.channel_list_changed.alpha2[0] = cmd[8]; | |
56 | event.channel_list_changed.alpha2[1] = cmd[9]; | |
57 | } | |
58 | } else { | |
59 | event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; | |
60 | } | |
61 | wpa_supplicant_event(ctx, EVENT_CHANNEL_LIST_CHANGED, &event); | |
62 | } | |
63 | } | |
64 | ||
65 | int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, | |
66 | size_t buf_len ) | |
67 | { | |
68 | struct i802_bss *bss = priv; | |
69 | struct wpa_driver_nl80211_data *drv = bss->drv; | |
70 | struct ifreq ifr; | |
71 | android_wifi_priv_cmd priv_cmd; | |
72 | int ret = 0; | |
73 | ||
74 | if (os_strcasecmp(cmd, "STOP") == 0) { | |
75 | linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); | |
76 | wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); | |
77 | } else if (os_strcasecmp(cmd, "START") == 0) { | |
78 | linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1); | |
79 | wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); | |
80 | } else if (os_strcasecmp(cmd, "MACADDR") == 0) { | |
81 | u8 macaddr[ETH_ALEN] = {}; | |
82 | ||
83 | ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr); | |
84 | if (!ret) | |
85 | ret = os_snprintf(buf, buf_len, | |
86 | "Macaddr = " MACSTR "\n", MAC2STR(macaddr)); | |
87 | } else { /* Use private command */ | |
88 | os_memcpy(buf, cmd, strlen(cmd) + 1); | |
89 | memset(&ifr, 0, sizeof(ifr)); | |
90 | memset(&priv_cmd, 0, sizeof(priv_cmd)); | |
91 | os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); | |
92 | ||
93 | priv_cmd.buf = buf; | |
94 | priv_cmd.used_len = buf_len; | |
95 | priv_cmd.total_len = buf_len; | |
96 | ifr.ifr_data = &priv_cmd; | |
97 | ||
98 | if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 2, &ifr)) < 0) { | |
99 | wpa_printf(MSG_ERROR, "%s: failed to issue private command: %s", __func__, cmd); | |
100 | wpa_driver_send_hang_msg(drv); | |
101 | } else { | |
102 | drv_errors = 0; | |
103 | ret = 0; | |
104 | if ((os_strcasecmp(cmd, "LINKSPEED") == 0) || | |
105 | (os_strcasecmp(cmd, "RSSI") == 0) || | |
106 | (os_strcasecmp(cmd, "GETBAND") == 0) || | |
107 | (os_strncasecmp(cmd, "WLS_BATCHING", 12) == 0)) | |
108 | ret = strlen(buf); | |
109 | wpa_driver_notify_country_change(drv->ctx, cmd); | |
110 | wpa_printf(MSG_DEBUG, "%s %s len = %d, %zu", __func__, buf, ret, strlen(buf)); | |
111 | } | |
112 | } | |
113 | return ret; | |
114 | } | |
115 | ||
116 | int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) | |
117 | { | |
118 | char buf[MAX_DRV_CMD_SIZE]; | |
119 | ||
120 | memset(buf, 0, sizeof(buf)); | |
121 | wpa_printf(MSG_DEBUG, "%s: Entry", __func__); | |
122 | snprintf(buf, sizeof(buf), "P2P_SET_NOA %d %d %d", count, start, duration); | |
123 | return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf)+1); | |
124 | } | |
125 | ||
126 | int wpa_driver_get_p2p_noa(void *priv __unused, u8 *buf __unused, size_t len __unused) | |
127 | { | |
128 | /* Return 0 till we handle p2p_presence request completely in the driver */ | |
129 | return 0; | |
130 | } | |
131 | ||
132 | int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) | |
133 | { | |
134 | char buf[MAX_DRV_CMD_SIZE]; | |
135 | ||
136 | memset(buf, 0, sizeof(buf)); | |
137 | wpa_printf(MSG_DEBUG, "%s: Entry", __func__); | |
138 | snprintf(buf, sizeof(buf), "P2P_SET_PS %d %d %d", legacy_ps, opp_ps, ctwindow); | |
139 | return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1); | |
140 | } | |
141 | ||
142 | int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, | |
143 | const struct wpabuf *proberesp, | |
144 | const struct wpabuf *assocresp) | |
145 | { | |
146 | char *buf; | |
147 | const struct wpabuf *ap_wps_p2p_ie = NULL; | |
148 | ||
149 | char *_cmd = "SET_AP_P2P_WPS_IE"; | |
150 | char *pbuf; | |
151 | int ret = 0; | |
152 | int i, buf_len; | |
153 | enum if_type { | |
154 | NONE, | |
155 | IF_TYPE_P2P_DEVICE, | |
156 | IF_TYPE_P2P_GROUP | |
157 | }; | |
158 | enum if_type iftype = NONE; | |
159 | struct cmd_desc { | |
160 | int cmd; | |
161 | const struct wpabuf *src; | |
162 | } cmd_arr[] = { | |
163 | {0x1, beacon}, | |
164 | {0x2, proberesp}, | |
a7c11d41 | 165 | {0x3, assocresp}, |
708cf3f0 JA |
166 | {-1, NULL} |
167 | }; | |
168 | ||
169 | wpa_printf(MSG_DEBUG, "%s: Entry", __func__); | |
170 | ||
171 | if (((proberesp != NULL) && (beacon == NULL)) || | |
172 | ((proberesp == NULL) && (beacon == NULL) && (assocresp == NULL))) | |
173 | /* P2P Device mode Probe Response IEs */ | |
174 | iftype = IF_TYPE_P2P_DEVICE; | |
175 | else if ((proberesp != NULL) && (beacon != NULL) && (assocresp != NULL)) | |
176 | iftype = IF_TYPE_P2P_GROUP; | |
177 | ||
178 | for (i = 0; cmd_arr[i].cmd != -1; i++) { | |
179 | ap_wps_p2p_ie = cmd_arr[i].src; | |
180 | if (ap_wps_p2p_ie) { | |
70f4bf60 | 181 | buf_len = strlen(_cmd) + 5 + wpabuf_len(ap_wps_p2p_ie); |
708cf3f0 JA |
182 | buf = os_zalloc(buf_len); |
183 | if (NULL == buf) { | |
184 | wpa_printf(MSG_ERROR, "%s: Out of memory", | |
185 | __func__); | |
186 | ret = -1; | |
187 | break; | |
188 | } | |
189 | } else { | |
190 | continue; | |
191 | } | |
192 | pbuf = buf; | |
193 | pbuf += snprintf(pbuf, buf_len - wpabuf_len(ap_wps_p2p_ie), | |
194 | "%s %d %d",_cmd, cmd_arr[i].cmd, iftype); | |
195 | *pbuf++ = '\0'; | |
196 | os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie), wpabuf_len(ap_wps_p2p_ie)); | |
197 | ret = wpa_driver_nl80211_driver_cmd(priv, buf, buf, buf_len); | |
198 | os_free(buf); | |
199 | if (ret < 0) | |
200 | break; | |
201 | } | |
202 | ||
203 | return ret; | |
204 | } |