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