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