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;
10978df158acSJeff Kirsher 	unsigned int key_index, index_specified;
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);
11088df158acSJeff Kirsher 	if (key_index) {
11098df158acSJeff Kirsher 		index_specified = 1;
11108df158acSJeff Kirsher 		key_index--;
11118df158acSJeff Kirsher 	} else {
11128df158acSJeff Kirsher 		index_specified = 0;
11138df158acSJeff Kirsher 		key_index = wl->current_key;
11148df158acSJeff Kirsher 	}
11158df158acSJeff Kirsher 
11168df158acSJeff Kirsher 	if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
11178df158acSJeff Kirsher 		switch (wl->auth_method) {
11188df158acSJeff Kirsher 		case GELIC_EURUS_AUTH_OPEN:
11198df158acSJeff Kirsher 			enc->flags = IW_ENCODE_OPEN;
11208df158acSJeff Kirsher 			break;
11218df158acSJeff Kirsher 		case GELIC_EURUS_AUTH_SHARED:
11228df158acSJeff Kirsher 			enc->flags = IW_ENCODE_RESTRICTED;
11238df158acSJeff Kirsher 			break;
11248df158acSJeff Kirsher 		}
11258df158acSJeff Kirsher 	} else
11268df158acSJeff Kirsher 		enc->flags = IW_ENCODE_DISABLED;
11278df158acSJeff Kirsher 
11288df158acSJeff Kirsher 	if (test_bit(key_index, &wl->key_enabled)) {
11298df158acSJeff Kirsher 		if (enc->length < wl->key_len[key_index]) {
11308df158acSJeff Kirsher 			ret = -EINVAL;
11318df158acSJeff Kirsher 			goto done;
11328df158acSJeff Kirsher 		}
11338df158acSJeff Kirsher 		enc->length = wl->key_len[key_index];
11348df158acSJeff Kirsher 		memcpy(extra, wl->key[key_index], wl->key_len[key_index]);
11358df158acSJeff Kirsher 	} else {
11368df158acSJeff Kirsher 		enc->length = 0;
11378df158acSJeff Kirsher 		enc->flags |= IW_ENCODE_NOKEY;
11388df158acSJeff Kirsher 	}
11398df158acSJeff Kirsher 	enc->flags |= key_index + 1;
11408df158acSJeff Kirsher 	pr_debug("%s: -> flag=%x len=%d\n", __func__,
11418df158acSJeff Kirsher 		 enc->flags, enc->length);
11428df158acSJeff Kirsher 
11438df158acSJeff Kirsher done:
11448df158acSJeff Kirsher 	spin_unlock_irqrestore(&wl->lock, irqflag);
11458df158acSJeff Kirsher 	return ret;
11468df158acSJeff Kirsher }
11478df158acSJeff Kirsher 
11488df158acSJeff Kirsher /* SIOC{S,G}IWAP */
11498df158acSJeff Kirsher static int gelic_wl_set_ap(struct net_device *netdev,
11508df158acSJeff Kirsher 			   struct iw_request_info *info,
11518df158acSJeff Kirsher 			   union iwreq_data *data, char *extra)
11528df158acSJeff Kirsher {
11538df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
11548df158acSJeff Kirsher 	unsigned long irqflag;
11558df158acSJeff Kirsher 
11568df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
11578df158acSJeff Kirsher 	if (data->ap_addr.sa_family != ARPHRD_ETHER)
11588df158acSJeff Kirsher 		return -EINVAL;
11598df158acSJeff Kirsher 
11608df158acSJeff Kirsher 	spin_lock_irqsave(&wl->lock, irqflag);
11618df158acSJeff Kirsher 	if (is_valid_ether_addr(data->ap_addr.sa_data)) {
11628df158acSJeff Kirsher 		memcpy(wl->bssid, data->ap_addr.sa_data,
11638df158acSJeff Kirsher 		       ETH_ALEN);
11648df158acSJeff Kirsher 		set_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
11658df158acSJeff Kirsher 		set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
11668df158acSJeff Kirsher 		pr_debug("%s: bss=%pM\n", __func__, wl->bssid);
11678df158acSJeff Kirsher 	} else {
11688df158acSJeff Kirsher 		pr_debug("%s: clear bssid\n", __func__);
11698df158acSJeff Kirsher 		clear_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
1170c7bf7169SJoe Perches 		eth_zero_addr(wl->bssid);
11718df158acSJeff Kirsher 	}
11728df158acSJeff Kirsher 	spin_unlock_irqrestore(&wl->lock, irqflag);
11738df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
11748df158acSJeff Kirsher 	return 0;
11758df158acSJeff Kirsher }
11768df158acSJeff Kirsher 
11778df158acSJeff Kirsher static int gelic_wl_get_ap(struct net_device *netdev,
11788df158acSJeff Kirsher 			   struct iw_request_info *info,
11798df158acSJeff Kirsher 			   union iwreq_data *data, char *extra)
11808df158acSJeff Kirsher {
11818df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
11828df158acSJeff Kirsher 	unsigned long irqflag;
11838df158acSJeff Kirsher 
11848df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
11858df158acSJeff Kirsher 	mutex_lock(&wl->assoc_stat_lock);
11868df158acSJeff Kirsher 	spin_lock_irqsave(&wl->lock, irqflag);
11878df158acSJeff Kirsher 	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) {
11888df158acSJeff Kirsher 		data->ap_addr.sa_family = ARPHRD_ETHER;
11898df158acSJeff Kirsher 		memcpy(data->ap_addr.sa_data, wl->active_bssid,
11908df158acSJeff Kirsher 		       ETH_ALEN);
11918df158acSJeff Kirsher 	} else
1192c7bf7169SJoe Perches 		eth_zero_addr(data->ap_addr.sa_data);
11938df158acSJeff Kirsher 
11948df158acSJeff Kirsher 	spin_unlock_irqrestore(&wl->lock, irqflag);
11958df158acSJeff Kirsher 	mutex_unlock(&wl->assoc_stat_lock);
11968df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
11978df158acSJeff Kirsher 	return 0;
11988df158acSJeff Kirsher }
11998df158acSJeff Kirsher 
12008df158acSJeff Kirsher /* SIOC{S,G}IWENCODEEXT */
12018df158acSJeff Kirsher static int gelic_wl_set_encodeext(struct net_device *netdev,
12028df158acSJeff Kirsher 				  struct iw_request_info *info,
12038df158acSJeff Kirsher 				  union iwreq_data *data, char *extra)
12048df158acSJeff Kirsher {
12058df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
12068df158acSJeff Kirsher 	struct iw_point *enc = &data->encoding;
12078df158acSJeff Kirsher 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
12088df158acSJeff Kirsher 	__u16 alg;
12098df158acSJeff Kirsher 	__u16 flags;
12108df158acSJeff Kirsher 	unsigned long irqflag;
12118df158acSJeff Kirsher 	int key_index;
12128df158acSJeff Kirsher 	int ret = 0;
12138df158acSJeff Kirsher 
12148df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
12158df158acSJeff Kirsher 	flags = enc->flags & IW_ENCODE_FLAGS;
12168df158acSJeff Kirsher 	alg = ext->alg;
12178df158acSJeff Kirsher 	key_index = enc->flags & IW_ENCODE_INDEX;
12188df158acSJeff Kirsher 
12198df158acSJeff Kirsher 	pr_debug("%s: key_index = %d\n", __func__, key_index);
12208df158acSJeff Kirsher 	pr_debug("%s: key_len = %d\n", __func__, enc->length);
12218df158acSJeff Kirsher 	pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
12228df158acSJeff Kirsher 	pr_debug("%s: ext_flag=%x\n", __func__, ext->ext_flags);
12238df158acSJeff Kirsher 	pr_debug("%s: ext_key_len=%x\n", __func__, ext->key_len);
12248df158acSJeff Kirsher 
12258df158acSJeff Kirsher 	if (GELIC_WEP_KEYS < key_index)
12268df158acSJeff Kirsher 		return -EINVAL;
12278df158acSJeff Kirsher 
12288df158acSJeff Kirsher 	spin_lock_irqsave(&wl->lock, irqflag);
12298df158acSJeff Kirsher 	if (key_index)
12308df158acSJeff Kirsher 		key_index--;
12318df158acSJeff Kirsher 	else
12328df158acSJeff Kirsher 		key_index = wl->current_key;
12338df158acSJeff Kirsher 
12348df158acSJeff Kirsher 	if (!enc->length && (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
12358df158acSJeff Kirsher 		/* reques to change default key index */
12368df158acSJeff Kirsher 		pr_debug("%s: request to change default key to %d\n",
12378df158acSJeff Kirsher 			 __func__, key_index);
12388df158acSJeff Kirsher 		wl->current_key = key_index;
12398df158acSJeff Kirsher 		goto done;
12408df158acSJeff Kirsher 	}
12418df158acSJeff Kirsher 
12428df158acSJeff Kirsher 	if (alg == IW_ENCODE_ALG_NONE || (flags & IW_ENCODE_DISABLED)) {
12438df158acSJeff Kirsher 		pr_debug("%s: alg disabled\n", __func__);
12448df158acSJeff Kirsher 		wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
12458df158acSJeff Kirsher 		wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
12468df158acSJeff Kirsher 		wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
12478df158acSJeff Kirsher 		wl->auth_method = GELIC_EURUS_AUTH_OPEN; /* should be open */
12488df158acSJeff Kirsher 	} else if (alg == IW_ENCODE_ALG_WEP) {
12498df158acSJeff Kirsher 		pr_debug("%s: WEP requested\n", __func__);
12508df158acSJeff Kirsher 		if (flags & IW_ENCODE_OPEN) {
12518df158acSJeff Kirsher 			pr_debug("%s: open key mode\n", __func__);
12528df158acSJeff Kirsher 			wl->auth_method = GELIC_EURUS_AUTH_OPEN;
12538df158acSJeff Kirsher 		}
12548df158acSJeff Kirsher 		if (flags & IW_ENCODE_RESTRICTED) {
12558df158acSJeff Kirsher 			pr_debug("%s: shared key mode\n", __func__);
12568df158acSJeff Kirsher 			wl->auth_method = GELIC_EURUS_AUTH_SHARED;
12578df158acSJeff Kirsher 		}
12588df158acSJeff Kirsher 		if (IW_ENCODING_TOKEN_MAX < ext->key_len) {
12598df158acSJeff Kirsher 			pr_info("%s: key is too long %d\n", __func__,
12608df158acSJeff Kirsher 				ext->key_len);
12618df158acSJeff Kirsher 			ret = -EINVAL;
12628df158acSJeff Kirsher 			goto done;
12638df158acSJeff Kirsher 		}
12648df158acSJeff Kirsher 		/* OK, update the key */
12658df158acSJeff Kirsher 		wl->key_len[key_index] = ext->key_len;
12668df158acSJeff Kirsher 		memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX);
12678df158acSJeff Kirsher 		memcpy(wl->key[key_index], ext->key, ext->key_len);
12688df158acSJeff Kirsher 		set_bit(key_index, &wl->key_enabled);
12698df158acSJeff Kirsher 		/* remember wep info changed */
12708df158acSJeff Kirsher 		set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
12718df158acSJeff Kirsher 	} else if (alg == IW_ENCODE_ALG_PMK) {
12728df158acSJeff Kirsher 		if (ext->key_len != WPA_PSK_LEN) {
12738df158acSJeff Kirsher 			pr_err("%s: PSK length wrong %d\n", __func__,
12748df158acSJeff Kirsher 			       ext->key_len);
12758df158acSJeff Kirsher 			ret = -EINVAL;
12768df158acSJeff Kirsher 			goto done;
12778df158acSJeff Kirsher 		}
12788df158acSJeff Kirsher 		memset(wl->psk, 0, sizeof(wl->psk));
12798df158acSJeff Kirsher 		memcpy(wl->psk, ext->key, ext->key_len);
12808df158acSJeff Kirsher 		wl->psk_len = ext->key_len;
12818df158acSJeff Kirsher 		wl->psk_type = GELIC_EURUS_WPA_PSK_BIN;
12828df158acSJeff Kirsher 		/* remember PSK configured */
12838df158acSJeff Kirsher 		set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat);
12848df158acSJeff Kirsher 	}
12858df158acSJeff Kirsher done:
12868df158acSJeff Kirsher 	spin_unlock_irqrestore(&wl->lock, irqflag);
12878df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
12888df158acSJeff Kirsher 	return ret;
12898df158acSJeff Kirsher }
12908df158acSJeff Kirsher 
12918df158acSJeff Kirsher static int gelic_wl_get_encodeext(struct net_device *netdev,
12928df158acSJeff Kirsher 				  struct iw_request_info *info,
12938df158acSJeff Kirsher 				  union iwreq_data *data, char *extra)
12948df158acSJeff Kirsher {
12958df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
12968df158acSJeff Kirsher 	struct iw_point *enc = &data->encoding;
12978df158acSJeff Kirsher 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
12988df158acSJeff Kirsher 	unsigned long irqflag;
12998df158acSJeff Kirsher 	int key_index;
13008df158acSJeff Kirsher 	int ret = 0;
13018df158acSJeff Kirsher 	int max_key_len;
13028df158acSJeff Kirsher 
13038df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
13048df158acSJeff Kirsher 
13058df158acSJeff Kirsher 	max_key_len = enc->length - sizeof(struct iw_encode_ext);
13068df158acSJeff Kirsher 	if (max_key_len < 0)
13078df158acSJeff Kirsher 		return -EINVAL;
13088df158acSJeff Kirsher 	key_index = enc->flags & IW_ENCODE_INDEX;
13098df158acSJeff Kirsher 
13108df158acSJeff Kirsher 	pr_debug("%s: key_index = %d\n", __func__, key_index);
13118df158acSJeff Kirsher 	pr_debug("%s: key_len = %d\n", __func__, enc->length);
13128df158acSJeff Kirsher 	pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
13138df158acSJeff Kirsher 
13148df158acSJeff Kirsher 	if (GELIC_WEP_KEYS < key_index)
13158df158acSJeff Kirsher 		return -EINVAL;
13168df158acSJeff Kirsher 
13178df158acSJeff Kirsher 	spin_lock_irqsave(&wl->lock, irqflag);
13188df158acSJeff Kirsher 	if (key_index)
13198df158acSJeff Kirsher 		key_index--;
13208df158acSJeff Kirsher 	else
13218df158acSJeff Kirsher 		key_index = wl->current_key;
13228df158acSJeff Kirsher 
13238df158acSJeff Kirsher 	memset(ext, 0, sizeof(struct iw_encode_ext));
13248df158acSJeff Kirsher 	switch (wl->group_cipher_method) {
13258df158acSJeff Kirsher 	case GELIC_WL_CIPHER_WEP:
13268df158acSJeff Kirsher 		ext->alg = IW_ENCODE_ALG_WEP;
13278df158acSJeff Kirsher 		enc->flags |= IW_ENCODE_ENABLED;
13288df158acSJeff Kirsher 		break;
13298df158acSJeff Kirsher 	case GELIC_WL_CIPHER_TKIP:
13308df158acSJeff Kirsher 		ext->alg = IW_ENCODE_ALG_TKIP;
13318df158acSJeff Kirsher 		enc->flags |= IW_ENCODE_ENABLED;
13328df158acSJeff Kirsher 		break;
13338df158acSJeff Kirsher 	case GELIC_WL_CIPHER_AES:
13348df158acSJeff Kirsher 		ext->alg = IW_ENCODE_ALG_CCMP;
13358df158acSJeff Kirsher 		enc->flags |= IW_ENCODE_ENABLED;
13368df158acSJeff Kirsher 		break;
13378df158acSJeff Kirsher 	case GELIC_WL_CIPHER_NONE:
13388df158acSJeff Kirsher 	default:
13398df158acSJeff Kirsher 		ext->alg = IW_ENCODE_ALG_NONE;
13408df158acSJeff Kirsher 		enc->flags |= IW_ENCODE_NOKEY;
13418df158acSJeff Kirsher 		break;
13428df158acSJeff Kirsher 	}
13438df158acSJeff Kirsher 
13448df158acSJeff Kirsher 	if (!(enc->flags & IW_ENCODE_NOKEY)) {
13458df158acSJeff Kirsher 		if (max_key_len < wl->key_len[key_index]) {
13468df158acSJeff Kirsher 			ret = -E2BIG;
13478df158acSJeff Kirsher 			goto out;
13488df158acSJeff Kirsher 		}
13498df158acSJeff Kirsher 		if (test_bit(key_index, &wl->key_enabled))
13508df158acSJeff Kirsher 			memcpy(ext->key, wl->key[key_index],
13518df158acSJeff Kirsher 			       wl->key_len[key_index]);
13528df158acSJeff Kirsher 		else
13538df158acSJeff Kirsher 			pr_debug("%s: disabled key requested ix=%d\n",
13548df158acSJeff Kirsher 				 __func__, key_index);
13558df158acSJeff Kirsher 	}
13568df158acSJeff Kirsher out:
13578df158acSJeff Kirsher 	spin_unlock_irqrestore(&wl->lock, irqflag);
13588df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
13598df158acSJeff Kirsher 	return ret;
13608df158acSJeff Kirsher }
13618df158acSJeff Kirsher /* SIOC{S,G}IWMODE */
13628df158acSJeff Kirsher static int gelic_wl_set_mode(struct net_device *netdev,
13638df158acSJeff Kirsher 			     struct iw_request_info *info,
13648df158acSJeff Kirsher 			     union iwreq_data *data, char *extra)
13658df158acSJeff Kirsher {
13668df158acSJeff Kirsher 	__u32 mode = data->mode;
13678df158acSJeff Kirsher 	int ret;
13688df158acSJeff Kirsher 
13698df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
13708df158acSJeff Kirsher 	if (mode == IW_MODE_INFRA)
13718df158acSJeff Kirsher 		ret = 0;
13728df158acSJeff Kirsher 	else
13738df158acSJeff Kirsher 		ret = -EOPNOTSUPP;
13748df158acSJeff Kirsher 	pr_debug("%s: -> %d\n", __func__, ret);
13758df158acSJeff Kirsher 	return ret;
13768df158acSJeff Kirsher }
13778df158acSJeff Kirsher 
13788df158acSJeff Kirsher static int gelic_wl_get_mode(struct net_device *netdev,
13798df158acSJeff Kirsher 			     struct iw_request_info *info,
13808df158acSJeff Kirsher 			     union iwreq_data *data, char *extra)
13818df158acSJeff Kirsher {
13828df158acSJeff Kirsher 	__u32 *mode = &data->mode;
13838df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
13848df158acSJeff Kirsher 	*mode = IW_MODE_INFRA;
13858df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
13868df158acSJeff Kirsher 	return 0;
13878df158acSJeff Kirsher }
13888df158acSJeff Kirsher 
13898df158acSJeff Kirsher /* SIOCGIWNICKN */
13908df158acSJeff Kirsher static int gelic_wl_get_nick(struct net_device *net_dev,
13918df158acSJeff Kirsher 				  struct iw_request_info *info,
13928df158acSJeff Kirsher 				  union iwreq_data *data, char *extra)
13938df158acSJeff Kirsher {
13948df158acSJeff Kirsher 	strcpy(extra, "gelic_wl");
13958df158acSJeff Kirsher 	data->data.length = strlen(extra);
13968df158acSJeff Kirsher 	data->data.flags = 1;
13978df158acSJeff Kirsher 	return 0;
13988df158acSJeff Kirsher }
13998df158acSJeff Kirsher 
14008df158acSJeff Kirsher 
14018df158acSJeff Kirsher /* --- */
14028df158acSJeff Kirsher 
14038df158acSJeff Kirsher static struct iw_statistics *gelic_wl_get_wireless_stats(
14048df158acSJeff Kirsher 	struct net_device *netdev)
14058df158acSJeff Kirsher {
14068df158acSJeff Kirsher 
14078df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
14088df158acSJeff Kirsher 	struct gelic_eurus_cmd *cmd;
14098df158acSJeff Kirsher 	struct iw_statistics *is;
14108df158acSJeff Kirsher 	struct gelic_eurus_rssi_info *rssi;
14118df158acSJeff Kirsher 	void *buf;
14128df158acSJeff Kirsher 
14138df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
14148df158acSJeff Kirsher 
14158df158acSJeff Kirsher 	buf = (void *)__get_free_page(GFP_KERNEL);
14168df158acSJeff Kirsher 	if (!buf)
14178df158acSJeff Kirsher 		return NULL;
14188df158acSJeff Kirsher 
14198df158acSJeff Kirsher 	is = &wl->iwstat;
14208df158acSJeff Kirsher 	memset(is, 0, sizeof(*is));
14218df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_RSSI_CFG,
14228df158acSJeff Kirsher 				   buf, sizeof(*rssi));
14238df158acSJeff Kirsher 	if (cmd && !cmd->status && !cmd->cmd_status) {
14248df158acSJeff Kirsher 		rssi = buf;
14258df158acSJeff Kirsher 		is->qual.level = be16_to_cpu(rssi->rssi);
14268df158acSJeff Kirsher 		is->qual.updated = IW_QUAL_LEVEL_UPDATED |
14278df158acSJeff Kirsher 			IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
14288df158acSJeff Kirsher 	} else
14298df158acSJeff Kirsher 		/* not associated */
14308df158acSJeff Kirsher 		is->qual.updated = IW_QUAL_ALL_INVALID;
14318df158acSJeff Kirsher 
14328df158acSJeff Kirsher 	kfree(cmd);
14338df158acSJeff Kirsher 	free_page((unsigned long)buf);
14348df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
14358df158acSJeff Kirsher 	return is;
14368df158acSJeff Kirsher }
14378df158acSJeff Kirsher 
14388df158acSJeff Kirsher /*
14398df158acSJeff Kirsher  *  scanning helpers
14408df158acSJeff Kirsher  */
14418df158acSJeff Kirsher static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan,
14428df158acSJeff Kirsher 			       u8 *essid, size_t essid_len)
14438df158acSJeff Kirsher {
14448df158acSJeff Kirsher 	struct gelic_eurus_cmd *cmd;
14458df158acSJeff Kirsher 	int ret = 0;
14468df158acSJeff Kirsher 	void *buf = NULL;
14478df158acSJeff Kirsher 	size_t len;
14488df158acSJeff Kirsher 
14498df158acSJeff Kirsher 	pr_debug("%s: <- always=%d\n", __func__, always_scan);
14508df158acSJeff Kirsher 	if (mutex_lock_interruptible(&wl->scan_lock))
14518df158acSJeff Kirsher 		return -ERESTARTSYS;
14528df158acSJeff Kirsher 
14538df158acSJeff Kirsher 	/*
14548df158acSJeff Kirsher 	 * If already a scan in progress, do not trigger more
14558df158acSJeff Kirsher 	 */
14568df158acSJeff Kirsher 	if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) {
14578df158acSJeff Kirsher 		pr_debug("%s: scanning now\n", __func__);
14588df158acSJeff Kirsher 		goto out;
14598df158acSJeff Kirsher 	}
14608df158acSJeff Kirsher 
14618df158acSJeff Kirsher 	init_completion(&wl->scan_done);
14628df158acSJeff Kirsher 	/*
14638df158acSJeff Kirsher 	 * If we have already a bss list, don't try to get new
14648df158acSJeff Kirsher 	 * unless we are doing an ESSID scan
14658df158acSJeff Kirsher 	 */
14668df158acSJeff Kirsher 	if ((!essid_len && !always_scan)
14678df158acSJeff Kirsher 	    && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) {
14688df158acSJeff Kirsher 		pr_debug("%s: already has the list\n", __func__);
14698df158acSJeff Kirsher 		complete(&wl->scan_done);
14708df158acSJeff Kirsher 		goto out;
14718df158acSJeff Kirsher 	}
14728df158acSJeff Kirsher 
14738df158acSJeff Kirsher 	/* ESSID scan ? */
14748df158acSJeff Kirsher 	if (essid_len && essid) {
14758df158acSJeff Kirsher 		buf = (void *)__get_free_page(GFP_KERNEL);
14768df158acSJeff Kirsher 		if (!buf) {
14778df158acSJeff Kirsher 			ret = -ENOMEM;
14788df158acSJeff Kirsher 			goto out;
14798df158acSJeff Kirsher 		}
14808df158acSJeff Kirsher 		len = IW_ESSID_MAX_SIZE; /* hypervisor always requires 32 */
14818df158acSJeff Kirsher 		memset(buf, 0, len);
14828df158acSJeff Kirsher 		memcpy(buf, essid, essid_len);
14838df158acSJeff Kirsher 		pr_debug("%s: essid scan='%s'\n", __func__, (char *)buf);
14848df158acSJeff Kirsher 	} else
14858df158acSJeff Kirsher 		len = 0;
14868df158acSJeff Kirsher 
14878df158acSJeff Kirsher 	/*
14888df158acSJeff Kirsher 	 * issue start scan request
14898df158acSJeff Kirsher 	 */
14908df158acSJeff Kirsher 	wl->scan_stat = GELIC_WL_SCAN_STAT_SCANNING;
14918df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_START_SCAN,
14928df158acSJeff Kirsher 				   buf, len);
14938df158acSJeff Kirsher 	if (!cmd || cmd->status || cmd->cmd_status) {
14948df158acSJeff Kirsher 		wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
14958df158acSJeff Kirsher 		complete(&wl->scan_done);
14968df158acSJeff Kirsher 		ret = -ENOMEM;
14978df158acSJeff Kirsher 		goto out;
14988df158acSJeff Kirsher 	}
14998df158acSJeff Kirsher 	kfree(cmd);
15008df158acSJeff Kirsher out:
15018df158acSJeff Kirsher 	free_page((unsigned long)buf);
15028df158acSJeff Kirsher 	mutex_unlock(&wl->scan_lock);
15038df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
15048df158acSJeff Kirsher 	return ret;
15058df158acSJeff Kirsher }
15068df158acSJeff Kirsher 
15078df158acSJeff Kirsher /*
15088df158acSJeff Kirsher  * retrieve scan result from the chip (hypervisor)
15098df158acSJeff Kirsher  * this function is invoked by schedule work.
15108df158acSJeff Kirsher  */
15118df158acSJeff Kirsher static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl)
15128df158acSJeff Kirsher {
15138df158acSJeff Kirsher 	struct gelic_eurus_cmd *cmd = NULL;
15148df158acSJeff Kirsher 	struct gelic_wl_scan_info *target, *tmp;
15158df158acSJeff Kirsher 	struct gelic_wl_scan_info *oldest = NULL;
15168df158acSJeff Kirsher 	struct gelic_eurus_scan_info *scan_info;
15178df158acSJeff Kirsher 	unsigned int scan_info_size;
15188df158acSJeff Kirsher 	union iwreq_data data;
15198df158acSJeff Kirsher 	unsigned long this_time = jiffies;
15208df158acSJeff Kirsher 	unsigned int data_len, i, found, r;
15218df158acSJeff Kirsher 	void *buf;
15228df158acSJeff Kirsher 
15238df158acSJeff Kirsher 	pr_debug("%s:start\n", __func__);
15248df158acSJeff Kirsher 	mutex_lock(&wl->scan_lock);
15258df158acSJeff Kirsher 
15268df158acSJeff Kirsher 	buf = (void *)__get_free_page(GFP_KERNEL);
15278df158acSJeff Kirsher 	if (!buf) {
15288df158acSJeff Kirsher 		pr_info("%s: scan buffer alloc failed\n", __func__);
15298df158acSJeff Kirsher 		goto out;
15308df158acSJeff Kirsher 	}
15318df158acSJeff Kirsher 
15328df158acSJeff Kirsher 	if (wl->scan_stat != GELIC_WL_SCAN_STAT_SCANNING) {
15338df158acSJeff Kirsher 		/*
15348df158acSJeff Kirsher 		 * stop() may be called while scanning, ignore result
15358df158acSJeff Kirsher 		 */
15368df158acSJeff Kirsher 		pr_debug("%s: scan complete when stat != scanning(%d)\n",
15378df158acSJeff Kirsher 			 __func__, wl->scan_stat);
15388df158acSJeff Kirsher 		goto out;
15398df158acSJeff Kirsher 	}
15408df158acSJeff Kirsher 
15418df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_SCAN,
15428df158acSJeff Kirsher 				   buf, PAGE_SIZE);
15438df158acSJeff Kirsher 	if (!cmd || cmd->status || cmd->cmd_status) {
15448df158acSJeff Kirsher 		wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
15458df158acSJeff Kirsher 		pr_info("%s:cmd failed\n", __func__);
15468df158acSJeff Kirsher 		kfree(cmd);
15478df158acSJeff Kirsher 		goto out;
15488df158acSJeff Kirsher 	}
15498df158acSJeff Kirsher 	data_len = cmd->size;
15508df158acSJeff Kirsher 	pr_debug("%s: data_len = %d\n", __func__, data_len);
15518df158acSJeff Kirsher 	kfree(cmd);
15528df158acSJeff Kirsher 
15538df158acSJeff Kirsher 	/* OK, bss list retrieved */
15548df158acSJeff Kirsher 	wl->scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST;
15558df158acSJeff Kirsher 
15568df158acSJeff Kirsher 	/* mark all entries are old */
15578df158acSJeff Kirsher 	list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
15588df158acSJeff Kirsher 		target->valid = 0;
15598df158acSJeff Kirsher 		/* expire too old entries */
15608df158acSJeff Kirsher 		if (time_before(target->last_scanned + wl->scan_age,
15618df158acSJeff Kirsher 				this_time)) {
15628df158acSJeff Kirsher 			kfree(target->hwinfo);
15638df158acSJeff Kirsher 			target->hwinfo = NULL;
15648df158acSJeff Kirsher 			list_move_tail(&target->list, &wl->network_free_list);
15658df158acSJeff Kirsher 		}
15668df158acSJeff Kirsher 	}
15678df158acSJeff Kirsher 
15688df158acSJeff Kirsher 	/* put them in the network_list */
15698df158acSJeff Kirsher 	for (i = 0, scan_info_size = 0, scan_info = buf;
15708df158acSJeff Kirsher 	     scan_info_size < data_len;
15718df158acSJeff Kirsher 	     i++, scan_info_size += be16_to_cpu(scan_info->size),
15728df158acSJeff Kirsher 	     scan_info = (void *)scan_info + be16_to_cpu(scan_info->size)) {
15738df158acSJeff Kirsher 		pr_debug("%s:size=%d bssid=%pM scan_info=%p\n", __func__,
15748df158acSJeff Kirsher 			 be16_to_cpu(scan_info->size),
15758df158acSJeff Kirsher 			 &scan_info->bssid[2], scan_info);
15768df158acSJeff Kirsher 
15778df158acSJeff Kirsher 		/*
15788df158acSJeff Kirsher 		 * The wireless firmware may return invalid channel 0 and/or
15798df158acSJeff Kirsher 		 * invalid rate if the AP emits zero length SSID ie. As this
15808df158acSJeff Kirsher 		 * scan information is useless, ignore it
15818df158acSJeff Kirsher 		 */
15828df158acSJeff Kirsher 		if (!be16_to_cpu(scan_info->channel) || !scan_info->rate[0]) {
15838df158acSJeff Kirsher 			pr_debug("%s: invalid scan info\n", __func__);
15848df158acSJeff Kirsher 			continue;
15858df158acSJeff Kirsher 		}
15868df158acSJeff Kirsher 
15878df158acSJeff Kirsher 		found = 0;
15888df158acSJeff Kirsher 		oldest = NULL;
15898df158acSJeff Kirsher 		list_for_each_entry(target, &wl->network_list, list) {
15902e42e474SJoe Perches 			if (ether_addr_equal(&target->hwinfo->bssid[2],
15918df158acSJeff Kirsher 					     &scan_info->bssid[2])) {
15928df158acSJeff Kirsher 				found = 1;
15938df158acSJeff Kirsher 				pr_debug("%s: same BBS found scanned list\n",
15948df158acSJeff Kirsher 					 __func__);
15958df158acSJeff Kirsher 				break;
15968df158acSJeff Kirsher 			}
15978df158acSJeff Kirsher 			if (!oldest ||
15988df158acSJeff Kirsher 			    (target->last_scanned < oldest->last_scanned))
15998df158acSJeff Kirsher 				oldest = target;
16008df158acSJeff Kirsher 		}
16018df158acSJeff Kirsher 
16028df158acSJeff Kirsher 		if (!found) {
16038df158acSJeff Kirsher 			/* not found in the list */
16048df158acSJeff Kirsher 			if (list_empty(&wl->network_free_list)) {
16058df158acSJeff Kirsher 				/* expire oldest */
16068df158acSJeff Kirsher 				target = oldest;
16078df158acSJeff Kirsher 			} else {
16088df158acSJeff Kirsher 				target = list_entry(wl->network_free_list.next,
16098df158acSJeff Kirsher 						    struct gelic_wl_scan_info,
16108df158acSJeff Kirsher 						    list);
16118df158acSJeff Kirsher 			}
16128df158acSJeff Kirsher 		}
16138df158acSJeff Kirsher 
16148df158acSJeff Kirsher 		/* update the item */
16158df158acSJeff Kirsher 		target->last_scanned = this_time;
16168df158acSJeff Kirsher 		target->valid = 1;
16178df158acSJeff Kirsher 		target->eurus_index = i;
16188df158acSJeff Kirsher 		kfree(target->hwinfo);
16195877debeSMuhammad Falak R Wani 		target->hwinfo = kmemdup(scan_info,
16205877debeSMuhammad Falak R Wani 					 be16_to_cpu(scan_info->size),
16218df158acSJeff Kirsher 					 GFP_KERNEL);
1622e404decbSJoe Perches 		if (!target->hwinfo)
16238df158acSJeff Kirsher 			continue;
1624e404decbSJoe Perches 
16258df158acSJeff Kirsher 		/* copy hw scan info */
16268df158acSJeff Kirsher 		target->essid_len = strnlen(scan_info->essid,
16278df158acSJeff Kirsher 					    sizeof(scan_info->essid));
16288df158acSJeff Kirsher 		target->rate_len = 0;
16298df158acSJeff Kirsher 		for (r = 0; r < 12; r++)
16308df158acSJeff Kirsher 			if (scan_info->rate[r])
16318df158acSJeff Kirsher 				target->rate_len++;
16328df158acSJeff Kirsher 		if (8 < target->rate_len)
16338df158acSJeff Kirsher 			pr_info("%s: AP returns %d rates\n", __func__,
16348df158acSJeff Kirsher 				target->rate_len);
16358df158acSJeff Kirsher 		target->rate_ext_len = 0;
16368df158acSJeff Kirsher 		for (r = 0; r < 16; r++)
16378df158acSJeff Kirsher 			if (scan_info->ext_rate[r])
16388df158acSJeff Kirsher 				target->rate_ext_len++;
16398df158acSJeff Kirsher 		list_move_tail(&target->list, &wl->network_list);
16408df158acSJeff Kirsher 	}
16418df158acSJeff Kirsher 	memset(&data, 0, sizeof(data));
16428df158acSJeff Kirsher 	wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWSCAN, &data,
16438df158acSJeff Kirsher 			    NULL);
16448df158acSJeff Kirsher out:
16458df158acSJeff Kirsher 	free_page((unsigned long)buf);
16468df158acSJeff Kirsher 	complete(&wl->scan_done);
16478df158acSJeff Kirsher 	mutex_unlock(&wl->scan_lock);
16488df158acSJeff Kirsher 	pr_debug("%s:end\n", __func__);
16498df158acSJeff Kirsher }
16508df158acSJeff Kirsher 
16518df158acSJeff Kirsher /*
16528df158acSJeff Kirsher  * Select an appropriate bss from current scan list regarding
16538df158acSJeff Kirsher  * current settings from userspace.
16548df158acSJeff Kirsher  * The caller must hold wl->scan_lock,
16558df158acSJeff Kirsher  * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
16568df158acSJeff Kirsher  */
16578df158acSJeff Kirsher static void update_best(struct gelic_wl_scan_info **best,
16588df158acSJeff Kirsher 			struct gelic_wl_scan_info *candid,
16598df158acSJeff Kirsher 			int *best_weight,
16608df158acSJeff Kirsher 			int *weight)
16618df158acSJeff Kirsher {
16628df158acSJeff Kirsher 	if (*best_weight < ++(*weight)) {
16638df158acSJeff Kirsher 		*best_weight = *weight;
16648df158acSJeff Kirsher 		*best = candid;
16658df158acSJeff Kirsher 	}
16668df158acSJeff Kirsher }
16678df158acSJeff Kirsher 
16688df158acSJeff Kirsher static
16698df158acSJeff Kirsher struct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl)
16708df158acSJeff Kirsher {
16718df158acSJeff Kirsher 	struct gelic_wl_scan_info *scan_info;
16728df158acSJeff Kirsher 	struct gelic_wl_scan_info *best_bss;
16738df158acSJeff Kirsher 	int weight, best_weight;
16748df158acSJeff Kirsher 	u16 security;
16758df158acSJeff Kirsher 
16768df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
16778df158acSJeff Kirsher 
16788df158acSJeff Kirsher 	best_bss = NULL;
16798df158acSJeff Kirsher 	best_weight = 0;
16808df158acSJeff Kirsher 
16818df158acSJeff Kirsher 	list_for_each_entry(scan_info, &wl->network_list, list) {
16828df158acSJeff Kirsher 		pr_debug("%s: station %p\n", __func__, scan_info);
16838df158acSJeff Kirsher 
16848df158acSJeff Kirsher 		if (!scan_info->valid) {
16858df158acSJeff Kirsher 			pr_debug("%s: station invalid\n", __func__);
16868df158acSJeff Kirsher 			continue;
16878df158acSJeff Kirsher 		}
16888df158acSJeff Kirsher 
16898df158acSJeff Kirsher 		/* If bss specified, check it only */
16908df158acSJeff Kirsher 		if (test_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat)) {
16912e42e474SJoe Perches 			if (ether_addr_equal(&scan_info->hwinfo->bssid[2],
16928df158acSJeff Kirsher 					     wl->bssid)) {
16938df158acSJeff Kirsher 				best_bss = scan_info;
16948df158acSJeff Kirsher 				pr_debug("%s: bssid matched\n", __func__);
16958df158acSJeff Kirsher 				break;
16968df158acSJeff Kirsher 			} else {
16977020637bSColin Ian King 				pr_debug("%s: bssid unmatched\n", __func__);
16988df158acSJeff Kirsher 				continue;
16998df158acSJeff Kirsher 			}
17008df158acSJeff Kirsher 		}
17018df158acSJeff Kirsher 
17028df158acSJeff Kirsher 		weight = 0;
17038df158acSJeff Kirsher 
17048df158acSJeff Kirsher 		/* security */
17058df158acSJeff Kirsher 		security = be16_to_cpu(scan_info->hwinfo->security) &
17068df158acSJeff Kirsher 			GELIC_EURUS_SCAN_SEC_MASK;
17078df158acSJeff Kirsher 		if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) {
17088df158acSJeff Kirsher 			if (security == GELIC_EURUS_SCAN_SEC_WPA2)
17098df158acSJeff Kirsher 				update_best(&best_bss, scan_info,
17108df158acSJeff Kirsher 					    &best_weight, &weight);
17118df158acSJeff Kirsher 			else
17128df158acSJeff Kirsher 				continue;
17138df158acSJeff Kirsher 		} else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA) {
17148df158acSJeff Kirsher 			if (security == GELIC_EURUS_SCAN_SEC_WPA)
17158df158acSJeff Kirsher 				update_best(&best_bss, scan_info,
17168df158acSJeff Kirsher 					    &best_weight, &weight);
17178df158acSJeff Kirsher 			else
17188df158acSJeff Kirsher 				continue;
17198df158acSJeff Kirsher 		} else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_NONE &&
17208df158acSJeff Kirsher 			   wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
17218df158acSJeff Kirsher 			if (security == GELIC_EURUS_SCAN_SEC_WEP)
17228df158acSJeff Kirsher 				update_best(&best_bss, scan_info,
17238df158acSJeff Kirsher 					    &best_weight, &weight);
17248df158acSJeff Kirsher 			else
17258df158acSJeff Kirsher 				continue;
17268df158acSJeff Kirsher 		}
17278df158acSJeff Kirsher 
17288df158acSJeff Kirsher 		/* If ESSID is set, check it */
17298df158acSJeff Kirsher 		if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) {
17308df158acSJeff Kirsher 			if ((scan_info->essid_len == wl->essid_len) &&
17318df158acSJeff Kirsher 			    !strncmp(wl->essid,
17328df158acSJeff Kirsher 				     scan_info->hwinfo->essid,
17338df158acSJeff Kirsher 				     scan_info->essid_len))
17348df158acSJeff Kirsher 				update_best(&best_bss, scan_info,
17358df158acSJeff Kirsher 					    &best_weight, &weight);
17368df158acSJeff Kirsher 			else
17378df158acSJeff Kirsher 				continue;
17388df158acSJeff Kirsher 		}
17398df158acSJeff Kirsher 	}
17408df158acSJeff Kirsher 
17418df158acSJeff Kirsher #ifdef DEBUG
17428df158acSJeff Kirsher 	pr_debug("%s: -> bss=%p\n", __func__, best_bss);
17438df158acSJeff Kirsher 	if (best_bss) {
17448df158acSJeff Kirsher 		pr_debug("%s:addr=%pM\n", __func__,
17458df158acSJeff Kirsher 			 &best_bss->hwinfo->bssid[2]);
17468df158acSJeff Kirsher 	}
17478df158acSJeff Kirsher #endif
17488df158acSJeff Kirsher 	return best_bss;
17498df158acSJeff Kirsher }
17508df158acSJeff Kirsher 
17518df158acSJeff Kirsher /*
17528df158acSJeff Kirsher  * Setup WEP configuration to the chip
17538df158acSJeff Kirsher  * The caller must hold wl->scan_lock,
17548df158acSJeff Kirsher  * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
17558df158acSJeff Kirsher  */
17568df158acSJeff Kirsher static int gelic_wl_do_wep_setup(struct gelic_wl_info *wl)
17578df158acSJeff Kirsher {
17588df158acSJeff Kirsher 	unsigned int i;
17598df158acSJeff Kirsher 	struct gelic_eurus_wep_cfg *wep;
17608df158acSJeff Kirsher 	struct gelic_eurus_cmd *cmd;
17618df158acSJeff Kirsher 	int wep104 = 0;
17628df158acSJeff Kirsher 	int have_key = 0;
17638df158acSJeff Kirsher 	int ret = 0;
17648df158acSJeff Kirsher 
17658df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
17668df158acSJeff Kirsher 	/* we can assume no one should uses the buffer */
17678df158acSJeff Kirsher 	wep = (struct gelic_eurus_wep_cfg *)__get_free_page(GFP_KERNEL);
17688df158acSJeff Kirsher 	if (!wep)
17698df158acSJeff Kirsher 		return -ENOMEM;
17708df158acSJeff Kirsher 
17718df158acSJeff Kirsher 	memset(wep, 0, sizeof(*wep));
17728df158acSJeff Kirsher 
17738df158acSJeff Kirsher 	if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
17748df158acSJeff Kirsher 		pr_debug("%s: WEP mode\n", __func__);
17758df158acSJeff Kirsher 		for (i = 0; i < GELIC_WEP_KEYS; i++) {
17768df158acSJeff Kirsher 			if (!test_bit(i, &wl->key_enabled))
17778df158acSJeff Kirsher 				continue;
17788df158acSJeff Kirsher 
17798df158acSJeff Kirsher 			pr_debug("%s: key#%d enabled\n", __func__, i);
17808df158acSJeff Kirsher 			have_key = 1;
17818df158acSJeff Kirsher 			if (wl->key_len[i] == 13)
17828df158acSJeff Kirsher 				wep104 = 1;
17838df158acSJeff Kirsher 			else if (wl->key_len[i] != 5) {
17848df158acSJeff Kirsher 				pr_info("%s: wrong wep key[%d]=%d\n",
17858df158acSJeff Kirsher 					__func__, i, wl->key_len[i]);
17868df158acSJeff Kirsher 				ret = -EINVAL;
17878df158acSJeff Kirsher 				goto out;
17888df158acSJeff Kirsher 			}
17898df158acSJeff Kirsher 			memcpy(wep->key[i], wl->key[i], wl->key_len[i]);
17908df158acSJeff Kirsher 		}
17918df158acSJeff Kirsher 
17928df158acSJeff Kirsher 		if (!have_key) {
17938df158acSJeff Kirsher 			pr_info("%s: all wep key disabled\n", __func__);
17948df158acSJeff Kirsher 			ret = -EINVAL;
17958df158acSJeff Kirsher 			goto out;
17968df158acSJeff Kirsher 		}
17978df158acSJeff Kirsher 
17988df158acSJeff Kirsher 		if (wep104) {
17998df158acSJeff Kirsher 			pr_debug("%s: 104bit key\n", __func__);
18008df158acSJeff Kirsher 			wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_104BIT);
18018df158acSJeff Kirsher 		} else {
18028df158acSJeff Kirsher 			pr_debug("%s: 40bit key\n", __func__);
18038df158acSJeff Kirsher 			wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_40BIT);
18048df158acSJeff Kirsher 		}
18058df158acSJeff Kirsher 	} else {
18068df158acSJeff Kirsher 		pr_debug("%s: NO encryption\n", __func__);
18078df158acSJeff Kirsher 		wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_NONE);
18088df158acSJeff Kirsher 	}
18098df158acSJeff Kirsher 
18108df158acSJeff Kirsher 	/* issue wep setup */
18118df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WEP_CFG,
18128df158acSJeff Kirsher 				   wep, sizeof(*wep));
18138df158acSJeff Kirsher 	if (!cmd)
18148df158acSJeff Kirsher 		ret = -ENOMEM;
18158df158acSJeff Kirsher 	else if (cmd->status || cmd->cmd_status)
18168df158acSJeff Kirsher 		ret = -ENXIO;
18178df158acSJeff Kirsher 
18188df158acSJeff Kirsher 	kfree(cmd);
18198df158acSJeff Kirsher out:
18208df158acSJeff Kirsher 	free_page((unsigned long)wep);
18218df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
18228df158acSJeff Kirsher 	return ret;
18238df158acSJeff Kirsher }
18248df158acSJeff Kirsher 
18258df158acSJeff Kirsher #ifdef DEBUG
18268df158acSJeff Kirsher static const char *wpasecstr(enum gelic_eurus_wpa_security sec)
18278df158acSJeff Kirsher {
18288df158acSJeff Kirsher 	switch (sec) {
18298df158acSJeff Kirsher 	case GELIC_EURUS_WPA_SEC_NONE:
18308df158acSJeff Kirsher 		return "NONE";
18318df158acSJeff Kirsher 	case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP:
18328df158acSJeff Kirsher 		return "WPA_TKIP_TKIP";
18338df158acSJeff Kirsher 	case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES:
18348df158acSJeff Kirsher 		return "WPA_TKIP_AES";
18358df158acSJeff Kirsher 	case GELIC_EURUS_WPA_SEC_WPA_AES_AES:
18368df158acSJeff Kirsher 		return "WPA_AES_AES";
18378df158acSJeff Kirsher 	case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP:
18388df158acSJeff Kirsher 		return "WPA2_TKIP_TKIP";
18398df158acSJeff Kirsher 	case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES:
18408df158acSJeff Kirsher 		return "WPA2_TKIP_AES";
18418df158acSJeff Kirsher 	case GELIC_EURUS_WPA_SEC_WPA2_AES_AES:
18428df158acSJeff Kirsher 		return "WPA2_AES_AES";
18438df158acSJeff Kirsher 	}
18448df158acSJeff Kirsher 	return "";
18458df158acSJeff Kirsher };
18468df158acSJeff Kirsher #endif
18478df158acSJeff Kirsher 
18488df158acSJeff Kirsher static int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl)
18498df158acSJeff Kirsher {
18508df158acSJeff Kirsher 	struct gelic_eurus_wpa_cfg *wpa;
18518df158acSJeff Kirsher 	struct gelic_eurus_cmd *cmd;
18528df158acSJeff Kirsher 	u16 security;
18538df158acSJeff Kirsher 	int ret = 0;
18548df158acSJeff Kirsher 
18558df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
18568df158acSJeff Kirsher 	/* we can assume no one should uses the buffer */
18578df158acSJeff Kirsher 	wpa = (struct gelic_eurus_wpa_cfg *)__get_free_page(GFP_KERNEL);
18588df158acSJeff Kirsher 	if (!wpa)
18598df158acSJeff Kirsher 		return -ENOMEM;
18608df158acSJeff Kirsher 
18618df158acSJeff Kirsher 	memset(wpa, 0, sizeof(*wpa));
18628df158acSJeff Kirsher 
18638df158acSJeff Kirsher 	if (!test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat))
18648df158acSJeff Kirsher 		pr_info("%s: PSK not configured yet\n", __func__);
18658df158acSJeff Kirsher 
18668df158acSJeff Kirsher 	/* copy key */
18678df158acSJeff Kirsher 	memcpy(wpa->psk, wl->psk, wl->psk_len);
18688df158acSJeff Kirsher 
18698df158acSJeff Kirsher 	/* set security level */
18708df158acSJeff Kirsher 	if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) {
18718df158acSJeff Kirsher 		if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) {
18728df158acSJeff Kirsher 			security = GELIC_EURUS_WPA_SEC_WPA2_AES_AES;
18738df158acSJeff Kirsher 		} else {
18748df158acSJeff Kirsher 			if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES &&
18758df158acSJeff Kirsher 			    precise_ie())
18768df158acSJeff Kirsher 				security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES;
18778df158acSJeff Kirsher 			else
18788df158acSJeff Kirsher 				security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP;
18798df158acSJeff Kirsher 		}
18808df158acSJeff Kirsher 	} else {
18818df158acSJeff Kirsher 		if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) {
18828df158acSJeff Kirsher 			security = GELIC_EURUS_WPA_SEC_WPA_AES_AES;
18838df158acSJeff Kirsher 		} else {
18848df158acSJeff Kirsher 			if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES &&
18858df158acSJeff Kirsher 			    precise_ie())
18868df158acSJeff Kirsher 				security = GELIC_EURUS_WPA_SEC_WPA_TKIP_AES;
18878df158acSJeff Kirsher 			else
18888df158acSJeff Kirsher 				security = GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP;
18898df158acSJeff Kirsher 		}
18908df158acSJeff Kirsher 	}
18918df158acSJeff Kirsher 	wpa->security = cpu_to_be16(security);
18928df158acSJeff Kirsher 
18938df158acSJeff Kirsher 	/* PSK type */
18948df158acSJeff Kirsher 	wpa->psk_type = cpu_to_be16(wl->psk_type);
18958df158acSJeff Kirsher #ifdef DEBUG
18968df158acSJeff Kirsher 	pr_debug("%s: sec=%s psktype=%s\n", __func__,
18978df158acSJeff Kirsher 		 wpasecstr(wpa->security),
18988df158acSJeff Kirsher 		 (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ?
18998df158acSJeff Kirsher 		 "BIN" : "passphrase");
19008df158acSJeff Kirsher #if 0
19018df158acSJeff Kirsher 	/*
19028df158acSJeff Kirsher 	 * don't enable here if you plan to submit
19038df158acSJeff Kirsher 	 * the debug log because this dumps your precious
19048df158acSJeff Kirsher 	 * passphrase/key.
19058df158acSJeff Kirsher 	 */
19068df158acSJeff Kirsher 	pr_debug("%s: psk=%s\n", __func__,
19078df158acSJeff Kirsher 		 (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ?
19088df158acSJeff Kirsher 		 "N/A" : wpa->psk);
19098df158acSJeff Kirsher #endif
19108df158acSJeff Kirsher #endif
19118df158acSJeff Kirsher 	/* issue wpa setup */
19128df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WPA_CFG,
19138df158acSJeff Kirsher 				   wpa, sizeof(*wpa));
19148df158acSJeff Kirsher 	if (!cmd)
19158df158acSJeff Kirsher 		ret = -ENOMEM;
19168df158acSJeff Kirsher 	else if (cmd->status || cmd->cmd_status)
19178df158acSJeff Kirsher 		ret = -ENXIO;
19188df158acSJeff Kirsher 	kfree(cmd);
19198df158acSJeff Kirsher 	free_page((unsigned long)wpa);
19208df158acSJeff Kirsher 	pr_debug("%s: --> %d\n", __func__, ret);
19218df158acSJeff Kirsher 	return ret;
19228df158acSJeff Kirsher }
19238df158acSJeff Kirsher 
19248df158acSJeff Kirsher /*
19258df158acSJeff Kirsher  * Start association. caller must hold assoc_stat_lock
19268df158acSJeff Kirsher  */
19278df158acSJeff Kirsher static int gelic_wl_associate_bss(struct gelic_wl_info *wl,
19288df158acSJeff Kirsher 				  struct gelic_wl_scan_info *bss)
19298df158acSJeff Kirsher {
19308df158acSJeff Kirsher 	struct gelic_eurus_cmd *cmd;
19318df158acSJeff Kirsher 	struct gelic_eurus_common_cfg *common;
19328df158acSJeff Kirsher 	int ret = 0;
19338df158acSJeff Kirsher 	unsigned long rc;
19348df158acSJeff Kirsher 
19358df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
19368df158acSJeff Kirsher 
19378df158acSJeff Kirsher 	/* do common config */
19388df158acSJeff Kirsher 	common = (struct gelic_eurus_common_cfg *)__get_free_page(GFP_KERNEL);
19398df158acSJeff Kirsher 	if (!common)
19408df158acSJeff Kirsher 		return -ENOMEM;
19418df158acSJeff Kirsher 
19428df158acSJeff Kirsher 	memset(common, 0, sizeof(*common));
19438df158acSJeff Kirsher 	common->bss_type = cpu_to_be16(GELIC_EURUS_BSS_INFRA);
19448df158acSJeff Kirsher 	common->op_mode = cpu_to_be16(GELIC_EURUS_OPMODE_11BG);
19458df158acSJeff Kirsher 
19468df158acSJeff Kirsher 	common->scan_index = cpu_to_be16(bss->eurus_index);
19478df158acSJeff Kirsher 	switch (wl->auth_method) {
19488df158acSJeff Kirsher 	case GELIC_EURUS_AUTH_OPEN:
19498df158acSJeff Kirsher 		common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_OPEN);
19508df158acSJeff Kirsher 		break;
19518df158acSJeff Kirsher 	case GELIC_EURUS_AUTH_SHARED:
19528df158acSJeff Kirsher 		common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_SHARED);
19538df158acSJeff Kirsher 		break;
19548df158acSJeff Kirsher 	}
19558df158acSJeff Kirsher 
19568df158acSJeff Kirsher #ifdef DEBUG
19578df158acSJeff Kirsher 	scan_list_dump(wl);
19588df158acSJeff Kirsher #endif
19598df158acSJeff Kirsher 	pr_debug("%s: common cfg index=%d bsstype=%d auth=%d\n", __func__,
19608df158acSJeff Kirsher 		 be16_to_cpu(common->scan_index),
19618df158acSJeff Kirsher 		 be16_to_cpu(common->bss_type),
19628df158acSJeff Kirsher 		 be16_to_cpu(common->auth_method));
19638df158acSJeff Kirsher 
19648df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_COMMON_CFG,
19658df158acSJeff Kirsher 				   common, sizeof(*common));
19668df158acSJeff Kirsher 	if (!cmd || cmd->status || cmd->cmd_status) {
19678df158acSJeff Kirsher 		ret = -ENOMEM;
19688df158acSJeff Kirsher 		kfree(cmd);
19698df158acSJeff Kirsher 		goto out;
19708df158acSJeff Kirsher 	}
19718df158acSJeff Kirsher 	kfree(cmd);
19728df158acSJeff Kirsher 
19738df158acSJeff Kirsher 	/* WEP/WPA */
19748df158acSJeff Kirsher 	switch (wl->wpa_level) {
19758df158acSJeff Kirsher 	case GELIC_WL_WPA_LEVEL_NONE:
19768df158acSJeff Kirsher 		/* If WEP or no security, setup WEP config */
19778df158acSJeff Kirsher 		ret = gelic_wl_do_wep_setup(wl);
19788df158acSJeff Kirsher 		break;
19798df158acSJeff Kirsher 	case GELIC_WL_WPA_LEVEL_WPA:
19808df158acSJeff Kirsher 	case GELIC_WL_WPA_LEVEL_WPA2:
19818df158acSJeff Kirsher 		ret = gelic_wl_do_wpa_setup(wl);
19828df158acSJeff Kirsher 		break;
19838df158acSJeff Kirsher 	}
19848df158acSJeff Kirsher 
19858df158acSJeff Kirsher 	if (ret) {
19868df158acSJeff Kirsher 		pr_debug("%s: WEP/WPA setup failed %d\n", __func__,
19878df158acSJeff Kirsher 			 ret);
19888df158acSJeff Kirsher 		ret = -EPERM;
19898df158acSJeff Kirsher 		gelic_wl_send_iwap_event(wl, NULL);
19908df158acSJeff Kirsher 		goto out;
19918df158acSJeff Kirsher 	}
19928df158acSJeff Kirsher 
19938df158acSJeff Kirsher 	/* start association */
19948df158acSJeff Kirsher 	init_completion(&wl->assoc_done);
19958df158acSJeff Kirsher 	wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATING;
19968df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_ASSOC,
19978df158acSJeff Kirsher 				   NULL, 0);
19988df158acSJeff Kirsher 	if (!cmd || cmd->status || cmd->cmd_status) {
19998df158acSJeff Kirsher 		pr_debug("%s: assoc request failed\n", __func__);
20008df158acSJeff Kirsher 		wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
20018df158acSJeff Kirsher 		kfree(cmd);
20028df158acSJeff Kirsher 		ret = -ENOMEM;
20038df158acSJeff Kirsher 		gelic_wl_send_iwap_event(wl, NULL);
20048df158acSJeff Kirsher 		goto out;
20058df158acSJeff Kirsher 	}
20068df158acSJeff Kirsher 	kfree(cmd);
20078df158acSJeff Kirsher 
20088df158acSJeff Kirsher 	/* wait for connected event */
20098df158acSJeff Kirsher 	rc = wait_for_completion_timeout(&wl->assoc_done, HZ * 4);/*FIXME*/
20108df158acSJeff Kirsher 
20118df158acSJeff Kirsher 	if (!rc) {
20128df158acSJeff Kirsher 		/* timeouted.  Maybe key or cyrpt mode is wrong */
20138df158acSJeff Kirsher 		pr_info("%s: connect timeout\n", __func__);
20148df158acSJeff Kirsher 		cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC,
20158df158acSJeff Kirsher 					   NULL, 0);
20168df158acSJeff Kirsher 		kfree(cmd);
20178df158acSJeff Kirsher 		wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
20188df158acSJeff Kirsher 		gelic_wl_send_iwap_event(wl, NULL);
20198df158acSJeff Kirsher 		ret = -ENXIO;
20208df158acSJeff Kirsher 	} else {
20218df158acSJeff Kirsher 		wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATED;
20228df158acSJeff Kirsher 		/* copy bssid */
20238df158acSJeff Kirsher 		memcpy(wl->active_bssid, &bss->hwinfo->bssid[2], ETH_ALEN);
20248df158acSJeff Kirsher 
20258df158acSJeff Kirsher 		/* send connect event */
20268df158acSJeff Kirsher 		gelic_wl_send_iwap_event(wl, wl->active_bssid);
20278df158acSJeff Kirsher 		pr_info("%s: connected\n", __func__);
20288df158acSJeff Kirsher 	}
20298df158acSJeff Kirsher out:
20308df158acSJeff Kirsher 	free_page((unsigned long)common);
20318df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
20328df158acSJeff Kirsher 	return ret;
20338df158acSJeff Kirsher }
20348df158acSJeff Kirsher 
20358df158acSJeff Kirsher /*
20368df158acSJeff Kirsher  * connected event
20378df158acSJeff Kirsher  */
20388df158acSJeff Kirsher static void gelic_wl_connected_event(struct gelic_wl_info *wl,
20398df158acSJeff Kirsher 				     u64 event)
20408df158acSJeff Kirsher {
20418df158acSJeff Kirsher 	u64 desired_event = 0;
20428df158acSJeff Kirsher 
20438df158acSJeff Kirsher 	switch (wl->wpa_level) {
20448df158acSJeff Kirsher 	case GELIC_WL_WPA_LEVEL_NONE:
20458df158acSJeff Kirsher 		desired_event = GELIC_LV1_WL_EVENT_CONNECTED;
20468df158acSJeff Kirsher 		break;
20478df158acSJeff Kirsher 	case GELIC_WL_WPA_LEVEL_WPA:
20488df158acSJeff Kirsher 	case GELIC_WL_WPA_LEVEL_WPA2:
20498df158acSJeff Kirsher 		desired_event = GELIC_LV1_WL_EVENT_WPA_CONNECTED;
20508df158acSJeff Kirsher 		break;
20518df158acSJeff Kirsher 	}
20528df158acSJeff Kirsher 
20538df158acSJeff Kirsher 	if (desired_event == event) {
20548df158acSJeff Kirsher 		pr_debug("%s: completed\n", __func__);
20558df158acSJeff Kirsher 		complete(&wl->assoc_done);
20568df158acSJeff Kirsher 		netif_carrier_on(port_to_netdev(wl_port(wl)));
20578df158acSJeff Kirsher 	} else
20588df158acSJeff Kirsher 		pr_debug("%s: event %#llx under wpa\n",
20598df158acSJeff Kirsher 				 __func__, event);
20608df158acSJeff Kirsher }
20618df158acSJeff Kirsher 
20628df158acSJeff Kirsher /*
20638df158acSJeff Kirsher  * disconnect event
20648df158acSJeff Kirsher  */
20658df158acSJeff Kirsher static void gelic_wl_disconnect_event(struct gelic_wl_info *wl,
20668df158acSJeff Kirsher 				      u64 event)
20678df158acSJeff Kirsher {
20688df158acSJeff Kirsher 	struct gelic_eurus_cmd *cmd;
20698df158acSJeff Kirsher 	int lock;
20708df158acSJeff Kirsher 
20718df158acSJeff Kirsher 	/*
20728df158acSJeff Kirsher 	 * If we fall here in the middle of association,
20738df158acSJeff Kirsher 	 * associate_bss() should be waiting for complation of
20748df158acSJeff Kirsher 	 * wl->assoc_done.
20758df158acSJeff Kirsher 	 * As it waits with timeout, just leave assoc_done
20768df158acSJeff Kirsher 	 * uncompleted, then it terminates with timeout
20778df158acSJeff Kirsher 	 */
20788df158acSJeff Kirsher 	if (!mutex_trylock(&wl->assoc_stat_lock)) {
20798df158acSJeff Kirsher 		pr_debug("%s: already locked\n", __func__);
20808df158acSJeff Kirsher 		lock = 0;
20818df158acSJeff Kirsher 	} else {
20828df158acSJeff Kirsher 		pr_debug("%s: obtain lock\n", __func__);
20838df158acSJeff Kirsher 		lock = 1;
20848df158acSJeff Kirsher 	}
20858df158acSJeff Kirsher 
20868df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0);
20878df158acSJeff Kirsher 	kfree(cmd);
20888df158acSJeff Kirsher 
20898df158acSJeff Kirsher 	/* send disconnected event to the supplicant */
20908df158acSJeff Kirsher 	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
20918df158acSJeff Kirsher 		gelic_wl_send_iwap_event(wl, NULL);
20928df158acSJeff Kirsher 
20938df158acSJeff Kirsher 	wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
20948df158acSJeff Kirsher 	netif_carrier_off(port_to_netdev(wl_port(wl)));
20958df158acSJeff Kirsher 
20968df158acSJeff Kirsher 	if (lock)
20978df158acSJeff Kirsher 		mutex_unlock(&wl->assoc_stat_lock);
20988df158acSJeff Kirsher }
20998df158acSJeff Kirsher /*
21008df158acSJeff Kirsher  * event worker
21018df158acSJeff Kirsher  */
21028df158acSJeff Kirsher #ifdef DEBUG
21038df158acSJeff Kirsher static const char *eventstr(enum gelic_lv1_wl_event event)
21048df158acSJeff Kirsher {
21058df158acSJeff Kirsher 	static char buf[32];
21068df158acSJeff Kirsher 	char *ret;
21078df158acSJeff Kirsher 	if (event & GELIC_LV1_WL_EVENT_DEVICE_READY)
21088df158acSJeff Kirsher 		ret = "EURUS_READY";
21098df158acSJeff Kirsher 	else if (event & GELIC_LV1_WL_EVENT_SCAN_COMPLETED)
21108df158acSJeff Kirsher 		ret = "SCAN_COMPLETED";
21118df158acSJeff Kirsher 	else if (event & GELIC_LV1_WL_EVENT_DEAUTH)
21128df158acSJeff Kirsher 		ret = "DEAUTH";
21138df158acSJeff Kirsher 	else if (event & GELIC_LV1_WL_EVENT_BEACON_LOST)
21148df158acSJeff Kirsher 		ret = "BEACON_LOST";
21158df158acSJeff Kirsher 	else if (event & GELIC_LV1_WL_EVENT_CONNECTED)
21168df158acSJeff Kirsher 		ret = "CONNECTED";
21178df158acSJeff Kirsher 	else if (event & GELIC_LV1_WL_EVENT_WPA_CONNECTED)
21188df158acSJeff Kirsher 		ret = "WPA_CONNECTED";
21198df158acSJeff Kirsher 	else if (event & GELIC_LV1_WL_EVENT_WPA_ERROR)
21208df158acSJeff Kirsher 		ret = "WPA_ERROR";
21218df158acSJeff Kirsher 	else {
21228df158acSJeff Kirsher 		sprintf(buf, "Unknown(%#x)", event);
21238df158acSJeff Kirsher 		ret = buf;
21248df158acSJeff Kirsher 	}
21258df158acSJeff Kirsher 	return ret;
21268df158acSJeff Kirsher }
21278df158acSJeff Kirsher #else
21288df158acSJeff Kirsher static const char *eventstr(enum gelic_lv1_wl_event event)
21298df158acSJeff Kirsher {
21308df158acSJeff Kirsher 	return NULL;
21318df158acSJeff Kirsher }
21328df158acSJeff Kirsher #endif
21338df158acSJeff Kirsher static void gelic_wl_event_worker(struct work_struct *work)
21348df158acSJeff Kirsher {
21358df158acSJeff Kirsher 	struct gelic_wl_info *wl;
21368df158acSJeff Kirsher 	struct gelic_port *port;
21378df158acSJeff Kirsher 	u64 event, tmp;
21388df158acSJeff Kirsher 	int status;
21398df158acSJeff Kirsher 
21408df158acSJeff Kirsher 	pr_debug("%s:start\n", __func__);
21418df158acSJeff Kirsher 	wl = container_of(work, struct gelic_wl_info, event_work.work);
21428df158acSJeff Kirsher 	port = wl_port(wl);
21438df158acSJeff Kirsher 	while (1) {
21448df158acSJeff Kirsher 		status = lv1_net_control(bus_id(port->card), dev_id(port->card),
21458df158acSJeff Kirsher 					 GELIC_LV1_GET_WLAN_EVENT, 0, 0, 0,
21468df158acSJeff Kirsher 					 &event, &tmp);
21478df158acSJeff Kirsher 		if (status) {
21488df158acSJeff Kirsher 			if (status != LV1_NO_ENTRY)
21498df158acSJeff Kirsher 				pr_debug("%s:wlan event failed %d\n",
21508df158acSJeff Kirsher 					 __func__, status);
21518df158acSJeff Kirsher 			/* got all events */
21528df158acSJeff Kirsher 			pr_debug("%s:end\n", __func__);
21538df158acSJeff Kirsher 			return;
21548df158acSJeff Kirsher 		}
21558df158acSJeff Kirsher 		pr_debug("%s: event=%s\n", __func__, eventstr(event));
21568df158acSJeff Kirsher 		switch (event) {
21578df158acSJeff Kirsher 		case GELIC_LV1_WL_EVENT_SCAN_COMPLETED:
21588df158acSJeff Kirsher 			gelic_wl_scan_complete_event(wl);
21598df158acSJeff Kirsher 			break;
21608df158acSJeff Kirsher 		case GELIC_LV1_WL_EVENT_BEACON_LOST:
21618df158acSJeff Kirsher 		case GELIC_LV1_WL_EVENT_DEAUTH:
21628df158acSJeff Kirsher 			gelic_wl_disconnect_event(wl, event);
21638df158acSJeff Kirsher 			break;
21648df158acSJeff Kirsher 		case GELIC_LV1_WL_EVENT_CONNECTED:
21658df158acSJeff Kirsher 		case GELIC_LV1_WL_EVENT_WPA_CONNECTED:
21668df158acSJeff Kirsher 			gelic_wl_connected_event(wl, event);
21678df158acSJeff Kirsher 			break;
21688df158acSJeff Kirsher 		default:
21698df158acSJeff Kirsher 			break;
21708df158acSJeff Kirsher 		}
21718df158acSJeff Kirsher 	} /* while */
21728df158acSJeff Kirsher }
21738df158acSJeff Kirsher /*
21748df158acSJeff Kirsher  * association worker
21758df158acSJeff Kirsher  */
21768df158acSJeff Kirsher static void gelic_wl_assoc_worker(struct work_struct *work)
21778df158acSJeff Kirsher {
21788df158acSJeff Kirsher 	struct gelic_wl_info *wl;
21798df158acSJeff Kirsher 
21808df158acSJeff Kirsher 	struct gelic_wl_scan_info *best_bss;
21818df158acSJeff Kirsher 	int ret;
21828df158acSJeff Kirsher 	unsigned long irqflag;
21838df158acSJeff Kirsher 	u8 *essid;
21848df158acSJeff Kirsher 	size_t essid_len;
21858df158acSJeff Kirsher 
21868df158acSJeff Kirsher 	wl = container_of(work, struct gelic_wl_info, assoc_work.work);
21878df158acSJeff Kirsher 
21888df158acSJeff Kirsher 	mutex_lock(&wl->assoc_stat_lock);
21898df158acSJeff Kirsher 
21908df158acSJeff Kirsher 	if (wl->assoc_stat != GELIC_WL_ASSOC_STAT_DISCONN)
21918df158acSJeff Kirsher 		goto out;
21928df158acSJeff Kirsher 
21938df158acSJeff Kirsher 	spin_lock_irqsave(&wl->lock, irqflag);
21948df158acSJeff Kirsher 	if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) {
21958df158acSJeff Kirsher 		pr_debug("%s: assoc ESSID configured %s\n", __func__,
21968df158acSJeff Kirsher 			 wl->essid);
21978df158acSJeff Kirsher 		essid = wl->essid;
21988df158acSJeff Kirsher 		essid_len = wl->essid_len;
21998df158acSJeff Kirsher 	} else {
22008df158acSJeff Kirsher 		essid = NULL;
22018df158acSJeff Kirsher 		essid_len = 0;
22028df158acSJeff Kirsher 	}
22038df158acSJeff Kirsher 	spin_unlock_irqrestore(&wl->lock, irqflag);
22048df158acSJeff Kirsher 
22058df158acSJeff Kirsher 	ret = gelic_wl_start_scan(wl, 0, essid, essid_len);
22068df158acSJeff Kirsher 	if (ret == -ERESTARTSYS) {
22078df158acSJeff Kirsher 		pr_debug("%s: scan start failed association\n", __func__);
22088df158acSJeff Kirsher 		schedule_delayed_work(&wl->assoc_work, HZ/10); /*FIXME*/
22098df158acSJeff Kirsher 		goto out;
22108df158acSJeff Kirsher 	} else if (ret) {
22118df158acSJeff Kirsher 		pr_info("%s: scan prerequisite failed\n", __func__);
22128df158acSJeff Kirsher 		goto out;
22138df158acSJeff Kirsher 	}
22148df158acSJeff Kirsher 
22158df158acSJeff Kirsher 	/*
22168df158acSJeff Kirsher 	 * Wait for bss scan completion
22178df158acSJeff Kirsher 	 * If we have scan list already, gelic_wl_start_scan()
22188df158acSJeff Kirsher 	 * returns OK and raises the complete.  Thus,
22198df158acSJeff Kirsher 	 * it's ok to wait unconditionally here
22208df158acSJeff Kirsher 	 */
22218df158acSJeff Kirsher 	wait_for_completion(&wl->scan_done);
22228df158acSJeff Kirsher 
22238df158acSJeff Kirsher 	pr_debug("%s: scan done\n", __func__);
22248df158acSJeff Kirsher 	mutex_lock(&wl->scan_lock);
22258df158acSJeff Kirsher 	if (wl->scan_stat != GELIC_WL_SCAN_STAT_GOT_LIST) {
22268df158acSJeff Kirsher 		gelic_wl_send_iwap_event(wl, NULL);
22278df158acSJeff Kirsher 		pr_info("%s: no scan list. association failed\n", __func__);
22288df158acSJeff Kirsher 		goto scan_lock_out;
22298df158acSJeff Kirsher 	}
22308df158acSJeff Kirsher 
22318df158acSJeff Kirsher 	/* find best matching bss */
22328df158acSJeff Kirsher 	best_bss = gelic_wl_find_best_bss(wl);
22338df158acSJeff Kirsher 	if (!best_bss) {
22348df158acSJeff Kirsher 		gelic_wl_send_iwap_event(wl, NULL);
22358df158acSJeff Kirsher 		pr_info("%s: no bss matched. association failed\n", __func__);
22368df158acSJeff Kirsher 		goto scan_lock_out;
22378df158acSJeff Kirsher 	}
22388df158acSJeff Kirsher 
22398df158acSJeff Kirsher 	/* ok, do association */
22408df158acSJeff Kirsher 	ret = gelic_wl_associate_bss(wl, best_bss);
22418df158acSJeff Kirsher 	if (ret)
22428df158acSJeff Kirsher 		pr_info("%s: association failed %d\n", __func__, ret);
22438df158acSJeff Kirsher scan_lock_out:
22448df158acSJeff Kirsher 	mutex_unlock(&wl->scan_lock);
22458df158acSJeff Kirsher out:
22468df158acSJeff Kirsher 	mutex_unlock(&wl->assoc_stat_lock);
22478df158acSJeff Kirsher }
22488df158acSJeff Kirsher /*
22498df158acSJeff Kirsher  * Interrupt handler
22508df158acSJeff Kirsher  * Called from the ethernet interrupt handler
22518df158acSJeff Kirsher  * Processes wireless specific virtual interrupts only
22528df158acSJeff Kirsher  */
22538df158acSJeff Kirsher void gelic_wl_interrupt(struct net_device *netdev, u64 status)
22548df158acSJeff Kirsher {
22558df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
22568df158acSJeff Kirsher 
22578df158acSJeff Kirsher 	if (status & GELIC_CARD_WLAN_COMMAND_COMPLETED) {
22588df158acSJeff Kirsher 		pr_debug("%s:cmd complete\n", __func__);
22598df158acSJeff Kirsher 		complete(&wl->cmd_done_intr);
22608df158acSJeff Kirsher 	}
22618df158acSJeff Kirsher 
22628df158acSJeff Kirsher 	if (status & GELIC_CARD_WLAN_EVENT_RECEIVED) {
22638df158acSJeff Kirsher 		pr_debug("%s:event received\n", __func__);
22648df158acSJeff Kirsher 		queue_delayed_work(wl->event_queue, &wl->event_work, 0);
22658df158acSJeff Kirsher 	}
22668df158acSJeff Kirsher }
22678df158acSJeff Kirsher 
22688df158acSJeff Kirsher /*
22698df158acSJeff Kirsher  * driver helpers
22708df158acSJeff Kirsher  */
22718df158acSJeff Kirsher static const iw_handler gelic_wl_wext_handler[] =
22728df158acSJeff Kirsher {
22738df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name),
22748df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range),
22758df158acSJeff Kirsher 	IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan),
22768df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan),
22778df158acSJeff Kirsher 	IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth),
22788df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth),
22798df158acSJeff Kirsher 	IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid),
22808df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid),
22818df158acSJeff Kirsher 	IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode),
22828df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode),
22838df158acSJeff Kirsher 	IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap),
22848df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap),
22858df158acSJeff Kirsher 	IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext),
22868df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext),
22878df158acSJeff Kirsher 	IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode),
22888df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode),
22898df158acSJeff Kirsher 	IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick),
22908df158acSJeff Kirsher };
22918df158acSJeff Kirsher 
22928df158acSJeff Kirsher static const struct iw_handler_def gelic_wl_wext_handler_def = {
22938df158acSJeff Kirsher 	.num_standard		= ARRAY_SIZE(gelic_wl_wext_handler),
22948df158acSJeff Kirsher 	.standard		= gelic_wl_wext_handler,
22958df158acSJeff Kirsher 	.get_wireless_stats	= gelic_wl_get_wireless_stats,
22968df158acSJeff Kirsher };
22978df158acSJeff Kirsher 
2298fe4a57c5SBill Pemberton static struct net_device *gelic_wl_alloc(struct gelic_card *card)
22998df158acSJeff Kirsher {
23008df158acSJeff Kirsher 	struct net_device *netdev;
23018df158acSJeff Kirsher 	struct gelic_port *port;
23028df158acSJeff Kirsher 	struct gelic_wl_info *wl;
23038df158acSJeff Kirsher 	unsigned int i;
23048df158acSJeff Kirsher 
23058df158acSJeff Kirsher 	pr_debug("%s:start\n", __func__);
23068df158acSJeff Kirsher 	netdev = alloc_etherdev(sizeof(struct gelic_port) +
23078df158acSJeff Kirsher 				sizeof(struct gelic_wl_info));
23088df158acSJeff Kirsher 	pr_debug("%s: netdev =%p card=%p\n", __func__, netdev, card);
23098df158acSJeff Kirsher 	if (!netdev)
23108df158acSJeff Kirsher 		return NULL;
23118df158acSJeff Kirsher 
23128df158acSJeff Kirsher 	strcpy(netdev->name, "wlan%d");
23138df158acSJeff Kirsher 
23148df158acSJeff Kirsher 	port = netdev_priv(netdev);
23158df158acSJeff Kirsher 	port->netdev = netdev;
23168df158acSJeff Kirsher 	port->card = card;
23178df158acSJeff Kirsher 	port->type = GELIC_PORT_WIRELESS;
23188df158acSJeff Kirsher 
23198df158acSJeff Kirsher 	wl = port_wl(port);
23208df158acSJeff Kirsher 	pr_debug("%s: wl=%p port=%p\n", __func__, wl, port);
23218df158acSJeff Kirsher 
23228df158acSJeff Kirsher 	/* allocate scan list */
23238df158acSJeff Kirsher 	wl->networks = kzalloc(sizeof(struct gelic_wl_scan_info) *
23248df158acSJeff Kirsher 			       GELIC_WL_BSS_MAX_ENT, GFP_KERNEL);
23258df158acSJeff Kirsher 
23268df158acSJeff Kirsher 	if (!wl->networks)
23278df158acSJeff Kirsher 		goto fail_bss;
23288df158acSJeff Kirsher 
23298df158acSJeff Kirsher 	wl->eurus_cmd_queue = create_singlethread_workqueue("gelic_cmd");
23308df158acSJeff Kirsher 	if (!wl->eurus_cmd_queue)
23318df158acSJeff Kirsher 		goto fail_cmd_workqueue;
23328df158acSJeff Kirsher 
23338df158acSJeff Kirsher 	wl->event_queue = create_singlethread_workqueue("gelic_event");
23348df158acSJeff Kirsher 	if (!wl->event_queue)
23358df158acSJeff Kirsher 		goto fail_event_workqueue;
23368df158acSJeff Kirsher 
23378df158acSJeff Kirsher 	INIT_LIST_HEAD(&wl->network_free_list);
23388df158acSJeff Kirsher 	INIT_LIST_HEAD(&wl->network_list);
23398df158acSJeff Kirsher 	for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++)
23408df158acSJeff Kirsher 		list_add_tail(&wl->networks[i].list,
23418df158acSJeff Kirsher 			      &wl->network_free_list);
23428df158acSJeff Kirsher 	init_completion(&wl->cmd_done_intr);
23438df158acSJeff Kirsher 
23448df158acSJeff Kirsher 	INIT_DELAYED_WORK(&wl->event_work, gelic_wl_event_worker);
23458df158acSJeff Kirsher 	INIT_DELAYED_WORK(&wl->assoc_work, gelic_wl_assoc_worker);
23468df158acSJeff Kirsher 	mutex_init(&wl->scan_lock);
23478df158acSJeff Kirsher 	mutex_init(&wl->assoc_stat_lock);
23488df158acSJeff Kirsher 
23498df158acSJeff Kirsher 	init_completion(&wl->scan_done);
23508df158acSJeff Kirsher 	/* for the case that no scan request is issued and stop() is called */
23518df158acSJeff Kirsher 	complete(&wl->scan_done);
23528df158acSJeff Kirsher 
23538df158acSJeff Kirsher 	spin_lock_init(&wl->lock);
23548df158acSJeff Kirsher 
23558df158acSJeff Kirsher 	wl->scan_age = 5*HZ; /* FIXME */
23568df158acSJeff Kirsher 
23578df158acSJeff Kirsher 	/* buffer for receiving scanned list etc */
23588df158acSJeff Kirsher 	BUILD_BUG_ON(PAGE_SIZE <
23598df158acSJeff Kirsher 		     sizeof(struct gelic_eurus_scan_info) *
23608df158acSJeff Kirsher 		     GELIC_EURUS_MAX_SCAN);
23618df158acSJeff Kirsher 	pr_debug("%s:end\n", __func__);
23628df158acSJeff Kirsher 	return netdev;
23638df158acSJeff Kirsher 
23648df158acSJeff Kirsher fail_event_workqueue:
23658df158acSJeff Kirsher 	destroy_workqueue(wl->eurus_cmd_queue);
23668df158acSJeff Kirsher fail_cmd_workqueue:
23678df158acSJeff Kirsher 	kfree(wl->networks);
23688df158acSJeff Kirsher fail_bss:
23698df158acSJeff Kirsher 	free_netdev(netdev);
23708df158acSJeff Kirsher 	pr_debug("%s:end error\n", __func__);
23718df158acSJeff Kirsher 	return NULL;
23728df158acSJeff Kirsher 
23738df158acSJeff Kirsher }
23748df158acSJeff Kirsher 
23758df158acSJeff Kirsher static void gelic_wl_free(struct gelic_wl_info *wl)
23768df158acSJeff Kirsher {
23778df158acSJeff Kirsher 	struct gelic_wl_scan_info *scan_info;
23788df158acSJeff Kirsher 	unsigned int i;
23798df158acSJeff Kirsher 
23808df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
23818df158acSJeff Kirsher 
23828df158acSJeff Kirsher 	pr_debug("%s: destroy queues\n", __func__);
23838df158acSJeff Kirsher 	destroy_workqueue(wl->eurus_cmd_queue);
23848df158acSJeff Kirsher 	destroy_workqueue(wl->event_queue);
23858df158acSJeff Kirsher 
23868df158acSJeff Kirsher 	scan_info = wl->networks;
23878df158acSJeff Kirsher 	for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++, scan_info++)
23888df158acSJeff Kirsher 		kfree(scan_info->hwinfo);
23898df158acSJeff Kirsher 	kfree(wl->networks);
23908df158acSJeff Kirsher 
23918df158acSJeff Kirsher 	free_netdev(port_to_netdev(wl_port(wl)));
23928df158acSJeff Kirsher 
23938df158acSJeff Kirsher 	pr_debug("%s: ->\n", __func__);
23948df158acSJeff Kirsher }
23958df158acSJeff Kirsher 
23968df158acSJeff Kirsher static int gelic_wl_try_associate(struct net_device *netdev)
23978df158acSJeff Kirsher {
23988df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
23998df158acSJeff Kirsher 	int ret = -1;
24008df158acSJeff Kirsher 	unsigned int i;
24018df158acSJeff Kirsher 
24028df158acSJeff Kirsher 	pr_debug("%s: <-\n", __func__);
24038df158acSJeff Kirsher 
24048df158acSJeff Kirsher 	/* check constraits for start association */
24058df158acSJeff Kirsher 	/* for no access restriction AP */
24068df158acSJeff Kirsher 	if (wl->group_cipher_method == GELIC_WL_CIPHER_NONE) {
24078df158acSJeff Kirsher 		if (test_bit(GELIC_WL_STAT_CONFIGURED,
24088df158acSJeff Kirsher 			     &wl->stat))
24098df158acSJeff Kirsher 			goto do_associate;
24108df158acSJeff Kirsher 		else {
24118df158acSJeff Kirsher 			pr_debug("%s: no wep, not configured\n", __func__);
24128df158acSJeff Kirsher 			return ret;
24138df158acSJeff Kirsher 		}
24148df158acSJeff Kirsher 	}
24158df158acSJeff Kirsher 
24168df158acSJeff Kirsher 	/* for WEP, one of four keys should be set */
24178df158acSJeff Kirsher 	if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
24188df158acSJeff Kirsher 		/* one of keys set */
24198df158acSJeff Kirsher 		for (i = 0; i < GELIC_WEP_KEYS; i++) {
24208df158acSJeff Kirsher 			if (test_bit(i, &wl->key_enabled))
24218df158acSJeff Kirsher 			    goto do_associate;
24228df158acSJeff Kirsher 		}
24238df158acSJeff Kirsher 		pr_debug("%s: WEP, but no key specified\n", __func__);
24248df158acSJeff Kirsher 		return ret;
24258df158acSJeff Kirsher 	}
24268df158acSJeff Kirsher 
24278df158acSJeff Kirsher 	/* for WPA[2], psk should be set */
24288df158acSJeff Kirsher 	if ((wl->group_cipher_method == GELIC_WL_CIPHER_TKIP) ||
24298df158acSJeff Kirsher 	    (wl->group_cipher_method == GELIC_WL_CIPHER_AES)) {
24308df158acSJeff Kirsher 		if (test_bit(GELIC_WL_STAT_WPA_PSK_SET,
24318df158acSJeff Kirsher 			     &wl->stat))
24328df158acSJeff Kirsher 			goto do_associate;
24338df158acSJeff Kirsher 		else {
24348df158acSJeff Kirsher 			pr_debug("%s: AES/TKIP, but PSK not configured\n",
24358df158acSJeff Kirsher 				 __func__);
24368df158acSJeff Kirsher 			return ret;
24378df158acSJeff Kirsher 		}
24388df158acSJeff Kirsher 	}
24398df158acSJeff Kirsher 
24408df158acSJeff Kirsher do_associate:
24418df158acSJeff Kirsher 	ret = schedule_delayed_work(&wl->assoc_work, 0);
24428df158acSJeff Kirsher 	pr_debug("%s: start association work %d\n", __func__, ret);
24438df158acSJeff Kirsher 	return ret;
24448df158acSJeff Kirsher }
24458df158acSJeff Kirsher 
24468df158acSJeff Kirsher /*
24478df158acSJeff Kirsher  * netdev handlers
24488df158acSJeff Kirsher  */
24498df158acSJeff Kirsher static int gelic_wl_open(struct net_device *netdev)
24508df158acSJeff Kirsher {
24518df158acSJeff Kirsher 	struct gelic_card *card = netdev_card(netdev);
24528df158acSJeff Kirsher 
24538df158acSJeff Kirsher 	pr_debug("%s:->%p\n", __func__, netdev);
24548df158acSJeff Kirsher 
24558df158acSJeff Kirsher 	gelic_card_up(card);
24568df158acSJeff Kirsher 
24578df158acSJeff Kirsher 	/* try to associate */
24588df158acSJeff Kirsher 	gelic_wl_try_associate(netdev);
24598df158acSJeff Kirsher 
24608df158acSJeff Kirsher 	netif_start_queue(netdev);
24618df158acSJeff Kirsher 
24628df158acSJeff Kirsher 	pr_debug("%s:<-\n", __func__);
24638df158acSJeff Kirsher 	return 0;
24648df158acSJeff Kirsher }
24658df158acSJeff Kirsher 
24668df158acSJeff Kirsher /*
24678df158acSJeff Kirsher  * reset state machine
24688df158acSJeff Kirsher  */
24698df158acSJeff Kirsher static int gelic_wl_reset_state(struct gelic_wl_info *wl)
24708df158acSJeff Kirsher {
24718df158acSJeff Kirsher 	struct gelic_wl_scan_info *target;
24728df158acSJeff Kirsher 	struct gelic_wl_scan_info *tmp;
24738df158acSJeff Kirsher 
24748df158acSJeff Kirsher 	/* empty scan list */
24758df158acSJeff Kirsher 	list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
24768df158acSJeff Kirsher 		list_move_tail(&target->list, &wl->network_free_list);
24778df158acSJeff Kirsher 	}
24788df158acSJeff Kirsher 	wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
24798df158acSJeff Kirsher 
24808df158acSJeff Kirsher 	/* clear configuration */
24818df158acSJeff Kirsher 	wl->auth_method = GELIC_EURUS_AUTH_OPEN;
24828df158acSJeff Kirsher 	wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
24838df158acSJeff Kirsher 	wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
24848df158acSJeff Kirsher 	wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
24858df158acSJeff Kirsher 
24868df158acSJeff Kirsher 	wl->key_enabled = 0;
24878df158acSJeff Kirsher 	wl->current_key = 0;
24888df158acSJeff Kirsher 
24898df158acSJeff Kirsher 	wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE;
24908df158acSJeff Kirsher 	wl->psk_len = 0;
24918df158acSJeff Kirsher 
24928df158acSJeff Kirsher 	wl->essid_len = 0;
24938df158acSJeff Kirsher 	memset(wl->essid, 0, sizeof(wl->essid));
24948df158acSJeff Kirsher 	memset(wl->bssid, 0, sizeof(wl->bssid));
24958df158acSJeff Kirsher 	memset(wl->active_bssid, 0, sizeof(wl->active_bssid));
24968df158acSJeff Kirsher 
24978df158acSJeff Kirsher 	wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
24988df158acSJeff Kirsher 
24998df158acSJeff Kirsher 	memset(&wl->iwstat, 0, sizeof(wl->iwstat));
25008df158acSJeff Kirsher 	/* all status bit clear */
25018df158acSJeff Kirsher 	wl->stat = 0;
25028df158acSJeff Kirsher 	return 0;
25038df158acSJeff Kirsher }
25048df158acSJeff Kirsher 
25058df158acSJeff Kirsher /*
25068df158acSJeff Kirsher  * Tell eurus to terminate association
25078df158acSJeff Kirsher  */
25088df158acSJeff Kirsher static void gelic_wl_disconnect(struct net_device *netdev)
25098df158acSJeff Kirsher {
25108df158acSJeff Kirsher 	struct gelic_port *port = netdev_priv(netdev);
25118df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(port);
25128df158acSJeff Kirsher 	struct gelic_eurus_cmd *cmd;
25138df158acSJeff Kirsher 
25148df158acSJeff Kirsher 	/*
25158df158acSJeff Kirsher 	 * If scann process is running on chip,
25168df158acSJeff Kirsher 	 * further requests will be rejected
25178df158acSJeff Kirsher 	 */
25188df158acSJeff Kirsher 	if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING)
25198df158acSJeff Kirsher 		wait_for_completion_timeout(&wl->scan_done, HZ);
25208df158acSJeff Kirsher 
25218df158acSJeff Kirsher 	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0);
25228df158acSJeff Kirsher 	kfree(cmd);
25238df158acSJeff Kirsher 	gelic_wl_send_iwap_event(wl, NULL);
25248df158acSJeff Kirsher };
25258df158acSJeff Kirsher 
25268df158acSJeff Kirsher static int gelic_wl_stop(struct net_device *netdev)
25278df158acSJeff Kirsher {
25288df158acSJeff Kirsher 	struct gelic_port *port = netdev_priv(netdev);
25298df158acSJeff Kirsher 	struct gelic_wl_info *wl = port_wl(port);
25308df158acSJeff Kirsher 	struct gelic_card *card = netdev_card(netdev);
25318df158acSJeff Kirsher 
25328df158acSJeff Kirsher 	pr_debug("%s:<-\n", __func__);
25338df158acSJeff Kirsher 
25348df158acSJeff Kirsher 	/*
25358df158acSJeff Kirsher 	 * Cancel pending association work.
25368df158acSJeff Kirsher 	 * event work can run after netdev down
25378df158acSJeff Kirsher 	 */
25388df158acSJeff Kirsher 	cancel_delayed_work(&wl->assoc_work);
25398df158acSJeff Kirsher 
25408df158acSJeff Kirsher 	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
25418df158acSJeff Kirsher 		gelic_wl_disconnect(netdev);
25428df158acSJeff Kirsher 
25438df158acSJeff Kirsher 	/* reset our state machine */
25448df158acSJeff Kirsher 	gelic_wl_reset_state(wl);
25458df158acSJeff Kirsher 
25468df158acSJeff Kirsher 	netif_stop_queue(netdev);
25478df158acSJeff Kirsher 
25488df158acSJeff Kirsher 	gelic_card_down(card);
25498df158acSJeff Kirsher 
25508df158acSJeff Kirsher 	pr_debug("%s:->\n", __func__);
25518df158acSJeff Kirsher 	return 0;
25528df158acSJeff Kirsher }
25538df158acSJeff Kirsher 
25548df158acSJeff Kirsher /* -- */
25558df158acSJeff Kirsher 
25568df158acSJeff Kirsher static const struct net_device_ops gelic_wl_netdevice_ops = {
25578df158acSJeff Kirsher 	.ndo_open = gelic_wl_open,
25588df158acSJeff Kirsher 	.ndo_stop = gelic_wl_stop,
25598df158acSJeff Kirsher 	.ndo_start_xmit = gelic_net_xmit,
2560afc4b13dSJiri Pirko 	.ndo_set_rx_mode = gelic_net_set_multi,
25618df158acSJeff Kirsher 	.ndo_change_mtu = gelic_net_change_mtu,
25628df158acSJeff Kirsher 	.ndo_tx_timeout = gelic_net_tx_timeout,
25638df158acSJeff Kirsher 	.ndo_set_mac_address = eth_mac_addr,
25648df158acSJeff Kirsher 	.ndo_validate_addr = eth_validate_addr,
25658df158acSJeff Kirsher #ifdef CONFIG_NET_POLL_CONTROLLER
25668df158acSJeff Kirsher 	.ndo_poll_controller = gelic_net_poll_controller,
25678df158acSJeff Kirsher #endif
25688df158acSJeff Kirsher };
25698df158acSJeff Kirsher 
25708df158acSJeff Kirsher static const struct ethtool_ops gelic_wl_ethtool_ops = {
25718df158acSJeff Kirsher 	.get_drvinfo	= gelic_net_get_drvinfo,
25728df158acSJeff Kirsher 	.get_link	= gelic_wl_get_link,
25738df158acSJeff Kirsher };
25748df158acSJeff Kirsher 
2575fe4a57c5SBill Pemberton static void gelic_wl_setup_netdev_ops(struct net_device *netdev)
25768df158acSJeff Kirsher {
25778df158acSJeff Kirsher 	struct gelic_wl_info *wl;
25788df158acSJeff Kirsher 	wl = port_wl(netdev_priv(netdev));
25798df158acSJeff Kirsher 	BUG_ON(!wl);
25808df158acSJeff Kirsher 	netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT;
25818df158acSJeff Kirsher 
25828df158acSJeff Kirsher 	netdev->ethtool_ops = &gelic_wl_ethtool_ops;
25838df158acSJeff Kirsher 	netdev->netdev_ops = &gelic_wl_netdevice_ops;
25848df158acSJeff Kirsher 	netdev->wireless_data = &wl->wireless_data;
25858df158acSJeff Kirsher 	netdev->wireless_handlers = &gelic_wl_wext_handler_def;
25868df158acSJeff Kirsher }
25878df158acSJeff Kirsher 
25888df158acSJeff Kirsher /*
25898df158acSJeff Kirsher  * driver probe/remove
25908df158acSJeff Kirsher  */
2591fe4a57c5SBill Pemberton int gelic_wl_driver_probe(struct gelic_card *card)
25928df158acSJeff Kirsher {
25938df158acSJeff Kirsher 	int ret;
25948df158acSJeff Kirsher 	struct net_device *netdev;
25958df158acSJeff Kirsher 
25968df158acSJeff Kirsher 	pr_debug("%s:start\n", __func__);
25978df158acSJeff Kirsher 
25988df158acSJeff Kirsher 	if (ps3_compare_firmware_version(1, 6, 0) < 0)
25998df158acSJeff Kirsher 		return 0;
26008df158acSJeff Kirsher 	if (!card->vlan[GELIC_PORT_WIRELESS].tx)
26018df158acSJeff Kirsher 		return 0;
26028df158acSJeff Kirsher 
26038df158acSJeff Kirsher 	/* alloc netdevice for wireless */
26048df158acSJeff Kirsher 	netdev = gelic_wl_alloc(card);
26058df158acSJeff Kirsher 	if (!netdev)
26068df158acSJeff Kirsher 		return -ENOMEM;
26078df158acSJeff Kirsher 
26088df158acSJeff Kirsher 	/* setup net_device structure */
26098df158acSJeff Kirsher 	SET_NETDEV_DEV(netdev, &card->dev->core);
26108df158acSJeff Kirsher 	gelic_wl_setup_netdev_ops(netdev);
26118df158acSJeff Kirsher 
26128df158acSJeff Kirsher 	/* setup some of net_device and register it */
26138df158acSJeff Kirsher 	ret = gelic_net_setup_netdev(netdev, card);
26148df158acSJeff Kirsher 	if (ret)
26158df158acSJeff Kirsher 		goto fail_setup;
26168df158acSJeff Kirsher 	card->netdev[GELIC_PORT_WIRELESS] = netdev;
26178df158acSJeff Kirsher 
26188df158acSJeff Kirsher 	/* add enable wireless interrupt */
26198df158acSJeff Kirsher 	card->irq_mask |= GELIC_CARD_WLAN_EVENT_RECEIVED |
26208df158acSJeff Kirsher 		GELIC_CARD_WLAN_COMMAND_COMPLETED;
26218df158acSJeff Kirsher 	/* to allow wireless commands while both interfaces are down */
26228df158acSJeff Kirsher 	gelic_card_set_irq_mask(card, GELIC_CARD_WLAN_EVENT_RECEIVED |
26238df158acSJeff Kirsher 				GELIC_CARD_WLAN_COMMAND_COMPLETED);
26248df158acSJeff Kirsher 	pr_debug("%s:end\n", __func__);
26258df158acSJeff Kirsher 	return 0;
26268df158acSJeff Kirsher 
26278df158acSJeff Kirsher fail_setup:
26288df158acSJeff Kirsher 	gelic_wl_free(port_wl(netdev_port(netdev)));
26298df158acSJeff Kirsher 
26308df158acSJeff Kirsher 	return ret;
26318df158acSJeff Kirsher }
26328df158acSJeff Kirsher 
26338df158acSJeff Kirsher int gelic_wl_driver_remove(struct gelic_card *card)
26348df158acSJeff Kirsher {
26358df158acSJeff Kirsher 	struct gelic_wl_info *wl;
26368df158acSJeff Kirsher 	struct net_device *netdev;
26378df158acSJeff Kirsher 
26388df158acSJeff Kirsher 	pr_debug("%s:start\n", __func__);
26398df158acSJeff Kirsher 
26408df158acSJeff Kirsher 	if (ps3_compare_firmware_version(1, 6, 0) < 0)
26418df158acSJeff Kirsher 		return 0;
26428df158acSJeff Kirsher 	if (!card->vlan[GELIC_PORT_WIRELESS].tx)
26438df158acSJeff Kirsher 		return 0;
26448df158acSJeff Kirsher 
26458df158acSJeff Kirsher 	netdev = card->netdev[GELIC_PORT_WIRELESS];
26468df158acSJeff Kirsher 	wl = port_wl(netdev_priv(netdev));
26478df158acSJeff Kirsher 
26488df158acSJeff Kirsher 	/* if the interface was not up, but associated */
26498df158acSJeff Kirsher 	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
26508df158acSJeff Kirsher 		gelic_wl_disconnect(netdev);
26518df158acSJeff Kirsher 
26528df158acSJeff Kirsher 	complete(&wl->cmd_done_intr);
26538df158acSJeff Kirsher 
26548df158acSJeff Kirsher 	/* cancel all work queue */
26558df158acSJeff Kirsher 	cancel_delayed_work(&wl->assoc_work);
26568df158acSJeff Kirsher 	cancel_delayed_work(&wl->event_work);
26578df158acSJeff Kirsher 	flush_workqueue(wl->eurus_cmd_queue);
26588df158acSJeff Kirsher 	flush_workqueue(wl->event_queue);
26598df158acSJeff Kirsher 
26608df158acSJeff Kirsher 	unregister_netdev(netdev);
26618df158acSJeff Kirsher 
26628df158acSJeff Kirsher 	/* disable wireless interrupt */
26638df158acSJeff Kirsher 	pr_debug("%s: disable intr\n", __func__);
26648df158acSJeff Kirsher 	card->irq_mask &= ~(GELIC_CARD_WLAN_EVENT_RECEIVED |
26658df158acSJeff Kirsher 			    GELIC_CARD_WLAN_COMMAND_COMPLETED);
26668df158acSJeff Kirsher 	/* free bss list, netdev*/
26678df158acSJeff Kirsher 	gelic_wl_free(wl);
26688df158acSJeff Kirsher 	pr_debug("%s:end\n", __func__);
26698df158acSJeff Kirsher 	return 0;
26708df158acSJeff Kirsher }
2671