diff options
author | Dmitry Shmidt <dimitrysh@google.com> | 2009-05-14 13:53:06 -0700 |
---|---|---|
committer | Dmitry Shmidt <dimitrysh@google.com> | 2009-05-14 13:53:06 -0700 |
commit | 8bcb495e4b36149a053402093e10ec7205db94a1 (patch) | |
tree | eb0842cb02b0d0262bff4091465558e41803109a /wilink_6_1/wpa_supplicant_lib/driver_ti.c | |
parent | 9aa8ee433182c5a59338d58ba4c045d2dcacb3f2 (diff) | |
download | ti-8bcb495e4b36149a053402093e10ec7205db94a1.tar.gz |
Add wpa_supplicant driver layer for wilink 6.1
Diffstat (limited to 'wilink_6_1/wpa_supplicant_lib/driver_ti.c')
-rwxr-xr-x | wilink_6_1/wpa_supplicant_lib/driver_ti.c | 2850 |
1 files changed, 2850 insertions, 0 deletions
diff --git a/wilink_6_1/wpa_supplicant_lib/driver_ti.c b/wilink_6_1/wpa_supplicant_lib/driver_ti.c new file mode 100755 index 0000000..be07dc5 --- /dev/null +++ b/wilink_6_1/wpa_supplicant_lib/driver_ti.c @@ -0,0 +1,2850 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if_arp.h> +#include <net/if.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "l2_packet.h" +#include "eloop.h" +#include "wpa_supplicant.h" +#include "priv_netlink.h" +#include "driver_wext.h" +#include "wpa.h" +#include "wpa_ctrl.h" + +#define TI_WLAN_WILINK_6_SUPPORT + +#ifdef TI_WLAN_WILINK_6_SUPPORT +#include "cu_ostypes.h" +#include "STADExternalIf.h" +#include "convert.h" +#endif + +#ifdef CONFIG_CLIENT_MLME +#include <netpacket/packet.h> +#include <hostapd_ioctl.h> +#include <ieee80211_common.h> +/* from net/mac80211.h */ +enum { + MODE_IEEE80211A = 0 /* IEEE 802.11a */, + MODE_IEEE80211B = 1 /* IEEE 802.11b only */, + MODE_ATHEROS_TURBO = 2 /* Atheros Turbo mode (2x.11a at 5 GHz) */, + MODE_IEEE80211G = 3 /* IEEE 802.11g (and 802.11b compatibility) */, + MODE_ATHEROS_TURBOG = 4 /* Atheros Turbo mode (2x.11g at 2.4 GHz) */, + NUM_IEEE80211_MODES = 5 +}; + +#include "mlme.h" + +#ifndef ETH_P_ALL +#define ETH_P_ALL 0x0003 +#endif +#endif /* CONFIG_CLIENT_MLME */ + +#define TIWLAN_DRV_NAME "tiwlan0" + +typedef enum { + BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0, + BLUETOOTH_COEXISTENCE_MODE_DISABLED, + BLUETOOTH_COEXISTENCE_MODE_SENSE +} EUIBTCoexMode; + + +struct wpa_driver_tista_data { + void *ctx; + int event_sock; + int ioctl_sock; + int mlme_sock; + char ifname[IFNAMSIZ + 1]; + int ifindex; + int ifindex2; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + struct wpa_driver_capa capa; + int has_capability; + int we_version_compiled; + + /* for set_auth_alg fallback */ + int use_crypt; + int auth_alg_fallback; + + int operstate; + + char mlmedev[IFNAMSIZ + 1]; + + int scan_complete_events; +#ifdef TI_WLAN_WILINK_6_SUPPORT + u8 own_addr[ETH_ALEN]; /* MAC address of WLAN interface */ + int driver_is_loaded; /* TRUE/FALSE flag if driver is already loaded and can be accessed */ + int scan_type; /* SCAN_TYPE_NORMAL_ACTIVE or SCAN_TYPE_NORMAL_PASSIVE */ + int scan_channels; /* Number of allowed scan channels */ + unsigned int link_speed; /* Link Speed */ + u32 btcoex_mode; /* BtCoex Mode */ +#endif +}; + + +static int wpa_driver_tista_flush_pmkid(void *priv); +static int wpa_driver_tista_get_range(void *priv); +void wpa_driver_tista_scan_timeout(void *eloop_ctx, void *timeout_ctx); +int wpa_driver_tista_alternative_ifindex(struct wpa_driver_tista_data *drv, + const char *ifname); +int wpa_driver_tista_get_ssid(void *priv, u8 *ssid); +int wpa_driver_tista_set_mode(void *priv, int mode); + +#ifdef TI_WLAN_WILINK_6_SUPPORT +static int wpa_driver_tista_private_send( void *priv, u32 ioctl_cmd, void *bufIn, u32 sizeIn, void *bufOut, u32 sizeOut ) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + ti_private_cmd_t private_cmd; + struct iwreq iwr; + s32 res; + + private_cmd.cmd = ioctl_cmd; + if(bufOut == NULL) + private_cmd.flags = PRIVATE_CMD_SET_FLAG; + else + private_cmd.flags = PRIVATE_CMD_GET_FLAG; + + private_cmd.in_buffer = bufIn; + private_cmd.in_buffer_len = sizeIn; + private_cmd.out_buffer = bufOut; + private_cmd.out_buffer_len = sizeOut; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + iwr.u.data.pointer = &private_cmd; + iwr.u.data.length = sizeof(ti_private_cmd_t); + iwr.u.data.flags = 0; + + res = ioctl(drv->ioctl_sock, SIOCIWFIRSTPRIV, &iwr); + if(res != 0) + { + wpa_printf(MSG_ERROR, "ERROR - wpa_driver_tista_private_send - error sending Wext private IOCTL to STA driver (ioctl_cmd = %x, res = %d, errno = %d)", + ioctl_cmd, res, errno); + return -1; + } + + wpa_printf(MSG_DEBUG, "wpa_driver_tista_private_send ioctl_cmd = %x res = %d", ioctl_cmd, res); + + return 0; +} + +static int wpa_driver_tista_driver_start( void *priv ) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + u32 uDummyBuf; + s32 res; + + res = wpa_driver_tista_private_send(priv, DRIVER_START_PARAM, &uDummyBuf, sizeof(uDummyBuf), NULL, 0); + + if(0 != res) + wpa_printf(MSG_ERROR, "ERROR - Failed to start driver!"); + else + wpa_printf(MSG_DEBUG, "wpa_driver_tista_driver_start success"); + + return res; +} + +static int wpa_driver_tista_driver_stop( void *priv ) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + u32 uDummyBuf; + s32 res; + + res = wpa_driver_tista_private_send(priv, DRIVER_STOP_PARAM, &uDummyBuf, sizeof(uDummyBuf), NULL, 0); + + if(0 != res) + wpa_printf(MSG_ERROR, "ERROR - Failed to stop driver!"); + else + wpa_printf(MSG_DEBUG, "wpa_driver_tista_driver_stop success"); + + return res; +} + +static void ti_init_scan_params( scan_Params_t *pScanParams, + int scanType, int noOfChan ) +{ + u8 i,j; + /* init application scan default params */ + pScanParams->desiredSsid.len = 0; + pScanParams->scanType = scanType; + pScanParams->band = RADIO_BAND_2_4_GHZ; + pScanParams->probeReqNumber = 3; + pScanParams->probeRequestRate = RATE_MASK_UNSPECIFIED; /* Let the FW select */; + pScanParams->numOfChannels = noOfChan; + for ( i = 0; i < 14; i++ ) + { + for ( j = 0; j < 6; j++ ) + { + pScanParams->channelEntry[ i ].normalChannelEntry.bssId[ j ] = 0xff; + } + pScanParams->channelEntry[ i ].normalChannelEntry.earlyTerminationEvent = SCAN_ET_COND_DISABLE; + pScanParams->channelEntry[ i ].normalChannelEntry.ETMaxNumOfAPframes = 0; + pScanParams->channelEntry[ i ].normalChannelEntry.maxChannelDwellTime = 60000; + pScanParams->channelEntry[ i ].normalChannelEntry.minChannelDwellTime = 30000; + pScanParams->channelEntry[ i ].normalChannelEntry.txPowerDbm = DEF_TX_POWER; + pScanParams->channelEntry[ i ].normalChannelEntry.channel = i + 1; + } +} + +/*----------------------------------------------------------------------------- +Routine Name: wpa_driver_tista_scan +Routine Description: request scan from driver +Arguments: + priv - pointer to private data structure + ssid - ssid buffer + ssid_len - length of ssid +Return Value: 0 on success, -1 on failure +-----------------------------------------------------------------------------*/ +static int wpa_driver_tista_scan( void *priv, const u8 *ssid, size_t ssid_len ) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + scan_Params_t scanParams; + s32 res; + + /* If driver is not initialized yet - we cannot access it so return */ + if(!(drv->driver_is_loaded)) + return -1; + + os_memset(&scanParams, 0, sizeof(scan_Params_t)); + + /* Initialize scan parameters */ + ti_init_scan_params(&scanParams, drv->scan_type, drv->scan_channels); + + if (ssid && ssid_len > 0 && ssid_len <= sizeof(scanParams.desiredSsid.str)) { + os_memcpy(scanParams.desiredSsid.str, ssid, ssid_len); + scanParams.desiredSsid.len = ssid_len; + } + + res = wpa_driver_tista_private_send(priv, TIWLN_802_11_START_APP_SCAN_SET, &scanParams, sizeof(scanParams), NULL, 0); + + if(0 != res) + wpa_printf(MSG_ERROR, "ERROR - Failed to do tista scan!"); + else + wpa_printf(MSG_DEBUG, "wpa_driver_tista_scan success"); + + return res; +} + +/*----------------------------------------------------------------------------- +Routine Name: wpa_driver_tista_get_mac_addr +Routine Description: return WLAN MAC address +Arguments: + priv - pointer to private data structure +Return Value: pointer to BSSID +-----------------------------------------------------------------------------*/ +const u8 *wpa_driver_tista_get_mac_addr( void *priv ) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + u8 mac[ETH_ALEN]; + + if(0 != wpa_driver_tista_private_send(priv, CTRL_DATA_MAC_ADDRESS, NULL, 0, + mac, ETH_ALEN)) + { + wpa_printf(MSG_ERROR, "ERROR - Failed to get mac address!"); + os_memset(drv->own_addr, 0, ETH_ALEN); + } + else + { + os_memcpy(drv->own_addr, mac, ETH_ALEN); + wpa_printf(MSG_DEBUG, "Macaddr = " MACSTR, MAC2STR(drv->own_addr)); + } + wpa_printf(MSG_DEBUG, "wpa_driver_tista_get_mac_addr success"); + + return (const u8 *)&drv->own_addr ; +} + +static int wpa_driver_tista_get_rssi(void *priv, int *rssi_data, int *rssi_beacon) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + TCuCommon_RoamingStatisticsTable buffer; + + if(0 != wpa_driver_tista_private_send(priv, TIWLN_802_11_RSSI, NULL, 0, + &buffer, sizeof(TCuCommon_RoamingStatisticsTable))) + { + wpa_printf(MSG_ERROR, "ERROR - Failed to get rssi level"); + *rssi_data = 0; + *rssi_beacon = 0; + return -1; + } + *rssi_data = (s8)buffer.rssi; + *rssi_beacon = (s8)buffer.rssiBeacon; + wpa_printf(MSG_DEBUG, "wpa_driver_tista_get_rssi data %d beacon %d success", *rssi_data, *rssi_beacon); + + return 0; +} + +static int wpa_driver_tista_config_power_management(void *priv, TPowerMgr_PowerMode *mode, u8 is_set) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + + if(is_set) // set power mode + { + if((mode->PowerMode) < POWER_MODE_MAX) + { + if(0 != wpa_driver_tista_private_send(priv, TIWLN_802_11_POWER_MODE_SET, + mode, sizeof(TPowerMgr_PowerMode), NULL, 0)) + { + wpa_printf(MSG_ERROR, "ERROR - Failed to set power mode"); + return -1; + } + } + else + { + wpa_printf(MSG_ERROR, "ERROR - Invalid Power Mode"); + return -1; + } + } + else // get power mode + { + if(0 != wpa_driver_tista_private_send(priv, TIWLN_802_11_POWER_MODE_GET, NULL, 0, + mode, sizeof(TPowerMgr_PowerMode))) + { + wpa_printf(MSG_ERROR, "ERROR - Failed to get power mode"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "wpa_driver_tista_config_power_management success"); + + return 0; +} + +static int wpa_driver_tista_enable_bt_coe(void *priv, u32 mode) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + u32 mode_set = mode; + + /* Mapping the mode between UI enum and driver enum */ + switch(mode_set) + { + case BLUETOOTH_COEXISTENCE_MODE_ENABLED: + case BLUETOOTH_COEXISTENCE_MODE_SENSE: + mode_set = SG_OPPORTUNISTIC; + break; + case BLUETOOTH_COEXISTENCE_MODE_DISABLED: + mode_set = SG_DISABLE; + break; + default: + wpa_printf(MSG_DEBUG, "wpa_driver_tista_enable_bt_coe - Unknown Mode"); + return -1; + break; + } + + if(0 != wpa_driver_tista_private_send(priv, SOFT_GEMINI_SET_ENABLE, + &mode_set, sizeof(u32), NULL, 0)) + { + wpa_printf(MSG_ERROR, "ERROR - Failed to enable BtCoe"); + return -1; + } + wpa_printf(MSG_DEBUG, "wpa_driver_tista_enable_bt_coe success"); + + return 0; +} + +static int wpa_driver_tista_get_bt_coe_status(void *priv, u32 *mode) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + u32 mode_get; + + if(0 != wpa_driver_tista_private_send(priv, SOFT_GEMINI_GET_CONFIG, NULL, 0, + &mode_get, sizeof(u32))) + { + wpa_printf(MSG_ERROR, "ERROR - Failed to get bt coe status"); + return -1; + } + *mode = mode_get; + wpa_printf(MSG_DEBUG, "wpa_driver_tista_get_bt_coe_status mode %d success", *mode); + + return 0; +} + +/*----------------------------------------------------------------------------- +Routine Name: wpa_driver_tista_driver_cmd +Routine Description: executes driver-specific commands +Arguments: + priv - pointer to private data structure + cmd - command + buf - return buffer + buf_len - buffer length +Return Value: actual buffer length - success, -1 - failure +-----------------------------------------------------------------------------*/ +static int wpa_driver_tista_driver_cmd( void *priv, char *cmd, char *buf, size_t buf_len ) +{ + struct wpa_driver_tista_data *drv = (struct wpa_driver_tista_data *)priv; + int ret = -1, prev_events; + + wpa_printf(MSG_DEBUG, "%s %s", __func__, cmd); + + if( os_strcasecmp(cmd, "start") == 0 ) { + wpa_printf(MSG_DEBUG,"Start command"); + ret = wpa_driver_tista_driver_start(priv); + if( ret == 0 ) { + drv->driver_is_loaded = TRUE; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); + } + } + else if( os_strcasecmp(cmd, "stop") == 0 ) { + wpa_printf(MSG_DEBUG,"Stop command"); + ret = wpa_driver_tista_driver_stop(priv); + if( ret == 0 ) { + drv->driver_is_loaded = FALSE; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); + } + } + else if( os_strcasecmp(cmd, "macaddr") == 0 ) { + wpa_driver_tista_get_mac_addr(priv); + wpa_printf(MSG_DEBUG, "Macaddr command"); + ret = sprintf(buf, "Macaddr = " MACSTR "\n", MAC2STR(drv->own_addr)); + buf[ret]='\0'; + wpa_printf(MSG_DEBUG, "buf %s", buf); + } + else if( os_strcasecmp(cmd, "scan-passive") == 0 ) { + wpa_printf(MSG_DEBUG,"Scan Passive command"); + drv->scan_type = SCAN_TYPE_NORMAL_PASSIVE; + ret = 0; + } + else if( os_strcasecmp(cmd, "scan-active") == 0 ) { + wpa_printf(MSG_DEBUG,"Scan Active command"); + drv->scan_type = SCAN_TYPE_NORMAL_ACTIVE; + ret = 0; + } + else if( os_strcasecmp(cmd, "linkspeed") == 0 ) { + wpa_printf(MSG_DEBUG,"Link Speed command"); + ret = sprintf(buf,"LinkSpeed %u\n", drv->link_speed); + buf[ret]='\0'; + wpa_printf(MSG_DEBUG, "buf %s", buf); + } + else if( os_strncasecmp(cmd, "scan-channels", 13) == 0 ) { + int noOfChan; + + noOfChan = atoi(cmd + 13); + wpa_printf(MSG_DEBUG,"Scan Channels command = %d", noOfChan); + if( (noOfChan > 0) && (noOfChan <= MAX_NUMBER_OF_CHANNELS_PER_SCAN) ) + drv->scan_channels = noOfChan; + ret = sprintf(buf,"Scan-Channels = %d\n", drv->scan_channels); + buf[ret]='\0'; + wpa_printf(MSG_DEBUG, "buf %s", buf); + } + else if( os_strcasecmp(cmd, "rssi") == 0 ) { + u8 ssid[MAX_SSID_LEN]; + int rssi_data, rssi_beacon, len; + + wpa_printf(MSG_DEBUG,"rssi command"); + + ret = wpa_driver_tista_get_rssi(priv, &rssi_data, &rssi_beacon); + if( ret == 0 ) { + len = wpa_driver_tista_get_ssid( priv, (u8 *)ssid ); + wpa_printf(MSG_DEBUG,"rssi_data %d rssi_beacon %d", rssi_data, rssi_beacon); + if( (len > 0) && (len <= MAX_SSID_LEN) ) { + os_memcpy( (void *)buf, (void *)ssid, len ); + ret = len; + ret += sprintf(&buf[ret], " rssi %d\n", rssi_beacon); + buf[ret]='\0'; + wpa_printf(MSG_DEBUG, "buf %s", buf); + } + else + { + wpa_printf(MSG_DEBUG, "Fail to get ssid when reporting rssi"); + ret = -1; + } + } + } + else if( os_strncasecmp(cmd, "powermode", 9) == 0 ) { + u32 mode; + TPowerMgr_PowerMode tMode; + + mode = (u32)atoi(cmd + 9); + wpa_printf(MSG_DEBUG,"Power Mode command = %u", mode); + if( mode < POWER_MODE_MAX ) + { + tMode.PowerMode = (PowerMgr_PowerMode_e)mode; + tMode.PowerMngPriority = POWER_MANAGER_USER_PRIORITY; + ret = wpa_driver_tista_config_power_management( priv, &tMode, 1 ); + } + } + else if (os_strncasecmp(cmd, "getpower", 8) == 0 ) { + u32 mode; + TPowerMgr_PowerMode tMode; + + ret = wpa_driver_tista_config_power_management( priv, &tMode, 0 ); + if( ret == 0 ) { + ret = sprintf(buf, "powermode = %u\n", tMode.PowerMode); + buf[ret]='\0'; + wpa_printf(MSG_DEBUG, "buf %s", buf); + } + } + else if( os_strncasecmp(cmd, "btcoexmode", 10) == 0 ) { + u32 mode; + + mode = (u32)atoi(cmd + 10); + wpa_printf(MSG_DEBUG,"BtCoex Mode command = %u", mode); + ret = wpa_driver_tista_enable_bt_coe( priv, mode ); + if( ret == 0 ) { + drv->btcoex_mode = mode; + } + } + else if( os_strcasecmp(cmd, "btcoexstat") == 0 ) { + u32 status = drv->btcoex_mode; + + wpa_printf(MSG_DEBUG,"BtCoex Status"); + ret = wpa_driver_tista_get_bt_coe_status( priv, &status ); + if( ret == 0 ) { + ret = sprintf(buf, "btcoexstatus = 0x%x\n", status); + buf[ret]='\0'; + wpa_printf(MSG_DEBUG, "buf %s", buf); + } + } + else { + wpa_printf(MSG_DEBUG,"Unsupported command"); + } + + + if(ret == 0) + { + os_memcpy(buf, "OK\n", 3); + ret = 3; + } + + return ret; +} +#endif + +static int wpa_driver_tista_send_oper_ifla(struct wpa_driver_tista_data *drv, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = drv->ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " + "%s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} + + +static int wpa_driver_tista_set_auth_param(struct wpa_driver_tista_data *drv, + int idx, u32 value) +{ + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { + perror("ioctl[SIOCSIWAUTH]"); + fprintf(stderr, "WEXT auth param %d value 0x%x - ", + idx, value); + ret = errno == EOPNOTSUPP ? -2 : -1; + } + + return ret; +} + + +/** + * wpa_driver_tista_get_bssid - Get BSSID, SIOCGIWAP + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * @bssid: Buffer for BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_tista_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + + +/** + * wpa_driver_tista_set_bssid - Set BSSID, SIOCSIWAP + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * @bssid: BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_tista_set_bssid(void *priv, const u8 *bssid) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + else + os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { + perror("ioctl[SIOCSIWAP]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_tista_get_ssid - Get SSID, SIOCGIWESSID + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * @ssid: Buffer for the SSID; must be at least 32 bytes long + * Returns: SSID length on success, -1 on failure + */ +int wpa_driver_tista_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0' && + drv->we_version_compiled < 21) + ret--; + } + + return ret; +} + + +/** + * wpa_driver_tista_set_ssid - Set SSID, SIOCSIWESSID + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * @ssid: SSID + * @ssid_len: Length of SSID (0..32) + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_tista_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + char buf[33]; + + if (ssid_len > 32) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ + iwr.u.essid.flags = (ssid_len != 0); + os_memset(buf, 0, sizeof(buf)); + os_memcpy(buf, ssid, ssid_len); + iwr.u.essid.pointer = (caddr_t) buf; + if (drv->we_version_compiled < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (ssid_len) + ssid_len++; + } + iwr.u.essid.length = ssid_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_tista_set_freq - Set frequency/channel, SIOCSIWFREQ + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * @freq: Frequency in MHz + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_tista_set_freq(void *priv, int freq) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.freq.m = freq * 100000; + iwr.u.freq.e = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + ret = -1; + } + + return ret; +} + + +static void +wpa_driver_tista_event_wireless_custom(struct wpa_driver_tista_data *drv, void *ctx, char *custom) +{ + union wpa_event_data data; +#ifdef TI_WLAN_WILINK_6_SUPPORT + IPC_EV_DATA * pData = NULL; +#endif + + wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", + custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast ") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { + char *spos; + int bytes; + + spos = custom + 17; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + return; + bytes /= 2; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.req_ies, bytes); + + spos += bytes * 2; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + spos += 9; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + goto done; + bytes /= 2; + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.resp_ies, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { + if (hwaddr_aton(custom + 17, data.stkstart.peer)) { + wpa_printf(MSG_DEBUG, "WEXT: unrecognized " + "STKSTART.request '%s'", custom + 17); + return; + } + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +#endif /* CONFIG_PEERKEY */ + } +#ifdef TI_WLAN_WILINK_6_SUPPORT + else + { + pData = (IPC_EV_DATA *)custom; + switch (pData->EvParams.uEventType) { + case IPC_EVENT_ASSOCIATED: + wpa_printf(MSG_DEBUG, "IPC_EVENT_ASSOCIATED"); + break; + case IPC_EVENT_DISASSOCIATED: + wpa_printf(MSG_DEBUG, "IPC_EVENT_DISASSOCIATED"); + break; + case IPC_EVENT_LINK_SPEED: + wpa_printf(MSG_DEBUG, "IPC_EVENT_LINK_SPEED"); + if(pData->uBufferSize == sizeof(u32)) + { + wpa_printf(MSG_DEBUG, "update link_speed"); + drv->link_speed = *((u32 *)pData->uBuffer) / 2; + } + + wpa_printf(MSG_INFO,"wpa_supplicant - Link Speed = %u", drv->link_speed ); + break; + case IPC_EVENT_AUTH_SUCC: + wpa_printf(MSG_DEBUG, "IPC_EVENT_AUTH_SUCC"); + break; + case IPC_EVENT_SCAN_REPORT: + wpa_printf(MSG_DEBUG, "IPC_EVENT_SCAN_REPORT"); + break; + case IPC_EVENT_SCAN_COMPLETE: + wpa_printf(MSG_DEBUG, "IPC_EVENT_SCAN_COMPLETE"); + break; + case IPC_EVENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "IPC_EVENT_TIMEOUT"); + break; + case IPC_EVENT_CCKM_START: + wpa_printf(MSG_DEBUG, "IPC_EVENT_CCKM_START"); + break; + case IPC_EVENT_MEDIA_SPECIFIC: + wpa_printf(MSG_DEBUG, "IPC_EVENT_MEDIA_SPECIFIC"); + break; + case IPC_EVENT_EAPOL: + wpa_printf(MSG_DEBUG, "IPC_EVENT_EAPOL"); + break; + case IPC_EVENT_BOUND: + wpa_printf(MSG_DEBUG, "IPC_EVENT_BOUND"); + break; + case IPC_EVENT_UNBOUND: + wpa_printf(MSG_DEBUG, "IPC_EVENT_UNBOUND"); + break; + case IPC_EVENT_PREAUTH_EAPOL: + wpa_printf(MSG_DEBUG, "IPC_EVENT_PREAUTH_EAPOL"); + break; + case IPC_EVENT_RESERVED2: + wpa_printf(MSG_DEBUG, "IPC_EVENT_RESERVED2"); + break; + case IPC_EVENT_LOW_RSSI: + wpa_printf(MSG_DEBUG, "IPC_EVENT_LOW_RSSI"); + break; + case IPC_EVENT_TSPEC_STATUS: + wpa_printf(MSG_DEBUG, "IPC_EVENT_TSPEC_STATUS"); + break; + case IPC_EVENT_TSPEC_RATE_STATUS: + wpa_printf(MSG_DEBUG, "IPC_EVENT_TSPEC_RATE_STATUS"); + break; + case IPC_EVENT_MEDIUM_TIME_CROSS: + wpa_printf(MSG_DEBUG, "IPC_EVENT_MEDIUM_TIME_CROSS"); + break; + case IPC_EVENT_ROAMING_COMPLETE: + wpa_printf(MSG_DEBUG, "IPC_EVENT_ROAMING_COMPLETE"); + break; + case IPC_EVENT_EAP_AUTH_FAILURE: + wpa_printf(MSG_DEBUG, "IPC_EVENT_EAP_AUTH_FAILURE"); + break; + case IPC_EVENT_WPA2_PREAUTHENTICATION: + wpa_printf(MSG_DEBUG, "IPC_EVENT_WPA2_PREAUTHENTICATION"); + break; + case IPC_EVENT_TRAFFIC_INTENSITY_THRESHOLD_CROSSED: + wpa_printf(MSG_DEBUG, "IPC_EVENT_TRAFFIC_INTENSITY_THRESHOLD_CROSSED"); + break; + case IPC_EVENT_GWSI: + wpa_printf(MSG_DEBUG, "IPC_EVENT_GWSI"); + break; + case IPC_EVENT_WPS_SESSION_OVERLAP: + wpa_printf(MSG_DEBUG, "IPC_EVENT_WPS_SESSION_OVERLAP"); + break; + case IPC_EVENT_RSSI_SNR_TRIGGER_0: + wpa_printf(MSG_DEBUG, "IPC_EVENT_RSSI_SNR_TRIGGER_0"); + break; + case IPC_EVENT_RSSI_SNR_TRIGGER_1: + wpa_printf(MSG_DEBUG, "IPC_EVENT_RSSI_SNR_TRIGGER_1"); + break; + case IPC_EVENT_LOGGER: + wpa_printf(MSG_DEBUG, "IPC_EVENT_LOGGER"); + break; + case IPC_EVENT_NOT_ASSOCIATED: + wpa_printf(MSG_DEBUG, "IPC_EVENT_NOT_ASSOCIATED"); + break; + default: + wpa_printf(MSG_DEBUG, "Unknown event"); + break; + } + } +#endif +} + +static int wpa_driver_tista_event_wireless_michaelmicfailure( + void *ctx, const char *ev, size_t len) +{ + const struct iw_michaelmicfailure *mic; + union wpa_event_data data; + + if (len < sizeof(*mic)) + return -1; + + mic = (const struct iw_michaelmicfailure *) ev; + + wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " + "flags=0x%x src_addr=" MACSTR, mic->flags, + MAC2STR(mic->src_addr.sa_data)); + + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + + return 0; +} + +static int wpa_driver_tista_event_wireless_pmkidcand( + struct wpa_driver_tista_data *drv, const char *ev, size_t len) +{ + const struct iw_pmkid_cand *cand; + union wpa_event_data data; + const u8 *addr; + + if (len < sizeof(*cand)) + return -1; + + cand = (const struct iw_pmkid_cand *) ev; + addr = (const u8 *) cand->bssid.sa_data; + + wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " + "flags=0x%x index=%d bssid=" MACSTR, cand->flags, + cand->index, MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); + data.pmkid_candidate.index = cand->index; + data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + + return 0; +} + +static int wpa_driver_tista_event_wireless_assocreqie( + struct wpa_driver_tista_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = os_malloc(len); + if (drv->assoc_req_ies == NULL) { + drv->assoc_req_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_req_ies, ev, len); + drv->assoc_req_ies_len = len; + + return 0; +} + +static int wpa_driver_tista_event_wireless_assocrespie( + struct wpa_driver_tista_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = os_malloc(len); + if (drv->assoc_resp_ies == NULL) { + drv->assoc_resp_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_resp_ies, ev, len); + drv->assoc_resp_ies_len = len; + + return 0; +} + +static void wpa_driver_tista_event_assoc_ies(struct wpa_driver_tista_data *drv) +{ + union wpa_event_data data; + + if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + if (drv->assoc_req_ies) { + data.assoc_info.req_ies = drv->assoc_req_ies; + drv->assoc_req_ies = NULL; + data.assoc_info.req_ies_len = drv->assoc_req_ies_len; + } + if (drv->assoc_resp_ies) { + data.assoc_info.resp_ies = drv->assoc_resp_ies; + drv->assoc_resp_ies = NULL; + data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(data.assoc_info.req_ies); + os_free(data.assoc_info.resp_ies); +} + +static void wpa_driver_tista_event_wireless(struct wpa_driver_tista_data *drv, + void *ctx, char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + wpa_hexdump(MSG_MSGDUMP, "wext event", (unsigned char *)data, len); + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version_compiled > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVASSOCRESPIE || + iwe->cmd == IWEVPMKIDCAND)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + wpa_printf(MSG_DEBUG, "WEXT: wpa_driver_tista_event_wireless cmd 0x%x", iwe->cmd); + + switch (iwe->cmd) { + case SIOCGIWAP: + wpa_printf(MSG_DEBUG, "Wireless event: new AP: " + MACSTR, + MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); + if (os_memcmp(iwe->u.ap_addr.sa_data, + "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == + 0 || + os_memcmp(iwe->u.ap_addr.sa_data, + "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == + 0) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + + } else { + wpa_driver_tista_event_assoc_ies(drv); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } + break; + case IWEVMICHAELMICFAILURE: + wpa_driver_tista_event_wireless_michaelmicfailure( + ctx, custom, iwe->u.data.length); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + wpa_driver_tista_event_wireless_custom(drv, ctx, buf); + os_free(buf); + break; + case SIOCGIWSCAN: + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_tista_scan_timeout, + drv, ctx); + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case IWEVASSOCREQIE: + wpa_driver_tista_event_wireless_assocreqie( + drv, custom, iwe->u.data.length); + break; + case IWEVASSOCRESPIE: + wpa_driver_tista_event_wireless_assocrespie( + drv, custom, iwe->u.data.length); + break; + case IWEVPMKIDCAND: + wpa_driver_tista_event_wireless_pmkidcand( + drv, custom, iwe->u.data.length); + break; + } + + pos += iwe->len; + } +} + +static void wpa_driver_tista_event_link(void *ctx, char *buf, size_t len, + int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); +} + +static void wpa_driver_tista_event_rtm_newlink(struct wpa_driver_tista_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (drv->ifindex != ifi->ifi_index && drv->ifindex2 != ifi->ifi_index) + { + wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT" : ""); + /* + * Some drivers send the association event before the operup event--in + * this case, lifting operstate in wpa_driver_tista_set_operstate() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + wpa_driver_tista_send_oper_ifla(drv, -1, IF_OPER_UP); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_tista_event_wireless( + drv, ctx, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } else if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_tista_event_link(ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } + attr = RTA_NEXT(attr, attrlen); + } +} + +static void wpa_driver_tista_event_rtm_dellink(struct wpa_driver_tista_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_tista_event_link(ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } + attr = RTA_NEXT(attr, attrlen); + } +} + +static void wpa_driver_tista_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_tista_event_rtm_newlink(eloop_ctx, sock_ctx, + h, plen); + break; + case RTM_DELLINK: + wpa_driver_tista_event_rtm_dellink(eloop_ctx, sock_ctx, + h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + +static int wpa_driver_tista_get_ifflags_ifname(struct wpa_driver_tista_data *drv, + const char *ifname, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + +/** + * wpa_driver_tista_get_ifflags - Get interface flags (SIOCGIFFLAGS) + * @drv: driver_tista private data + * @flags: Pointer to returned flags value + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_tista_get_ifflags(struct wpa_driver_tista_data *drv, int *flags) +{ + return wpa_driver_tista_get_ifflags_ifname(drv, drv->ifname, flags); +} + +static int wpa_driver_tista_set_ifflags_ifname(struct wpa_driver_tista_data *drv, + const char *ifname, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + +/** + * wpa_driver_tista_set_ifflags - Set interface flags (SIOCSIFFLAGS) + * @drv: driver_tista private data + * @flags: New value for flags + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_tista_set_ifflags(struct wpa_driver_tista_data *drv, int flags) +{ + return wpa_driver_tista_set_ifflags_ifname(drv, drv->ifname, flags); +} + +/** + * wpa_driver_tista_init - Initialize WE driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +void * wpa_driver_tista_init(void *ctx, const char *ifname) +{ + int s, flags; + struct sockaddr_nl local; + struct wpa_driver_tista_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strncpy(drv->ifname, ifname, sizeof(drv->ifname)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + os_free(drv); + return NULL; + } + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_tista_event_receive, drv, ctx); + drv->event_sock = s; + + drv->mlme_sock = -1; + + /* + * Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_tista_flush_pmkid(drv); + + if (wpa_driver_tista_set_mode(drv, 0) < 0) { + printf("Could not configure driver to use managed mode\n"); + } + + if (wpa_driver_tista_get_ifflags(drv, &flags) != 0 || + wpa_driver_tista_set_ifflags(drv, flags | IFF_UP) != 0) { + printf("Could not set interface '%s' UP\n", drv->ifname); + } + + wpa_driver_tista_get_range(drv); + + drv->ifindex = if_nametoindex(drv->ifname); + + if (os_strncmp(ifname, "wlan", 4) == 0) { + /* + * Host AP driver may use both wlan# and wifi# interface in + * wireless events. Since some of the versions included WE-18 + * support, let's add the alternative ifindex also from + * driver_tista.c for the time being. This may be removed at + * some point once it is believed that old versions of the + * driver are not in use anymore. + */ + char ifname2[IFNAMSIZ + 1]; + os_strncpy(ifname2, ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_tista_alternative_ifindex(drv, ifname2); + } + + wpa_driver_tista_send_oper_ifla(drv, 1, IF_OPER_DORMANT); + +#ifdef TI_WLAN_WILINK_6_SUPPORT + /* Signal that driver is not loaded yet */ + drv->driver_is_loaded = TRUE; + + /* Set default scan type */ + drv->scan_type = SCAN_TYPE_NORMAL_ACTIVE; + + /* Set default amount of channels */ + drv->scan_channels = 14; + + /* Link Speed will be set by the message from the driver */ + drv->link_speed = 0; + + /* BtCoex mode is read from tiwlan.ini file */ + drv->btcoex_mode = 0; /* SG_DISABLE */ +#endif + + return drv; +} + +/** + * wpa_driver_tista_deinit - Deinitialize WE driver interface + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_tista_init(). + */ +void wpa_driver_tista_deinit(void *priv) +{ + struct wpa_driver_tista_data *drv = priv; + int flags; + + eloop_cancel_timeout(wpa_driver_tista_scan_timeout, drv, drv->ctx); + + /* + * Clear possibly configured driver parameters in order to make it + * easier to use the driver after wpa_supplicant has been terminated. + */ + wpa_driver_tista_set_bssid(drv, (u8 *) "\x00\x00\x00\x00\x00\x00"); + + wpa_driver_tista_send_oper_ifla(priv, 0, IF_OPER_UP); + + eloop_unregister_read_sock(drv->event_sock); + if (drv->mlme_sock >= 0) + eloop_unregister_read_sock(drv->mlme_sock); + + if (wpa_driver_tista_get_ifflags(drv, &flags) == 0) + (void) wpa_driver_tista_set_ifflags(drv, flags & ~IFF_UP); + +#ifdef CONFIG_CLIENT_MLME + if (drv->mlmedev[0] && + wpa_driver_tista_get_ifflags_ifname(drv, drv->mlmedev, &flags) == 0) + (void) wpa_driver_tista_set_ifflags_ifname(drv, drv->mlmedev, + flags & ~IFF_UP); +#endif /* CONFIG_CLIENT_MLME */ + + close(drv->event_sock); + close(drv->ioctl_sock); + if (drv->mlme_sock >= 0) + close(drv->mlme_sock); + os_free(drv->assoc_req_ies); + os_free(drv->assoc_resp_ies); + os_free(drv); +} + +/** + * wpa_driver_tista_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Unused + * @timeout_ctx: ctx argument given to wpa_driver_tista_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +void wpa_driver_tista_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + +/* Compare function for sorting scan results. Return >0 if @b is considered + * better. */ +static int wpa_scan_result_compar(const void *a, const void *b) +{ + const struct wpa_scan_result *wa = a; + const struct wpa_scan_result *wb = b; + + /* WPA/WPA2 support preferred */ + if ((wb->wpa_ie_len || wb->rsn_ie_len) && + !(wa->wpa_ie_len || wa->rsn_ie_len)) + return 1; + if (!(wb->wpa_ie_len || wb->rsn_ie_len) && + (wa->wpa_ie_len || wa->rsn_ie_len)) + return -1; + + /* privacy support preferred */ + if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 && + (wb->caps & IEEE80211_CAP_PRIVACY)) + return 1; + if ((wa->caps & IEEE80211_CAP_PRIVACY) && + (wb->caps & IEEE80211_CAP_PRIVACY) == 0) + return -1; + + /* best/max rate preferred if signal level close enough XXX */ + if (wa->maxrate != wb->maxrate && abs(wb->level - wa->level) < 5) + return wb->maxrate - wa->maxrate; + + /* use freq for channel preference */ + + /* all things being equal, use signal level; if signal levels are + * identical, use quality values since some drivers may only report + * that value and leave the signal level zero */ + if (wb->level == wa->level) + return wb->qual - wa->qual; + return wb->level - wa->level; +} + +/** + * wpa_driver_tista_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * @results: Pointer to buffer for scan results + * @max_size: Maximum number of entries (buffer size) + * Returns: Number of scan result entries used on success, -1 on + * failure + * + * If scan results include more than max_size BSSes, max_size will be + * returned and the remaining entries will not be included in the + * buffer. + */ +int wpa_driver_tista_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + size_t ap_num = 0; + int first, maxrate; + u8 *res_buf; + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *genie, *gpos, *gend; + struct iw_param p; + size_t len, clen, res_buf_len; + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + wpa_printf(MSG_ERROR," +++++++++ SCAN RESULTS +++++++ \n\n\n"); + wpa_printf(MSG_ERROR,"struct iw_event = %d, %d", sizeof(struct iw_event), IW_EV_LCP_LEN); + res_buf_len = IW_SCAN_MAX_DATA; + for (;;) { + res_buf = os_malloc(res_buf_len); + if (res_buf == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = res_buf; + iwr.u.data.length = res_buf_len; + + if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0) + break; + + if (errno == E2BIG && res_buf_len < 100000) { + os_free(res_buf); + res_buf = NULL; + res_buf_len *= 2; + wpa_printf(MSG_DEBUG, "Scan results did not fit - " + "trying larger buffer (%lu bytes)", + (unsigned long) res_buf_len); + } else { + perror("ioctl[SIOCGIWSCAN]"); + os_free(res_buf); + return -1; + } + } + + len = iwr.u.data.length; + ap_num = 0; + first = 1; + + pos = (char *) res_buf; + end = (char *) res_buf + len; + + while (pos + IW_EV_LCP_LEN <= end) { + int ssid_len; + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_ERROR,"len = 0x%x, cmd = 0x%x, ver = %d", iwe->len, iwe->cmd, drv->we_version_compiled); + if (iwe->len <= IW_EV_LCP_LEN) + break; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version_compiled > 18 && + (iwe->cmd == SIOCGIWESSID || + iwe->cmd == SIOCGIWENCODE || + iwe->cmd == IWEVGENIE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + if (!first) + ap_num++; + first = 0; + if (ap_num < max_size) { + os_memcpy(results[ap_num].bssid, + iwe->u.ap_addr.sa_data, ETH_ALEN); + } + break; + case SIOCGIWMODE: + if (ap_num >= max_size) + break; + if (iwe->u.mode == IW_MODE_ADHOC) + results[ap_num].caps |= IEEE80211_CAP_IBSS; + else if (iwe->u.mode == IW_MODE_MASTER || + iwe->u.mode == IW_MODE_INFRA) + results[ap_num].caps |= IEEE80211_CAP_ESS; + break; + case SIOCGIWESSID: + ssid_len = iwe->u.essid.length; + if (custom + ssid_len > end) + break; + if (iwe->u.essid.flags && + ssid_len > 0 && + ssid_len <= IW_ESSID_MAX_SIZE) { + if (ap_num < max_size) { + os_memcpy(results[ap_num].ssid, custom, + ssid_len); + results[ap_num].ssid_len = ssid_len; + } + } + break; + case SIOCGIWFREQ: + if (ap_num < max_size) { + int divi = 1000000, i; + if (iwe->u.freq.e == 0) { + /* + * Some drivers do not report + * frequency, but a channel. Try to map + * this to frequency by assuming they + * are using IEEE 802.11b/g. + */ + if (iwe->u.freq.m >= 1 && + iwe->u.freq.m <= 13) { + results[ap_num].freq = + 2407 + + 5 * iwe->u.freq.m; + break; + } else if (iwe->u.freq.m == 14) { + results[ap_num].freq = 2484; + break; + } + } + if (iwe->u.freq.e > 6) { + wpa_printf( + MSG_DEBUG, "Invalid freq " + "in scan results (BSSID=" + MACSTR ": m=%d e=%d\n", + MAC2STR(results[ap_num].bssid), + iwe->u.freq.m, iwe->u.freq.e); + break; + } + for (i = 0; i < iwe->u.freq.e; i++) + divi /= 10; + results[ap_num].freq = iwe->u.freq.m / divi; + } + wpa_printf(MSG_ERROR,"+++++++++ SIOCGIWFREQ ++++++++++"); + break; + case IWEVQUAL: + if (ap_num < max_size) { + results[ap_num].qual = iwe->u.qual.qual; + results[ap_num].noise = (s8)iwe->u.qual.noise; + results[ap_num].level = (s8)iwe->u.qual.level; + wpa_printf(MSG_DEBUG, "qual %d noise %d level %d", iwe->u.qual.qual, iwe->u.qual.noise, iwe->u.qual.level); + } + wpa_printf(MSG_ERROR,"+++++++++ IWEVQUAL ++++++++++"); + break; + case SIOCGIWENCODE: + if (ap_num < max_size && + !(iwe->u.data.flags & IW_ENCODE_DISABLED)) + results[ap_num].caps |= IEEE80211_CAP_PRIVACY; + break; + case SIOCGIWRATE: + custom = pos + IW_EV_LCP_LEN; + clen = iwe->len; + if (custom + clen > end) + break; + maxrate = 0; + while (((ssize_t) clen) >= + (ssize_t) sizeof(struct iw_param)) { + /* Note: may be misaligned, make a local, + * aligned copy */ + os_memcpy(&p, custom, sizeof(struct iw_param)); + if (p.value > maxrate) + maxrate = p.value; + clen -= sizeof(struct iw_param); + custom += sizeof(struct iw_param); + } + if (ap_num < max_size) + results[ap_num].maxrate = maxrate; + break; + case IWEVGENIE: + if (ap_num >= max_size) + break; + gpos = genie = custom; + gend = genie + iwe->u.data.length; + if (gend > end) { + wpa_printf(MSG_INFO, "IWEVGENIE overflow"); + break; + } + while (gpos + 1 < gend && + gpos + 2 + (u8) gpos[1] <= gend) { + u8 ie = gpos[0], ielen = gpos[1] + 2; + if (ielen > SSID_MAX_WPA_IE_LEN) { + gpos += ielen; + continue; + } + switch (ie) { + case GENERIC_INFO_ELEM: + if (ielen < 2 + 4 || + os_memcmp(&gpos[2], + "\x00\x50\xf2\x01", 4) != + 0) + break; + os_memcpy(results[ap_num].wpa_ie, gpos, + ielen); + results[ap_num].wpa_ie_len = ielen; + break; + case RSN_INFO_ELEM: + os_memcpy(results[ap_num].rsn_ie, gpos, + ielen); + results[ap_num].rsn_ie_len = ielen; + break; + } + gpos += ielen; + } + break; + case IWEVCUSTOM: + clen = iwe->u.data.length; + if (custom + clen > end) + break; + if (clen > 7 && + os_strncmp(custom, "wpa_ie=", 7) == 0 && + ap_num < max_size) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1) + break; + bytes /= 2; + if (bytes > SSID_MAX_WPA_IE_LEN) { + wpa_printf(MSG_INFO, "Too long WPA IE " + "(%d)", bytes); + break; + } + hexstr2bin(spos, results[ap_num].wpa_ie, + bytes); + results[ap_num].wpa_ie_len = bytes; + } else if (clen > 7 && + os_strncmp(custom, "rsn_ie=", 7) == 0 && + ap_num < max_size) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1) + break; + bytes /= 2; + if (bytes > SSID_MAX_WPA_IE_LEN) { + wpa_printf(MSG_INFO, "Too long RSN IE " + "(%d)", bytes); + break; + } + hexstr2bin(spos, results[ap_num].rsn_ie, + bytes); + results[ap_num].rsn_ie_len = bytes; + } + break; + } + + pos += iwe->len; + } + os_free(res_buf); + res_buf = NULL; + if (!first) + ap_num++; + if (ap_num > max_size) { + wpa_printf(MSG_DEBUG, "Too small scan result buffer - " + "%lu BSSes but room only for %lu", + (unsigned long) ap_num, + (unsigned long) max_size); + ap_num = max_size; + } + qsort(results, ap_num, sizeof(struct wpa_scan_result), + wpa_scan_result_compar); + + wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)", + (unsigned long) len, (unsigned long) ap_num); + + return ap_num; +} + +static int wpa_driver_tista_get_range(void *priv) +{ + struct wpa_driver_tista_data *drv = priv; + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: %d", sizeof(struct iw_range)); + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + os_free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->has_capability = 1; + drv->we_version_compiled = range->we_version_compiled; + if (range->enc_capa & IW_ENC_CAPA_WPA) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + if (range->enc_capa & IW_ENC_CAPA_WPA2) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + } + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x", + drv->capa.key_mgmt, drv->capa.enc); + } else { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: %d %d %d", sizeof(struct iw_range), range->we_version_compiled, iwr.u.data.length); + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " + "assuming WPA is not supported"); + } + + os_free(range); + return 0; +} + +static int wpa_driver_tista_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_tista_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + return wpa_driver_tista_set_auth_param(drv, IW_AUTH_WPA_ENABLED, + enabled); +} + +static int wpa_driver_tista_set_key_ext(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, + size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + struct iw_encode_ext *ext; + + if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu", + __FUNCTION__, (unsigned long) seq_len); + return -1; + } + + ext = os_zalloc(sizeof(*ext) + key_len); + if (ext == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + key_len; + + if (addr == NULL || + os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; + if (set_tx) + ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; + + ext->addr.sa_family = ARPHRD_ETHER; + if (addr) + os_memcpy(ext->addr.sa_data, addr, ETH_ALEN); + else + os_memset(ext->addr.sa_data, 0xff, ETH_ALEN); + if (key && key_len) { + os_memcpy(ext + 1, key, key_len); + ext->key_len = key_len; + } + switch (alg) { + case WPA_ALG_NONE: + ext->alg = IW_ENCODE_ALG_NONE; + break; + case WPA_ALG_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + break; + case WPA_ALG_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + break; + case WPA_ALG_CCMP: + ext->alg = IW_ENCODE_ALG_CCMP; + break; + default: + wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d", + __FUNCTION__, alg); + os_free(ext); + return -1; + } + + if (seq && seq_len) { + ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID; + os_memcpy(ext->rx_seq, seq, seq_len); + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) { + ret = errno == EOPNOTSUPP ? -2 : -1; + if (errno == ENODEV) { + /* + * ndiswrapper seems to be returning incorrect error + * code.. */ + ret = -2; + } + + perror("ioctl[SIOCSIWENCODEEXT]"); + } + + os_free(ext); + return ret; +} + +/** + * wpa_driver_tista_set_key - Configure encryption key + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * @priv: Private driver interface data + * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. + * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys + * @set_tx: Configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: Sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: Length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets + * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: Length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16) + * Returns: 0 on success, -1 on failure + * + * This function uses SIOCSIWENCODEEXT by default, but tries to use + * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key. + */ +int wpa_driver_tista_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", + __FUNCTION__, alg, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + ret = wpa_driver_tista_set_key_ext(drv, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); + if (ret == 0) + return 0; + + if (ret == -2 && + (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT, trying SIOCSIWENCODE"); + ret = 0; + } else { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT"); + return ret; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) key; + iwr.u.encoding.length = key_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + if (set_tx && alg != WPA_ALG_NONE) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE] (set_tx)"); + ret = -1; + } + } + + return ret; +} + +static int wpa_driver_tista_set_countermeasures(void *priv, + int enabled) +{ + struct wpa_driver_tista_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_tista_set_auth_param(drv, + IW_AUTH_TKIP_COUNTERMEASURES, + enabled); +} + +static int wpa_driver_tista_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_tista_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->use_crypt = enabled; + return wpa_driver_tista_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, + enabled); +} + +static int wpa_driver_tista_mlme(struct wpa_driver_tista_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct iwreq iwr; + struct iw_mlme mlme; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.cmd = cmd; + mlme.reason_code = reason_code; + mlme.addr.sa_family = ARPHRD_ETHER; + os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); + iwr.u.data.pointer = (caddr_t) &mlme; + iwr.u.data.length = sizeof(mlme); + + if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { + perror("ioctl[SIOCSIWMLME]"); + ret = -1; + } + + return ret; +} + +static int wpa_driver_tista_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_tista_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_tista_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); +} + +static int wpa_driver_tista_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_tista_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_tista_mlme(drv, addr, IW_MLME_DISASSOC, + reason_code); +} + +#ifndef TI_WLAN_WILINK_6_SUPPORT +static int wpa_driver_tista_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} +#endif + +static int wpa_driver_tista_cipher2wext(int cipher) +{ + switch (cipher) { + case CIPHER_NONE: + return IW_AUTH_CIPHER_NONE; + case CIPHER_WEP40: + return IW_AUTH_CIPHER_WEP40; + case CIPHER_TKIP: + return IW_AUTH_CIPHER_TKIP; + case CIPHER_CCMP: + return IW_AUTH_CIPHER_CCMP; + case CIPHER_WEP104: + return IW_AUTH_CIPHER_WEP104; + default: + return 0; + } +} + +static int wpa_driver_tista_keymgmt2wext(int keymgmt) +{ + switch (keymgmt) { + case KEY_MGMT_802_1X: + case KEY_MGMT_802_1X_NO_WPA: + return IW_AUTH_KEY_MGMT_802_1X; + case KEY_MGMT_PSK: + return IW_AUTH_KEY_MGMT_PSK; + default: + return 0; + } +} + +static int +wpa_driver_tista_auth_alg_fallback(struct wpa_driver_tista_data *drv, + struct wpa_driver_associate_params *params) +{ + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " + "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* Just changing mode, not actual keys */ + iwr.u.encoding.flags = 0; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + + /* + * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two + * different things. Here they are used to indicate Open System vs. + * Shared Key authentication algorithm. However, some drivers may use + * them to select between open/restricted WEP encrypted (open = allow + * both unencrypted and encrypted frames; restricted = only allow + * encrypted frames). + */ + + if (!drv->use_crypt) { + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + } else { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + iwr.u.encoding.flags |= IW_ENCODE_OPEN; + if (params->auth_alg & AUTH_ALG_SHARED_KEY) + iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + return ret; +} + +static int +wpa_driver_tista_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_tista_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + int value; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * If the driver did not support SIOCSIWAUTH, fallback to + * SIOCSIWENCODE here. + */ + if (drv->auth_alg_fallback && + wpa_driver_tista_auth_alg_fallback(drv, params) < 0) + ret = -1; + + if (!params->bssid && + wpa_driver_tista_set_bssid(drv, NULL) < 0) + ret = -1; + + if (wpa_driver_tista_set_mode(drv, params->mode) < 0) + ret = -1; + /* TODO: should consider getting wpa version and cipher/key_mgmt suites + * from configuration, not from here, where only the selected suite is + * available */ +#ifndef TI_WLAN_WILINK_6_SUPPORT + if (wpa_driver_tista_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + < 0) + ret = -1; +#endif + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == RSN_INFO_ELEM) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_tista_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_tista_cipher2wext(params->pairwise_suite); + if (wpa_driver_tista_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_tista_cipher2wext(params->group_suite); + if (wpa_driver_tista_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_tista_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_tista_set_auth_param(drv, + IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + value = params->key_mgmt_suite != KEY_MGMT_NONE || + params->pairwise_suite != CIPHER_NONE || + params->group_suite != CIPHER_NONE || + params->wpa_ie_len; + if (wpa_driver_tista_set_auth_param(drv, + IW_AUTH_PRIVACY_INVOKED, value) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (wpa_driver_tista_set_auth_param(drv, + IW_AUTH_RX_UNENCRYPTED_EAPOL, + allow_unencrypted_eapol) < 0) + ret = -1; + if (params->freq && wpa_driver_tista_set_freq(drv, params->freq) < 0) + ret = -1; + if (wpa_driver_tista_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + if (params->bssid && + wpa_driver_tista_set_bssid(drv, params->bssid) < 0) + ret = -1; + + return ret; +} + +static int wpa_driver_tista_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_tista_data *drv = priv; + int algs = 0, res; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= IW_AUTH_ALG_OPEN_SYSTEM; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= IW_AUTH_ALG_SHARED_KEY; + if (auth_alg & AUTH_ALG_LEAP) + algs |= IW_AUTH_ALG_LEAP; + if (algs == 0) { + /* at least one algorithm should be set */ + algs = IW_AUTH_ALG_OPEN_SYSTEM; + } + + res = wpa_driver_tista_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, + algs); + drv->auth_alg_fallback = res == -2; + return res; +} + +/** + * wpa_driver_tista_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE + * @priv: Pointer to private wext data from wpa_driver_tista_init() + * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_tista_set_mode(void *priv, int mode) +{ + struct wpa_driver_tista_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + perror("ioctl[SIOCSIWMODE]"); + ret = -1; + } + + return ret; +} + +static int wpa_driver_tista_pmksa(struct wpa_driver_tista_data *drv, + u32 cmd, const u8 *bssid, const u8 *pmkid) +{ + struct iwreq iwr; + struct iw_pmksa pmksa; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&pmksa, 0, sizeof(pmksa)); + pmksa.cmd = cmd; + pmksa.bssid.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); + if (pmkid) + os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); + iwr.u.data.pointer = (caddr_t) &pmksa; + iwr.u.data.length = sizeof(pmksa); + + if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { + if (errno != EOPNOTSUPP) + perror("ioctl[SIOCSIWPMKSA]"); + ret = -1; + } + + return ret; +} + +static int wpa_driver_tista_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_tista_data *drv = priv; + return wpa_driver_tista_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); +} + +static int wpa_driver_tista_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_tista_data *drv = priv; + return wpa_driver_tista_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); +} + + +static int wpa_driver_tista_flush_pmkid(void *priv) +{ + struct wpa_driver_tista_data *drv = priv; + return wpa_driver_tista_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); +} + +static int wpa_driver_tista_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_tista_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + +int wpa_driver_tista_alternative_ifindex(struct wpa_driver_tista_data *drv, + const char *ifname) +{ + if (ifname == NULL) { + drv->ifindex2 = -1; + return 0; + } + + drv->ifindex2 = if_nametoindex(ifname); + if (drv->ifindex2 <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for " + "wireless events", drv->ifindex2, ifname); + + return 0; +} + +int wpa_driver_tista_set_operstate(void *priv, int state) +{ + struct wpa_driver_tista_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return wpa_driver_tista_send_oper_ifla( + drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +#ifdef CONFIG_CLIENT_MLME +static int hostapd_ioctl(struct wpa_driver_tista_data *drv, + struct prism2_hostapd_param *param, int len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + return -1; + } + + return 0; +} + +static struct wpa_hw_modes * +wpa_driver_tista_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct wpa_driver_tista_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *pos, *end; + struct wpa_hw_modes *modes = NULL; + int i; + + param = os_zalloc(PRISM2_HOSTAPD_MAX_BUF_SIZE); + if (param == NULL) + return NULL; + param->cmd = PRISM2_HOSTAPD_GET_HW_FEATURES; + + if (hostapd_ioctl(drv, param, PRISM2_HOSTAPD_MAX_BUF_SIZE) < 0) { + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + goto out; + } + + *num_modes = param->u.hw_features.num_modes; + *flags = param->u.hw_features.flags; + + pos = param->u.hw_features.data; + end = pos + PRISM2_HOSTAPD_MAX_BUF_SIZE - + (param->u.hw_features.data - (u8 *) param); + + modes = os_zalloc(*num_modes * sizeof(struct wpa_hw_modes)); + if (modes == NULL) + goto out; + + for (i = 0; i < *num_modes; i++) { + struct hostapd_ioctl_hw_modes_hdr *hdr; + struct wpa_hw_modes *feature; + int clen, rlen; + + hdr = (struct hostapd_ioctl_hw_modes_hdr *) pos; + pos = (u8 *) (hdr + 1); + clen = hdr->num_channels * sizeof(struct wpa_channel_data); + rlen = hdr->num_rates * sizeof(struct wpa_rate_data); + + feature = &modes[i]; + switch (hdr->mode) { + case MODE_IEEE80211A: + feature->mode = WPA_MODE_IEEE80211A; + break; + case MODE_IEEE80211B: + feature->mode = WPA_MODE_IEEE80211B; + break; + case MODE_IEEE80211G: + feature->mode = WPA_MODE_IEEE80211G; + break; + case MODE_ATHEROS_TURBO: + case MODE_ATHEROS_TURBOG: + wpa_printf(MSG_ERROR, "Skip unsupported hw_mode=%d in " + "get_hw_features data", hdr->mode); + pos += clen + rlen; + continue; + default: + wpa_printf(MSG_ERROR, "Unknown hw_mode=%d in " + "get_hw_features data", hdr->mode); + ieee80211_sta_free_hw_features(modes, *num_modes); + modes = NULL; + break; + } + feature->num_channels = hdr->num_channels; + feature->num_rates = hdr->num_rates; + + feature->channels = os_malloc(clen); + feature->rates = os_malloc(rlen); + if (!feature->channels || !feature->rates || + pos + clen + rlen > end) { + ieee80211_sta_free_hw_features(modes, *num_modes); + modes = NULL; + break; + } + + os_memcpy(feature->channels, pos, clen); + pos += clen; + os_memcpy(feature->rates, pos, rlen); + pos += rlen; + } + +out: + os_free(param); + return modes; +} + +int wpa_driver_tista_set_channel(void *priv, wpa_hw_mode phymode, int chan, + int freq) +{ + return wpa_driver_tista_set_freq(priv, freq); +} + +static void wpa_driver_tista_mlme_read(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_tista_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + struct ieee80211_frame_info *fi; + struct ieee80211_rx_status rx_status; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv[MLME]"); + return; + } + + if (len < (int) sizeof(struct ieee80211_frame_info)) { + wpa_printf(MSG_DEBUG, "WEXT: Too short MLME frame (len=%d)", + len); + return; + } + + fi = (struct ieee80211_frame_info *) buf; + if (ntohl(fi->version) != IEEE80211_FI_VERSION) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid MLME frame info version " + "0x%x", ntohl(fi->version)); + return; + } + + os_memset(&rx_status, 0, sizeof(rx_status)); + rx_status.ssi = ntohl(fi->ssi_signal); + rx_status.channel = ntohl(fi->channel); + + ieee80211_sta_rx(drv->ctx, buf + sizeof(struct ieee80211_frame_info), + len - sizeof(struct ieee80211_frame_info), + &rx_status); +} + +static int wpa_driver_tista_open_mlme(struct wpa_driver_tista_data *drv) +{ + int flags, ifindex, s, *i; + struct sockaddr_ll addr; + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = PRISM2_PARAM_USER_SPACE_MLME; + *i++ = 1; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + wpa_printf(MSG_ERROR, "WEXT: Failed to configure driver to " + "use user space MLME"); + return -1; + } + + ifindex = if_nametoindex(drv->mlmedev); + if (ifindex == 0) { + wpa_printf(MSG_ERROR, "WEXT: mlmedev='%s' not found", + drv->mlmedev); + return -1; + } + + if (wpa_driver_tista_get_ifflags_ifname(drv, drv->mlmedev, &flags) != 0 + || wpa_driver_tista_set_ifflags_ifname(drv, drv->mlmedev, + flags | IFF_UP) != 0) { + wpa_printf(MSG_ERROR, "WEXT: Could not set interface " + "'%s' UP", drv->mlmedev); + return -1; + } + + s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (s < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifindex; + + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(MLME)"); + return -1; + } + + if (eloop_register_read_sock(s, wpa_driver_tista_mlme_read, drv, NULL)) + { + wpa_printf(MSG_ERROR, "WEXT: Could not register MLME read " + "socket"); + close(s); + return -1; + } + + return s; +} + +static int wpa_driver_tista_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct wpa_driver_tista_data *drv = priv; + int ret; + + ret = send(drv->mlme_sock, data, data_len, 0); + if (ret < 0) { + perror("send[MLME]"); + return -1; + } + + return 0; +} + +static int wpa_driver_tista_mlme_add_sta(void *priv, const u8 *addr, + const u8 *supp_rates, + size_t supp_rates_len) +{ + struct wpa_driver_tista_data *drv = priv; + struct prism2_hostapd_param param; + size_t len; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_ADD_STA; + os_memcpy(param.sta_addr, addr, ETH_ALEN); + len = supp_rates_len; + if (len > sizeof(param.u.add_sta.supp_rates)) + len = sizeof(param.u.add_sta.supp_rates); + os_memcpy(param.u.add_sta.supp_rates, supp_rates, len); + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + +static int wpa_driver_tista_mlme_remove_sta(void *priv, const u8 *addr) +{ + struct wpa_driver_tista_data *drv = priv; + struct prism2_hostapd_param param; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_REMOVE_STA; + os_memcpy(param.sta_addr, addr, ETH_ALEN); + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + +#endif /* CONFIG_CLIENT_MLME */ + +static int wpa_driver_tista_set_param(void *priv, const char *param) +{ +#ifdef CONFIG_CLIENT_MLME + struct wpa_driver_tista_data *drv = priv; + const char *pos, *pos2; + size_t len; + + if (param == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + + pos = os_strstr(param, "mlmedev="); + if (pos) { + pos += 8; + pos2 = os_strchr(pos, ' '); + if (pos2) + len = pos2 - pos; + else + len = os_strlen(pos); + if (len + 1 > sizeof(drv->mlmedev)) + return -1; + os_memcpy(drv->mlmedev, pos, len); + drv->mlmedev[len] = '\0'; + wpa_printf(MSG_DEBUG, "WEXT: Using user space MLME with " + "mlmedev='%s'", drv->mlmedev); + drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + + drv->mlme_sock = wpa_driver_tista_open_mlme(drv); + if (drv->mlme_sock < 0) + return -1; + } +#endif /* CONFIG_CLIENT_MLME */ + + return 0; +} + +int wpa_driver_tista_get_version(struct wpa_driver_tista_data *drv) +{ + return drv->we_version_compiled; +} + +const struct wpa_driver_ops wpa_driver_custom_ops = { + .name = TIWLAN_DRV_NAME, + .desc = "TI Station Driver (1271)", + .get_bssid = wpa_driver_tista_get_bssid, + .get_ssid = wpa_driver_tista_get_ssid, + .set_wpa = wpa_driver_tista_set_wpa, + .set_key = wpa_driver_tista_set_key, + .set_countermeasures = wpa_driver_tista_set_countermeasures, + .set_drop_unencrypted = wpa_driver_tista_set_drop_unencrypted, + .scan = wpa_driver_tista_scan, + .get_scan_results = wpa_driver_tista_get_scan_results, + .deauthenticate = wpa_driver_tista_deauthenticate, + .disassociate = wpa_driver_tista_disassociate, + .associate = wpa_driver_tista_associate, + .set_auth_alg = wpa_driver_tista_set_auth_alg, + .init = wpa_driver_tista_init, + .deinit = wpa_driver_tista_deinit, + .set_param = wpa_driver_tista_set_param, + .add_pmkid = wpa_driver_tista_add_pmkid, + .remove_pmkid = wpa_driver_tista_remove_pmkid, + .flush_pmkid = wpa_driver_tista_flush_pmkid, + .get_capa = wpa_driver_tista_get_capa, + .set_operstate = wpa_driver_tista_set_operstate, +#ifdef CONFIG_CLIENT_MLME + .get_hw_feature_data = wpa_driver_tista_get_hw_feature_data, + .set_channel = wpa_driver_tista_set_channel, + .set_ssid = wpa_driver_tista_set_ssid, + .set_bssid = wpa_driver_tista_set_bssid, + .send_mlme = wpa_driver_tista_send_mlme, + .mlme_add_sta = wpa_driver_tista_mlme_add_sta, + .mlme_remove_sta = wpa_driver_tista_mlme_remove_sta, +#endif /* CONFIG_CLIENT_MLME */ +#ifdef TI_WLAN_WILINK_6_SUPPORT + .driver_cmd = wpa_driver_tista_driver_cmd +#endif +}; |