18df158acSJeff Kirsher /* 28df158acSJeff Kirsher * PS3 gelic network driver. 38df158acSJeff Kirsher * 48df158acSJeff Kirsher * Copyright (C) 2007 Sony Computer Entertainment Inc. 58df158acSJeff Kirsher * Copyright 2007 Sony Corporation 68df158acSJeff Kirsher * 78df158acSJeff Kirsher * This program is free software; you can redistribute it and/or modify 88df158acSJeff Kirsher * it under the terms of the GNU General Public License version 2 98df158acSJeff Kirsher * as published by the Free Software Foundation. 108df158acSJeff Kirsher * 118df158acSJeff Kirsher * This program is distributed in the hope that it will be useful, 128df158acSJeff Kirsher * but WITHOUT ANY WARRANTY; without even the implied warranty of 138df158acSJeff Kirsher * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 148df158acSJeff Kirsher * GNU General Public License for more details. 158df158acSJeff Kirsher * 168df158acSJeff Kirsher * You should have received a copy of the GNU General Public License 178df158acSJeff Kirsher * along with this program; if not, write to the Free Software 188df158acSJeff Kirsher * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 198df158acSJeff Kirsher */ 208df158acSJeff Kirsher #undef DEBUG 218df158acSJeff Kirsher 228df158acSJeff Kirsher #include <linux/kernel.h> 238df158acSJeff Kirsher #include <linux/module.h> 248df158acSJeff Kirsher #include <linux/slab.h> 258df158acSJeff Kirsher 268df158acSJeff Kirsher #include <linux/etherdevice.h> 278df158acSJeff Kirsher #include <linux/ethtool.h> 288df158acSJeff Kirsher #include <linux/if_vlan.h> 298df158acSJeff Kirsher 308df158acSJeff Kirsher #include <linux/in.h> 318df158acSJeff Kirsher #include <linux/ip.h> 328df158acSJeff Kirsher #include <linux/tcp.h> 338df158acSJeff Kirsher #include <linux/wireless.h> 348df158acSJeff Kirsher #include <linux/ieee80211.h> 358df158acSJeff Kirsher #include <linux/if_arp.h> 368df158acSJeff Kirsher #include <linux/ctype.h> 378df158acSJeff Kirsher #include <linux/string.h> 388df158acSJeff Kirsher #include <net/iw_handler.h> 398df158acSJeff Kirsher 408df158acSJeff Kirsher #include <linux/dma-mapping.h> 418df158acSJeff Kirsher #include <net/checksum.h> 428df158acSJeff Kirsher #include <asm/firmware.h> 438df158acSJeff Kirsher #include <asm/ps3.h> 448df158acSJeff Kirsher #include <asm/lv1call.h> 458df158acSJeff Kirsher 468df158acSJeff Kirsher #include "ps3_gelic_net.h" 478df158acSJeff Kirsher #include "ps3_gelic_wireless.h" 488df158acSJeff Kirsher 498df158acSJeff Kirsher 508df158acSJeff Kirsher static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan, 518df158acSJeff Kirsher u8 *essid, size_t essid_len); 528df158acSJeff Kirsher static int gelic_wl_try_associate(struct net_device *netdev); 538df158acSJeff Kirsher 548df158acSJeff Kirsher /* 558df158acSJeff Kirsher * tables 568df158acSJeff Kirsher */ 578df158acSJeff Kirsher 588df158acSJeff Kirsher /* 802.11b/g channel to freq in MHz */ 598df158acSJeff Kirsher static const int channel_freq[] = { 608df158acSJeff Kirsher 2412, 2417, 2422, 2427, 2432, 618df158acSJeff Kirsher 2437, 2442, 2447, 2452, 2457, 628df158acSJeff Kirsher 2462, 2467, 2472, 2484 638df158acSJeff Kirsher }; 648df158acSJeff Kirsher #define NUM_CHANNELS ARRAY_SIZE(channel_freq) 658df158acSJeff Kirsher 668df158acSJeff Kirsher /* in bps */ 678df158acSJeff Kirsher static const int bitrate_list[] = { 688df158acSJeff Kirsher 1000000, 698df158acSJeff Kirsher 2000000, 708df158acSJeff Kirsher 5500000, 718df158acSJeff Kirsher 11000000, 728df158acSJeff Kirsher 6000000, 738df158acSJeff Kirsher 9000000, 748df158acSJeff Kirsher 12000000, 758df158acSJeff Kirsher 18000000, 768df158acSJeff Kirsher 24000000, 778df158acSJeff Kirsher 36000000, 788df158acSJeff Kirsher 48000000, 798df158acSJeff Kirsher 54000000 808df158acSJeff Kirsher }; 818df158acSJeff Kirsher #define NUM_BITRATES ARRAY_SIZE(bitrate_list) 828df158acSJeff Kirsher 838df158acSJeff Kirsher /* 848df158acSJeff Kirsher * wpa2 support requires the hypervisor version 2.0 or later 858df158acSJeff Kirsher */ 868df158acSJeff Kirsher static inline int wpa2_capable(void) 878df158acSJeff Kirsher { 888df158acSJeff Kirsher return 0 <= ps3_compare_firmware_version(2, 0, 0); 898df158acSJeff Kirsher } 908df158acSJeff Kirsher 918df158acSJeff Kirsher static inline int precise_ie(void) 928df158acSJeff Kirsher { 938df158acSJeff Kirsher return 0 <= ps3_compare_firmware_version(2, 2, 0); 948df158acSJeff Kirsher } 958df158acSJeff Kirsher /* 968df158acSJeff Kirsher * post_eurus_cmd helpers 978df158acSJeff Kirsher */ 988df158acSJeff Kirsher struct eurus_cmd_arg_info { 998df158acSJeff Kirsher int pre_arg; /* command requires arg1, arg2 at POST COMMAND */ 1008df158acSJeff Kirsher int post_arg; /* command requires arg1, arg2 at GET_RESULT */ 1018df158acSJeff Kirsher }; 1028df158acSJeff Kirsher 1038df158acSJeff Kirsher static const struct eurus_cmd_arg_info cmd_info[GELIC_EURUS_CMD_MAX_INDEX] = { 1048df158acSJeff Kirsher [GELIC_EURUS_CMD_SET_COMMON_CFG] = { .pre_arg = 1}, 1058df158acSJeff Kirsher [GELIC_EURUS_CMD_SET_WEP_CFG] = { .pre_arg = 1}, 1068df158acSJeff Kirsher [GELIC_EURUS_CMD_SET_WPA_CFG] = { .pre_arg = 1}, 1078df158acSJeff Kirsher [GELIC_EURUS_CMD_GET_COMMON_CFG] = { .post_arg = 1}, 1088df158acSJeff Kirsher [GELIC_EURUS_CMD_GET_WEP_CFG] = { .post_arg = 1}, 1098df158acSJeff Kirsher [GELIC_EURUS_CMD_GET_WPA_CFG] = { .post_arg = 1}, 1108df158acSJeff Kirsher [GELIC_EURUS_CMD_GET_RSSI_CFG] = { .post_arg = 1}, 1118df158acSJeff Kirsher [GELIC_EURUS_CMD_START_SCAN] = { .pre_arg = 1}, 1128df158acSJeff Kirsher [GELIC_EURUS_CMD_GET_SCAN] = { .post_arg = 1}, 1138df158acSJeff Kirsher }; 1148df158acSJeff Kirsher 1158df158acSJeff Kirsher #ifdef DEBUG 1168df158acSJeff Kirsher static const char *cmdstr(enum gelic_eurus_command ix) 1178df158acSJeff Kirsher { 1188df158acSJeff Kirsher switch (ix) { 1198df158acSJeff Kirsher case GELIC_EURUS_CMD_ASSOC: 1208df158acSJeff Kirsher return "ASSOC"; 1218df158acSJeff Kirsher case GELIC_EURUS_CMD_DISASSOC: 1228df158acSJeff Kirsher return "DISASSOC"; 1238df158acSJeff Kirsher case GELIC_EURUS_CMD_START_SCAN: 1248df158acSJeff Kirsher return "SCAN"; 1258df158acSJeff Kirsher case GELIC_EURUS_CMD_GET_SCAN: 1268df158acSJeff Kirsher return "GET SCAN"; 1278df158acSJeff Kirsher case GELIC_EURUS_CMD_SET_COMMON_CFG: 1288df158acSJeff Kirsher return "SET_COMMON_CFG"; 1298df158acSJeff Kirsher case GELIC_EURUS_CMD_GET_COMMON_CFG: 1308df158acSJeff Kirsher return "GET_COMMON_CFG"; 1318df158acSJeff Kirsher case GELIC_EURUS_CMD_SET_WEP_CFG: 1328df158acSJeff Kirsher return "SET_WEP_CFG"; 1338df158acSJeff Kirsher case GELIC_EURUS_CMD_GET_WEP_CFG: 1348df158acSJeff Kirsher return "GET_WEP_CFG"; 1358df158acSJeff Kirsher case GELIC_EURUS_CMD_SET_WPA_CFG: 1368df158acSJeff Kirsher return "SET_WPA_CFG"; 1378df158acSJeff Kirsher case GELIC_EURUS_CMD_GET_WPA_CFG: 1388df158acSJeff Kirsher return "GET_WPA_CFG"; 1398df158acSJeff Kirsher case GELIC_EURUS_CMD_GET_RSSI_CFG: 1408df158acSJeff Kirsher return "GET_RSSI"; 1418df158acSJeff Kirsher default: 1428df158acSJeff Kirsher break; 1438df158acSJeff Kirsher } 1448df158acSJeff Kirsher return ""; 1458df158acSJeff Kirsher }; 1468df158acSJeff Kirsher #else 1478df158acSJeff Kirsher static inline const char *cmdstr(enum gelic_eurus_command ix) 1488df158acSJeff Kirsher { 1498df158acSJeff Kirsher return ""; 1508df158acSJeff Kirsher } 1518df158acSJeff Kirsher #endif 1528df158acSJeff Kirsher 1538df158acSJeff Kirsher /* synchronously do eurus commands */ 1548df158acSJeff Kirsher static void gelic_eurus_sync_cmd_worker(struct work_struct *work) 1558df158acSJeff Kirsher { 1568df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 1578df158acSJeff Kirsher struct gelic_card *card; 1588df158acSJeff Kirsher struct gelic_wl_info *wl; 1598df158acSJeff Kirsher 1608df158acSJeff Kirsher u64 arg1, arg2; 1618df158acSJeff Kirsher 1628df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 1638df158acSJeff Kirsher cmd = container_of(work, struct gelic_eurus_cmd, work); 1648df158acSJeff Kirsher BUG_ON(cmd_info[cmd->cmd].pre_arg && 1658df158acSJeff Kirsher cmd_info[cmd->cmd].post_arg); 1668df158acSJeff Kirsher wl = cmd->wl; 1678df158acSJeff Kirsher card = port_to_card(wl_port(wl)); 1688df158acSJeff Kirsher 1698df158acSJeff Kirsher if (cmd_info[cmd->cmd].pre_arg) { 1708df158acSJeff Kirsher arg1 = (cmd->buffer) ? 1718df158acSJeff Kirsher ps3_mm_phys_to_lpar(__pa(cmd->buffer)) : 1728df158acSJeff Kirsher 0; 1738df158acSJeff Kirsher arg2 = cmd->buf_size; 1748df158acSJeff Kirsher } else { 1758df158acSJeff Kirsher arg1 = 0; 1768df158acSJeff Kirsher arg2 = 0; 1778df158acSJeff Kirsher } 1788df158acSJeff Kirsher init_completion(&wl->cmd_done_intr); 1798df158acSJeff Kirsher pr_debug("%s: cmd='%s' start\n", __func__, cmdstr(cmd->cmd)); 1808df158acSJeff Kirsher cmd->status = lv1_net_control(bus_id(card), dev_id(card), 1818df158acSJeff Kirsher GELIC_LV1_POST_WLAN_CMD, 1828df158acSJeff Kirsher cmd->cmd, arg1, arg2, 1838df158acSJeff Kirsher &cmd->tag, &cmd->size); 1848df158acSJeff Kirsher if (cmd->status) { 1858df158acSJeff Kirsher complete(&cmd->done); 1868df158acSJeff Kirsher pr_info("%s: cmd issue failed\n", __func__); 1878df158acSJeff Kirsher return; 1888df158acSJeff Kirsher } 1898df158acSJeff Kirsher 1908df158acSJeff Kirsher wait_for_completion(&wl->cmd_done_intr); 1918df158acSJeff Kirsher 1928df158acSJeff Kirsher if (cmd_info[cmd->cmd].post_arg) { 1938df158acSJeff Kirsher arg1 = ps3_mm_phys_to_lpar(__pa(cmd->buffer)); 1948df158acSJeff Kirsher arg2 = cmd->buf_size; 1958df158acSJeff Kirsher } else { 1968df158acSJeff Kirsher arg1 = 0; 1978df158acSJeff Kirsher arg2 = 0; 1988df158acSJeff Kirsher } 1998df158acSJeff Kirsher 2008df158acSJeff Kirsher cmd->status = lv1_net_control(bus_id(card), dev_id(card), 2018df158acSJeff Kirsher GELIC_LV1_GET_WLAN_CMD_RESULT, 2028df158acSJeff Kirsher cmd->tag, arg1, arg2, 2038df158acSJeff Kirsher &cmd->cmd_status, &cmd->size); 2048df158acSJeff Kirsher #ifdef DEBUG 2058df158acSJeff Kirsher if (cmd->status || cmd->cmd_status) { 2068df158acSJeff Kirsher pr_debug("%s: cmd done tag=%#lx arg1=%#lx, arg2=%#lx\n", __func__, 2078df158acSJeff Kirsher cmd->tag, arg1, arg2); 2088df158acSJeff Kirsher pr_debug("%s: cmd done status=%#x cmd_status=%#lx size=%#lx\n", 2098df158acSJeff Kirsher __func__, cmd->status, cmd->cmd_status, cmd->size); 2108df158acSJeff Kirsher } 2118df158acSJeff Kirsher #endif 2128df158acSJeff Kirsher complete(&cmd->done); 2138df158acSJeff Kirsher pr_debug("%s: cmd='%s' done\n", __func__, cmdstr(cmd->cmd)); 2148df158acSJeff Kirsher } 2158df158acSJeff Kirsher 2168df158acSJeff Kirsher static struct gelic_eurus_cmd *gelic_eurus_sync_cmd(struct gelic_wl_info *wl, 2178df158acSJeff Kirsher unsigned int eurus_cmd, 2188df158acSJeff Kirsher void *buffer, 2198df158acSJeff Kirsher unsigned int buf_size) 2208df158acSJeff Kirsher { 2218df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 2228df158acSJeff Kirsher 2238df158acSJeff Kirsher /* allocate cmd */ 2248df158acSJeff Kirsher cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 2258df158acSJeff Kirsher if (!cmd) 2268df158acSJeff Kirsher return NULL; 2278df158acSJeff Kirsher 2288df158acSJeff Kirsher /* initialize members */ 2298df158acSJeff Kirsher cmd->cmd = eurus_cmd; 2308df158acSJeff Kirsher cmd->buffer = buffer; 2318df158acSJeff Kirsher cmd->buf_size = buf_size; 2328df158acSJeff Kirsher cmd->wl = wl; 2338df158acSJeff Kirsher INIT_WORK(&cmd->work, gelic_eurus_sync_cmd_worker); 2348df158acSJeff Kirsher init_completion(&cmd->done); 2358df158acSJeff Kirsher queue_work(wl->eurus_cmd_queue, &cmd->work); 2368df158acSJeff Kirsher 2378df158acSJeff Kirsher /* wait for command completion */ 2388df158acSJeff Kirsher wait_for_completion(&cmd->done); 2398df158acSJeff Kirsher 2408df158acSJeff Kirsher return cmd; 2418df158acSJeff Kirsher } 2428df158acSJeff Kirsher 2438df158acSJeff Kirsher static u32 gelic_wl_get_link(struct net_device *netdev) 2448df158acSJeff Kirsher { 2458df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); 2468df158acSJeff Kirsher u32 ret; 2478df158acSJeff Kirsher 2488df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 2498df158acSJeff Kirsher mutex_lock(&wl->assoc_stat_lock); 2508df158acSJeff Kirsher if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) 2518df158acSJeff Kirsher ret = 1; 2528df158acSJeff Kirsher else 2538df158acSJeff Kirsher ret = 0; 2548df158acSJeff Kirsher mutex_unlock(&wl->assoc_stat_lock); 2558df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 2568df158acSJeff Kirsher return ret; 2578df158acSJeff Kirsher } 2588df158acSJeff Kirsher 2598df158acSJeff Kirsher static void gelic_wl_send_iwap_event(struct gelic_wl_info *wl, u8 *bssid) 2608df158acSJeff Kirsher { 2618df158acSJeff Kirsher union iwreq_data data; 2628df158acSJeff Kirsher 2638df158acSJeff Kirsher memset(&data, 0, sizeof(data)); 2648df158acSJeff Kirsher if (bssid) 2658df158acSJeff Kirsher memcpy(data.ap_addr.sa_data, bssid, ETH_ALEN); 2668df158acSJeff Kirsher data.ap_addr.sa_family = ARPHRD_ETHER; 2678df158acSJeff Kirsher wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWAP, 2688df158acSJeff Kirsher &data, NULL); 2698df158acSJeff Kirsher } 2708df158acSJeff Kirsher 2718df158acSJeff Kirsher /* 2728df158acSJeff Kirsher * wireless extension handlers and helpers 2738df158acSJeff Kirsher */ 2748df158acSJeff Kirsher 2758df158acSJeff Kirsher /* SIOGIWNAME */ 2768df158acSJeff Kirsher static int gelic_wl_get_name(struct net_device *dev, 2778df158acSJeff Kirsher struct iw_request_info *info, 2788df158acSJeff Kirsher union iwreq_data *iwreq, char *extra) 2798df158acSJeff Kirsher { 2808df158acSJeff Kirsher strcpy(iwreq->name, "IEEE 802.11bg"); 2818df158acSJeff Kirsher return 0; 2828df158acSJeff Kirsher } 2838df158acSJeff Kirsher 2848df158acSJeff Kirsher static void gelic_wl_get_ch_info(struct gelic_wl_info *wl) 2858df158acSJeff Kirsher { 2868df158acSJeff Kirsher struct gelic_card *card = port_to_card(wl_port(wl)); 2878df158acSJeff Kirsher u64 ch_info_raw, tmp; 2888df158acSJeff Kirsher int status; 2898df158acSJeff Kirsher 2908df158acSJeff Kirsher if (!test_and_set_bit(GELIC_WL_STAT_CH_INFO, &wl->stat)) { 2918df158acSJeff Kirsher status = lv1_net_control(bus_id(card), dev_id(card), 2928df158acSJeff Kirsher GELIC_LV1_GET_CHANNEL, 0, 0, 0, 2938df158acSJeff Kirsher &ch_info_raw, 2948df158acSJeff Kirsher &tmp); 2958df158acSJeff Kirsher /* some fw versions may return error */ 2968df158acSJeff Kirsher if (status) { 2978df158acSJeff Kirsher if (status != LV1_NO_ENTRY) 2988df158acSJeff Kirsher pr_info("%s: available ch unknown\n", __func__); 2998df158acSJeff Kirsher wl->ch_info = 0x07ff;/* 11 ch */ 3008df158acSJeff Kirsher } else 3018df158acSJeff Kirsher /* 16 bits of MSB has available channels */ 3028df158acSJeff Kirsher wl->ch_info = ch_info_raw >> 48; 3038df158acSJeff Kirsher } 3048df158acSJeff Kirsher } 3058df158acSJeff Kirsher 3068df158acSJeff Kirsher /* SIOGIWRANGE */ 3078df158acSJeff Kirsher static int gelic_wl_get_range(struct net_device *netdev, 3088df158acSJeff Kirsher struct iw_request_info *info, 3098df158acSJeff Kirsher union iwreq_data *iwreq, char *extra) 3108df158acSJeff Kirsher { 3118df158acSJeff Kirsher struct iw_point *point = &iwreq->data; 3128df158acSJeff Kirsher struct iw_range *range = (struct iw_range *)extra; 3138df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); 3148df158acSJeff Kirsher unsigned int i, chs; 3158df158acSJeff Kirsher 3168df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 3178df158acSJeff Kirsher point->length = sizeof(struct iw_range); 3188df158acSJeff Kirsher memset(range, 0, sizeof(struct iw_range)); 3198df158acSJeff Kirsher 3208df158acSJeff Kirsher range->we_version_compiled = WIRELESS_EXT; 3218df158acSJeff Kirsher range->we_version_source = 22; 3228df158acSJeff Kirsher 3238df158acSJeff Kirsher /* available channels and frequencies */ 3248df158acSJeff Kirsher gelic_wl_get_ch_info(wl); 3258df158acSJeff Kirsher 3268df158acSJeff Kirsher for (i = 0, chs = 0; 3278df158acSJeff Kirsher i < NUM_CHANNELS && chs < IW_MAX_FREQUENCIES; i++) 3288df158acSJeff Kirsher if (wl->ch_info & (1 << i)) { 3298df158acSJeff Kirsher range->freq[chs].i = i + 1; 3308df158acSJeff Kirsher range->freq[chs].m = channel_freq[i]; 3318df158acSJeff Kirsher range->freq[chs].e = 6; 3328df158acSJeff Kirsher chs++; 3338df158acSJeff Kirsher } 3348df158acSJeff Kirsher range->num_frequency = chs; 3358df158acSJeff Kirsher range->old_num_frequency = chs; 3368df158acSJeff Kirsher range->num_channels = chs; 3378df158acSJeff Kirsher range->old_num_channels = chs; 3388df158acSJeff Kirsher 3398df158acSJeff Kirsher /* bitrates */ 3408df158acSJeff Kirsher for (i = 0; i < NUM_BITRATES; i++) 3418df158acSJeff Kirsher range->bitrate[i] = bitrate_list[i]; 3428df158acSJeff Kirsher range->num_bitrates = i; 3438df158acSJeff Kirsher 3448df158acSJeff Kirsher /* signal levels */ 3458df158acSJeff Kirsher range->max_qual.qual = 100; /* relative value */ 3468df158acSJeff Kirsher range->max_qual.level = 100; 3478df158acSJeff Kirsher range->avg_qual.qual = 50; 3488df158acSJeff Kirsher range->avg_qual.level = 50; 3498df158acSJeff Kirsher range->sensitivity = 0; 3508df158acSJeff Kirsher 3518df158acSJeff Kirsher /* Event capability */ 3528df158acSJeff Kirsher IW_EVENT_CAPA_SET_KERNEL(range->event_capa); 3538df158acSJeff Kirsher IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); 3548df158acSJeff Kirsher IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); 3558df158acSJeff Kirsher 3568df158acSJeff Kirsher /* encryption capability */ 3578df158acSJeff Kirsher range->enc_capa = IW_ENC_CAPA_WPA | 3588df158acSJeff Kirsher IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP | 3598df158acSJeff Kirsher IW_ENC_CAPA_4WAY_HANDSHAKE; 3608df158acSJeff Kirsher if (wpa2_capable()) 3618df158acSJeff Kirsher range->enc_capa |= IW_ENC_CAPA_WPA2; 3628df158acSJeff Kirsher range->encoding_size[0] = 5; /* 40bit WEP */ 3638df158acSJeff Kirsher range->encoding_size[1] = 13; /* 104bit WEP */ 3648df158acSJeff Kirsher range->encoding_size[2] = 32; /* WPA-PSK */ 3658df158acSJeff Kirsher range->num_encoding_sizes = 3; 3668df158acSJeff Kirsher range->max_encoding_tokens = GELIC_WEP_KEYS; 3678df158acSJeff Kirsher 3688df158acSJeff Kirsher /* scan capability */ 3698df158acSJeff Kirsher range->scan_capa = IW_SCAN_CAPA_ESSID; 3708df158acSJeff Kirsher 3718df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 3728df158acSJeff Kirsher return 0; 3738df158acSJeff Kirsher 3748df158acSJeff Kirsher } 3758df158acSJeff Kirsher 3768df158acSJeff Kirsher /* SIOC{G,S}IWSCAN */ 3778df158acSJeff Kirsher static int gelic_wl_set_scan(struct net_device *netdev, 3788df158acSJeff Kirsher struct iw_request_info *info, 3798df158acSJeff Kirsher union iwreq_data *wrqu, char *extra) 3808df158acSJeff Kirsher { 3818df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 3828df158acSJeff Kirsher struct iw_scan_req *req; 3838df158acSJeff Kirsher u8 *essid = NULL; 3848df158acSJeff Kirsher size_t essid_len = 0; 3858df158acSJeff Kirsher 3868df158acSJeff Kirsher if (wrqu->data.length == sizeof(struct iw_scan_req) && 3878df158acSJeff Kirsher wrqu->data.flags & IW_SCAN_THIS_ESSID) { 3888df158acSJeff Kirsher req = (struct iw_scan_req*)extra; 3898df158acSJeff Kirsher essid = req->essid; 3908df158acSJeff Kirsher essid_len = req->essid_len; 3918df158acSJeff Kirsher pr_debug("%s: ESSID scan =%s\n", __func__, essid); 3928df158acSJeff Kirsher } 3938df158acSJeff Kirsher return gelic_wl_start_scan(wl, 1, essid, essid_len); 3948df158acSJeff Kirsher } 3958df158acSJeff Kirsher 3968df158acSJeff Kirsher #define OUI_LEN 3 3978df158acSJeff Kirsher static const u8 rsn_oui[OUI_LEN] = { 0x00, 0x0f, 0xac }; 3988df158acSJeff Kirsher static const u8 wpa_oui[OUI_LEN] = { 0x00, 0x50, 0xf2 }; 3998df158acSJeff Kirsher 4008df158acSJeff Kirsher /* 4018df158acSJeff Kirsher * synthesize WPA/RSN IE data 4028df158acSJeff Kirsher * See WiFi WPA specification and IEEE 802.11-2007 7.3.2.25 4038df158acSJeff Kirsher * for the format 4048df158acSJeff Kirsher */ 4058df158acSJeff Kirsher static size_t gelic_wl_synthesize_ie(u8 *buf, 4068df158acSJeff Kirsher struct gelic_eurus_scan_info *scan) 4078df158acSJeff Kirsher { 4088df158acSJeff Kirsher 4098df158acSJeff Kirsher const u8 *oui_header; 4108df158acSJeff Kirsher u8 *start = buf; 4118df158acSJeff Kirsher int rsn; 4128df158acSJeff Kirsher int ccmp; 4138df158acSJeff Kirsher 4148df158acSJeff Kirsher pr_debug("%s: <- sec=%16x\n", __func__, scan->security); 4158df158acSJeff Kirsher switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_MASK) { 4168df158acSJeff Kirsher case GELIC_EURUS_SCAN_SEC_WPA: 4178df158acSJeff Kirsher rsn = 0; 4188df158acSJeff Kirsher break; 4198df158acSJeff Kirsher case GELIC_EURUS_SCAN_SEC_WPA2: 4208df158acSJeff Kirsher rsn = 1; 4218df158acSJeff Kirsher break; 4228df158acSJeff Kirsher default: 4238df158acSJeff Kirsher /* WEP or none. No IE returned */ 4248df158acSJeff Kirsher return 0; 4258df158acSJeff Kirsher } 4268df158acSJeff Kirsher 4278df158acSJeff Kirsher switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_WPA_MASK) { 4288df158acSJeff Kirsher case GELIC_EURUS_SCAN_SEC_WPA_TKIP: 4298df158acSJeff Kirsher ccmp = 0; 4308df158acSJeff Kirsher break; 4318df158acSJeff Kirsher case GELIC_EURUS_SCAN_SEC_WPA_AES: 4328df158acSJeff Kirsher ccmp = 1; 4338df158acSJeff Kirsher break; 4348df158acSJeff Kirsher default: 4358df158acSJeff Kirsher if (rsn) { 4368df158acSJeff Kirsher ccmp = 1; 4378df158acSJeff Kirsher pr_info("%s: no cipher info. defaulted to CCMP\n", 4388df158acSJeff Kirsher __func__); 4398df158acSJeff Kirsher } else { 4408df158acSJeff Kirsher ccmp = 0; 4418df158acSJeff Kirsher pr_info("%s: no cipher info. defaulted to TKIP\n", 4428df158acSJeff Kirsher __func__); 4438df158acSJeff Kirsher } 4448df158acSJeff Kirsher } 4458df158acSJeff Kirsher 4468df158acSJeff Kirsher if (rsn) 4478df158acSJeff Kirsher oui_header = rsn_oui; 4488df158acSJeff Kirsher else 4498df158acSJeff Kirsher oui_header = wpa_oui; 4508df158acSJeff Kirsher 4518df158acSJeff Kirsher /* element id */ 4528df158acSJeff Kirsher if (rsn) 4538df158acSJeff Kirsher *buf++ = WLAN_EID_RSN; 4548df158acSJeff Kirsher else 455c46597f1SArend van Spriel *buf++ = WLAN_EID_VENDOR_SPECIFIC; 4568df158acSJeff Kirsher 4578df158acSJeff Kirsher /* length filed; set later */ 4588df158acSJeff Kirsher buf++; 4598df158acSJeff Kirsher 4608df158acSJeff Kirsher /* wpa special header */ 4618df158acSJeff Kirsher if (!rsn) { 4628df158acSJeff Kirsher memcpy(buf, wpa_oui, OUI_LEN); 4638df158acSJeff Kirsher buf += OUI_LEN; 4648df158acSJeff Kirsher *buf++ = 0x01; 4658df158acSJeff Kirsher } 4668df158acSJeff Kirsher 4678df158acSJeff Kirsher /* version */ 4688df158acSJeff Kirsher *buf++ = 0x01; /* version 1.0 */ 4698df158acSJeff Kirsher *buf++ = 0x00; 4708df158acSJeff Kirsher 4718df158acSJeff Kirsher /* group cipher */ 4728df158acSJeff Kirsher memcpy(buf, oui_header, OUI_LEN); 4738df158acSJeff Kirsher buf += OUI_LEN; 4748df158acSJeff Kirsher 4758df158acSJeff Kirsher if (ccmp) 4768df158acSJeff Kirsher *buf++ = 0x04; /* CCMP */ 4778df158acSJeff Kirsher else 4788df158acSJeff Kirsher *buf++ = 0x02; /* TKIP */ 4798df158acSJeff Kirsher 4808df158acSJeff Kirsher /* pairwise key count always 1 */ 4818df158acSJeff Kirsher *buf++ = 0x01; 4828df158acSJeff Kirsher *buf++ = 0x00; 4838df158acSJeff Kirsher 4848df158acSJeff Kirsher /* pairwise key suit */ 4858df158acSJeff Kirsher memcpy(buf, oui_header, OUI_LEN); 4868df158acSJeff Kirsher buf += OUI_LEN; 4878df158acSJeff Kirsher if (ccmp) 4888df158acSJeff Kirsher *buf++ = 0x04; /* CCMP */ 4898df158acSJeff Kirsher else 4908df158acSJeff Kirsher *buf++ = 0x02; /* TKIP */ 4918df158acSJeff Kirsher 4928df158acSJeff Kirsher /* AKM count is 1 */ 4938df158acSJeff Kirsher *buf++ = 0x01; 4948df158acSJeff Kirsher *buf++ = 0x00; 4958df158acSJeff Kirsher 4968df158acSJeff Kirsher /* AKM suite is assumed as PSK*/ 4978df158acSJeff Kirsher memcpy(buf, oui_header, OUI_LEN); 4988df158acSJeff Kirsher buf += OUI_LEN; 4998df158acSJeff Kirsher *buf++ = 0x02; /* PSK */ 5008df158acSJeff Kirsher 5018df158acSJeff Kirsher /* RSN capabilities is 0 */ 5028df158acSJeff Kirsher *buf++ = 0x00; 5038df158acSJeff Kirsher *buf++ = 0x00; 5048df158acSJeff Kirsher 5058df158acSJeff Kirsher /* set length field */ 5068df158acSJeff Kirsher start[1] = (buf - start - 2); 5078df158acSJeff Kirsher 5088df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 5098df158acSJeff Kirsher return buf - start; 5108df158acSJeff Kirsher } 5118df158acSJeff Kirsher 5128df158acSJeff Kirsher struct ie_item { 5138df158acSJeff Kirsher u8 *data; 5148df158acSJeff Kirsher u8 len; 5158df158acSJeff Kirsher }; 5168df158acSJeff Kirsher 5178df158acSJeff Kirsher struct ie_info { 5188df158acSJeff Kirsher struct ie_item wpa; 5198df158acSJeff Kirsher struct ie_item rsn; 5208df158acSJeff Kirsher }; 5218df158acSJeff Kirsher 5228df158acSJeff Kirsher static void gelic_wl_parse_ie(u8 *data, size_t len, 5238df158acSJeff Kirsher struct ie_info *ie_info) 5248df158acSJeff Kirsher { 5258df158acSJeff Kirsher size_t data_left = len; 5268df158acSJeff Kirsher u8 *pos = data; 5278df158acSJeff Kirsher u8 item_len; 5288df158acSJeff Kirsher u8 item_id; 5298df158acSJeff Kirsher 5308df158acSJeff Kirsher pr_debug("%s: data=%p len=%ld\n", __func__, 5318df158acSJeff Kirsher data, len); 5328df158acSJeff Kirsher memset(ie_info, 0, sizeof(struct ie_info)); 5338df158acSJeff Kirsher 5348df158acSJeff Kirsher while (2 <= data_left) { 5358df158acSJeff Kirsher item_id = *pos++; 5368df158acSJeff Kirsher item_len = *pos++; 5378df158acSJeff Kirsher data_left -= 2; 5388df158acSJeff Kirsher 5398df158acSJeff Kirsher if (data_left < item_len) 5408df158acSJeff Kirsher break; 5418df158acSJeff Kirsher 5428df158acSJeff Kirsher switch (item_id) { 543c46597f1SArend van Spriel case WLAN_EID_VENDOR_SPECIFIC: 5448df158acSJeff Kirsher if ((OUI_LEN + 1 <= item_len) && 5458df158acSJeff Kirsher !memcmp(pos, wpa_oui, OUI_LEN) && 5468df158acSJeff Kirsher pos[OUI_LEN] == 0x01) { 5478df158acSJeff Kirsher ie_info->wpa.data = pos - 2; 5488df158acSJeff Kirsher ie_info->wpa.len = item_len + 2; 5498df158acSJeff Kirsher } 5508df158acSJeff Kirsher break; 5518df158acSJeff Kirsher case WLAN_EID_RSN: 5528df158acSJeff Kirsher ie_info->rsn.data = pos - 2; 5538df158acSJeff Kirsher /* length includes the header */ 5548df158acSJeff Kirsher ie_info->rsn.len = item_len + 2; 5558df158acSJeff Kirsher break; 5568df158acSJeff Kirsher default: 5578df158acSJeff Kirsher pr_debug("%s: ignore %#x,%d\n", __func__, 5588df158acSJeff Kirsher item_id, item_len); 5598df158acSJeff Kirsher break; 5608df158acSJeff Kirsher } 5618df158acSJeff Kirsher pos += item_len; 5628df158acSJeff Kirsher data_left -= item_len; 5638df158acSJeff Kirsher } 5648df158acSJeff Kirsher pr_debug("%s: wpa=%p,%d wpa2=%p,%d\n", __func__, 5658df158acSJeff Kirsher ie_info->wpa.data, ie_info->wpa.len, 5668df158acSJeff Kirsher ie_info->rsn.data, ie_info->rsn.len); 5678df158acSJeff Kirsher } 5688df158acSJeff Kirsher 5698df158acSJeff Kirsher 5708df158acSJeff Kirsher /* 5718df158acSJeff Kirsher * translate the scan informations from hypervisor to a 5728df158acSJeff Kirsher * independent format 5738df158acSJeff Kirsher */ 5748df158acSJeff Kirsher static char *gelic_wl_translate_scan(struct net_device *netdev, 5758df158acSJeff Kirsher struct iw_request_info *info, 5768df158acSJeff Kirsher char *ev, 5778df158acSJeff Kirsher char *stop, 5788df158acSJeff Kirsher struct gelic_wl_scan_info *network) 5798df158acSJeff Kirsher { 5808df158acSJeff Kirsher struct iw_event iwe; 5818df158acSJeff Kirsher struct gelic_eurus_scan_info *scan = network->hwinfo; 5828df158acSJeff Kirsher char *tmp; 5838df158acSJeff Kirsher u8 rate; 5848df158acSJeff Kirsher unsigned int i, j, len; 5858df158acSJeff Kirsher u8 buf[64]; /* arbitrary size large enough */ 5868df158acSJeff Kirsher 5878df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 5888df158acSJeff Kirsher 5898df158acSJeff Kirsher /* first entry should be AP's mac address */ 5908df158acSJeff Kirsher iwe.cmd = SIOCGIWAP; 5918df158acSJeff Kirsher iwe.u.ap_addr.sa_family = ARPHRD_ETHER; 5928df158acSJeff Kirsher memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN); 5938df158acSJeff Kirsher ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_ADDR_LEN); 5948df158acSJeff Kirsher 5958df158acSJeff Kirsher /* ESSID */ 5968df158acSJeff Kirsher iwe.cmd = SIOCGIWESSID; 5978df158acSJeff Kirsher iwe.u.data.flags = 1; 5988df158acSJeff Kirsher iwe.u.data.length = strnlen(scan->essid, 32); 5998df158acSJeff Kirsher ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); 6008df158acSJeff Kirsher 6018df158acSJeff Kirsher /* FREQUENCY */ 6028df158acSJeff Kirsher iwe.cmd = SIOCGIWFREQ; 6038df158acSJeff Kirsher iwe.u.freq.m = be16_to_cpu(scan->channel); 6048df158acSJeff Kirsher iwe.u.freq.e = 0; /* table value in MHz */ 6058df158acSJeff Kirsher iwe.u.freq.i = 0; 6068df158acSJeff Kirsher ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_FREQ_LEN); 6078df158acSJeff Kirsher 6088df158acSJeff Kirsher /* RATES */ 6098df158acSJeff Kirsher iwe.cmd = SIOCGIWRATE; 6108df158acSJeff Kirsher iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; 6118df158acSJeff Kirsher /* to stuff multiple values in one event */ 6128df158acSJeff Kirsher tmp = ev + iwe_stream_lcp_len(info); 6138df158acSJeff Kirsher /* put them in ascendant order (older is first) */ 6148df158acSJeff Kirsher i = 0; 6158df158acSJeff Kirsher j = 0; 6168df158acSJeff Kirsher pr_debug("%s: rates=%d rate=%d\n", __func__, 6178df158acSJeff Kirsher network->rate_len, network->rate_ext_len); 6188df158acSJeff Kirsher while (i < network->rate_len) { 6198df158acSJeff Kirsher if (j < network->rate_ext_len && 6208df158acSJeff Kirsher ((scan->ext_rate[j] & 0x7f) < (scan->rate[i] & 0x7f))) 6218df158acSJeff Kirsher rate = scan->ext_rate[j++] & 0x7f; 6228df158acSJeff Kirsher else 6238df158acSJeff Kirsher rate = scan->rate[i++] & 0x7f; 6248df158acSJeff Kirsher iwe.u.bitrate.value = rate * 500000; /* 500kbps unit */ 6258df158acSJeff Kirsher tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, 6268df158acSJeff Kirsher IW_EV_PARAM_LEN); 6278df158acSJeff Kirsher } 6288df158acSJeff Kirsher while (j < network->rate_ext_len) { 6298df158acSJeff Kirsher iwe.u.bitrate.value = (scan->ext_rate[j++] & 0x7f) * 500000; 6308df158acSJeff Kirsher tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, 6318df158acSJeff Kirsher IW_EV_PARAM_LEN); 6328df158acSJeff Kirsher } 6338df158acSJeff Kirsher /* Check if we added any rate */ 6348df158acSJeff Kirsher if (iwe_stream_lcp_len(info) < (tmp - ev)) 6358df158acSJeff Kirsher ev = tmp; 6368df158acSJeff Kirsher 6378df158acSJeff Kirsher /* ENCODE */ 6388df158acSJeff Kirsher iwe.cmd = SIOCGIWENCODE; 6398df158acSJeff Kirsher if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_PRIVACY) 6408df158acSJeff Kirsher iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; 6418df158acSJeff Kirsher else 6428df158acSJeff Kirsher iwe.u.data.flags = IW_ENCODE_DISABLED; 6438df158acSJeff Kirsher iwe.u.data.length = 0; 6448df158acSJeff Kirsher ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); 6458df158acSJeff Kirsher 6468df158acSJeff Kirsher /* MODE */ 6478df158acSJeff Kirsher iwe.cmd = SIOCGIWMODE; 6488df158acSJeff Kirsher if (be16_to_cpu(scan->capability) & 6498df158acSJeff Kirsher (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { 6508df158acSJeff Kirsher if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_ESS) 6518df158acSJeff Kirsher iwe.u.mode = IW_MODE_MASTER; 6528df158acSJeff Kirsher else 6538df158acSJeff Kirsher iwe.u.mode = IW_MODE_ADHOC; 6548df158acSJeff Kirsher ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_UINT_LEN); 6558df158acSJeff Kirsher } 6568df158acSJeff Kirsher 6578df158acSJeff Kirsher /* QUAL */ 6588df158acSJeff Kirsher iwe.cmd = IWEVQUAL; 6598df158acSJeff Kirsher iwe.u.qual.updated = IW_QUAL_ALL_UPDATED | 6608df158acSJeff Kirsher IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; 6618df158acSJeff Kirsher iwe.u.qual.level = be16_to_cpu(scan->rssi); 6628df158acSJeff Kirsher iwe.u.qual.qual = be16_to_cpu(scan->rssi); 6638df158acSJeff Kirsher iwe.u.qual.noise = 0; 6648df158acSJeff Kirsher ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_QUAL_LEN); 6658df158acSJeff Kirsher 6668df158acSJeff Kirsher /* RSN */ 6678df158acSJeff Kirsher memset(&iwe, 0, sizeof(iwe)); 6688df158acSJeff Kirsher if (be16_to_cpu(scan->size) <= sizeof(*scan)) { 6698df158acSJeff Kirsher /* If wpa[2] capable station, synthesize IE and put it */ 6708df158acSJeff Kirsher len = gelic_wl_synthesize_ie(buf, scan); 6718df158acSJeff Kirsher if (len) { 6728df158acSJeff Kirsher iwe.cmd = IWEVGENIE; 6738df158acSJeff Kirsher iwe.u.data.length = len; 6748df158acSJeff Kirsher ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); 6758df158acSJeff Kirsher } 6768df158acSJeff Kirsher } else { 6778df158acSJeff Kirsher /* this scan info has IE data */ 6788df158acSJeff Kirsher struct ie_info ie_info; 6798df158acSJeff Kirsher size_t data_len; 6808df158acSJeff Kirsher 6818df158acSJeff Kirsher data_len = be16_to_cpu(scan->size) - sizeof(*scan); 6828df158acSJeff Kirsher 6838df158acSJeff Kirsher gelic_wl_parse_ie(scan->elements, data_len, &ie_info); 6848df158acSJeff Kirsher 6858df158acSJeff Kirsher if (ie_info.wpa.len && (ie_info.wpa.len <= sizeof(buf))) { 6868df158acSJeff Kirsher memcpy(buf, ie_info.wpa.data, ie_info.wpa.len); 6878df158acSJeff Kirsher iwe.cmd = IWEVGENIE; 6888df158acSJeff Kirsher iwe.u.data.length = ie_info.wpa.len; 6898df158acSJeff Kirsher ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); 6908df158acSJeff Kirsher } 6918df158acSJeff Kirsher 6928df158acSJeff Kirsher if (ie_info.rsn.len && (ie_info.rsn.len <= sizeof(buf))) { 6938df158acSJeff Kirsher memset(&iwe, 0, sizeof(iwe)); 6948df158acSJeff Kirsher memcpy(buf, ie_info.rsn.data, ie_info.rsn.len); 6958df158acSJeff Kirsher iwe.cmd = IWEVGENIE; 6968df158acSJeff Kirsher iwe.u.data.length = ie_info.rsn.len; 6978df158acSJeff Kirsher ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); 6988df158acSJeff Kirsher } 6998df158acSJeff Kirsher } 7008df158acSJeff Kirsher 7018df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 7028df158acSJeff Kirsher return ev; 7038df158acSJeff Kirsher } 7048df158acSJeff Kirsher 7058df158acSJeff Kirsher 7068df158acSJeff Kirsher static int gelic_wl_get_scan(struct net_device *netdev, 7078df158acSJeff Kirsher struct iw_request_info *info, 7088df158acSJeff Kirsher union iwreq_data *wrqu, char *extra) 7098df158acSJeff Kirsher { 7108df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 7118df158acSJeff Kirsher struct gelic_wl_scan_info *scan_info; 7128df158acSJeff Kirsher char *ev = extra; 7138df158acSJeff Kirsher char *stop = ev + wrqu->data.length; 7148df158acSJeff Kirsher int ret = 0; 7158df158acSJeff Kirsher unsigned long this_time = jiffies; 7168df158acSJeff Kirsher 7178df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 7188df158acSJeff Kirsher if (mutex_lock_interruptible(&wl->scan_lock)) 7198df158acSJeff Kirsher return -EAGAIN; 7208df158acSJeff Kirsher 7218df158acSJeff Kirsher switch (wl->scan_stat) { 7228df158acSJeff Kirsher case GELIC_WL_SCAN_STAT_SCANNING: 7238df158acSJeff Kirsher /* If a scan in progress, caller should call me again */ 7248df158acSJeff Kirsher ret = -EAGAIN; 7258df158acSJeff Kirsher goto out; 7268df158acSJeff Kirsher case GELIC_WL_SCAN_STAT_INIT: 7278df158acSJeff Kirsher /* last scan request failed or never issued */ 7288df158acSJeff Kirsher ret = -ENODEV; 7298df158acSJeff Kirsher goto out; 7308df158acSJeff Kirsher case GELIC_WL_SCAN_STAT_GOT_LIST: 7318df158acSJeff Kirsher /* ok, use current list */ 7328df158acSJeff Kirsher break; 7338df158acSJeff Kirsher } 7348df158acSJeff Kirsher 7358df158acSJeff Kirsher list_for_each_entry(scan_info, &wl->network_list, list) { 7368df158acSJeff Kirsher if (wl->scan_age == 0 || 7378df158acSJeff Kirsher time_after(scan_info->last_scanned + wl->scan_age, 7388df158acSJeff Kirsher this_time)) 7398df158acSJeff Kirsher ev = gelic_wl_translate_scan(netdev, info, 7408df158acSJeff Kirsher ev, stop, 7418df158acSJeff Kirsher scan_info); 7428df158acSJeff Kirsher else 7438df158acSJeff Kirsher pr_debug("%s:entry too old\n", __func__); 7448df158acSJeff Kirsher 7458df158acSJeff Kirsher if (stop - ev <= IW_EV_ADDR_LEN) { 7468df158acSJeff Kirsher ret = -E2BIG; 7478df158acSJeff Kirsher goto out; 7488df158acSJeff Kirsher } 7498df158acSJeff Kirsher } 7508df158acSJeff Kirsher 7518df158acSJeff Kirsher wrqu->data.length = ev - extra; 7528df158acSJeff Kirsher wrqu->data.flags = 0; 7538df158acSJeff Kirsher out: 7548df158acSJeff Kirsher mutex_unlock(&wl->scan_lock); 7558df158acSJeff Kirsher pr_debug("%s: -> %d %d\n", __func__, ret, wrqu->data.length); 7568df158acSJeff Kirsher return ret; 7578df158acSJeff Kirsher } 7588df158acSJeff Kirsher 7598df158acSJeff Kirsher #ifdef DEBUG 7608df158acSJeff Kirsher static void scan_list_dump(struct gelic_wl_info *wl) 7618df158acSJeff Kirsher { 7628df158acSJeff Kirsher struct gelic_wl_scan_info *scan_info; 7638df158acSJeff Kirsher int i; 7648df158acSJeff Kirsher 7658df158acSJeff Kirsher i = 0; 7668df158acSJeff Kirsher list_for_each_entry(scan_info, &wl->network_list, list) { 7678df158acSJeff Kirsher pr_debug("%s: item %d\n", __func__, i++); 7688df158acSJeff Kirsher pr_debug("valid=%d eurusindex=%d last=%lx\n", 7698df158acSJeff Kirsher scan_info->valid, scan_info->eurus_index, 7708df158acSJeff Kirsher scan_info->last_scanned); 7718df158acSJeff Kirsher pr_debug("r_len=%d r_ext_len=%d essid_len=%d\n", 7728df158acSJeff Kirsher scan_info->rate_len, scan_info->rate_ext_len, 7738df158acSJeff Kirsher scan_info->essid_len); 7748df158acSJeff Kirsher /* -- */ 7758df158acSJeff Kirsher pr_debug("bssid=%pM\n", &scan_info->hwinfo->bssid[2]); 7768df158acSJeff Kirsher pr_debug("essid=%s\n", scan_info->hwinfo->essid); 7778df158acSJeff Kirsher } 7788df158acSJeff Kirsher } 7798df158acSJeff Kirsher #endif 7808df158acSJeff Kirsher 7818df158acSJeff Kirsher static int gelic_wl_set_auth(struct net_device *netdev, 7828df158acSJeff Kirsher struct iw_request_info *info, 7838df158acSJeff Kirsher union iwreq_data *data, char *extra) 7848df158acSJeff Kirsher { 7858df158acSJeff Kirsher struct iw_param *param = &data->param; 7868df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); 7878df158acSJeff Kirsher unsigned long irqflag; 7888df158acSJeff Kirsher int ret = 0; 7898df158acSJeff Kirsher 7908df158acSJeff Kirsher pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX); 7918df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 7928df158acSJeff Kirsher switch (param->flags & IW_AUTH_INDEX) { 7938df158acSJeff Kirsher case IW_AUTH_WPA_VERSION: 7948df158acSJeff Kirsher if (param->value & IW_AUTH_WPA_VERSION_DISABLED) { 7958df158acSJeff Kirsher pr_debug("%s: NO WPA selected\n", __func__); 7968df158acSJeff Kirsher wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; 7978df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_WEP; 7988df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; 7998df158acSJeff Kirsher } 8008df158acSJeff Kirsher if (param->value & IW_AUTH_WPA_VERSION_WPA) { 8018df158acSJeff Kirsher pr_debug("%s: WPA version 1 selected\n", __func__); 8028df158acSJeff Kirsher wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA; 8038df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_TKIP; 8048df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP; 8058df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_OPEN; 8068df158acSJeff Kirsher } 8078df158acSJeff Kirsher if (param->value & IW_AUTH_WPA_VERSION_WPA2) { 8088df158acSJeff Kirsher /* 8098df158acSJeff Kirsher * As the hypervisor may not tell the cipher 8108df158acSJeff Kirsher * information of the AP if it is WPA2, 8118df158acSJeff Kirsher * you will not decide suitable cipher from 8128df158acSJeff Kirsher * its beacon. 8138df158acSJeff Kirsher * You should have knowledge about the AP's 8148df158acSJeff Kirsher * cipher information in other method prior to 8158df158acSJeff Kirsher * the association. 8168df158acSJeff Kirsher */ 8178df158acSJeff Kirsher if (!precise_ie()) 8188df158acSJeff Kirsher pr_info("%s: WPA2 may not work\n", __func__); 8198df158acSJeff Kirsher if (wpa2_capable()) { 8208df158acSJeff Kirsher wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2; 8218df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_AES; 8228df158acSJeff Kirsher wl->pairwise_cipher_method = 8238df158acSJeff Kirsher GELIC_WL_CIPHER_AES; 8248df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_OPEN; 8258df158acSJeff Kirsher } else 8268df158acSJeff Kirsher ret = -EINVAL; 8278df158acSJeff Kirsher } 8288df158acSJeff Kirsher break; 8298df158acSJeff Kirsher 8308df158acSJeff Kirsher case IW_AUTH_CIPHER_PAIRWISE: 8318df158acSJeff Kirsher if (param->value & 8328df158acSJeff Kirsher (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) { 8338df158acSJeff Kirsher pr_debug("%s: WEP selected\n", __func__); 8348df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; 8358df158acSJeff Kirsher } 8368df158acSJeff Kirsher if (param->value & IW_AUTH_CIPHER_TKIP) { 8378df158acSJeff Kirsher pr_debug("%s: TKIP selected\n", __func__); 8388df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP; 8398df158acSJeff Kirsher } 8408df158acSJeff Kirsher if (param->value & IW_AUTH_CIPHER_CCMP) { 8418df158acSJeff Kirsher pr_debug("%s: CCMP selected\n", __func__); 8428df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES; 8438df158acSJeff Kirsher } 8448df158acSJeff Kirsher if (param->value & IW_AUTH_CIPHER_NONE) { 8458df158acSJeff Kirsher pr_debug("%s: no auth selected\n", __func__); 8468df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; 8478df158acSJeff Kirsher } 8488df158acSJeff Kirsher break; 8498df158acSJeff Kirsher case IW_AUTH_CIPHER_GROUP: 8508df158acSJeff Kirsher if (param->value & 8518df158acSJeff Kirsher (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) { 8528df158acSJeff Kirsher pr_debug("%s: WEP selected\n", __func__); 8538df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_WEP; 8548df158acSJeff Kirsher } 8558df158acSJeff Kirsher if (param->value & IW_AUTH_CIPHER_TKIP) { 8568df158acSJeff Kirsher pr_debug("%s: TKIP selected\n", __func__); 8578df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_TKIP; 8588df158acSJeff Kirsher } 8598df158acSJeff Kirsher if (param->value & IW_AUTH_CIPHER_CCMP) { 8608df158acSJeff Kirsher pr_debug("%s: CCMP selected\n", __func__); 8618df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_AES; 8628df158acSJeff Kirsher } 8638df158acSJeff Kirsher if (param->value & IW_AUTH_CIPHER_NONE) { 8648df158acSJeff Kirsher pr_debug("%s: no auth selected\n", __func__); 8658df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_NONE; 8668df158acSJeff Kirsher } 8678df158acSJeff Kirsher break; 8688df158acSJeff Kirsher case IW_AUTH_80211_AUTH_ALG: 8698df158acSJeff Kirsher if (param->value & IW_AUTH_ALG_SHARED_KEY) { 8708df158acSJeff Kirsher pr_debug("%s: shared key specified\n", __func__); 8718df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_SHARED; 8728df158acSJeff Kirsher } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { 8738df158acSJeff Kirsher pr_debug("%s: open system specified\n", __func__); 8748df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_OPEN; 8758df158acSJeff Kirsher } else 8768df158acSJeff Kirsher ret = -EINVAL; 8778df158acSJeff Kirsher break; 8788df158acSJeff Kirsher 8798df158acSJeff Kirsher case IW_AUTH_WPA_ENABLED: 8808df158acSJeff Kirsher if (param->value) { 8818df158acSJeff Kirsher pr_debug("%s: WPA enabled\n", __func__); 8828df158acSJeff Kirsher wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA; 8838df158acSJeff Kirsher } else { 8848df158acSJeff Kirsher pr_debug("%s: WPA disabled\n", __func__); 8858df158acSJeff Kirsher wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; 8868df158acSJeff Kirsher } 8878df158acSJeff Kirsher break; 8888df158acSJeff Kirsher 8898df158acSJeff Kirsher case IW_AUTH_KEY_MGMT: 8908df158acSJeff Kirsher if (param->value & IW_AUTH_KEY_MGMT_PSK) 8918df158acSJeff Kirsher break; 8928df158acSJeff Kirsher /* intentionally fall through */ 8938df158acSJeff Kirsher default: 8948df158acSJeff Kirsher ret = -EOPNOTSUPP; 8958df158acSJeff Kirsher break; 8968df158acSJeff Kirsher } 8978df158acSJeff Kirsher 8988df158acSJeff Kirsher if (!ret) 8998df158acSJeff Kirsher set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); 9008df158acSJeff Kirsher 9018df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 9028df158acSJeff Kirsher pr_debug("%s: -> %d\n", __func__, ret); 9038df158acSJeff Kirsher return ret; 9048df158acSJeff Kirsher } 9058df158acSJeff Kirsher 9068df158acSJeff Kirsher static int gelic_wl_get_auth(struct net_device *netdev, 9078df158acSJeff Kirsher struct iw_request_info *info, 9088df158acSJeff Kirsher union iwreq_data *iwreq, char *extra) 9098df158acSJeff Kirsher { 9108df158acSJeff Kirsher struct iw_param *param = &iwreq->param; 9118df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_port(netdev)); 9128df158acSJeff Kirsher unsigned long irqflag; 9138df158acSJeff Kirsher int ret = 0; 9148df158acSJeff Kirsher 9158df158acSJeff Kirsher pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX); 9168df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 9178df158acSJeff Kirsher switch (param->flags & IW_AUTH_INDEX) { 9188df158acSJeff Kirsher case IW_AUTH_WPA_VERSION: 9198df158acSJeff Kirsher switch (wl->wpa_level) { 9208df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_WPA: 9218df158acSJeff Kirsher param->value |= IW_AUTH_WPA_VERSION_WPA; 9228df158acSJeff Kirsher break; 9238df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_WPA2: 9248df158acSJeff Kirsher param->value |= IW_AUTH_WPA_VERSION_WPA2; 9258df158acSJeff Kirsher break; 9268df158acSJeff Kirsher default: 9278df158acSJeff Kirsher param->value |= IW_AUTH_WPA_VERSION_DISABLED; 9288df158acSJeff Kirsher } 9298df158acSJeff Kirsher break; 9308df158acSJeff Kirsher 9318df158acSJeff Kirsher case IW_AUTH_80211_AUTH_ALG: 9328df158acSJeff Kirsher if (wl->auth_method == GELIC_EURUS_AUTH_SHARED) 9338df158acSJeff Kirsher param->value = IW_AUTH_ALG_SHARED_KEY; 9348df158acSJeff Kirsher else if (wl->auth_method == GELIC_EURUS_AUTH_OPEN) 9358df158acSJeff Kirsher param->value = IW_AUTH_ALG_OPEN_SYSTEM; 9368df158acSJeff Kirsher break; 9378df158acSJeff Kirsher 9388df158acSJeff Kirsher case IW_AUTH_WPA_ENABLED: 9398df158acSJeff Kirsher switch (wl->wpa_level) { 9408df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_WPA: 9418df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_WPA2: 9428df158acSJeff Kirsher param->value = 1; 9438df158acSJeff Kirsher break; 9448df158acSJeff Kirsher default: 9458df158acSJeff Kirsher param->value = 0; 9468df158acSJeff Kirsher break; 9478df158acSJeff Kirsher } 9488df158acSJeff Kirsher break; 9498df158acSJeff Kirsher default: 9508df158acSJeff Kirsher ret = -EOPNOTSUPP; 9518df158acSJeff Kirsher } 9528df158acSJeff Kirsher 9538df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 9548df158acSJeff Kirsher pr_debug("%s: -> %d\n", __func__, ret); 9558df158acSJeff Kirsher return ret; 9568df158acSJeff Kirsher } 9578df158acSJeff Kirsher 9588df158acSJeff Kirsher /* SIOC{S,G}IWESSID */ 9598df158acSJeff Kirsher static int gelic_wl_set_essid(struct net_device *netdev, 9608df158acSJeff Kirsher struct iw_request_info *info, 9618df158acSJeff Kirsher union iwreq_data *data, char *extra) 9628df158acSJeff Kirsher { 9638df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 9648df158acSJeff Kirsher unsigned long irqflag; 9658df158acSJeff Kirsher 9668df158acSJeff Kirsher pr_debug("%s: <- l=%d f=%d\n", __func__, 9678df158acSJeff Kirsher data->essid.length, data->essid.flags); 9688df158acSJeff Kirsher if (IW_ESSID_MAX_SIZE < data->essid.length) 9698df158acSJeff Kirsher return -EINVAL; 9708df158acSJeff Kirsher 9718df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 9728df158acSJeff Kirsher if (data->essid.flags) { 9738df158acSJeff Kirsher wl->essid_len = data->essid.length; 9748df158acSJeff Kirsher memcpy(wl->essid, extra, wl->essid_len); 9758df158acSJeff Kirsher pr_debug("%s: essid = '%s'\n", __func__, extra); 9768df158acSJeff Kirsher set_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat); 9778df158acSJeff Kirsher } else { 9788df158acSJeff Kirsher pr_debug("%s: ESSID any\n", __func__); 9798df158acSJeff Kirsher clear_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat); 9808df158acSJeff Kirsher } 9818df158acSJeff Kirsher set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); 9828df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 9838df158acSJeff Kirsher 9848df158acSJeff Kirsher 9858df158acSJeff Kirsher gelic_wl_try_associate(netdev); /* FIXME */ 9868df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 9878df158acSJeff Kirsher return 0; 9888df158acSJeff Kirsher } 9898df158acSJeff Kirsher 9908df158acSJeff Kirsher static int gelic_wl_get_essid(struct net_device *netdev, 9918df158acSJeff Kirsher struct iw_request_info *info, 9928df158acSJeff Kirsher union iwreq_data *data, char *extra) 9938df158acSJeff Kirsher { 9948df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 9958df158acSJeff Kirsher unsigned long irqflag; 9968df158acSJeff Kirsher 9978df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 9988df158acSJeff Kirsher mutex_lock(&wl->assoc_stat_lock); 9998df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 10008df158acSJeff Kirsher if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat) || 10018df158acSJeff Kirsher wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) { 10028df158acSJeff Kirsher memcpy(extra, wl->essid, wl->essid_len); 10038df158acSJeff Kirsher data->essid.length = wl->essid_len; 10048df158acSJeff Kirsher data->essid.flags = 1; 10058df158acSJeff Kirsher } else 10068df158acSJeff Kirsher data->essid.flags = 0; 10078df158acSJeff Kirsher 10088df158acSJeff Kirsher mutex_unlock(&wl->assoc_stat_lock); 10098df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 10108df158acSJeff Kirsher pr_debug("%s: -> len=%d\n", __func__, data->essid.length); 10118df158acSJeff Kirsher 10128df158acSJeff Kirsher return 0; 10138df158acSJeff Kirsher } 10148df158acSJeff Kirsher 10158df158acSJeff Kirsher /* SIO{S,G}IWENCODE */ 10168df158acSJeff Kirsher static int gelic_wl_set_encode(struct net_device *netdev, 10178df158acSJeff Kirsher struct iw_request_info *info, 10188df158acSJeff Kirsher union iwreq_data *data, char *extra) 10198df158acSJeff Kirsher { 10208df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 10218df158acSJeff Kirsher struct iw_point *enc = &data->encoding; 10228df158acSJeff Kirsher __u16 flags; 10238df158acSJeff Kirsher unsigned long irqflag; 10248df158acSJeff Kirsher int key_index, index_specified; 10258df158acSJeff Kirsher int ret = 0; 10268df158acSJeff Kirsher 10278df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 10288df158acSJeff Kirsher flags = enc->flags & IW_ENCODE_FLAGS; 10298df158acSJeff Kirsher key_index = enc->flags & IW_ENCODE_INDEX; 10308df158acSJeff Kirsher 10318df158acSJeff Kirsher pr_debug("%s: key_index = %d\n", __func__, key_index); 10328df158acSJeff Kirsher pr_debug("%s: key_len = %d\n", __func__, enc->length); 10338df158acSJeff Kirsher pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); 10348df158acSJeff Kirsher 10358df158acSJeff Kirsher if (GELIC_WEP_KEYS < key_index) 10368df158acSJeff Kirsher return -EINVAL; 10378df158acSJeff Kirsher 10388df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 10398df158acSJeff Kirsher if (key_index) { 10408df158acSJeff Kirsher index_specified = 1; 10418df158acSJeff Kirsher key_index--; 10428df158acSJeff Kirsher } else { 10438df158acSJeff Kirsher index_specified = 0; 10448df158acSJeff Kirsher key_index = wl->current_key; 10458df158acSJeff Kirsher } 10468df158acSJeff Kirsher 10478df158acSJeff Kirsher if (flags & IW_ENCODE_NOKEY) { 10488df158acSJeff Kirsher /* if just IW_ENCODE_NOKEY, change current key index */ 10498df158acSJeff Kirsher if (!flags && index_specified) { 10508df158acSJeff Kirsher wl->current_key = key_index; 10518df158acSJeff Kirsher goto done; 10528df158acSJeff Kirsher } 10538df158acSJeff Kirsher 10548df158acSJeff Kirsher if (flags & IW_ENCODE_DISABLED) { 10558df158acSJeff Kirsher if (!index_specified) { 10568df158acSJeff Kirsher /* disable encryption */ 10578df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_NONE; 10588df158acSJeff Kirsher wl->pairwise_cipher_method = 10598df158acSJeff Kirsher GELIC_WL_CIPHER_NONE; 10608df158acSJeff Kirsher /* invalidate all key */ 10618df158acSJeff Kirsher wl->key_enabled = 0; 10628df158acSJeff Kirsher } else 10638df158acSJeff Kirsher clear_bit(key_index, &wl->key_enabled); 10648df158acSJeff Kirsher } 10658df158acSJeff Kirsher 10668df158acSJeff Kirsher if (flags & IW_ENCODE_OPEN) 10678df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_OPEN; 10688df158acSJeff Kirsher if (flags & IW_ENCODE_RESTRICTED) { 10698df158acSJeff Kirsher pr_info("%s: shared key mode enabled\n", __func__); 10708df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_SHARED; 10718df158acSJeff Kirsher } 10728df158acSJeff Kirsher } else { 10738df158acSJeff Kirsher if (IW_ENCODING_TOKEN_MAX < enc->length) { 10748df158acSJeff Kirsher ret = -EINVAL; 10758df158acSJeff Kirsher goto done; 10768df158acSJeff Kirsher } 10778df158acSJeff Kirsher wl->key_len[key_index] = enc->length; 10788df158acSJeff Kirsher memcpy(wl->key[key_index], extra, enc->length); 10798df158acSJeff Kirsher set_bit(key_index, &wl->key_enabled); 10808df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP; 10818df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_WEP; 10828df158acSJeff Kirsher } 10838df158acSJeff Kirsher set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); 10848df158acSJeff Kirsher done: 10858df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 10868df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 10878df158acSJeff Kirsher return ret; 10888df158acSJeff Kirsher } 10898df158acSJeff Kirsher 10908df158acSJeff Kirsher static int gelic_wl_get_encode(struct net_device *netdev, 10918df158acSJeff Kirsher struct iw_request_info *info, 10928df158acSJeff Kirsher union iwreq_data *data, char *extra) 10938df158acSJeff Kirsher { 10948df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 10958df158acSJeff Kirsher struct iw_point *enc = &data->encoding; 10968df158acSJeff Kirsher unsigned long irqflag; 1097684d777aSzhong jiang unsigned int key_index; 10988df158acSJeff Kirsher int ret = 0; 10998df158acSJeff Kirsher 11008df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 11018df158acSJeff Kirsher key_index = enc->flags & IW_ENCODE_INDEX; 11028df158acSJeff Kirsher pr_debug("%s: flag=%#x point=%p len=%d extra=%p\n", __func__, 11038df158acSJeff Kirsher enc->flags, enc->pointer, enc->length, extra); 11048df158acSJeff Kirsher if (GELIC_WEP_KEYS < key_index) 11058df158acSJeff Kirsher return -EINVAL; 11068df158acSJeff Kirsher 11078df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 1108684d777aSzhong jiang if (key_index) 11098df158acSJeff Kirsher key_index--; 1110684d777aSzhong jiang else 11118df158acSJeff Kirsher key_index = wl->current_key; 11128df158acSJeff Kirsher 11138df158acSJeff Kirsher if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { 11148df158acSJeff Kirsher switch (wl->auth_method) { 11158df158acSJeff Kirsher case GELIC_EURUS_AUTH_OPEN: 11168df158acSJeff Kirsher enc->flags = IW_ENCODE_OPEN; 11178df158acSJeff Kirsher break; 11188df158acSJeff Kirsher case GELIC_EURUS_AUTH_SHARED: 11198df158acSJeff Kirsher enc->flags = IW_ENCODE_RESTRICTED; 11208df158acSJeff Kirsher break; 11218df158acSJeff Kirsher } 11228df158acSJeff Kirsher } else 11238df158acSJeff Kirsher enc->flags = IW_ENCODE_DISABLED; 11248df158acSJeff Kirsher 11258df158acSJeff Kirsher if (test_bit(key_index, &wl->key_enabled)) { 11268df158acSJeff Kirsher if (enc->length < wl->key_len[key_index]) { 11278df158acSJeff Kirsher ret = -EINVAL; 11288df158acSJeff Kirsher goto done; 11298df158acSJeff Kirsher } 11308df158acSJeff Kirsher enc->length = wl->key_len[key_index]; 11318df158acSJeff Kirsher memcpy(extra, wl->key[key_index], wl->key_len[key_index]); 11328df158acSJeff Kirsher } else { 11338df158acSJeff Kirsher enc->length = 0; 11348df158acSJeff Kirsher enc->flags |= IW_ENCODE_NOKEY; 11358df158acSJeff Kirsher } 11368df158acSJeff Kirsher enc->flags |= key_index + 1; 11378df158acSJeff Kirsher pr_debug("%s: -> flag=%x len=%d\n", __func__, 11388df158acSJeff Kirsher enc->flags, enc->length); 11398df158acSJeff Kirsher 11408df158acSJeff Kirsher done: 11418df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 11428df158acSJeff Kirsher return ret; 11438df158acSJeff Kirsher } 11448df158acSJeff Kirsher 11458df158acSJeff Kirsher /* SIOC{S,G}IWAP */ 11468df158acSJeff Kirsher static int gelic_wl_set_ap(struct net_device *netdev, 11478df158acSJeff Kirsher struct iw_request_info *info, 11488df158acSJeff Kirsher union iwreq_data *data, char *extra) 11498df158acSJeff Kirsher { 11508df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 11518df158acSJeff Kirsher unsigned long irqflag; 11528df158acSJeff Kirsher 11538df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 11548df158acSJeff Kirsher if (data->ap_addr.sa_family != ARPHRD_ETHER) 11558df158acSJeff Kirsher return -EINVAL; 11568df158acSJeff Kirsher 11578df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 11588df158acSJeff Kirsher if (is_valid_ether_addr(data->ap_addr.sa_data)) { 11598df158acSJeff Kirsher memcpy(wl->bssid, data->ap_addr.sa_data, 11608df158acSJeff Kirsher ETH_ALEN); 11618df158acSJeff Kirsher set_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat); 11628df158acSJeff Kirsher set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); 11638df158acSJeff Kirsher pr_debug("%s: bss=%pM\n", __func__, wl->bssid); 11648df158acSJeff Kirsher } else { 11658df158acSJeff Kirsher pr_debug("%s: clear bssid\n", __func__); 11668df158acSJeff Kirsher clear_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat); 1167c7bf7169SJoe Perches eth_zero_addr(wl->bssid); 11688df158acSJeff Kirsher } 11698df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 11708df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 11718df158acSJeff Kirsher return 0; 11728df158acSJeff Kirsher } 11738df158acSJeff Kirsher 11748df158acSJeff Kirsher static int gelic_wl_get_ap(struct net_device *netdev, 11758df158acSJeff Kirsher struct iw_request_info *info, 11768df158acSJeff Kirsher union iwreq_data *data, char *extra) 11778df158acSJeff Kirsher { 11788df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 11798df158acSJeff Kirsher unsigned long irqflag; 11808df158acSJeff Kirsher 11818df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 11828df158acSJeff Kirsher mutex_lock(&wl->assoc_stat_lock); 11838df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 11848df158acSJeff Kirsher if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) { 11858df158acSJeff Kirsher data->ap_addr.sa_family = ARPHRD_ETHER; 11868df158acSJeff Kirsher memcpy(data->ap_addr.sa_data, wl->active_bssid, 11878df158acSJeff Kirsher ETH_ALEN); 11888df158acSJeff Kirsher } else 1189c7bf7169SJoe Perches eth_zero_addr(data->ap_addr.sa_data); 11908df158acSJeff Kirsher 11918df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 11928df158acSJeff Kirsher mutex_unlock(&wl->assoc_stat_lock); 11938df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 11948df158acSJeff Kirsher return 0; 11958df158acSJeff Kirsher } 11968df158acSJeff Kirsher 11978df158acSJeff Kirsher /* SIOC{S,G}IWENCODEEXT */ 11988df158acSJeff Kirsher static int gelic_wl_set_encodeext(struct net_device *netdev, 11998df158acSJeff Kirsher struct iw_request_info *info, 12008df158acSJeff Kirsher union iwreq_data *data, char *extra) 12018df158acSJeff Kirsher { 12028df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 12038df158acSJeff Kirsher struct iw_point *enc = &data->encoding; 12048df158acSJeff Kirsher struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 12058df158acSJeff Kirsher __u16 alg; 12068df158acSJeff Kirsher __u16 flags; 12078df158acSJeff Kirsher unsigned long irqflag; 12088df158acSJeff Kirsher int key_index; 12098df158acSJeff Kirsher int ret = 0; 12108df158acSJeff Kirsher 12118df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 12128df158acSJeff Kirsher flags = enc->flags & IW_ENCODE_FLAGS; 12138df158acSJeff Kirsher alg = ext->alg; 12148df158acSJeff Kirsher key_index = enc->flags & IW_ENCODE_INDEX; 12158df158acSJeff Kirsher 12168df158acSJeff Kirsher pr_debug("%s: key_index = %d\n", __func__, key_index); 12178df158acSJeff Kirsher pr_debug("%s: key_len = %d\n", __func__, enc->length); 12188df158acSJeff Kirsher pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); 12198df158acSJeff Kirsher pr_debug("%s: ext_flag=%x\n", __func__, ext->ext_flags); 12208df158acSJeff Kirsher pr_debug("%s: ext_key_len=%x\n", __func__, ext->key_len); 12218df158acSJeff Kirsher 12228df158acSJeff Kirsher if (GELIC_WEP_KEYS < key_index) 12238df158acSJeff Kirsher return -EINVAL; 12248df158acSJeff Kirsher 12258df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 12268df158acSJeff Kirsher if (key_index) 12278df158acSJeff Kirsher key_index--; 12288df158acSJeff Kirsher else 12298df158acSJeff Kirsher key_index = wl->current_key; 12308df158acSJeff Kirsher 12318df158acSJeff Kirsher if (!enc->length && (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) { 12328df158acSJeff Kirsher /* reques to change default key index */ 12338df158acSJeff Kirsher pr_debug("%s: request to change default key to %d\n", 12348df158acSJeff Kirsher __func__, key_index); 12358df158acSJeff Kirsher wl->current_key = key_index; 12368df158acSJeff Kirsher goto done; 12378df158acSJeff Kirsher } 12388df158acSJeff Kirsher 12398df158acSJeff Kirsher if (alg == IW_ENCODE_ALG_NONE || (flags & IW_ENCODE_DISABLED)) { 12408df158acSJeff Kirsher pr_debug("%s: alg disabled\n", __func__); 12418df158acSJeff Kirsher wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; 12428df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_NONE; 12438df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; 12448df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_OPEN; /* should be open */ 12458df158acSJeff Kirsher } else if (alg == IW_ENCODE_ALG_WEP) { 12468df158acSJeff Kirsher pr_debug("%s: WEP requested\n", __func__); 12478df158acSJeff Kirsher if (flags & IW_ENCODE_OPEN) { 12488df158acSJeff Kirsher pr_debug("%s: open key mode\n", __func__); 12498df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_OPEN; 12508df158acSJeff Kirsher } 12518df158acSJeff Kirsher if (flags & IW_ENCODE_RESTRICTED) { 12528df158acSJeff Kirsher pr_debug("%s: shared key mode\n", __func__); 12538df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_SHARED; 12548df158acSJeff Kirsher } 12558df158acSJeff Kirsher if (IW_ENCODING_TOKEN_MAX < ext->key_len) { 12568df158acSJeff Kirsher pr_info("%s: key is too long %d\n", __func__, 12578df158acSJeff Kirsher ext->key_len); 12588df158acSJeff Kirsher ret = -EINVAL; 12598df158acSJeff Kirsher goto done; 12608df158acSJeff Kirsher } 12618df158acSJeff Kirsher /* OK, update the key */ 12628df158acSJeff Kirsher wl->key_len[key_index] = ext->key_len; 12638df158acSJeff Kirsher memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX); 12648df158acSJeff Kirsher memcpy(wl->key[key_index], ext->key, ext->key_len); 12658df158acSJeff Kirsher set_bit(key_index, &wl->key_enabled); 12668df158acSJeff Kirsher /* remember wep info changed */ 12678df158acSJeff Kirsher set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat); 12688df158acSJeff Kirsher } else if (alg == IW_ENCODE_ALG_PMK) { 12698df158acSJeff Kirsher if (ext->key_len != WPA_PSK_LEN) { 12708df158acSJeff Kirsher pr_err("%s: PSK length wrong %d\n", __func__, 12718df158acSJeff Kirsher ext->key_len); 12728df158acSJeff Kirsher ret = -EINVAL; 12738df158acSJeff Kirsher goto done; 12748df158acSJeff Kirsher } 12758df158acSJeff Kirsher memset(wl->psk, 0, sizeof(wl->psk)); 12768df158acSJeff Kirsher memcpy(wl->psk, ext->key, ext->key_len); 12778df158acSJeff Kirsher wl->psk_len = ext->key_len; 12788df158acSJeff Kirsher wl->psk_type = GELIC_EURUS_WPA_PSK_BIN; 12798df158acSJeff Kirsher /* remember PSK configured */ 12808df158acSJeff Kirsher set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat); 12818df158acSJeff Kirsher } 12828df158acSJeff Kirsher done: 12838df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 12848df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 12858df158acSJeff Kirsher return ret; 12868df158acSJeff Kirsher } 12878df158acSJeff Kirsher 12888df158acSJeff Kirsher static int gelic_wl_get_encodeext(struct net_device *netdev, 12898df158acSJeff Kirsher struct iw_request_info *info, 12908df158acSJeff Kirsher union iwreq_data *data, char *extra) 12918df158acSJeff Kirsher { 12928df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 12938df158acSJeff Kirsher struct iw_point *enc = &data->encoding; 12948df158acSJeff Kirsher struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; 12958df158acSJeff Kirsher unsigned long irqflag; 12968df158acSJeff Kirsher int key_index; 12978df158acSJeff Kirsher int ret = 0; 12988df158acSJeff Kirsher int max_key_len; 12998df158acSJeff Kirsher 13008df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 13018df158acSJeff Kirsher 13028df158acSJeff Kirsher max_key_len = enc->length - sizeof(struct iw_encode_ext); 13038df158acSJeff Kirsher if (max_key_len < 0) 13048df158acSJeff Kirsher return -EINVAL; 13058df158acSJeff Kirsher key_index = enc->flags & IW_ENCODE_INDEX; 13068df158acSJeff Kirsher 13078df158acSJeff Kirsher pr_debug("%s: key_index = %d\n", __func__, key_index); 13088df158acSJeff Kirsher pr_debug("%s: key_len = %d\n", __func__, enc->length); 13098df158acSJeff Kirsher pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS); 13108df158acSJeff Kirsher 13118df158acSJeff Kirsher if (GELIC_WEP_KEYS < key_index) 13128df158acSJeff Kirsher return -EINVAL; 13138df158acSJeff Kirsher 13148df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 13158df158acSJeff Kirsher if (key_index) 13168df158acSJeff Kirsher key_index--; 13178df158acSJeff Kirsher else 13188df158acSJeff Kirsher key_index = wl->current_key; 13198df158acSJeff Kirsher 13208df158acSJeff Kirsher memset(ext, 0, sizeof(struct iw_encode_ext)); 13218df158acSJeff Kirsher switch (wl->group_cipher_method) { 13228df158acSJeff Kirsher case GELIC_WL_CIPHER_WEP: 13238df158acSJeff Kirsher ext->alg = IW_ENCODE_ALG_WEP; 13248df158acSJeff Kirsher enc->flags |= IW_ENCODE_ENABLED; 13258df158acSJeff Kirsher break; 13268df158acSJeff Kirsher case GELIC_WL_CIPHER_TKIP: 13278df158acSJeff Kirsher ext->alg = IW_ENCODE_ALG_TKIP; 13288df158acSJeff Kirsher enc->flags |= IW_ENCODE_ENABLED; 13298df158acSJeff Kirsher break; 13308df158acSJeff Kirsher case GELIC_WL_CIPHER_AES: 13318df158acSJeff Kirsher ext->alg = IW_ENCODE_ALG_CCMP; 13328df158acSJeff Kirsher enc->flags |= IW_ENCODE_ENABLED; 13338df158acSJeff Kirsher break; 13348df158acSJeff Kirsher case GELIC_WL_CIPHER_NONE: 13358df158acSJeff Kirsher default: 13368df158acSJeff Kirsher ext->alg = IW_ENCODE_ALG_NONE; 13378df158acSJeff Kirsher enc->flags |= IW_ENCODE_NOKEY; 13388df158acSJeff Kirsher break; 13398df158acSJeff Kirsher } 13408df158acSJeff Kirsher 13418df158acSJeff Kirsher if (!(enc->flags & IW_ENCODE_NOKEY)) { 13428df158acSJeff Kirsher if (max_key_len < wl->key_len[key_index]) { 13438df158acSJeff Kirsher ret = -E2BIG; 13448df158acSJeff Kirsher goto out; 13458df158acSJeff Kirsher } 13468df158acSJeff Kirsher if (test_bit(key_index, &wl->key_enabled)) 13478df158acSJeff Kirsher memcpy(ext->key, wl->key[key_index], 13488df158acSJeff Kirsher wl->key_len[key_index]); 13498df158acSJeff Kirsher else 13508df158acSJeff Kirsher pr_debug("%s: disabled key requested ix=%d\n", 13518df158acSJeff Kirsher __func__, key_index); 13528df158acSJeff Kirsher } 13538df158acSJeff Kirsher out: 13548df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 13558df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 13568df158acSJeff Kirsher return ret; 13578df158acSJeff Kirsher } 13588df158acSJeff Kirsher /* SIOC{S,G}IWMODE */ 13598df158acSJeff Kirsher static int gelic_wl_set_mode(struct net_device *netdev, 13608df158acSJeff Kirsher struct iw_request_info *info, 13618df158acSJeff Kirsher union iwreq_data *data, char *extra) 13628df158acSJeff Kirsher { 13638df158acSJeff Kirsher __u32 mode = data->mode; 13648df158acSJeff Kirsher int ret; 13658df158acSJeff Kirsher 13668df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 13678df158acSJeff Kirsher if (mode == IW_MODE_INFRA) 13688df158acSJeff Kirsher ret = 0; 13698df158acSJeff Kirsher else 13708df158acSJeff Kirsher ret = -EOPNOTSUPP; 13718df158acSJeff Kirsher pr_debug("%s: -> %d\n", __func__, ret); 13728df158acSJeff Kirsher return ret; 13738df158acSJeff Kirsher } 13748df158acSJeff Kirsher 13758df158acSJeff Kirsher static int gelic_wl_get_mode(struct net_device *netdev, 13768df158acSJeff Kirsher struct iw_request_info *info, 13778df158acSJeff Kirsher union iwreq_data *data, char *extra) 13788df158acSJeff Kirsher { 13798df158acSJeff Kirsher __u32 *mode = &data->mode; 13808df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 13818df158acSJeff Kirsher *mode = IW_MODE_INFRA; 13828df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 13838df158acSJeff Kirsher return 0; 13848df158acSJeff Kirsher } 13858df158acSJeff Kirsher 13868df158acSJeff Kirsher /* SIOCGIWNICKN */ 13878df158acSJeff Kirsher static int gelic_wl_get_nick(struct net_device *net_dev, 13888df158acSJeff Kirsher struct iw_request_info *info, 13898df158acSJeff Kirsher union iwreq_data *data, char *extra) 13908df158acSJeff Kirsher { 13918df158acSJeff Kirsher strcpy(extra, "gelic_wl"); 13928df158acSJeff Kirsher data->data.length = strlen(extra); 13938df158acSJeff Kirsher data->data.flags = 1; 13948df158acSJeff Kirsher return 0; 13958df158acSJeff Kirsher } 13968df158acSJeff Kirsher 13978df158acSJeff Kirsher 13988df158acSJeff Kirsher /* --- */ 13998df158acSJeff Kirsher 14008df158acSJeff Kirsher static struct iw_statistics *gelic_wl_get_wireless_stats( 14018df158acSJeff Kirsher struct net_device *netdev) 14028df158acSJeff Kirsher { 14038df158acSJeff Kirsher 14048df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 14058df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 14068df158acSJeff Kirsher struct iw_statistics *is; 14078df158acSJeff Kirsher struct gelic_eurus_rssi_info *rssi; 14088df158acSJeff Kirsher void *buf; 14098df158acSJeff Kirsher 14108df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 14118df158acSJeff Kirsher 14128df158acSJeff Kirsher buf = (void *)__get_free_page(GFP_KERNEL); 14138df158acSJeff Kirsher if (!buf) 14148df158acSJeff Kirsher return NULL; 14158df158acSJeff Kirsher 14168df158acSJeff Kirsher is = &wl->iwstat; 14178df158acSJeff Kirsher memset(is, 0, sizeof(*is)); 14188df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_RSSI_CFG, 14198df158acSJeff Kirsher buf, sizeof(*rssi)); 14208df158acSJeff Kirsher if (cmd && !cmd->status && !cmd->cmd_status) { 14218df158acSJeff Kirsher rssi = buf; 14228df158acSJeff Kirsher is->qual.level = be16_to_cpu(rssi->rssi); 14238df158acSJeff Kirsher is->qual.updated = IW_QUAL_LEVEL_UPDATED | 14248df158acSJeff Kirsher IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; 14258df158acSJeff Kirsher } else 14268df158acSJeff Kirsher /* not associated */ 14278df158acSJeff Kirsher is->qual.updated = IW_QUAL_ALL_INVALID; 14288df158acSJeff Kirsher 14298df158acSJeff Kirsher kfree(cmd); 14308df158acSJeff Kirsher free_page((unsigned long)buf); 14318df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 14328df158acSJeff Kirsher return is; 14338df158acSJeff Kirsher } 14348df158acSJeff Kirsher 14358df158acSJeff Kirsher /* 14368df158acSJeff Kirsher * scanning helpers 14378df158acSJeff Kirsher */ 14388df158acSJeff Kirsher static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan, 14398df158acSJeff Kirsher u8 *essid, size_t essid_len) 14408df158acSJeff Kirsher { 14418df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 14428df158acSJeff Kirsher int ret = 0; 14438df158acSJeff Kirsher void *buf = NULL; 14448df158acSJeff Kirsher size_t len; 14458df158acSJeff Kirsher 14468df158acSJeff Kirsher pr_debug("%s: <- always=%d\n", __func__, always_scan); 14478df158acSJeff Kirsher if (mutex_lock_interruptible(&wl->scan_lock)) 14488df158acSJeff Kirsher return -ERESTARTSYS; 14498df158acSJeff Kirsher 14508df158acSJeff Kirsher /* 14518df158acSJeff Kirsher * If already a scan in progress, do not trigger more 14528df158acSJeff Kirsher */ 14538df158acSJeff Kirsher if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) { 14548df158acSJeff Kirsher pr_debug("%s: scanning now\n", __func__); 14558df158acSJeff Kirsher goto out; 14568df158acSJeff Kirsher } 14578df158acSJeff Kirsher 14588df158acSJeff Kirsher init_completion(&wl->scan_done); 14598df158acSJeff Kirsher /* 14608df158acSJeff Kirsher * If we have already a bss list, don't try to get new 14618df158acSJeff Kirsher * unless we are doing an ESSID scan 14628df158acSJeff Kirsher */ 14638df158acSJeff Kirsher if ((!essid_len && !always_scan) 14648df158acSJeff Kirsher && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) { 14658df158acSJeff Kirsher pr_debug("%s: already has the list\n", __func__); 14668df158acSJeff Kirsher complete(&wl->scan_done); 14678df158acSJeff Kirsher goto out; 14688df158acSJeff Kirsher } 14698df158acSJeff Kirsher 14708df158acSJeff Kirsher /* ESSID scan ? */ 14718df158acSJeff Kirsher if (essid_len && essid) { 14728df158acSJeff Kirsher buf = (void *)__get_free_page(GFP_KERNEL); 14738df158acSJeff Kirsher if (!buf) { 14748df158acSJeff Kirsher ret = -ENOMEM; 14758df158acSJeff Kirsher goto out; 14768df158acSJeff Kirsher } 14778df158acSJeff Kirsher len = IW_ESSID_MAX_SIZE; /* hypervisor always requires 32 */ 14788df158acSJeff Kirsher memset(buf, 0, len); 14798df158acSJeff Kirsher memcpy(buf, essid, essid_len); 14808df158acSJeff Kirsher pr_debug("%s: essid scan='%s'\n", __func__, (char *)buf); 14818df158acSJeff Kirsher } else 14828df158acSJeff Kirsher len = 0; 14838df158acSJeff Kirsher 14848df158acSJeff Kirsher /* 14858df158acSJeff Kirsher * issue start scan request 14868df158acSJeff Kirsher */ 14878df158acSJeff Kirsher wl->scan_stat = GELIC_WL_SCAN_STAT_SCANNING; 14888df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_START_SCAN, 14898df158acSJeff Kirsher buf, len); 14908df158acSJeff Kirsher if (!cmd || cmd->status || cmd->cmd_status) { 14918df158acSJeff Kirsher wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; 14928df158acSJeff Kirsher complete(&wl->scan_done); 14938df158acSJeff Kirsher ret = -ENOMEM; 14948df158acSJeff Kirsher goto out; 14958df158acSJeff Kirsher } 14968df158acSJeff Kirsher kfree(cmd); 14978df158acSJeff Kirsher out: 14988df158acSJeff Kirsher free_page((unsigned long)buf); 14998df158acSJeff Kirsher mutex_unlock(&wl->scan_lock); 15008df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 15018df158acSJeff Kirsher return ret; 15028df158acSJeff Kirsher } 15038df158acSJeff Kirsher 15048df158acSJeff Kirsher /* 15058df158acSJeff Kirsher * retrieve scan result from the chip (hypervisor) 15068df158acSJeff Kirsher * this function is invoked by schedule work. 15078df158acSJeff Kirsher */ 15088df158acSJeff Kirsher static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl) 15098df158acSJeff Kirsher { 15108df158acSJeff Kirsher struct gelic_eurus_cmd *cmd = NULL; 15118df158acSJeff Kirsher struct gelic_wl_scan_info *target, *tmp; 15128df158acSJeff Kirsher struct gelic_wl_scan_info *oldest = NULL; 15138df158acSJeff Kirsher struct gelic_eurus_scan_info *scan_info; 15148df158acSJeff Kirsher unsigned int scan_info_size; 15158df158acSJeff Kirsher union iwreq_data data; 15168df158acSJeff Kirsher unsigned long this_time = jiffies; 15178df158acSJeff Kirsher unsigned int data_len, i, found, r; 15188df158acSJeff Kirsher void *buf; 15198df158acSJeff Kirsher 15208df158acSJeff Kirsher pr_debug("%s:start\n", __func__); 15218df158acSJeff Kirsher mutex_lock(&wl->scan_lock); 15228df158acSJeff Kirsher 15238df158acSJeff Kirsher buf = (void *)__get_free_page(GFP_KERNEL); 15248df158acSJeff Kirsher if (!buf) { 15258df158acSJeff Kirsher pr_info("%s: scan buffer alloc failed\n", __func__); 15268df158acSJeff Kirsher goto out; 15278df158acSJeff Kirsher } 15288df158acSJeff Kirsher 15298df158acSJeff Kirsher if (wl->scan_stat != GELIC_WL_SCAN_STAT_SCANNING) { 15308df158acSJeff Kirsher /* 15318df158acSJeff Kirsher * stop() may be called while scanning, ignore result 15328df158acSJeff Kirsher */ 15338df158acSJeff Kirsher pr_debug("%s: scan complete when stat != scanning(%d)\n", 15348df158acSJeff Kirsher __func__, wl->scan_stat); 15358df158acSJeff Kirsher goto out; 15368df158acSJeff Kirsher } 15378df158acSJeff Kirsher 15388df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_SCAN, 15398df158acSJeff Kirsher buf, PAGE_SIZE); 15408df158acSJeff Kirsher if (!cmd || cmd->status || cmd->cmd_status) { 15418df158acSJeff Kirsher wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; 15428df158acSJeff Kirsher pr_info("%s:cmd failed\n", __func__); 15438df158acSJeff Kirsher kfree(cmd); 15448df158acSJeff Kirsher goto out; 15458df158acSJeff Kirsher } 15468df158acSJeff Kirsher data_len = cmd->size; 15478df158acSJeff Kirsher pr_debug("%s: data_len = %d\n", __func__, data_len); 15488df158acSJeff Kirsher kfree(cmd); 15498df158acSJeff Kirsher 15508df158acSJeff Kirsher /* OK, bss list retrieved */ 15518df158acSJeff Kirsher wl->scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST; 15528df158acSJeff Kirsher 15538df158acSJeff Kirsher /* mark all entries are old */ 15548df158acSJeff Kirsher list_for_each_entry_safe(target, tmp, &wl->network_list, list) { 15558df158acSJeff Kirsher target->valid = 0; 15568df158acSJeff Kirsher /* expire too old entries */ 15578df158acSJeff Kirsher if (time_before(target->last_scanned + wl->scan_age, 15588df158acSJeff Kirsher this_time)) { 15598df158acSJeff Kirsher kfree(target->hwinfo); 15608df158acSJeff Kirsher target->hwinfo = NULL; 15618df158acSJeff Kirsher list_move_tail(&target->list, &wl->network_free_list); 15628df158acSJeff Kirsher } 15638df158acSJeff Kirsher } 15648df158acSJeff Kirsher 15658df158acSJeff Kirsher /* put them in the network_list */ 15668df158acSJeff Kirsher for (i = 0, scan_info_size = 0, scan_info = buf; 15678df158acSJeff Kirsher scan_info_size < data_len; 15688df158acSJeff Kirsher i++, scan_info_size += be16_to_cpu(scan_info->size), 15698df158acSJeff Kirsher scan_info = (void *)scan_info + be16_to_cpu(scan_info->size)) { 15708df158acSJeff Kirsher pr_debug("%s:size=%d bssid=%pM scan_info=%p\n", __func__, 15718df158acSJeff Kirsher be16_to_cpu(scan_info->size), 15728df158acSJeff Kirsher &scan_info->bssid[2], scan_info); 15738df158acSJeff Kirsher 15748df158acSJeff Kirsher /* 15758df158acSJeff Kirsher * The wireless firmware may return invalid channel 0 and/or 15768df158acSJeff Kirsher * invalid rate if the AP emits zero length SSID ie. As this 15778df158acSJeff Kirsher * scan information is useless, ignore it 15788df158acSJeff Kirsher */ 15798df158acSJeff Kirsher if (!be16_to_cpu(scan_info->channel) || !scan_info->rate[0]) { 15808df158acSJeff Kirsher pr_debug("%s: invalid scan info\n", __func__); 15818df158acSJeff Kirsher continue; 15828df158acSJeff Kirsher } 15838df158acSJeff Kirsher 15848df158acSJeff Kirsher found = 0; 15858df158acSJeff Kirsher oldest = NULL; 15868df158acSJeff Kirsher list_for_each_entry(target, &wl->network_list, list) { 15872e42e474SJoe Perches if (ether_addr_equal(&target->hwinfo->bssid[2], 15888df158acSJeff Kirsher &scan_info->bssid[2])) { 15898df158acSJeff Kirsher found = 1; 15908df158acSJeff Kirsher pr_debug("%s: same BBS found scanned list\n", 15918df158acSJeff Kirsher __func__); 15928df158acSJeff Kirsher break; 15938df158acSJeff Kirsher } 15948df158acSJeff Kirsher if (!oldest || 15958df158acSJeff Kirsher (target->last_scanned < oldest->last_scanned)) 15968df158acSJeff Kirsher oldest = target; 15978df158acSJeff Kirsher } 15988df158acSJeff Kirsher 15998df158acSJeff Kirsher if (!found) { 16008df158acSJeff Kirsher /* not found in the list */ 16018df158acSJeff Kirsher if (list_empty(&wl->network_free_list)) { 16028df158acSJeff Kirsher /* expire oldest */ 16038df158acSJeff Kirsher target = oldest; 16048df158acSJeff Kirsher } else { 16058df158acSJeff Kirsher target = list_entry(wl->network_free_list.next, 16068df158acSJeff Kirsher struct gelic_wl_scan_info, 16078df158acSJeff Kirsher list); 16088df158acSJeff Kirsher } 16098df158acSJeff Kirsher } 16108df158acSJeff Kirsher 16118df158acSJeff Kirsher /* update the item */ 16128df158acSJeff Kirsher target->last_scanned = this_time; 16138df158acSJeff Kirsher target->valid = 1; 16148df158acSJeff Kirsher target->eurus_index = i; 16158df158acSJeff Kirsher kfree(target->hwinfo); 16165877debeSMuhammad Falak R Wani target->hwinfo = kmemdup(scan_info, 16175877debeSMuhammad Falak R Wani be16_to_cpu(scan_info->size), 16188df158acSJeff Kirsher GFP_KERNEL); 1619e404decbSJoe Perches if (!target->hwinfo) 16208df158acSJeff Kirsher continue; 1621e404decbSJoe Perches 16228df158acSJeff Kirsher /* copy hw scan info */ 16238df158acSJeff Kirsher target->essid_len = strnlen(scan_info->essid, 16248df158acSJeff Kirsher sizeof(scan_info->essid)); 16258df158acSJeff Kirsher target->rate_len = 0; 16268df158acSJeff Kirsher for (r = 0; r < 12; r++) 16278df158acSJeff Kirsher if (scan_info->rate[r]) 16288df158acSJeff Kirsher target->rate_len++; 16298df158acSJeff Kirsher if (8 < target->rate_len) 16308df158acSJeff Kirsher pr_info("%s: AP returns %d rates\n", __func__, 16318df158acSJeff Kirsher target->rate_len); 16328df158acSJeff Kirsher target->rate_ext_len = 0; 16338df158acSJeff Kirsher for (r = 0; r < 16; r++) 16348df158acSJeff Kirsher if (scan_info->ext_rate[r]) 16358df158acSJeff Kirsher target->rate_ext_len++; 16368df158acSJeff Kirsher list_move_tail(&target->list, &wl->network_list); 16378df158acSJeff Kirsher } 16388df158acSJeff Kirsher memset(&data, 0, sizeof(data)); 16398df158acSJeff Kirsher wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWSCAN, &data, 16408df158acSJeff Kirsher NULL); 16418df158acSJeff Kirsher out: 16428df158acSJeff Kirsher free_page((unsigned long)buf); 16438df158acSJeff Kirsher complete(&wl->scan_done); 16448df158acSJeff Kirsher mutex_unlock(&wl->scan_lock); 16458df158acSJeff Kirsher pr_debug("%s:end\n", __func__); 16468df158acSJeff Kirsher } 16478df158acSJeff Kirsher 16488df158acSJeff Kirsher /* 16498df158acSJeff Kirsher * Select an appropriate bss from current scan list regarding 16508df158acSJeff Kirsher * current settings from userspace. 16518df158acSJeff Kirsher * The caller must hold wl->scan_lock, 16528df158acSJeff Kirsher * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST 16538df158acSJeff Kirsher */ 16548df158acSJeff Kirsher static void update_best(struct gelic_wl_scan_info **best, 16558df158acSJeff Kirsher struct gelic_wl_scan_info *candid, 16568df158acSJeff Kirsher int *best_weight, 16578df158acSJeff Kirsher int *weight) 16588df158acSJeff Kirsher { 16598df158acSJeff Kirsher if (*best_weight < ++(*weight)) { 16608df158acSJeff Kirsher *best_weight = *weight; 16618df158acSJeff Kirsher *best = candid; 16628df158acSJeff Kirsher } 16638df158acSJeff Kirsher } 16648df158acSJeff Kirsher 16658df158acSJeff Kirsher static 16668df158acSJeff Kirsher struct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl) 16678df158acSJeff Kirsher { 16688df158acSJeff Kirsher struct gelic_wl_scan_info *scan_info; 16698df158acSJeff Kirsher struct gelic_wl_scan_info *best_bss; 16708df158acSJeff Kirsher int weight, best_weight; 16718df158acSJeff Kirsher u16 security; 16728df158acSJeff Kirsher 16738df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 16748df158acSJeff Kirsher 16758df158acSJeff Kirsher best_bss = NULL; 16768df158acSJeff Kirsher best_weight = 0; 16778df158acSJeff Kirsher 16788df158acSJeff Kirsher list_for_each_entry(scan_info, &wl->network_list, list) { 16798df158acSJeff Kirsher pr_debug("%s: station %p\n", __func__, scan_info); 16808df158acSJeff Kirsher 16818df158acSJeff Kirsher if (!scan_info->valid) { 16828df158acSJeff Kirsher pr_debug("%s: station invalid\n", __func__); 16838df158acSJeff Kirsher continue; 16848df158acSJeff Kirsher } 16858df158acSJeff Kirsher 16868df158acSJeff Kirsher /* If bss specified, check it only */ 16878df158acSJeff Kirsher if (test_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat)) { 16882e42e474SJoe Perches if (ether_addr_equal(&scan_info->hwinfo->bssid[2], 16898df158acSJeff Kirsher wl->bssid)) { 16908df158acSJeff Kirsher best_bss = scan_info; 16918df158acSJeff Kirsher pr_debug("%s: bssid matched\n", __func__); 16928df158acSJeff Kirsher break; 16938df158acSJeff Kirsher } else { 16947020637bSColin Ian King pr_debug("%s: bssid unmatched\n", __func__); 16958df158acSJeff Kirsher continue; 16968df158acSJeff Kirsher } 16978df158acSJeff Kirsher } 16988df158acSJeff Kirsher 16998df158acSJeff Kirsher weight = 0; 17008df158acSJeff Kirsher 17018df158acSJeff Kirsher /* security */ 17028df158acSJeff Kirsher security = be16_to_cpu(scan_info->hwinfo->security) & 17038df158acSJeff Kirsher GELIC_EURUS_SCAN_SEC_MASK; 17048df158acSJeff Kirsher if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) { 17058df158acSJeff Kirsher if (security == GELIC_EURUS_SCAN_SEC_WPA2) 17068df158acSJeff Kirsher update_best(&best_bss, scan_info, 17078df158acSJeff Kirsher &best_weight, &weight); 17088df158acSJeff Kirsher else 17098df158acSJeff Kirsher continue; 17108df158acSJeff Kirsher } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA) { 17118df158acSJeff Kirsher if (security == GELIC_EURUS_SCAN_SEC_WPA) 17128df158acSJeff Kirsher update_best(&best_bss, scan_info, 17138df158acSJeff Kirsher &best_weight, &weight); 17148df158acSJeff Kirsher else 17158df158acSJeff Kirsher continue; 17168df158acSJeff Kirsher } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_NONE && 17178df158acSJeff Kirsher wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { 17188df158acSJeff Kirsher if (security == GELIC_EURUS_SCAN_SEC_WEP) 17198df158acSJeff Kirsher update_best(&best_bss, scan_info, 17208df158acSJeff Kirsher &best_weight, &weight); 17218df158acSJeff Kirsher else 17228df158acSJeff Kirsher continue; 17238df158acSJeff Kirsher } 17248df158acSJeff Kirsher 17258df158acSJeff Kirsher /* If ESSID is set, check it */ 17268df158acSJeff Kirsher if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) { 17278df158acSJeff Kirsher if ((scan_info->essid_len == wl->essid_len) && 17288df158acSJeff Kirsher !strncmp(wl->essid, 17298df158acSJeff Kirsher scan_info->hwinfo->essid, 17308df158acSJeff Kirsher scan_info->essid_len)) 17318df158acSJeff Kirsher update_best(&best_bss, scan_info, 17328df158acSJeff Kirsher &best_weight, &weight); 17338df158acSJeff Kirsher else 17348df158acSJeff Kirsher continue; 17358df158acSJeff Kirsher } 17368df158acSJeff Kirsher } 17378df158acSJeff Kirsher 17388df158acSJeff Kirsher #ifdef DEBUG 17398df158acSJeff Kirsher pr_debug("%s: -> bss=%p\n", __func__, best_bss); 17408df158acSJeff Kirsher if (best_bss) { 17418df158acSJeff Kirsher pr_debug("%s:addr=%pM\n", __func__, 17428df158acSJeff Kirsher &best_bss->hwinfo->bssid[2]); 17438df158acSJeff Kirsher } 17448df158acSJeff Kirsher #endif 17458df158acSJeff Kirsher return best_bss; 17468df158acSJeff Kirsher } 17478df158acSJeff Kirsher 17488df158acSJeff Kirsher /* 17498df158acSJeff Kirsher * Setup WEP configuration to the chip 17508df158acSJeff Kirsher * The caller must hold wl->scan_lock, 17518df158acSJeff Kirsher * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST 17528df158acSJeff Kirsher */ 17538df158acSJeff Kirsher static int gelic_wl_do_wep_setup(struct gelic_wl_info *wl) 17548df158acSJeff Kirsher { 17558df158acSJeff Kirsher unsigned int i; 17568df158acSJeff Kirsher struct gelic_eurus_wep_cfg *wep; 17578df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 17588df158acSJeff Kirsher int wep104 = 0; 17598df158acSJeff Kirsher int have_key = 0; 17608df158acSJeff Kirsher int ret = 0; 17618df158acSJeff Kirsher 17628df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 17638df158acSJeff Kirsher /* we can assume no one should uses the buffer */ 17648df158acSJeff Kirsher wep = (struct gelic_eurus_wep_cfg *)__get_free_page(GFP_KERNEL); 17658df158acSJeff Kirsher if (!wep) 17668df158acSJeff Kirsher return -ENOMEM; 17678df158acSJeff Kirsher 17688df158acSJeff Kirsher memset(wep, 0, sizeof(*wep)); 17698df158acSJeff Kirsher 17708df158acSJeff Kirsher if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { 17718df158acSJeff Kirsher pr_debug("%s: WEP mode\n", __func__); 17728df158acSJeff Kirsher for (i = 0; i < GELIC_WEP_KEYS; i++) { 17738df158acSJeff Kirsher if (!test_bit(i, &wl->key_enabled)) 17748df158acSJeff Kirsher continue; 17758df158acSJeff Kirsher 17768df158acSJeff Kirsher pr_debug("%s: key#%d enabled\n", __func__, i); 17778df158acSJeff Kirsher have_key = 1; 17788df158acSJeff Kirsher if (wl->key_len[i] == 13) 17798df158acSJeff Kirsher wep104 = 1; 17808df158acSJeff Kirsher else if (wl->key_len[i] != 5) { 17818df158acSJeff Kirsher pr_info("%s: wrong wep key[%d]=%d\n", 17828df158acSJeff Kirsher __func__, i, wl->key_len[i]); 17838df158acSJeff Kirsher ret = -EINVAL; 17848df158acSJeff Kirsher goto out; 17858df158acSJeff Kirsher } 17868df158acSJeff Kirsher memcpy(wep->key[i], wl->key[i], wl->key_len[i]); 17878df158acSJeff Kirsher } 17888df158acSJeff Kirsher 17898df158acSJeff Kirsher if (!have_key) { 17908df158acSJeff Kirsher pr_info("%s: all wep key disabled\n", __func__); 17918df158acSJeff Kirsher ret = -EINVAL; 17928df158acSJeff Kirsher goto out; 17938df158acSJeff Kirsher } 17948df158acSJeff Kirsher 17958df158acSJeff Kirsher if (wep104) { 17968df158acSJeff Kirsher pr_debug("%s: 104bit key\n", __func__); 17978df158acSJeff Kirsher wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_104BIT); 17988df158acSJeff Kirsher } else { 17998df158acSJeff Kirsher pr_debug("%s: 40bit key\n", __func__); 18008df158acSJeff Kirsher wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_40BIT); 18018df158acSJeff Kirsher } 18028df158acSJeff Kirsher } else { 18038df158acSJeff Kirsher pr_debug("%s: NO encryption\n", __func__); 18048df158acSJeff Kirsher wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_NONE); 18058df158acSJeff Kirsher } 18068df158acSJeff Kirsher 18078df158acSJeff Kirsher /* issue wep setup */ 18088df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WEP_CFG, 18098df158acSJeff Kirsher wep, sizeof(*wep)); 18108df158acSJeff Kirsher if (!cmd) 18118df158acSJeff Kirsher ret = -ENOMEM; 18128df158acSJeff Kirsher else if (cmd->status || cmd->cmd_status) 18138df158acSJeff Kirsher ret = -ENXIO; 18148df158acSJeff Kirsher 18158df158acSJeff Kirsher kfree(cmd); 18168df158acSJeff Kirsher out: 18178df158acSJeff Kirsher free_page((unsigned long)wep); 18188df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 18198df158acSJeff Kirsher return ret; 18208df158acSJeff Kirsher } 18218df158acSJeff Kirsher 18228df158acSJeff Kirsher #ifdef DEBUG 18238df158acSJeff Kirsher static const char *wpasecstr(enum gelic_eurus_wpa_security sec) 18248df158acSJeff Kirsher { 18258df158acSJeff Kirsher switch (sec) { 18268df158acSJeff Kirsher case GELIC_EURUS_WPA_SEC_NONE: 18278df158acSJeff Kirsher return "NONE"; 18288df158acSJeff Kirsher case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP: 18298df158acSJeff Kirsher return "WPA_TKIP_TKIP"; 18308df158acSJeff Kirsher case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES: 18318df158acSJeff Kirsher return "WPA_TKIP_AES"; 18328df158acSJeff Kirsher case GELIC_EURUS_WPA_SEC_WPA_AES_AES: 18338df158acSJeff Kirsher return "WPA_AES_AES"; 18348df158acSJeff Kirsher case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP: 18358df158acSJeff Kirsher return "WPA2_TKIP_TKIP"; 18368df158acSJeff Kirsher case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES: 18378df158acSJeff Kirsher return "WPA2_TKIP_AES"; 18388df158acSJeff Kirsher case GELIC_EURUS_WPA_SEC_WPA2_AES_AES: 18398df158acSJeff Kirsher return "WPA2_AES_AES"; 18408df158acSJeff Kirsher } 18418df158acSJeff Kirsher return ""; 18428df158acSJeff Kirsher }; 18438df158acSJeff Kirsher #endif 18448df158acSJeff Kirsher 18458df158acSJeff Kirsher static int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl) 18468df158acSJeff Kirsher { 18478df158acSJeff Kirsher struct gelic_eurus_wpa_cfg *wpa; 18488df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 18498df158acSJeff Kirsher u16 security; 18508df158acSJeff Kirsher int ret = 0; 18518df158acSJeff Kirsher 18528df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 18538df158acSJeff Kirsher /* we can assume no one should uses the buffer */ 18548df158acSJeff Kirsher wpa = (struct gelic_eurus_wpa_cfg *)__get_free_page(GFP_KERNEL); 18558df158acSJeff Kirsher if (!wpa) 18568df158acSJeff Kirsher return -ENOMEM; 18578df158acSJeff Kirsher 18588df158acSJeff Kirsher memset(wpa, 0, sizeof(*wpa)); 18598df158acSJeff Kirsher 18608df158acSJeff Kirsher if (!test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat)) 18618df158acSJeff Kirsher pr_info("%s: PSK not configured yet\n", __func__); 18628df158acSJeff Kirsher 18638df158acSJeff Kirsher /* copy key */ 18648df158acSJeff Kirsher memcpy(wpa->psk, wl->psk, wl->psk_len); 18658df158acSJeff Kirsher 18668df158acSJeff Kirsher /* set security level */ 18678df158acSJeff Kirsher if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) { 18688df158acSJeff Kirsher if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) { 18698df158acSJeff Kirsher security = GELIC_EURUS_WPA_SEC_WPA2_AES_AES; 18708df158acSJeff Kirsher } else { 18718df158acSJeff Kirsher if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES && 18728df158acSJeff Kirsher precise_ie()) 18738df158acSJeff Kirsher security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES; 18748df158acSJeff Kirsher else 18758df158acSJeff Kirsher security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP; 18768df158acSJeff Kirsher } 18778df158acSJeff Kirsher } else { 18788df158acSJeff Kirsher if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) { 18798df158acSJeff Kirsher security = GELIC_EURUS_WPA_SEC_WPA_AES_AES; 18808df158acSJeff Kirsher } else { 18818df158acSJeff Kirsher if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES && 18828df158acSJeff Kirsher precise_ie()) 18838df158acSJeff Kirsher security = GELIC_EURUS_WPA_SEC_WPA_TKIP_AES; 18848df158acSJeff Kirsher else 18858df158acSJeff Kirsher security = GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP; 18868df158acSJeff Kirsher } 18878df158acSJeff Kirsher } 18888df158acSJeff Kirsher wpa->security = cpu_to_be16(security); 18898df158acSJeff Kirsher 18908df158acSJeff Kirsher /* PSK type */ 18918df158acSJeff Kirsher wpa->psk_type = cpu_to_be16(wl->psk_type); 18928df158acSJeff Kirsher #ifdef DEBUG 18938df158acSJeff Kirsher pr_debug("%s: sec=%s psktype=%s\n", __func__, 18948df158acSJeff Kirsher wpasecstr(wpa->security), 18958df158acSJeff Kirsher (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ? 18968df158acSJeff Kirsher "BIN" : "passphrase"); 18978df158acSJeff Kirsher #if 0 18988df158acSJeff Kirsher /* 18998df158acSJeff Kirsher * don't enable here if you plan to submit 19008df158acSJeff Kirsher * the debug log because this dumps your precious 19018df158acSJeff Kirsher * passphrase/key. 19028df158acSJeff Kirsher */ 19038df158acSJeff Kirsher pr_debug("%s: psk=%s\n", __func__, 19048df158acSJeff Kirsher (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ? 19058df158acSJeff Kirsher "N/A" : wpa->psk); 19068df158acSJeff Kirsher #endif 19078df158acSJeff Kirsher #endif 19088df158acSJeff Kirsher /* issue wpa setup */ 19098df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WPA_CFG, 19108df158acSJeff Kirsher wpa, sizeof(*wpa)); 19118df158acSJeff Kirsher if (!cmd) 19128df158acSJeff Kirsher ret = -ENOMEM; 19138df158acSJeff Kirsher else if (cmd->status || cmd->cmd_status) 19148df158acSJeff Kirsher ret = -ENXIO; 19158df158acSJeff Kirsher kfree(cmd); 19168df158acSJeff Kirsher free_page((unsigned long)wpa); 19178df158acSJeff Kirsher pr_debug("%s: --> %d\n", __func__, ret); 19188df158acSJeff Kirsher return ret; 19198df158acSJeff Kirsher } 19208df158acSJeff Kirsher 19218df158acSJeff Kirsher /* 19228df158acSJeff Kirsher * Start association. caller must hold assoc_stat_lock 19238df158acSJeff Kirsher */ 19248df158acSJeff Kirsher static int gelic_wl_associate_bss(struct gelic_wl_info *wl, 19258df158acSJeff Kirsher struct gelic_wl_scan_info *bss) 19268df158acSJeff Kirsher { 19278df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 19288df158acSJeff Kirsher struct gelic_eurus_common_cfg *common; 19298df158acSJeff Kirsher int ret = 0; 19308df158acSJeff Kirsher unsigned long rc; 19318df158acSJeff Kirsher 19328df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 19338df158acSJeff Kirsher 19348df158acSJeff Kirsher /* do common config */ 19358df158acSJeff Kirsher common = (struct gelic_eurus_common_cfg *)__get_free_page(GFP_KERNEL); 19368df158acSJeff Kirsher if (!common) 19378df158acSJeff Kirsher return -ENOMEM; 19388df158acSJeff Kirsher 19398df158acSJeff Kirsher memset(common, 0, sizeof(*common)); 19408df158acSJeff Kirsher common->bss_type = cpu_to_be16(GELIC_EURUS_BSS_INFRA); 19418df158acSJeff Kirsher common->op_mode = cpu_to_be16(GELIC_EURUS_OPMODE_11BG); 19428df158acSJeff Kirsher 19438df158acSJeff Kirsher common->scan_index = cpu_to_be16(bss->eurus_index); 19448df158acSJeff Kirsher switch (wl->auth_method) { 19458df158acSJeff Kirsher case GELIC_EURUS_AUTH_OPEN: 19468df158acSJeff Kirsher common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_OPEN); 19478df158acSJeff Kirsher break; 19488df158acSJeff Kirsher case GELIC_EURUS_AUTH_SHARED: 19498df158acSJeff Kirsher common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_SHARED); 19508df158acSJeff Kirsher break; 19518df158acSJeff Kirsher } 19528df158acSJeff Kirsher 19538df158acSJeff Kirsher #ifdef DEBUG 19548df158acSJeff Kirsher scan_list_dump(wl); 19558df158acSJeff Kirsher #endif 19568df158acSJeff Kirsher pr_debug("%s: common cfg index=%d bsstype=%d auth=%d\n", __func__, 19578df158acSJeff Kirsher be16_to_cpu(common->scan_index), 19588df158acSJeff Kirsher be16_to_cpu(common->bss_type), 19598df158acSJeff Kirsher be16_to_cpu(common->auth_method)); 19608df158acSJeff Kirsher 19618df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_COMMON_CFG, 19628df158acSJeff Kirsher common, sizeof(*common)); 19638df158acSJeff Kirsher if (!cmd || cmd->status || cmd->cmd_status) { 19648df158acSJeff Kirsher ret = -ENOMEM; 19658df158acSJeff Kirsher kfree(cmd); 19668df158acSJeff Kirsher goto out; 19678df158acSJeff Kirsher } 19688df158acSJeff Kirsher kfree(cmd); 19698df158acSJeff Kirsher 19708df158acSJeff Kirsher /* WEP/WPA */ 19718df158acSJeff Kirsher switch (wl->wpa_level) { 19728df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_NONE: 19738df158acSJeff Kirsher /* If WEP or no security, setup WEP config */ 19748df158acSJeff Kirsher ret = gelic_wl_do_wep_setup(wl); 19758df158acSJeff Kirsher break; 19768df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_WPA: 19778df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_WPA2: 19788df158acSJeff Kirsher ret = gelic_wl_do_wpa_setup(wl); 19798df158acSJeff Kirsher break; 19808df158acSJeff Kirsher } 19818df158acSJeff Kirsher 19828df158acSJeff Kirsher if (ret) { 19838df158acSJeff Kirsher pr_debug("%s: WEP/WPA setup failed %d\n", __func__, 19848df158acSJeff Kirsher ret); 19858df158acSJeff Kirsher ret = -EPERM; 19868df158acSJeff Kirsher gelic_wl_send_iwap_event(wl, NULL); 19878df158acSJeff Kirsher goto out; 19888df158acSJeff Kirsher } 19898df158acSJeff Kirsher 19908df158acSJeff Kirsher /* start association */ 19918df158acSJeff Kirsher init_completion(&wl->assoc_done); 19928df158acSJeff Kirsher wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATING; 19938df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_ASSOC, 19948df158acSJeff Kirsher NULL, 0); 19958df158acSJeff Kirsher if (!cmd || cmd->status || cmd->cmd_status) { 19968df158acSJeff Kirsher pr_debug("%s: assoc request failed\n", __func__); 19978df158acSJeff Kirsher wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; 19988df158acSJeff Kirsher kfree(cmd); 19998df158acSJeff Kirsher ret = -ENOMEM; 20008df158acSJeff Kirsher gelic_wl_send_iwap_event(wl, NULL); 20018df158acSJeff Kirsher goto out; 20028df158acSJeff Kirsher } 20038df158acSJeff Kirsher kfree(cmd); 20048df158acSJeff Kirsher 20058df158acSJeff Kirsher /* wait for connected event */ 20068df158acSJeff Kirsher rc = wait_for_completion_timeout(&wl->assoc_done, HZ * 4);/*FIXME*/ 20078df158acSJeff Kirsher 20088df158acSJeff Kirsher if (!rc) { 20098df158acSJeff Kirsher /* timeouted. Maybe key or cyrpt mode is wrong */ 20108df158acSJeff Kirsher pr_info("%s: connect timeout\n", __func__); 20118df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, 20128df158acSJeff Kirsher NULL, 0); 20138df158acSJeff Kirsher kfree(cmd); 20148df158acSJeff Kirsher wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; 20158df158acSJeff Kirsher gelic_wl_send_iwap_event(wl, NULL); 20168df158acSJeff Kirsher ret = -ENXIO; 20178df158acSJeff Kirsher } else { 20188df158acSJeff Kirsher wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATED; 20198df158acSJeff Kirsher /* copy bssid */ 20208df158acSJeff Kirsher memcpy(wl->active_bssid, &bss->hwinfo->bssid[2], ETH_ALEN); 20218df158acSJeff Kirsher 20228df158acSJeff Kirsher /* send connect event */ 20238df158acSJeff Kirsher gelic_wl_send_iwap_event(wl, wl->active_bssid); 20248df158acSJeff Kirsher pr_info("%s: connected\n", __func__); 20258df158acSJeff Kirsher } 20268df158acSJeff Kirsher out: 20278df158acSJeff Kirsher free_page((unsigned long)common); 20288df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 20298df158acSJeff Kirsher return ret; 20308df158acSJeff Kirsher } 20318df158acSJeff Kirsher 20328df158acSJeff Kirsher /* 20338df158acSJeff Kirsher * connected event 20348df158acSJeff Kirsher */ 20358df158acSJeff Kirsher static void gelic_wl_connected_event(struct gelic_wl_info *wl, 20368df158acSJeff Kirsher u64 event) 20378df158acSJeff Kirsher { 20388df158acSJeff Kirsher u64 desired_event = 0; 20398df158acSJeff Kirsher 20408df158acSJeff Kirsher switch (wl->wpa_level) { 20418df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_NONE: 20428df158acSJeff Kirsher desired_event = GELIC_LV1_WL_EVENT_CONNECTED; 20438df158acSJeff Kirsher break; 20448df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_WPA: 20458df158acSJeff Kirsher case GELIC_WL_WPA_LEVEL_WPA2: 20468df158acSJeff Kirsher desired_event = GELIC_LV1_WL_EVENT_WPA_CONNECTED; 20478df158acSJeff Kirsher break; 20488df158acSJeff Kirsher } 20498df158acSJeff Kirsher 20508df158acSJeff Kirsher if (desired_event == event) { 20518df158acSJeff Kirsher pr_debug("%s: completed\n", __func__); 20528df158acSJeff Kirsher complete(&wl->assoc_done); 20538df158acSJeff Kirsher netif_carrier_on(port_to_netdev(wl_port(wl))); 20548df158acSJeff Kirsher } else 20558df158acSJeff Kirsher pr_debug("%s: event %#llx under wpa\n", 20568df158acSJeff Kirsher __func__, event); 20578df158acSJeff Kirsher } 20588df158acSJeff Kirsher 20598df158acSJeff Kirsher /* 20608df158acSJeff Kirsher * disconnect event 20618df158acSJeff Kirsher */ 20628df158acSJeff Kirsher static void gelic_wl_disconnect_event(struct gelic_wl_info *wl, 20638df158acSJeff Kirsher u64 event) 20648df158acSJeff Kirsher { 20658df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 20668df158acSJeff Kirsher int lock; 20678df158acSJeff Kirsher 20688df158acSJeff Kirsher /* 20698df158acSJeff Kirsher * If we fall here in the middle of association, 20708df158acSJeff Kirsher * associate_bss() should be waiting for complation of 20718df158acSJeff Kirsher * wl->assoc_done. 20728df158acSJeff Kirsher * As it waits with timeout, just leave assoc_done 20738df158acSJeff Kirsher * uncompleted, then it terminates with timeout 20748df158acSJeff Kirsher */ 20758df158acSJeff Kirsher if (!mutex_trylock(&wl->assoc_stat_lock)) { 20768df158acSJeff Kirsher pr_debug("%s: already locked\n", __func__); 20778df158acSJeff Kirsher lock = 0; 20788df158acSJeff Kirsher } else { 20798df158acSJeff Kirsher pr_debug("%s: obtain lock\n", __func__); 20808df158acSJeff Kirsher lock = 1; 20818df158acSJeff Kirsher } 20828df158acSJeff Kirsher 20838df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0); 20848df158acSJeff Kirsher kfree(cmd); 20858df158acSJeff Kirsher 20868df158acSJeff Kirsher /* send disconnected event to the supplicant */ 20878df158acSJeff Kirsher if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) 20888df158acSJeff Kirsher gelic_wl_send_iwap_event(wl, NULL); 20898df158acSJeff Kirsher 20908df158acSJeff Kirsher wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; 20918df158acSJeff Kirsher netif_carrier_off(port_to_netdev(wl_port(wl))); 20928df158acSJeff Kirsher 20938df158acSJeff Kirsher if (lock) 20948df158acSJeff Kirsher mutex_unlock(&wl->assoc_stat_lock); 20958df158acSJeff Kirsher } 20968df158acSJeff Kirsher /* 20978df158acSJeff Kirsher * event worker 20988df158acSJeff Kirsher */ 20998df158acSJeff Kirsher #ifdef DEBUG 21008df158acSJeff Kirsher static const char *eventstr(enum gelic_lv1_wl_event event) 21018df158acSJeff Kirsher { 21028df158acSJeff Kirsher static char buf[32]; 21038df158acSJeff Kirsher char *ret; 21048df158acSJeff Kirsher if (event & GELIC_LV1_WL_EVENT_DEVICE_READY) 21058df158acSJeff Kirsher ret = "EURUS_READY"; 21068df158acSJeff Kirsher else if (event & GELIC_LV1_WL_EVENT_SCAN_COMPLETED) 21078df158acSJeff Kirsher ret = "SCAN_COMPLETED"; 21088df158acSJeff Kirsher else if (event & GELIC_LV1_WL_EVENT_DEAUTH) 21098df158acSJeff Kirsher ret = "DEAUTH"; 21108df158acSJeff Kirsher else if (event & GELIC_LV1_WL_EVENT_BEACON_LOST) 21118df158acSJeff Kirsher ret = "BEACON_LOST"; 21128df158acSJeff Kirsher else if (event & GELIC_LV1_WL_EVENT_CONNECTED) 21138df158acSJeff Kirsher ret = "CONNECTED"; 21148df158acSJeff Kirsher else if (event & GELIC_LV1_WL_EVENT_WPA_CONNECTED) 21158df158acSJeff Kirsher ret = "WPA_CONNECTED"; 21168df158acSJeff Kirsher else if (event & GELIC_LV1_WL_EVENT_WPA_ERROR) 21178df158acSJeff Kirsher ret = "WPA_ERROR"; 21188df158acSJeff Kirsher else { 21198df158acSJeff Kirsher sprintf(buf, "Unknown(%#x)", event); 21208df158acSJeff Kirsher ret = buf; 21218df158acSJeff Kirsher } 21228df158acSJeff Kirsher return ret; 21238df158acSJeff Kirsher } 21248df158acSJeff Kirsher #else 21258df158acSJeff Kirsher static const char *eventstr(enum gelic_lv1_wl_event event) 21268df158acSJeff Kirsher { 21278df158acSJeff Kirsher return NULL; 21288df158acSJeff Kirsher } 21298df158acSJeff Kirsher #endif 21308df158acSJeff Kirsher static void gelic_wl_event_worker(struct work_struct *work) 21318df158acSJeff Kirsher { 21328df158acSJeff Kirsher struct gelic_wl_info *wl; 21338df158acSJeff Kirsher struct gelic_port *port; 21348df158acSJeff Kirsher u64 event, tmp; 21358df158acSJeff Kirsher int status; 21368df158acSJeff Kirsher 21378df158acSJeff Kirsher pr_debug("%s:start\n", __func__); 21388df158acSJeff Kirsher wl = container_of(work, struct gelic_wl_info, event_work.work); 21398df158acSJeff Kirsher port = wl_port(wl); 21408df158acSJeff Kirsher while (1) { 21418df158acSJeff Kirsher status = lv1_net_control(bus_id(port->card), dev_id(port->card), 21428df158acSJeff Kirsher GELIC_LV1_GET_WLAN_EVENT, 0, 0, 0, 21438df158acSJeff Kirsher &event, &tmp); 21448df158acSJeff Kirsher if (status) { 21458df158acSJeff Kirsher if (status != LV1_NO_ENTRY) 21468df158acSJeff Kirsher pr_debug("%s:wlan event failed %d\n", 21478df158acSJeff Kirsher __func__, status); 21488df158acSJeff Kirsher /* got all events */ 21498df158acSJeff Kirsher pr_debug("%s:end\n", __func__); 21508df158acSJeff Kirsher return; 21518df158acSJeff Kirsher } 21528df158acSJeff Kirsher pr_debug("%s: event=%s\n", __func__, eventstr(event)); 21538df158acSJeff Kirsher switch (event) { 21548df158acSJeff Kirsher case GELIC_LV1_WL_EVENT_SCAN_COMPLETED: 21558df158acSJeff Kirsher gelic_wl_scan_complete_event(wl); 21568df158acSJeff Kirsher break; 21578df158acSJeff Kirsher case GELIC_LV1_WL_EVENT_BEACON_LOST: 21588df158acSJeff Kirsher case GELIC_LV1_WL_EVENT_DEAUTH: 21598df158acSJeff Kirsher gelic_wl_disconnect_event(wl, event); 21608df158acSJeff Kirsher break; 21618df158acSJeff Kirsher case GELIC_LV1_WL_EVENT_CONNECTED: 21628df158acSJeff Kirsher case GELIC_LV1_WL_EVENT_WPA_CONNECTED: 21638df158acSJeff Kirsher gelic_wl_connected_event(wl, event); 21648df158acSJeff Kirsher break; 21658df158acSJeff Kirsher default: 21668df158acSJeff Kirsher break; 21678df158acSJeff Kirsher } 21688df158acSJeff Kirsher } /* while */ 21698df158acSJeff Kirsher } 21708df158acSJeff Kirsher /* 21718df158acSJeff Kirsher * association worker 21728df158acSJeff Kirsher */ 21738df158acSJeff Kirsher static void gelic_wl_assoc_worker(struct work_struct *work) 21748df158acSJeff Kirsher { 21758df158acSJeff Kirsher struct gelic_wl_info *wl; 21768df158acSJeff Kirsher 21778df158acSJeff Kirsher struct gelic_wl_scan_info *best_bss; 21788df158acSJeff Kirsher int ret; 21798df158acSJeff Kirsher unsigned long irqflag; 21808df158acSJeff Kirsher u8 *essid; 21818df158acSJeff Kirsher size_t essid_len; 21828df158acSJeff Kirsher 21838df158acSJeff Kirsher wl = container_of(work, struct gelic_wl_info, assoc_work.work); 21848df158acSJeff Kirsher 21858df158acSJeff Kirsher mutex_lock(&wl->assoc_stat_lock); 21868df158acSJeff Kirsher 21878df158acSJeff Kirsher if (wl->assoc_stat != GELIC_WL_ASSOC_STAT_DISCONN) 21888df158acSJeff Kirsher goto out; 21898df158acSJeff Kirsher 21908df158acSJeff Kirsher spin_lock_irqsave(&wl->lock, irqflag); 21918df158acSJeff Kirsher if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) { 21928df158acSJeff Kirsher pr_debug("%s: assoc ESSID configured %s\n", __func__, 21938df158acSJeff Kirsher wl->essid); 21948df158acSJeff Kirsher essid = wl->essid; 21958df158acSJeff Kirsher essid_len = wl->essid_len; 21968df158acSJeff Kirsher } else { 21978df158acSJeff Kirsher essid = NULL; 21988df158acSJeff Kirsher essid_len = 0; 21998df158acSJeff Kirsher } 22008df158acSJeff Kirsher spin_unlock_irqrestore(&wl->lock, irqflag); 22018df158acSJeff Kirsher 22028df158acSJeff Kirsher ret = gelic_wl_start_scan(wl, 0, essid, essid_len); 22038df158acSJeff Kirsher if (ret == -ERESTARTSYS) { 22048df158acSJeff Kirsher pr_debug("%s: scan start failed association\n", __func__); 22058df158acSJeff Kirsher schedule_delayed_work(&wl->assoc_work, HZ/10); /*FIXME*/ 22068df158acSJeff Kirsher goto out; 22078df158acSJeff Kirsher } else if (ret) { 22088df158acSJeff Kirsher pr_info("%s: scan prerequisite failed\n", __func__); 22098df158acSJeff Kirsher goto out; 22108df158acSJeff Kirsher } 22118df158acSJeff Kirsher 22128df158acSJeff Kirsher /* 22138df158acSJeff Kirsher * Wait for bss scan completion 22148df158acSJeff Kirsher * If we have scan list already, gelic_wl_start_scan() 22158df158acSJeff Kirsher * returns OK and raises the complete. Thus, 22168df158acSJeff Kirsher * it's ok to wait unconditionally here 22178df158acSJeff Kirsher */ 22188df158acSJeff Kirsher wait_for_completion(&wl->scan_done); 22198df158acSJeff Kirsher 22208df158acSJeff Kirsher pr_debug("%s: scan done\n", __func__); 22218df158acSJeff Kirsher mutex_lock(&wl->scan_lock); 22228df158acSJeff Kirsher if (wl->scan_stat != GELIC_WL_SCAN_STAT_GOT_LIST) { 22238df158acSJeff Kirsher gelic_wl_send_iwap_event(wl, NULL); 22248df158acSJeff Kirsher pr_info("%s: no scan list. association failed\n", __func__); 22258df158acSJeff Kirsher goto scan_lock_out; 22268df158acSJeff Kirsher } 22278df158acSJeff Kirsher 22288df158acSJeff Kirsher /* find best matching bss */ 22298df158acSJeff Kirsher best_bss = gelic_wl_find_best_bss(wl); 22308df158acSJeff Kirsher if (!best_bss) { 22318df158acSJeff Kirsher gelic_wl_send_iwap_event(wl, NULL); 22328df158acSJeff Kirsher pr_info("%s: no bss matched. association failed\n", __func__); 22338df158acSJeff Kirsher goto scan_lock_out; 22348df158acSJeff Kirsher } 22358df158acSJeff Kirsher 22368df158acSJeff Kirsher /* ok, do association */ 22378df158acSJeff Kirsher ret = gelic_wl_associate_bss(wl, best_bss); 22388df158acSJeff Kirsher if (ret) 22398df158acSJeff Kirsher pr_info("%s: association failed %d\n", __func__, ret); 22408df158acSJeff Kirsher scan_lock_out: 22418df158acSJeff Kirsher mutex_unlock(&wl->scan_lock); 22428df158acSJeff Kirsher out: 22438df158acSJeff Kirsher mutex_unlock(&wl->assoc_stat_lock); 22448df158acSJeff Kirsher } 22458df158acSJeff Kirsher /* 22468df158acSJeff Kirsher * Interrupt handler 22478df158acSJeff Kirsher * Called from the ethernet interrupt handler 22488df158acSJeff Kirsher * Processes wireless specific virtual interrupts only 22498df158acSJeff Kirsher */ 22508df158acSJeff Kirsher void gelic_wl_interrupt(struct net_device *netdev, u64 status) 22518df158acSJeff Kirsher { 22528df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 22538df158acSJeff Kirsher 22548df158acSJeff Kirsher if (status & GELIC_CARD_WLAN_COMMAND_COMPLETED) { 22558df158acSJeff Kirsher pr_debug("%s:cmd complete\n", __func__); 22568df158acSJeff Kirsher complete(&wl->cmd_done_intr); 22578df158acSJeff Kirsher } 22588df158acSJeff Kirsher 22598df158acSJeff Kirsher if (status & GELIC_CARD_WLAN_EVENT_RECEIVED) { 22608df158acSJeff Kirsher pr_debug("%s:event received\n", __func__); 22618df158acSJeff Kirsher queue_delayed_work(wl->event_queue, &wl->event_work, 0); 22628df158acSJeff Kirsher } 22638df158acSJeff Kirsher } 22648df158acSJeff Kirsher 22658df158acSJeff Kirsher /* 22668df158acSJeff Kirsher * driver helpers 22678df158acSJeff Kirsher */ 22688df158acSJeff Kirsher static const iw_handler gelic_wl_wext_handler[] = 22698df158acSJeff Kirsher { 22708df158acSJeff Kirsher IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name), 22718df158acSJeff Kirsher IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range), 22728df158acSJeff Kirsher IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan), 22738df158acSJeff Kirsher IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan), 22748df158acSJeff Kirsher IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth), 22758df158acSJeff Kirsher IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth), 22768df158acSJeff Kirsher IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid), 22778df158acSJeff Kirsher IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid), 22788df158acSJeff Kirsher IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode), 22798df158acSJeff Kirsher IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode), 22808df158acSJeff Kirsher IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap), 22818df158acSJeff Kirsher IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap), 22828df158acSJeff Kirsher IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext), 22838df158acSJeff Kirsher IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext), 22848df158acSJeff Kirsher IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode), 22858df158acSJeff Kirsher IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode), 22868df158acSJeff Kirsher IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick), 22878df158acSJeff Kirsher }; 22888df158acSJeff Kirsher 22898df158acSJeff Kirsher static const struct iw_handler_def gelic_wl_wext_handler_def = { 22908df158acSJeff Kirsher .num_standard = ARRAY_SIZE(gelic_wl_wext_handler), 22918df158acSJeff Kirsher .standard = gelic_wl_wext_handler, 22928df158acSJeff Kirsher .get_wireless_stats = gelic_wl_get_wireless_stats, 22938df158acSJeff Kirsher }; 22948df158acSJeff Kirsher 2295fe4a57c5SBill Pemberton static struct net_device *gelic_wl_alloc(struct gelic_card *card) 22968df158acSJeff Kirsher { 22978df158acSJeff Kirsher struct net_device *netdev; 22988df158acSJeff Kirsher struct gelic_port *port; 22998df158acSJeff Kirsher struct gelic_wl_info *wl; 23008df158acSJeff Kirsher unsigned int i; 23018df158acSJeff Kirsher 23028df158acSJeff Kirsher pr_debug("%s:start\n", __func__); 23038df158acSJeff Kirsher netdev = alloc_etherdev(sizeof(struct gelic_port) + 23048df158acSJeff Kirsher sizeof(struct gelic_wl_info)); 23058df158acSJeff Kirsher pr_debug("%s: netdev =%p card=%p\n", __func__, netdev, card); 23068df158acSJeff Kirsher if (!netdev) 23078df158acSJeff Kirsher return NULL; 23088df158acSJeff Kirsher 23098df158acSJeff Kirsher strcpy(netdev->name, "wlan%d"); 23108df158acSJeff Kirsher 23118df158acSJeff Kirsher port = netdev_priv(netdev); 23128df158acSJeff Kirsher port->netdev = netdev; 23138df158acSJeff Kirsher port->card = card; 23148df158acSJeff Kirsher port->type = GELIC_PORT_WIRELESS; 23158df158acSJeff Kirsher 23168df158acSJeff Kirsher wl = port_wl(port); 23178df158acSJeff Kirsher pr_debug("%s: wl=%p port=%p\n", __func__, wl, port); 23188df158acSJeff Kirsher 23198df158acSJeff Kirsher /* allocate scan list */ 23206396bb22SKees Cook wl->networks = kcalloc(GELIC_WL_BSS_MAX_ENT, 23216396bb22SKees Cook sizeof(struct gelic_wl_scan_info), 23226396bb22SKees Cook GFP_KERNEL); 23238df158acSJeff Kirsher 23248df158acSJeff Kirsher if (!wl->networks) 23258df158acSJeff Kirsher goto fail_bss; 23268df158acSJeff Kirsher 23278df158acSJeff Kirsher wl->eurus_cmd_queue = create_singlethread_workqueue("gelic_cmd"); 23288df158acSJeff Kirsher if (!wl->eurus_cmd_queue) 23298df158acSJeff Kirsher goto fail_cmd_workqueue; 23308df158acSJeff Kirsher 23318df158acSJeff Kirsher wl->event_queue = create_singlethread_workqueue("gelic_event"); 23328df158acSJeff Kirsher if (!wl->event_queue) 23338df158acSJeff Kirsher goto fail_event_workqueue; 23348df158acSJeff Kirsher 23358df158acSJeff Kirsher INIT_LIST_HEAD(&wl->network_free_list); 23368df158acSJeff Kirsher INIT_LIST_HEAD(&wl->network_list); 23378df158acSJeff Kirsher for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++) 23388df158acSJeff Kirsher list_add_tail(&wl->networks[i].list, 23398df158acSJeff Kirsher &wl->network_free_list); 23408df158acSJeff Kirsher init_completion(&wl->cmd_done_intr); 23418df158acSJeff Kirsher 23428df158acSJeff Kirsher INIT_DELAYED_WORK(&wl->event_work, gelic_wl_event_worker); 23438df158acSJeff Kirsher INIT_DELAYED_WORK(&wl->assoc_work, gelic_wl_assoc_worker); 23448df158acSJeff Kirsher mutex_init(&wl->scan_lock); 23458df158acSJeff Kirsher mutex_init(&wl->assoc_stat_lock); 23468df158acSJeff Kirsher 23478df158acSJeff Kirsher init_completion(&wl->scan_done); 23488df158acSJeff Kirsher /* for the case that no scan request is issued and stop() is called */ 23498df158acSJeff Kirsher complete(&wl->scan_done); 23508df158acSJeff Kirsher 23518df158acSJeff Kirsher spin_lock_init(&wl->lock); 23528df158acSJeff Kirsher 23538df158acSJeff Kirsher wl->scan_age = 5*HZ; /* FIXME */ 23548df158acSJeff Kirsher 23558df158acSJeff Kirsher /* buffer for receiving scanned list etc */ 23568df158acSJeff Kirsher BUILD_BUG_ON(PAGE_SIZE < 23578df158acSJeff Kirsher sizeof(struct gelic_eurus_scan_info) * 23588df158acSJeff Kirsher GELIC_EURUS_MAX_SCAN); 23598df158acSJeff Kirsher pr_debug("%s:end\n", __func__); 23608df158acSJeff Kirsher return netdev; 23618df158acSJeff Kirsher 23628df158acSJeff Kirsher fail_event_workqueue: 23638df158acSJeff Kirsher destroy_workqueue(wl->eurus_cmd_queue); 23648df158acSJeff Kirsher fail_cmd_workqueue: 23658df158acSJeff Kirsher kfree(wl->networks); 23668df158acSJeff Kirsher fail_bss: 23678df158acSJeff Kirsher free_netdev(netdev); 23688df158acSJeff Kirsher pr_debug("%s:end error\n", __func__); 23698df158acSJeff Kirsher return NULL; 23708df158acSJeff Kirsher 23718df158acSJeff Kirsher } 23728df158acSJeff Kirsher 23738df158acSJeff Kirsher static void gelic_wl_free(struct gelic_wl_info *wl) 23748df158acSJeff Kirsher { 23758df158acSJeff Kirsher struct gelic_wl_scan_info *scan_info; 23768df158acSJeff Kirsher unsigned int i; 23778df158acSJeff Kirsher 23788df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 23798df158acSJeff Kirsher 23808df158acSJeff Kirsher pr_debug("%s: destroy queues\n", __func__); 23818df158acSJeff Kirsher destroy_workqueue(wl->eurus_cmd_queue); 23828df158acSJeff Kirsher destroy_workqueue(wl->event_queue); 23838df158acSJeff Kirsher 23848df158acSJeff Kirsher scan_info = wl->networks; 23858df158acSJeff Kirsher for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++, scan_info++) 23868df158acSJeff Kirsher kfree(scan_info->hwinfo); 23878df158acSJeff Kirsher kfree(wl->networks); 23888df158acSJeff Kirsher 23898df158acSJeff Kirsher free_netdev(port_to_netdev(wl_port(wl))); 23908df158acSJeff Kirsher 23918df158acSJeff Kirsher pr_debug("%s: ->\n", __func__); 23928df158acSJeff Kirsher } 23938df158acSJeff Kirsher 23948df158acSJeff Kirsher static int gelic_wl_try_associate(struct net_device *netdev) 23958df158acSJeff Kirsher { 23968df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(netdev_priv(netdev)); 23978df158acSJeff Kirsher int ret = -1; 23988df158acSJeff Kirsher unsigned int i; 23998df158acSJeff Kirsher 24008df158acSJeff Kirsher pr_debug("%s: <-\n", __func__); 24018df158acSJeff Kirsher 24028df158acSJeff Kirsher /* check constraits for start association */ 24038df158acSJeff Kirsher /* for no access restriction AP */ 24048df158acSJeff Kirsher if (wl->group_cipher_method == GELIC_WL_CIPHER_NONE) { 24058df158acSJeff Kirsher if (test_bit(GELIC_WL_STAT_CONFIGURED, 24068df158acSJeff Kirsher &wl->stat)) 24078df158acSJeff Kirsher goto do_associate; 24088df158acSJeff Kirsher else { 24098df158acSJeff Kirsher pr_debug("%s: no wep, not configured\n", __func__); 24108df158acSJeff Kirsher return ret; 24118df158acSJeff Kirsher } 24128df158acSJeff Kirsher } 24138df158acSJeff Kirsher 24148df158acSJeff Kirsher /* for WEP, one of four keys should be set */ 24158df158acSJeff Kirsher if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) { 24168df158acSJeff Kirsher /* one of keys set */ 24178df158acSJeff Kirsher for (i = 0; i < GELIC_WEP_KEYS; i++) { 24188df158acSJeff Kirsher if (test_bit(i, &wl->key_enabled)) 24198df158acSJeff Kirsher goto do_associate; 24208df158acSJeff Kirsher } 24218df158acSJeff Kirsher pr_debug("%s: WEP, but no key specified\n", __func__); 24228df158acSJeff Kirsher return ret; 24238df158acSJeff Kirsher } 24248df158acSJeff Kirsher 24258df158acSJeff Kirsher /* for WPA[2], psk should be set */ 24268df158acSJeff Kirsher if ((wl->group_cipher_method == GELIC_WL_CIPHER_TKIP) || 24278df158acSJeff Kirsher (wl->group_cipher_method == GELIC_WL_CIPHER_AES)) { 24288df158acSJeff Kirsher if (test_bit(GELIC_WL_STAT_WPA_PSK_SET, 24298df158acSJeff Kirsher &wl->stat)) 24308df158acSJeff Kirsher goto do_associate; 24318df158acSJeff Kirsher else { 24328df158acSJeff Kirsher pr_debug("%s: AES/TKIP, but PSK not configured\n", 24338df158acSJeff Kirsher __func__); 24348df158acSJeff Kirsher return ret; 24358df158acSJeff Kirsher } 24368df158acSJeff Kirsher } 24378df158acSJeff Kirsher 24388df158acSJeff Kirsher do_associate: 24398df158acSJeff Kirsher ret = schedule_delayed_work(&wl->assoc_work, 0); 24408df158acSJeff Kirsher pr_debug("%s: start association work %d\n", __func__, ret); 24418df158acSJeff Kirsher return ret; 24428df158acSJeff Kirsher } 24438df158acSJeff Kirsher 24448df158acSJeff Kirsher /* 24458df158acSJeff Kirsher * netdev handlers 24468df158acSJeff Kirsher */ 24478df158acSJeff Kirsher static int gelic_wl_open(struct net_device *netdev) 24488df158acSJeff Kirsher { 24498df158acSJeff Kirsher struct gelic_card *card = netdev_card(netdev); 24508df158acSJeff Kirsher 24518df158acSJeff Kirsher pr_debug("%s:->%p\n", __func__, netdev); 24528df158acSJeff Kirsher 24538df158acSJeff Kirsher gelic_card_up(card); 24548df158acSJeff Kirsher 24558df158acSJeff Kirsher /* try to associate */ 24568df158acSJeff Kirsher gelic_wl_try_associate(netdev); 24578df158acSJeff Kirsher 24588df158acSJeff Kirsher netif_start_queue(netdev); 24598df158acSJeff Kirsher 24608df158acSJeff Kirsher pr_debug("%s:<-\n", __func__); 24618df158acSJeff Kirsher return 0; 24628df158acSJeff Kirsher } 24638df158acSJeff Kirsher 24648df158acSJeff Kirsher /* 24658df158acSJeff Kirsher * reset state machine 24668df158acSJeff Kirsher */ 24678df158acSJeff Kirsher static int gelic_wl_reset_state(struct gelic_wl_info *wl) 24688df158acSJeff Kirsher { 24698df158acSJeff Kirsher struct gelic_wl_scan_info *target; 24708df158acSJeff Kirsher struct gelic_wl_scan_info *tmp; 24718df158acSJeff Kirsher 24728df158acSJeff Kirsher /* empty scan list */ 24738df158acSJeff Kirsher list_for_each_entry_safe(target, tmp, &wl->network_list, list) { 24748df158acSJeff Kirsher list_move_tail(&target->list, &wl->network_free_list); 24758df158acSJeff Kirsher } 24768df158acSJeff Kirsher wl->scan_stat = GELIC_WL_SCAN_STAT_INIT; 24778df158acSJeff Kirsher 24788df158acSJeff Kirsher /* clear configuration */ 24798df158acSJeff Kirsher wl->auth_method = GELIC_EURUS_AUTH_OPEN; 24808df158acSJeff Kirsher wl->group_cipher_method = GELIC_WL_CIPHER_NONE; 24818df158acSJeff Kirsher wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE; 24828df158acSJeff Kirsher wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE; 24838df158acSJeff Kirsher 24848df158acSJeff Kirsher wl->key_enabled = 0; 24858df158acSJeff Kirsher wl->current_key = 0; 24868df158acSJeff Kirsher 24878df158acSJeff Kirsher wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE; 24888df158acSJeff Kirsher wl->psk_len = 0; 24898df158acSJeff Kirsher 24908df158acSJeff Kirsher wl->essid_len = 0; 24918df158acSJeff Kirsher memset(wl->essid, 0, sizeof(wl->essid)); 24928df158acSJeff Kirsher memset(wl->bssid, 0, sizeof(wl->bssid)); 24938df158acSJeff Kirsher memset(wl->active_bssid, 0, sizeof(wl->active_bssid)); 24948df158acSJeff Kirsher 24958df158acSJeff Kirsher wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN; 24968df158acSJeff Kirsher 24978df158acSJeff Kirsher memset(&wl->iwstat, 0, sizeof(wl->iwstat)); 24988df158acSJeff Kirsher /* all status bit clear */ 24998df158acSJeff Kirsher wl->stat = 0; 25008df158acSJeff Kirsher return 0; 25018df158acSJeff Kirsher } 25028df158acSJeff Kirsher 25038df158acSJeff Kirsher /* 25048df158acSJeff Kirsher * Tell eurus to terminate association 25058df158acSJeff Kirsher */ 25068df158acSJeff Kirsher static void gelic_wl_disconnect(struct net_device *netdev) 25078df158acSJeff Kirsher { 25088df158acSJeff Kirsher struct gelic_port *port = netdev_priv(netdev); 25098df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(port); 25108df158acSJeff Kirsher struct gelic_eurus_cmd *cmd; 25118df158acSJeff Kirsher 25128df158acSJeff Kirsher /* 25138df158acSJeff Kirsher * If scann process is running on chip, 25148df158acSJeff Kirsher * further requests will be rejected 25158df158acSJeff Kirsher */ 25168df158acSJeff Kirsher if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) 25178df158acSJeff Kirsher wait_for_completion_timeout(&wl->scan_done, HZ); 25188df158acSJeff Kirsher 25198df158acSJeff Kirsher cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0); 25208df158acSJeff Kirsher kfree(cmd); 25218df158acSJeff Kirsher gelic_wl_send_iwap_event(wl, NULL); 25228df158acSJeff Kirsher }; 25238df158acSJeff Kirsher 25248df158acSJeff Kirsher static int gelic_wl_stop(struct net_device *netdev) 25258df158acSJeff Kirsher { 25268df158acSJeff Kirsher struct gelic_port *port = netdev_priv(netdev); 25278df158acSJeff Kirsher struct gelic_wl_info *wl = port_wl(port); 25288df158acSJeff Kirsher struct gelic_card *card = netdev_card(netdev); 25298df158acSJeff Kirsher 25308df158acSJeff Kirsher pr_debug("%s:<-\n", __func__); 25318df158acSJeff Kirsher 25328df158acSJeff Kirsher /* 25338df158acSJeff Kirsher * Cancel pending association work. 25348df158acSJeff Kirsher * event work can run after netdev down 25358df158acSJeff Kirsher */ 25368df158acSJeff Kirsher cancel_delayed_work(&wl->assoc_work); 25378df158acSJeff Kirsher 25388df158acSJeff Kirsher if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) 25398df158acSJeff Kirsher gelic_wl_disconnect(netdev); 25408df158acSJeff Kirsher 25418df158acSJeff Kirsher /* reset our state machine */ 25428df158acSJeff Kirsher gelic_wl_reset_state(wl); 25438df158acSJeff Kirsher 25448df158acSJeff Kirsher netif_stop_queue(netdev); 25458df158acSJeff Kirsher 25468df158acSJeff Kirsher gelic_card_down(card); 25478df158acSJeff Kirsher 25488df158acSJeff Kirsher pr_debug("%s:->\n", __func__); 25498df158acSJeff Kirsher return 0; 25508df158acSJeff Kirsher } 25518df158acSJeff Kirsher 25528df158acSJeff Kirsher /* -- */ 25538df158acSJeff Kirsher 25548df158acSJeff Kirsher static const struct net_device_ops gelic_wl_netdevice_ops = { 25558df158acSJeff Kirsher .ndo_open = gelic_wl_open, 25568df158acSJeff Kirsher .ndo_stop = gelic_wl_stop, 25578df158acSJeff Kirsher .ndo_start_xmit = gelic_net_xmit, 2558afc4b13dSJiri Pirko .ndo_set_rx_mode = gelic_net_set_multi, 25598df158acSJeff Kirsher .ndo_tx_timeout = gelic_net_tx_timeout, 25608df158acSJeff Kirsher .ndo_set_mac_address = eth_mac_addr, 25618df158acSJeff Kirsher .ndo_validate_addr = eth_validate_addr, 25628df158acSJeff Kirsher #ifdef CONFIG_NET_POLL_CONTROLLER 25638df158acSJeff Kirsher .ndo_poll_controller = gelic_net_poll_controller, 25648df158acSJeff Kirsher #endif 25658df158acSJeff Kirsher }; 25668df158acSJeff Kirsher 25678df158acSJeff Kirsher static const struct ethtool_ops gelic_wl_ethtool_ops = { 25688df158acSJeff Kirsher .get_drvinfo = gelic_net_get_drvinfo, 25698df158acSJeff Kirsher .get_link = gelic_wl_get_link, 25708df158acSJeff Kirsher }; 25718df158acSJeff Kirsher 2572fe4a57c5SBill Pemberton static void gelic_wl_setup_netdev_ops(struct net_device *netdev) 25738df158acSJeff Kirsher { 25748df158acSJeff Kirsher struct gelic_wl_info *wl; 25758df158acSJeff Kirsher wl = port_wl(netdev_priv(netdev)); 25768df158acSJeff Kirsher BUG_ON(!wl); 25778df158acSJeff Kirsher netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT; 25788df158acSJeff Kirsher 25798df158acSJeff Kirsher netdev->ethtool_ops = &gelic_wl_ethtool_ops; 25808df158acSJeff Kirsher netdev->netdev_ops = &gelic_wl_netdevice_ops; 25818df158acSJeff Kirsher netdev->wireless_data = &wl->wireless_data; 25828df158acSJeff Kirsher netdev->wireless_handlers = &gelic_wl_wext_handler_def; 25838df158acSJeff Kirsher } 25848df158acSJeff Kirsher 25858df158acSJeff Kirsher /* 25868df158acSJeff Kirsher * driver probe/remove 25878df158acSJeff Kirsher */ 2588fe4a57c5SBill Pemberton int gelic_wl_driver_probe(struct gelic_card *card) 25898df158acSJeff Kirsher { 25908df158acSJeff Kirsher int ret; 25918df158acSJeff Kirsher struct net_device *netdev; 25928df158acSJeff Kirsher 25938df158acSJeff Kirsher pr_debug("%s:start\n", __func__); 25948df158acSJeff Kirsher 25958df158acSJeff Kirsher if (ps3_compare_firmware_version(1, 6, 0) < 0) 25968df158acSJeff Kirsher return 0; 25978df158acSJeff Kirsher if (!card->vlan[GELIC_PORT_WIRELESS].tx) 25988df158acSJeff Kirsher return 0; 25998df158acSJeff Kirsher 26008df158acSJeff Kirsher /* alloc netdevice for wireless */ 26018df158acSJeff Kirsher netdev = gelic_wl_alloc(card); 26028df158acSJeff Kirsher if (!netdev) 26038df158acSJeff Kirsher return -ENOMEM; 26048df158acSJeff Kirsher 26058df158acSJeff Kirsher /* setup net_device structure */ 26068df158acSJeff Kirsher SET_NETDEV_DEV(netdev, &card->dev->core); 26078df158acSJeff Kirsher gelic_wl_setup_netdev_ops(netdev); 26088df158acSJeff Kirsher 26098df158acSJeff Kirsher /* setup some of net_device and register it */ 26108df158acSJeff Kirsher ret = gelic_net_setup_netdev(netdev, card); 26118df158acSJeff Kirsher if (ret) 26128df158acSJeff Kirsher goto fail_setup; 26138df158acSJeff Kirsher card->netdev[GELIC_PORT_WIRELESS] = netdev; 26148df158acSJeff Kirsher 26158df158acSJeff Kirsher /* add enable wireless interrupt */ 26168df158acSJeff Kirsher card->irq_mask |= GELIC_CARD_WLAN_EVENT_RECEIVED | 26178df158acSJeff Kirsher GELIC_CARD_WLAN_COMMAND_COMPLETED; 26188df158acSJeff Kirsher /* to allow wireless commands while both interfaces are down */ 26198df158acSJeff Kirsher gelic_card_set_irq_mask(card, GELIC_CARD_WLAN_EVENT_RECEIVED | 26208df158acSJeff Kirsher GELIC_CARD_WLAN_COMMAND_COMPLETED); 26218df158acSJeff Kirsher pr_debug("%s:end\n", __func__); 26228df158acSJeff Kirsher return 0; 26238df158acSJeff Kirsher 26248df158acSJeff Kirsher fail_setup: 26258df158acSJeff Kirsher gelic_wl_free(port_wl(netdev_port(netdev))); 26268df158acSJeff Kirsher 26278df158acSJeff Kirsher return ret; 26288df158acSJeff Kirsher } 26298df158acSJeff Kirsher 26308df158acSJeff Kirsher int gelic_wl_driver_remove(struct gelic_card *card) 26318df158acSJeff Kirsher { 26328df158acSJeff Kirsher struct gelic_wl_info *wl; 26338df158acSJeff Kirsher struct net_device *netdev; 26348df158acSJeff Kirsher 26358df158acSJeff Kirsher pr_debug("%s:start\n", __func__); 26368df158acSJeff Kirsher 26378df158acSJeff Kirsher if (ps3_compare_firmware_version(1, 6, 0) < 0) 26388df158acSJeff Kirsher return 0; 26398df158acSJeff Kirsher if (!card->vlan[GELIC_PORT_WIRELESS].tx) 26408df158acSJeff Kirsher return 0; 26418df158acSJeff Kirsher 26428df158acSJeff Kirsher netdev = card->netdev[GELIC_PORT_WIRELESS]; 26438df158acSJeff Kirsher wl = port_wl(netdev_priv(netdev)); 26448df158acSJeff Kirsher 26458df158acSJeff Kirsher /* if the interface was not up, but associated */ 26468df158acSJeff Kirsher if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) 26478df158acSJeff Kirsher gelic_wl_disconnect(netdev); 26488df158acSJeff Kirsher 26498df158acSJeff Kirsher complete(&wl->cmd_done_intr); 26508df158acSJeff Kirsher 26518df158acSJeff Kirsher /* cancel all work queue */ 26528df158acSJeff Kirsher cancel_delayed_work(&wl->assoc_work); 26538df158acSJeff Kirsher cancel_delayed_work(&wl->event_work); 26548df158acSJeff Kirsher flush_workqueue(wl->eurus_cmd_queue); 26558df158acSJeff Kirsher flush_workqueue(wl->event_queue); 26568df158acSJeff Kirsher 26578df158acSJeff Kirsher unregister_netdev(netdev); 26588df158acSJeff Kirsher 26598df158acSJeff Kirsher /* disable wireless interrupt */ 26608df158acSJeff Kirsher pr_debug("%s: disable intr\n", __func__); 26618df158acSJeff Kirsher card->irq_mask &= ~(GELIC_CARD_WLAN_EVENT_RECEIVED | 26628df158acSJeff Kirsher GELIC_CARD_WLAN_COMMAND_COMPLETED); 26638df158acSJeff Kirsher /* free bss list, netdev*/ 26648df158acSJeff Kirsher gelic_wl_free(wl); 26658df158acSJeff Kirsher pr_debug("%s:end\n", __func__); 26668df158acSJeff Kirsher return 0; 26678df158acSJeff Kirsher } 2668