162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  PS3 gelic network driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2007 Sony Computer Entertainment Inc.
662306a36Sopenharmony_ci * Copyright 2007 Sony Corporation
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#undef DEBUG
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/etherdevice.h>
1562306a36Sopenharmony_ci#include <linux/ethtool.h>
1662306a36Sopenharmony_ci#include <linux/if_vlan.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/in.h>
1962306a36Sopenharmony_ci#include <linux/ip.h>
2062306a36Sopenharmony_ci#include <linux/tcp.h>
2162306a36Sopenharmony_ci#include <linux/wireless.h>
2262306a36Sopenharmony_ci#include <linux/ieee80211.h>
2362306a36Sopenharmony_ci#include <linux/if_arp.h>
2462306a36Sopenharmony_ci#include <linux/ctype.h>
2562306a36Sopenharmony_ci#include <linux/string.h>
2662306a36Sopenharmony_ci#include <net/iw_handler.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2962306a36Sopenharmony_ci#include <net/checksum.h>
3062306a36Sopenharmony_ci#include <asm/firmware.h>
3162306a36Sopenharmony_ci#include <asm/ps3.h>
3262306a36Sopenharmony_ci#include <asm/lv1call.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "ps3_gelic_net.h"
3562306a36Sopenharmony_ci#include "ps3_gelic_wireless.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan,
3962306a36Sopenharmony_ci			       u8 *essid, size_t essid_len);
4062306a36Sopenharmony_cistatic int gelic_wl_try_associate(struct net_device *netdev);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * tables
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* 802.11b/g channel to freq in MHz */
4762306a36Sopenharmony_cistatic const int channel_freq[] = {
4862306a36Sopenharmony_ci	2412, 2417, 2422, 2427, 2432,
4962306a36Sopenharmony_ci	2437, 2442, 2447, 2452, 2457,
5062306a36Sopenharmony_ci	2462, 2467, 2472, 2484
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci#define NUM_CHANNELS ARRAY_SIZE(channel_freq)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* in bps */
5562306a36Sopenharmony_cistatic const int bitrate_list[] = {
5662306a36Sopenharmony_ci	  1000000,
5762306a36Sopenharmony_ci	  2000000,
5862306a36Sopenharmony_ci	  5500000,
5962306a36Sopenharmony_ci	 11000000,
6062306a36Sopenharmony_ci	  6000000,
6162306a36Sopenharmony_ci	  9000000,
6262306a36Sopenharmony_ci	 12000000,
6362306a36Sopenharmony_ci	 18000000,
6462306a36Sopenharmony_ci	 24000000,
6562306a36Sopenharmony_ci	 36000000,
6662306a36Sopenharmony_ci	 48000000,
6762306a36Sopenharmony_ci	 54000000
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci#define NUM_BITRATES ARRAY_SIZE(bitrate_list)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * wpa2 support requires the hypervisor version 2.0 or later
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistatic inline int wpa2_capable(void)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	return 0 <= ps3_compare_firmware_version(2, 0, 0);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic inline int precise_ie(void)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	return 0 <= ps3_compare_firmware_version(2, 2, 0);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * post_eurus_cmd helpers
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistruct eurus_cmd_arg_info {
8762306a36Sopenharmony_ci	int pre_arg; /* command requires arg1, arg2 at POST COMMAND */
8862306a36Sopenharmony_ci	int post_arg; /* command requires arg1, arg2 at GET_RESULT */
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic const struct eurus_cmd_arg_info cmd_info[GELIC_EURUS_CMD_MAX_INDEX] = {
9262306a36Sopenharmony_ci	[GELIC_EURUS_CMD_SET_COMMON_CFG] = { .pre_arg = 1},
9362306a36Sopenharmony_ci	[GELIC_EURUS_CMD_SET_WEP_CFG]    = { .pre_arg = 1},
9462306a36Sopenharmony_ci	[GELIC_EURUS_CMD_SET_WPA_CFG]    = { .pre_arg = 1},
9562306a36Sopenharmony_ci	[GELIC_EURUS_CMD_GET_COMMON_CFG] = { .post_arg = 1},
9662306a36Sopenharmony_ci	[GELIC_EURUS_CMD_GET_WEP_CFG]    = { .post_arg = 1},
9762306a36Sopenharmony_ci	[GELIC_EURUS_CMD_GET_WPA_CFG]    = { .post_arg = 1},
9862306a36Sopenharmony_ci	[GELIC_EURUS_CMD_GET_RSSI_CFG]   = { .post_arg = 1},
9962306a36Sopenharmony_ci	[GELIC_EURUS_CMD_START_SCAN]     = { .pre_arg = 1},
10062306a36Sopenharmony_ci	[GELIC_EURUS_CMD_GET_SCAN]       = { .post_arg = 1},
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#ifdef DEBUG
10462306a36Sopenharmony_cistatic const char *cmdstr(enum gelic_eurus_command ix)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	switch (ix) {
10762306a36Sopenharmony_ci	case GELIC_EURUS_CMD_ASSOC:
10862306a36Sopenharmony_ci		return "ASSOC";
10962306a36Sopenharmony_ci	case GELIC_EURUS_CMD_DISASSOC:
11062306a36Sopenharmony_ci		return "DISASSOC";
11162306a36Sopenharmony_ci	case GELIC_EURUS_CMD_START_SCAN:
11262306a36Sopenharmony_ci		return "SCAN";
11362306a36Sopenharmony_ci	case GELIC_EURUS_CMD_GET_SCAN:
11462306a36Sopenharmony_ci		return "GET SCAN";
11562306a36Sopenharmony_ci	case GELIC_EURUS_CMD_SET_COMMON_CFG:
11662306a36Sopenharmony_ci		return "SET_COMMON_CFG";
11762306a36Sopenharmony_ci	case GELIC_EURUS_CMD_GET_COMMON_CFG:
11862306a36Sopenharmony_ci		return "GET_COMMON_CFG";
11962306a36Sopenharmony_ci	case GELIC_EURUS_CMD_SET_WEP_CFG:
12062306a36Sopenharmony_ci		return "SET_WEP_CFG";
12162306a36Sopenharmony_ci	case GELIC_EURUS_CMD_GET_WEP_CFG:
12262306a36Sopenharmony_ci		return "GET_WEP_CFG";
12362306a36Sopenharmony_ci	case GELIC_EURUS_CMD_SET_WPA_CFG:
12462306a36Sopenharmony_ci		return "SET_WPA_CFG";
12562306a36Sopenharmony_ci	case GELIC_EURUS_CMD_GET_WPA_CFG:
12662306a36Sopenharmony_ci		return "GET_WPA_CFG";
12762306a36Sopenharmony_ci	case GELIC_EURUS_CMD_GET_RSSI_CFG:
12862306a36Sopenharmony_ci		return "GET_RSSI";
12962306a36Sopenharmony_ci	default:
13062306a36Sopenharmony_ci		break;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	return "";
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci#else
13562306a36Sopenharmony_cistatic inline const char *cmdstr(enum gelic_eurus_command ix)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	return "";
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci#endif
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/* synchronously do eurus commands */
14262306a36Sopenharmony_cistatic void gelic_eurus_sync_cmd_worker(struct work_struct *work)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
14562306a36Sopenharmony_ci	struct gelic_card *card;
14662306a36Sopenharmony_ci	struct gelic_wl_info *wl;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	u64 arg1, arg2;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
15162306a36Sopenharmony_ci	cmd = container_of(work, struct gelic_eurus_cmd, work);
15262306a36Sopenharmony_ci	BUG_ON(cmd_info[cmd->cmd].pre_arg &&
15362306a36Sopenharmony_ci	       cmd_info[cmd->cmd].post_arg);
15462306a36Sopenharmony_ci	wl = cmd->wl;
15562306a36Sopenharmony_ci	card = port_to_card(wl_port(wl));
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (cmd_info[cmd->cmd].pre_arg) {
15862306a36Sopenharmony_ci		arg1 = (cmd->buffer) ?
15962306a36Sopenharmony_ci			ps3_mm_phys_to_lpar(__pa(cmd->buffer)) :
16062306a36Sopenharmony_ci			0;
16162306a36Sopenharmony_ci		arg2 = cmd->buf_size;
16262306a36Sopenharmony_ci	} else {
16362306a36Sopenharmony_ci		arg1 = 0;
16462306a36Sopenharmony_ci		arg2 = 0;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	init_completion(&wl->cmd_done_intr);
16762306a36Sopenharmony_ci	pr_debug("%s: cmd='%s' start\n", __func__, cmdstr(cmd->cmd));
16862306a36Sopenharmony_ci	cmd->status = lv1_net_control(bus_id(card), dev_id(card),
16962306a36Sopenharmony_ci				      GELIC_LV1_POST_WLAN_CMD,
17062306a36Sopenharmony_ci				      cmd->cmd, arg1, arg2,
17162306a36Sopenharmony_ci				      &cmd->tag, &cmd->size);
17262306a36Sopenharmony_ci	if (cmd->status) {
17362306a36Sopenharmony_ci		complete(&cmd->done);
17462306a36Sopenharmony_ci		pr_info("%s: cmd issue failed\n", __func__);
17562306a36Sopenharmony_ci		return;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	wait_for_completion(&wl->cmd_done_intr);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (cmd_info[cmd->cmd].post_arg) {
18162306a36Sopenharmony_ci		arg1 = ps3_mm_phys_to_lpar(__pa(cmd->buffer));
18262306a36Sopenharmony_ci		arg2 = cmd->buf_size;
18362306a36Sopenharmony_ci	} else {
18462306a36Sopenharmony_ci		arg1 = 0;
18562306a36Sopenharmony_ci		arg2 = 0;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	cmd->status = lv1_net_control(bus_id(card), dev_id(card),
18962306a36Sopenharmony_ci				      GELIC_LV1_GET_WLAN_CMD_RESULT,
19062306a36Sopenharmony_ci				      cmd->tag, arg1, arg2,
19162306a36Sopenharmony_ci				      &cmd->cmd_status, &cmd->size);
19262306a36Sopenharmony_ci#ifdef DEBUG
19362306a36Sopenharmony_ci	if (cmd->status || cmd->cmd_status) {
19462306a36Sopenharmony_ci	pr_debug("%s: cmd done tag=%#lx arg1=%#lx, arg2=%#lx\n", __func__,
19562306a36Sopenharmony_ci		 cmd->tag, arg1, arg2);
19662306a36Sopenharmony_ci	pr_debug("%s: cmd done status=%#x cmd_status=%#lx size=%#lx\n",
19762306a36Sopenharmony_ci		 __func__, cmd->status, cmd->cmd_status, cmd->size);
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci#endif
20062306a36Sopenharmony_ci	complete(&cmd->done);
20162306a36Sopenharmony_ci	pr_debug("%s: cmd='%s' done\n", __func__, cmdstr(cmd->cmd));
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic struct gelic_eurus_cmd *gelic_eurus_sync_cmd(struct gelic_wl_info *wl,
20562306a36Sopenharmony_ci						    unsigned int eurus_cmd,
20662306a36Sopenharmony_ci						    void *buffer,
20762306a36Sopenharmony_ci						    unsigned int buf_size)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* allocate cmd */
21262306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
21362306a36Sopenharmony_ci	if (!cmd)
21462306a36Sopenharmony_ci		return NULL;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* initialize members */
21762306a36Sopenharmony_ci	cmd->cmd = eurus_cmd;
21862306a36Sopenharmony_ci	cmd->buffer = buffer;
21962306a36Sopenharmony_ci	cmd->buf_size = buf_size;
22062306a36Sopenharmony_ci	cmd->wl = wl;
22162306a36Sopenharmony_ci	INIT_WORK(&cmd->work, gelic_eurus_sync_cmd_worker);
22262306a36Sopenharmony_ci	init_completion(&cmd->done);
22362306a36Sopenharmony_ci	queue_work(wl->eurus_cmd_queue, &cmd->work);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* wait for command completion */
22662306a36Sopenharmony_ci	wait_for_completion(&cmd->done);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return cmd;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic u32 gelic_wl_get_link(struct net_device *netdev)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
23462306a36Sopenharmony_ci	u32 ret;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
23762306a36Sopenharmony_ci	mutex_lock(&wl->assoc_stat_lock);
23862306a36Sopenharmony_ci	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
23962306a36Sopenharmony_ci		ret = 1;
24062306a36Sopenharmony_ci	else
24162306a36Sopenharmony_ci		ret = 0;
24262306a36Sopenharmony_ci	mutex_unlock(&wl->assoc_stat_lock);
24362306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
24462306a36Sopenharmony_ci	return ret;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void gelic_wl_send_iwap_event(struct gelic_wl_info *wl, u8 *bssid)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	union iwreq_data data;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	memset(&data, 0, sizeof(data));
25262306a36Sopenharmony_ci	if (bssid)
25362306a36Sopenharmony_ci		memcpy(data.ap_addr.sa_data, bssid, ETH_ALEN);
25462306a36Sopenharmony_ci	data.ap_addr.sa_family = ARPHRD_ETHER;
25562306a36Sopenharmony_ci	wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWAP,
25662306a36Sopenharmony_ci			    &data, NULL);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/*
26062306a36Sopenharmony_ci * wireless extension handlers and helpers
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci/* SIOGIWNAME */
26462306a36Sopenharmony_cistatic int gelic_wl_get_name(struct net_device *dev,
26562306a36Sopenharmony_ci			     struct iw_request_info *info,
26662306a36Sopenharmony_ci			     union iwreq_data *iwreq, char *extra)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	strcpy(iwreq->name, "IEEE 802.11bg");
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void gelic_wl_get_ch_info(struct gelic_wl_info *wl)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct gelic_card *card = port_to_card(wl_port(wl));
27562306a36Sopenharmony_ci	u64 ch_info_raw, tmp;
27662306a36Sopenharmony_ci	int status;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (!test_and_set_bit(GELIC_WL_STAT_CH_INFO, &wl->stat)) {
27962306a36Sopenharmony_ci		status = lv1_net_control(bus_id(card), dev_id(card),
28062306a36Sopenharmony_ci					 GELIC_LV1_GET_CHANNEL, 0, 0, 0,
28162306a36Sopenharmony_ci					 &ch_info_raw,
28262306a36Sopenharmony_ci					 &tmp);
28362306a36Sopenharmony_ci		/* some fw versions may return error */
28462306a36Sopenharmony_ci		if (status) {
28562306a36Sopenharmony_ci			if (status != LV1_NO_ENTRY)
28662306a36Sopenharmony_ci				pr_info("%s: available ch unknown\n", __func__);
28762306a36Sopenharmony_ci			wl->ch_info = 0x07ff;/* 11 ch */
28862306a36Sopenharmony_ci		} else
28962306a36Sopenharmony_ci			/* 16 bits of MSB has available channels */
29062306a36Sopenharmony_ci			wl->ch_info = ch_info_raw >> 48;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/* SIOGIWRANGE */
29562306a36Sopenharmony_cistatic int gelic_wl_get_range(struct net_device *netdev,
29662306a36Sopenharmony_ci			      struct iw_request_info *info,
29762306a36Sopenharmony_ci			      union iwreq_data *iwreq, char *extra)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct iw_point *point = &iwreq->data;
30062306a36Sopenharmony_ci	struct iw_range *range = (struct iw_range *)extra;
30162306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
30262306a36Sopenharmony_ci	unsigned int i, chs;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
30562306a36Sopenharmony_ci	point->length = sizeof(struct iw_range);
30662306a36Sopenharmony_ci	memset(range, 0, sizeof(struct iw_range));
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	range->we_version_compiled = WIRELESS_EXT;
30962306a36Sopenharmony_ci	range->we_version_source = 22;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* available channels and frequencies */
31262306a36Sopenharmony_ci	gelic_wl_get_ch_info(wl);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	for (i = 0, chs = 0;
31562306a36Sopenharmony_ci	     i < NUM_CHANNELS && chs < IW_MAX_FREQUENCIES; i++)
31662306a36Sopenharmony_ci		if (wl->ch_info & (1 << i)) {
31762306a36Sopenharmony_ci			range->freq[chs].i = i + 1;
31862306a36Sopenharmony_ci			range->freq[chs].m = channel_freq[i];
31962306a36Sopenharmony_ci			range->freq[chs].e = 6;
32062306a36Sopenharmony_ci			chs++;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci	range->num_frequency = chs;
32362306a36Sopenharmony_ci	range->old_num_frequency = chs;
32462306a36Sopenharmony_ci	range->num_channels = chs;
32562306a36Sopenharmony_ci	range->old_num_channels = chs;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* bitrates */
32862306a36Sopenharmony_ci	for (i = 0; i < NUM_BITRATES; i++)
32962306a36Sopenharmony_ci		range->bitrate[i] = bitrate_list[i];
33062306a36Sopenharmony_ci	range->num_bitrates = i;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* signal levels */
33362306a36Sopenharmony_ci	range->max_qual.qual = 100; /* relative value */
33462306a36Sopenharmony_ci	range->max_qual.level = 100;
33562306a36Sopenharmony_ci	range->avg_qual.qual = 50;
33662306a36Sopenharmony_ci	range->avg_qual.level = 50;
33762306a36Sopenharmony_ci	range->sensitivity = 0;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* Event capability */
34062306a36Sopenharmony_ci	IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
34162306a36Sopenharmony_ci	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
34262306a36Sopenharmony_ci	IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* encryption capability */
34562306a36Sopenharmony_ci	range->enc_capa = IW_ENC_CAPA_WPA |
34662306a36Sopenharmony_ci		IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP |
34762306a36Sopenharmony_ci		IW_ENC_CAPA_4WAY_HANDSHAKE;
34862306a36Sopenharmony_ci	if (wpa2_capable())
34962306a36Sopenharmony_ci		range->enc_capa |= IW_ENC_CAPA_WPA2;
35062306a36Sopenharmony_ci	range->encoding_size[0] = 5;	/* 40bit WEP */
35162306a36Sopenharmony_ci	range->encoding_size[1] = 13;	/* 104bit WEP */
35262306a36Sopenharmony_ci	range->encoding_size[2] = 32;	/* WPA-PSK */
35362306a36Sopenharmony_ci	range->num_encoding_sizes = 3;
35462306a36Sopenharmony_ci	range->max_encoding_tokens = GELIC_WEP_KEYS;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* scan capability */
35762306a36Sopenharmony_ci	range->scan_capa = IW_SCAN_CAPA_ESSID;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/* SIOC{G,S}IWSCAN */
36562306a36Sopenharmony_cistatic int gelic_wl_set_scan(struct net_device *netdev,
36662306a36Sopenharmony_ci			   struct iw_request_info *info,
36762306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
37062306a36Sopenharmony_ci	struct iw_scan_req *req;
37162306a36Sopenharmony_ci	u8 *essid = NULL;
37262306a36Sopenharmony_ci	size_t essid_len = 0;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (wrqu->data.length == sizeof(struct iw_scan_req) &&
37562306a36Sopenharmony_ci	    wrqu->data.flags & IW_SCAN_THIS_ESSID) {
37662306a36Sopenharmony_ci		req = (struct iw_scan_req*)extra;
37762306a36Sopenharmony_ci		essid = req->essid;
37862306a36Sopenharmony_ci		essid_len = req->essid_len;
37962306a36Sopenharmony_ci		pr_debug("%s: ESSID scan =%s\n", __func__, essid);
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci	return gelic_wl_start_scan(wl, 1, essid, essid_len);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci#define OUI_LEN 3
38562306a36Sopenharmony_cistatic const u8 rsn_oui[OUI_LEN] = { 0x00, 0x0f, 0xac };
38662306a36Sopenharmony_cistatic const u8 wpa_oui[OUI_LEN] = { 0x00, 0x50, 0xf2 };
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/*
38962306a36Sopenharmony_ci * synthesize WPA/RSN IE data
39062306a36Sopenharmony_ci * See WiFi WPA specification and IEEE 802.11-2007 7.3.2.25
39162306a36Sopenharmony_ci * for the format
39262306a36Sopenharmony_ci */
39362306a36Sopenharmony_cistatic size_t gelic_wl_synthesize_ie(u8 *buf,
39462306a36Sopenharmony_ci				     struct gelic_eurus_scan_info *scan)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	const u8 *oui_header;
39862306a36Sopenharmony_ci	u8 *start = buf;
39962306a36Sopenharmony_ci	int rsn;
40062306a36Sopenharmony_ci	int ccmp;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	pr_debug("%s: <- sec=%16x\n", __func__, scan->security);
40362306a36Sopenharmony_ci	switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_MASK) {
40462306a36Sopenharmony_ci	case GELIC_EURUS_SCAN_SEC_WPA:
40562306a36Sopenharmony_ci		rsn = 0;
40662306a36Sopenharmony_ci		break;
40762306a36Sopenharmony_ci	case GELIC_EURUS_SCAN_SEC_WPA2:
40862306a36Sopenharmony_ci		rsn = 1;
40962306a36Sopenharmony_ci		break;
41062306a36Sopenharmony_ci	default:
41162306a36Sopenharmony_ci		/* WEP or none.  No IE returned */
41262306a36Sopenharmony_ci		return 0;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_WPA_MASK) {
41662306a36Sopenharmony_ci	case GELIC_EURUS_SCAN_SEC_WPA_TKIP:
41762306a36Sopenharmony_ci		ccmp = 0;
41862306a36Sopenharmony_ci		break;
41962306a36Sopenharmony_ci	case GELIC_EURUS_SCAN_SEC_WPA_AES:
42062306a36Sopenharmony_ci		ccmp = 1;
42162306a36Sopenharmony_ci		break;
42262306a36Sopenharmony_ci	default:
42362306a36Sopenharmony_ci		if (rsn) {
42462306a36Sopenharmony_ci			ccmp = 1;
42562306a36Sopenharmony_ci			pr_info("%s: no cipher info. defaulted to CCMP\n",
42662306a36Sopenharmony_ci				__func__);
42762306a36Sopenharmony_ci		} else {
42862306a36Sopenharmony_ci			ccmp = 0;
42962306a36Sopenharmony_ci			pr_info("%s: no cipher info. defaulted to TKIP\n",
43062306a36Sopenharmony_ci				__func__);
43162306a36Sopenharmony_ci		}
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (rsn)
43562306a36Sopenharmony_ci		oui_header = rsn_oui;
43662306a36Sopenharmony_ci	else
43762306a36Sopenharmony_ci		oui_header = wpa_oui;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* element id */
44062306a36Sopenharmony_ci	if (rsn)
44162306a36Sopenharmony_ci		*buf++ = WLAN_EID_RSN;
44262306a36Sopenharmony_ci	else
44362306a36Sopenharmony_ci		*buf++ = WLAN_EID_VENDOR_SPECIFIC;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* length filed; set later */
44662306a36Sopenharmony_ci	buf++;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* wpa special header */
44962306a36Sopenharmony_ci	if (!rsn) {
45062306a36Sopenharmony_ci		memcpy(buf, wpa_oui, OUI_LEN);
45162306a36Sopenharmony_ci		buf += OUI_LEN;
45262306a36Sopenharmony_ci		*buf++ = 0x01;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* version */
45662306a36Sopenharmony_ci	*buf++ = 0x01; /* version 1.0 */
45762306a36Sopenharmony_ci	*buf++ = 0x00;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* group cipher */
46062306a36Sopenharmony_ci	memcpy(buf, oui_header, OUI_LEN);
46162306a36Sopenharmony_ci	buf += OUI_LEN;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (ccmp)
46462306a36Sopenharmony_ci		*buf++ = 0x04; /* CCMP */
46562306a36Sopenharmony_ci	else
46662306a36Sopenharmony_ci		*buf++ = 0x02; /* TKIP */
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* pairwise key count always 1 */
46962306a36Sopenharmony_ci	*buf++ = 0x01;
47062306a36Sopenharmony_ci	*buf++ = 0x00;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/* pairwise key suit */
47362306a36Sopenharmony_ci	memcpy(buf, oui_header, OUI_LEN);
47462306a36Sopenharmony_ci	buf += OUI_LEN;
47562306a36Sopenharmony_ci	if (ccmp)
47662306a36Sopenharmony_ci		*buf++ = 0x04; /* CCMP */
47762306a36Sopenharmony_ci	else
47862306a36Sopenharmony_ci		*buf++ = 0x02; /* TKIP */
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* AKM count is 1 */
48162306a36Sopenharmony_ci	*buf++ = 0x01;
48262306a36Sopenharmony_ci	*buf++ = 0x00;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* AKM suite is assumed as PSK*/
48562306a36Sopenharmony_ci	memcpy(buf, oui_header, OUI_LEN);
48662306a36Sopenharmony_ci	buf += OUI_LEN;
48762306a36Sopenharmony_ci	*buf++ = 0x02; /* PSK */
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* RSN capabilities is 0 */
49062306a36Sopenharmony_ci	*buf++ = 0x00;
49162306a36Sopenharmony_ci	*buf++ = 0x00;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* set length field */
49462306a36Sopenharmony_ci	start[1] = (buf - start - 2);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
49762306a36Sopenharmony_ci	return buf - start;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistruct ie_item {
50162306a36Sopenharmony_ci	u8 *data;
50262306a36Sopenharmony_ci	u8 len;
50362306a36Sopenharmony_ci};
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistruct ie_info {
50662306a36Sopenharmony_ci	struct ie_item wpa;
50762306a36Sopenharmony_ci	struct ie_item rsn;
50862306a36Sopenharmony_ci};
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic void gelic_wl_parse_ie(u8 *data, size_t len,
51162306a36Sopenharmony_ci			      struct ie_info *ie_info)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	size_t data_left = len;
51462306a36Sopenharmony_ci	u8 *pos = data;
51562306a36Sopenharmony_ci	u8 item_len;
51662306a36Sopenharmony_ci	u8 item_id;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	pr_debug("%s: data=%p len=%ld\n", __func__,
51962306a36Sopenharmony_ci		 data, len);
52062306a36Sopenharmony_ci	memset(ie_info, 0, sizeof(struct ie_info));
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	while (2 <= data_left) {
52362306a36Sopenharmony_ci		item_id = *pos++;
52462306a36Sopenharmony_ci		item_len = *pos++;
52562306a36Sopenharmony_ci		data_left -= 2;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		if (data_left < item_len)
52862306a36Sopenharmony_ci			break;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		switch (item_id) {
53162306a36Sopenharmony_ci		case WLAN_EID_VENDOR_SPECIFIC:
53262306a36Sopenharmony_ci			if ((OUI_LEN + 1 <= item_len) &&
53362306a36Sopenharmony_ci			    !memcmp(pos, wpa_oui, OUI_LEN) &&
53462306a36Sopenharmony_ci			    pos[OUI_LEN] == 0x01) {
53562306a36Sopenharmony_ci				ie_info->wpa.data = pos - 2;
53662306a36Sopenharmony_ci				ie_info->wpa.len = item_len + 2;
53762306a36Sopenharmony_ci			}
53862306a36Sopenharmony_ci			break;
53962306a36Sopenharmony_ci		case WLAN_EID_RSN:
54062306a36Sopenharmony_ci			ie_info->rsn.data = pos - 2;
54162306a36Sopenharmony_ci			/* length includes the header */
54262306a36Sopenharmony_ci			ie_info->rsn.len = item_len + 2;
54362306a36Sopenharmony_ci			break;
54462306a36Sopenharmony_ci		default:
54562306a36Sopenharmony_ci			pr_debug("%s: ignore %#x,%d\n", __func__,
54662306a36Sopenharmony_ci				 item_id, item_len);
54762306a36Sopenharmony_ci			break;
54862306a36Sopenharmony_ci		}
54962306a36Sopenharmony_ci		pos += item_len;
55062306a36Sopenharmony_ci		data_left -= item_len;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci	pr_debug("%s: wpa=%p,%d wpa2=%p,%d\n", __func__,
55362306a36Sopenharmony_ci		 ie_info->wpa.data, ie_info->wpa.len,
55462306a36Sopenharmony_ci		 ie_info->rsn.data, ie_info->rsn.len);
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/*
55962306a36Sopenharmony_ci * translate the scan informations from hypervisor to a
56062306a36Sopenharmony_ci * independent format
56162306a36Sopenharmony_ci */
56262306a36Sopenharmony_cistatic char *gelic_wl_translate_scan(struct net_device *netdev,
56362306a36Sopenharmony_ci				     struct iw_request_info *info,
56462306a36Sopenharmony_ci				     char *ev,
56562306a36Sopenharmony_ci				     char *stop,
56662306a36Sopenharmony_ci				     struct gelic_wl_scan_info *network)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct iw_event iwe;
56962306a36Sopenharmony_ci	struct gelic_eurus_scan_info *scan = network->hwinfo;
57062306a36Sopenharmony_ci	char *tmp;
57162306a36Sopenharmony_ci	u8 rate;
57262306a36Sopenharmony_ci	unsigned int i, j, len;
57362306a36Sopenharmony_ci	u8 buf[64]; /* arbitrary size large enough */
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* first entry should be AP's mac address */
57862306a36Sopenharmony_ci	iwe.cmd = SIOCGIWAP;
57962306a36Sopenharmony_ci	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
58062306a36Sopenharmony_ci	memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN);
58162306a36Sopenharmony_ci	ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_ADDR_LEN);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* ESSID */
58462306a36Sopenharmony_ci	iwe.cmd = SIOCGIWESSID;
58562306a36Sopenharmony_ci	iwe.u.data.flags = 1;
58662306a36Sopenharmony_ci	iwe.u.data.length = strnlen(scan->essid, 32);
58762306a36Sopenharmony_ci	ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* FREQUENCY */
59062306a36Sopenharmony_ci	iwe.cmd = SIOCGIWFREQ;
59162306a36Sopenharmony_ci	iwe.u.freq.m = be16_to_cpu(scan->channel);
59262306a36Sopenharmony_ci	iwe.u.freq.e = 0; /* table value in MHz */
59362306a36Sopenharmony_ci	iwe.u.freq.i = 0;
59462306a36Sopenharmony_ci	ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_FREQ_LEN);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	/* RATES */
59762306a36Sopenharmony_ci	iwe.cmd = SIOCGIWRATE;
59862306a36Sopenharmony_ci	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
59962306a36Sopenharmony_ci	/* to stuff multiple values in one event */
60062306a36Sopenharmony_ci	tmp = ev + iwe_stream_lcp_len(info);
60162306a36Sopenharmony_ci	/* put them in ascendant order (older is first) */
60262306a36Sopenharmony_ci	i = 0;
60362306a36Sopenharmony_ci	j = 0;
60462306a36Sopenharmony_ci	pr_debug("%s: rates=%d rate=%d\n", __func__,
60562306a36Sopenharmony_ci		 network->rate_len, network->rate_ext_len);
60662306a36Sopenharmony_ci	while (i < network->rate_len) {
60762306a36Sopenharmony_ci		if (j < network->rate_ext_len &&
60862306a36Sopenharmony_ci		    ((scan->ext_rate[j] & 0x7f) < (scan->rate[i] & 0x7f)))
60962306a36Sopenharmony_ci		    rate = scan->ext_rate[j++] & 0x7f;
61062306a36Sopenharmony_ci		else
61162306a36Sopenharmony_ci		    rate = scan->rate[i++] & 0x7f;
61262306a36Sopenharmony_ci		iwe.u.bitrate.value = rate * 500000; /* 500kbps unit */
61362306a36Sopenharmony_ci		tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe,
61462306a36Sopenharmony_ci					   IW_EV_PARAM_LEN);
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci	while (j < network->rate_ext_len) {
61762306a36Sopenharmony_ci		iwe.u.bitrate.value = (scan->ext_rate[j++] & 0x7f) * 500000;
61862306a36Sopenharmony_ci		tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe,
61962306a36Sopenharmony_ci					   IW_EV_PARAM_LEN);
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	/* Check if we added any rate */
62262306a36Sopenharmony_ci	if (iwe_stream_lcp_len(info) < (tmp - ev))
62362306a36Sopenharmony_ci		ev = tmp;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* ENCODE */
62662306a36Sopenharmony_ci	iwe.cmd = SIOCGIWENCODE;
62762306a36Sopenharmony_ci	if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_PRIVACY)
62862306a36Sopenharmony_ci		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
62962306a36Sopenharmony_ci	else
63062306a36Sopenharmony_ci		iwe.u.data.flags = IW_ENCODE_DISABLED;
63162306a36Sopenharmony_ci	iwe.u.data.length = 0;
63262306a36Sopenharmony_ci	ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* MODE */
63562306a36Sopenharmony_ci	iwe.cmd = SIOCGIWMODE;
63662306a36Sopenharmony_ci	if (be16_to_cpu(scan->capability) &
63762306a36Sopenharmony_ci	    (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
63862306a36Sopenharmony_ci		if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_ESS)
63962306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_MASTER;
64062306a36Sopenharmony_ci		else
64162306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_ADHOC;
64262306a36Sopenharmony_ci		ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_UINT_LEN);
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* QUAL */
64662306a36Sopenharmony_ci	iwe.cmd = IWEVQUAL;
64762306a36Sopenharmony_ci	iwe.u.qual.updated  = IW_QUAL_ALL_UPDATED |
64862306a36Sopenharmony_ci			IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
64962306a36Sopenharmony_ci	iwe.u.qual.level = be16_to_cpu(scan->rssi);
65062306a36Sopenharmony_ci	iwe.u.qual.qual = be16_to_cpu(scan->rssi);
65162306a36Sopenharmony_ci	iwe.u.qual.noise = 0;
65262306a36Sopenharmony_ci	ev  = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_QUAL_LEN);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* RSN */
65562306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
65662306a36Sopenharmony_ci	if (be16_to_cpu(scan->size) <= sizeof(*scan)) {
65762306a36Sopenharmony_ci		/* If wpa[2] capable station, synthesize IE and put it */
65862306a36Sopenharmony_ci		len = gelic_wl_synthesize_ie(buf, scan);
65962306a36Sopenharmony_ci		if (len) {
66062306a36Sopenharmony_ci			iwe.cmd = IWEVGENIE;
66162306a36Sopenharmony_ci			iwe.u.data.length = len;
66262306a36Sopenharmony_ci			ev = iwe_stream_add_point(info, ev, stop, &iwe, buf);
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci	} else {
66562306a36Sopenharmony_ci		/* this scan info has IE data */
66662306a36Sopenharmony_ci		struct ie_info ie_info;
66762306a36Sopenharmony_ci		size_t data_len;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		data_len = be16_to_cpu(scan->size) - sizeof(*scan);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		gelic_wl_parse_ie(scan->elements, data_len, &ie_info);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		if (ie_info.wpa.len && (ie_info.wpa.len <= sizeof(buf))) {
67462306a36Sopenharmony_ci			memcpy(buf, ie_info.wpa.data, ie_info.wpa.len);
67562306a36Sopenharmony_ci			iwe.cmd = IWEVGENIE;
67662306a36Sopenharmony_ci			iwe.u.data.length = ie_info.wpa.len;
67762306a36Sopenharmony_ci			ev = iwe_stream_add_point(info, ev, stop, &iwe, buf);
67862306a36Sopenharmony_ci		}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		if (ie_info.rsn.len && (ie_info.rsn.len <= sizeof(buf))) {
68162306a36Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
68262306a36Sopenharmony_ci			memcpy(buf, ie_info.rsn.data, ie_info.rsn.len);
68362306a36Sopenharmony_ci			iwe.cmd = IWEVGENIE;
68462306a36Sopenharmony_ci			iwe.u.data.length = ie_info.rsn.len;
68562306a36Sopenharmony_ci			ev = iwe_stream_add_point(info, ev, stop, &iwe, buf);
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
69062306a36Sopenharmony_ci	return ev;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic int gelic_wl_get_scan(struct net_device *netdev,
69562306a36Sopenharmony_ci			     struct iw_request_info *info,
69662306a36Sopenharmony_ci			     union iwreq_data *wrqu, char *extra)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
69962306a36Sopenharmony_ci	struct gelic_wl_scan_info *scan_info;
70062306a36Sopenharmony_ci	char *ev = extra;
70162306a36Sopenharmony_ci	char *stop = ev + wrqu->data.length;
70262306a36Sopenharmony_ci	int ret = 0;
70362306a36Sopenharmony_ci	unsigned long this_time = jiffies;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
70662306a36Sopenharmony_ci	if (mutex_lock_interruptible(&wl->scan_lock))
70762306a36Sopenharmony_ci		return -EAGAIN;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	switch (wl->scan_stat) {
71062306a36Sopenharmony_ci	case GELIC_WL_SCAN_STAT_SCANNING:
71162306a36Sopenharmony_ci		/* If a scan in progress, caller should call me again */
71262306a36Sopenharmony_ci		ret = -EAGAIN;
71362306a36Sopenharmony_ci		goto out;
71462306a36Sopenharmony_ci	case GELIC_WL_SCAN_STAT_INIT:
71562306a36Sopenharmony_ci		/* last scan request failed or never issued */
71662306a36Sopenharmony_ci		ret = -ENODEV;
71762306a36Sopenharmony_ci		goto out;
71862306a36Sopenharmony_ci	case GELIC_WL_SCAN_STAT_GOT_LIST:
71962306a36Sopenharmony_ci		/* ok, use current list */
72062306a36Sopenharmony_ci		break;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	list_for_each_entry(scan_info, &wl->network_list, list) {
72462306a36Sopenharmony_ci		if (wl->scan_age == 0 ||
72562306a36Sopenharmony_ci		    time_after(scan_info->last_scanned + wl->scan_age,
72662306a36Sopenharmony_ci			       this_time))
72762306a36Sopenharmony_ci			ev = gelic_wl_translate_scan(netdev, info,
72862306a36Sopenharmony_ci						     ev, stop,
72962306a36Sopenharmony_ci						     scan_info);
73062306a36Sopenharmony_ci		else
73162306a36Sopenharmony_ci			pr_debug("%s:entry too old\n", __func__);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		if (stop - ev <= IW_EV_ADDR_LEN) {
73462306a36Sopenharmony_ci			ret = -E2BIG;
73562306a36Sopenharmony_ci			goto out;
73662306a36Sopenharmony_ci		}
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	wrqu->data.length = ev - extra;
74062306a36Sopenharmony_ci	wrqu->data.flags = 0;
74162306a36Sopenharmony_ciout:
74262306a36Sopenharmony_ci	mutex_unlock(&wl->scan_lock);
74362306a36Sopenharmony_ci	pr_debug("%s: -> %d %d\n", __func__, ret, wrqu->data.length);
74462306a36Sopenharmony_ci	return ret;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci#ifdef DEBUG
74862306a36Sopenharmony_cistatic void scan_list_dump(struct gelic_wl_info *wl)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct gelic_wl_scan_info *scan_info;
75162306a36Sopenharmony_ci	int i;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	i = 0;
75462306a36Sopenharmony_ci	list_for_each_entry(scan_info, &wl->network_list, list) {
75562306a36Sopenharmony_ci		pr_debug("%s: item %d\n", __func__, i++);
75662306a36Sopenharmony_ci		pr_debug("valid=%d eurusindex=%d last=%lx\n",
75762306a36Sopenharmony_ci			 scan_info->valid, scan_info->eurus_index,
75862306a36Sopenharmony_ci			 scan_info->last_scanned);
75962306a36Sopenharmony_ci		pr_debug("r_len=%d r_ext_len=%d essid_len=%d\n",
76062306a36Sopenharmony_ci			 scan_info->rate_len, scan_info->rate_ext_len,
76162306a36Sopenharmony_ci			 scan_info->essid_len);
76262306a36Sopenharmony_ci		/* -- */
76362306a36Sopenharmony_ci		pr_debug("bssid=%pM\n", &scan_info->hwinfo->bssid[2]);
76462306a36Sopenharmony_ci		pr_debug("essid=%s\n", scan_info->hwinfo->essid);
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci#endif
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic int gelic_wl_set_auth(struct net_device *netdev,
77062306a36Sopenharmony_ci			     struct iw_request_info *info,
77162306a36Sopenharmony_ci			     union iwreq_data *data, char *extra)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct iw_param *param = &data->param;
77462306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
77562306a36Sopenharmony_ci	unsigned long irqflag;
77662306a36Sopenharmony_ci	int ret = 0;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX);
77962306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
78062306a36Sopenharmony_ci	switch (param->flags & IW_AUTH_INDEX) {
78162306a36Sopenharmony_ci	case IW_AUTH_WPA_VERSION:
78262306a36Sopenharmony_ci		if (param->value & IW_AUTH_WPA_VERSION_DISABLED) {
78362306a36Sopenharmony_ci			pr_debug("%s: NO WPA selected\n", __func__);
78462306a36Sopenharmony_ci			wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
78562306a36Sopenharmony_ci			wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
78662306a36Sopenharmony_ci			wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
78762306a36Sopenharmony_ci		}
78862306a36Sopenharmony_ci		if (param->value & IW_AUTH_WPA_VERSION_WPA) {
78962306a36Sopenharmony_ci			pr_debug("%s: WPA version 1 selected\n", __func__);
79062306a36Sopenharmony_ci			wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
79162306a36Sopenharmony_ci			wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
79262306a36Sopenharmony_ci			wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
79362306a36Sopenharmony_ci			wl->auth_method = GELIC_EURUS_AUTH_OPEN;
79462306a36Sopenharmony_ci		}
79562306a36Sopenharmony_ci		if (param->value & IW_AUTH_WPA_VERSION_WPA2) {
79662306a36Sopenharmony_ci			/*
79762306a36Sopenharmony_ci			 * As the hypervisor may not tell the cipher
79862306a36Sopenharmony_ci			 * information of the AP if it is WPA2,
79962306a36Sopenharmony_ci			 * you will not decide suitable cipher from
80062306a36Sopenharmony_ci			 * its beacon.
80162306a36Sopenharmony_ci			 * You should have knowledge about the AP's
80262306a36Sopenharmony_ci			 * cipher information in other method prior to
80362306a36Sopenharmony_ci			 * the association.
80462306a36Sopenharmony_ci			 */
80562306a36Sopenharmony_ci			if (!precise_ie())
80662306a36Sopenharmony_ci				pr_info("%s: WPA2 may not work\n", __func__);
80762306a36Sopenharmony_ci			if (wpa2_capable()) {
80862306a36Sopenharmony_ci				wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2;
80962306a36Sopenharmony_ci				wl->group_cipher_method = GELIC_WL_CIPHER_AES;
81062306a36Sopenharmony_ci				wl->pairwise_cipher_method =
81162306a36Sopenharmony_ci					GELIC_WL_CIPHER_AES;
81262306a36Sopenharmony_ci				wl->auth_method = GELIC_EURUS_AUTH_OPEN;
81362306a36Sopenharmony_ci			} else
81462306a36Sopenharmony_ci				ret = -EINVAL;
81562306a36Sopenharmony_ci		}
81662306a36Sopenharmony_ci		break;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	case IW_AUTH_CIPHER_PAIRWISE:
81962306a36Sopenharmony_ci		if (param->value &
82062306a36Sopenharmony_ci		    (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
82162306a36Sopenharmony_ci			pr_debug("%s: WEP selected\n", __func__);
82262306a36Sopenharmony_ci			wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci		if (param->value & IW_AUTH_CIPHER_TKIP) {
82562306a36Sopenharmony_ci			pr_debug("%s: TKIP selected\n", __func__);
82662306a36Sopenharmony_ci			wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
82762306a36Sopenharmony_ci		}
82862306a36Sopenharmony_ci		if (param->value & IW_AUTH_CIPHER_CCMP) {
82962306a36Sopenharmony_ci			pr_debug("%s: CCMP selected\n", __func__);
83062306a36Sopenharmony_ci			wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES;
83162306a36Sopenharmony_ci		}
83262306a36Sopenharmony_ci		if (param->value & IW_AUTH_CIPHER_NONE) {
83362306a36Sopenharmony_ci			pr_debug("%s: no auth selected\n", __func__);
83462306a36Sopenharmony_ci			wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
83562306a36Sopenharmony_ci		}
83662306a36Sopenharmony_ci		break;
83762306a36Sopenharmony_ci	case IW_AUTH_CIPHER_GROUP:
83862306a36Sopenharmony_ci		if (param->value &
83962306a36Sopenharmony_ci		    (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
84062306a36Sopenharmony_ci			pr_debug("%s: WEP selected\n", __func__);
84162306a36Sopenharmony_ci			wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
84262306a36Sopenharmony_ci		}
84362306a36Sopenharmony_ci		if (param->value & IW_AUTH_CIPHER_TKIP) {
84462306a36Sopenharmony_ci			pr_debug("%s: TKIP selected\n", __func__);
84562306a36Sopenharmony_ci			wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
84662306a36Sopenharmony_ci		}
84762306a36Sopenharmony_ci		if (param->value & IW_AUTH_CIPHER_CCMP) {
84862306a36Sopenharmony_ci			pr_debug("%s: CCMP selected\n", __func__);
84962306a36Sopenharmony_ci			wl->group_cipher_method = GELIC_WL_CIPHER_AES;
85062306a36Sopenharmony_ci		}
85162306a36Sopenharmony_ci		if (param->value & IW_AUTH_CIPHER_NONE) {
85262306a36Sopenharmony_ci			pr_debug("%s: no auth selected\n", __func__);
85362306a36Sopenharmony_ci			wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci		break;
85662306a36Sopenharmony_ci	case IW_AUTH_80211_AUTH_ALG:
85762306a36Sopenharmony_ci		if (param->value & IW_AUTH_ALG_SHARED_KEY) {
85862306a36Sopenharmony_ci			pr_debug("%s: shared key specified\n", __func__);
85962306a36Sopenharmony_ci			wl->auth_method = GELIC_EURUS_AUTH_SHARED;
86062306a36Sopenharmony_ci		} else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
86162306a36Sopenharmony_ci			pr_debug("%s: open system specified\n", __func__);
86262306a36Sopenharmony_ci			wl->auth_method = GELIC_EURUS_AUTH_OPEN;
86362306a36Sopenharmony_ci		} else
86462306a36Sopenharmony_ci			ret = -EINVAL;
86562306a36Sopenharmony_ci		break;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	case IW_AUTH_WPA_ENABLED:
86862306a36Sopenharmony_ci		if (param->value) {
86962306a36Sopenharmony_ci			pr_debug("%s: WPA enabled\n", __func__);
87062306a36Sopenharmony_ci			wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
87162306a36Sopenharmony_ci		} else {
87262306a36Sopenharmony_ci			pr_debug("%s: WPA disabled\n", __func__);
87362306a36Sopenharmony_ci			wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci		break;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	case IW_AUTH_KEY_MGMT:
87862306a36Sopenharmony_ci		if (param->value & IW_AUTH_KEY_MGMT_PSK)
87962306a36Sopenharmony_ci			break;
88062306a36Sopenharmony_ci		fallthrough;
88162306a36Sopenharmony_ci	default:
88262306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (!ret)
88762306a36Sopenharmony_ci		set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
89062306a36Sopenharmony_ci	pr_debug("%s: -> %d\n", __func__, ret);
89162306a36Sopenharmony_ci	return ret;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic int gelic_wl_get_auth(struct net_device *netdev,
89562306a36Sopenharmony_ci			     struct iw_request_info *info,
89662306a36Sopenharmony_ci			     union iwreq_data *iwreq, char *extra)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct iw_param *param = &iwreq->param;
89962306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
90062306a36Sopenharmony_ci	unsigned long irqflag;
90162306a36Sopenharmony_ci	int ret = 0;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX);
90462306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
90562306a36Sopenharmony_ci	switch (param->flags & IW_AUTH_INDEX) {
90662306a36Sopenharmony_ci	case IW_AUTH_WPA_VERSION:
90762306a36Sopenharmony_ci		switch (wl->wpa_level) {
90862306a36Sopenharmony_ci		case GELIC_WL_WPA_LEVEL_WPA:
90962306a36Sopenharmony_ci			param->value |= IW_AUTH_WPA_VERSION_WPA;
91062306a36Sopenharmony_ci			break;
91162306a36Sopenharmony_ci		case GELIC_WL_WPA_LEVEL_WPA2:
91262306a36Sopenharmony_ci			param->value |= IW_AUTH_WPA_VERSION_WPA2;
91362306a36Sopenharmony_ci			break;
91462306a36Sopenharmony_ci		default:
91562306a36Sopenharmony_ci			param->value |= IW_AUTH_WPA_VERSION_DISABLED;
91662306a36Sopenharmony_ci		}
91762306a36Sopenharmony_ci		break;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	case IW_AUTH_80211_AUTH_ALG:
92062306a36Sopenharmony_ci		if (wl->auth_method == GELIC_EURUS_AUTH_SHARED)
92162306a36Sopenharmony_ci			param->value = IW_AUTH_ALG_SHARED_KEY;
92262306a36Sopenharmony_ci		else if (wl->auth_method == GELIC_EURUS_AUTH_OPEN)
92362306a36Sopenharmony_ci			param->value = IW_AUTH_ALG_OPEN_SYSTEM;
92462306a36Sopenharmony_ci		break;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	case IW_AUTH_WPA_ENABLED:
92762306a36Sopenharmony_ci		switch (wl->wpa_level) {
92862306a36Sopenharmony_ci		case GELIC_WL_WPA_LEVEL_WPA:
92962306a36Sopenharmony_ci		case GELIC_WL_WPA_LEVEL_WPA2:
93062306a36Sopenharmony_ci			param->value = 1;
93162306a36Sopenharmony_ci			break;
93262306a36Sopenharmony_ci		default:
93362306a36Sopenharmony_ci			param->value = 0;
93462306a36Sopenharmony_ci			break;
93562306a36Sopenharmony_ci		}
93662306a36Sopenharmony_ci		break;
93762306a36Sopenharmony_ci	default:
93862306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
94262306a36Sopenharmony_ci	pr_debug("%s: -> %d\n", __func__, ret);
94362306a36Sopenharmony_ci	return ret;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci/* SIOC{S,G}IWESSID */
94762306a36Sopenharmony_cistatic int gelic_wl_set_essid(struct net_device *netdev,
94862306a36Sopenharmony_ci			      struct iw_request_info *info,
94962306a36Sopenharmony_ci			      union iwreq_data *data, char *extra)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
95262306a36Sopenharmony_ci	unsigned long irqflag;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	pr_debug("%s: <- l=%d f=%d\n", __func__,
95562306a36Sopenharmony_ci		 data->essid.length, data->essid.flags);
95662306a36Sopenharmony_ci	if (IW_ESSID_MAX_SIZE < data->essid.length)
95762306a36Sopenharmony_ci		return -EINVAL;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
96062306a36Sopenharmony_ci	if (data->essid.flags) {
96162306a36Sopenharmony_ci		wl->essid_len = data->essid.length;
96262306a36Sopenharmony_ci		memcpy(wl->essid, extra, wl->essid_len);
96362306a36Sopenharmony_ci		pr_debug("%s: essid = '%s'\n", __func__, extra);
96462306a36Sopenharmony_ci		set_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat);
96562306a36Sopenharmony_ci	} else {
96662306a36Sopenharmony_ci		pr_debug("%s: ESSID any\n", __func__);
96762306a36Sopenharmony_ci		clear_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat);
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci	set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
97062306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	gelic_wl_try_associate(netdev); /* FIXME */
97462306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
97562306a36Sopenharmony_ci	return 0;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic int gelic_wl_get_essid(struct net_device *netdev,
97962306a36Sopenharmony_ci			      struct iw_request_info *info,
98062306a36Sopenharmony_ci			      union iwreq_data *data, char *extra)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
98362306a36Sopenharmony_ci	unsigned long irqflag;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
98662306a36Sopenharmony_ci	mutex_lock(&wl->assoc_stat_lock);
98762306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
98862306a36Sopenharmony_ci	if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat) ||
98962306a36Sopenharmony_ci	    wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) {
99062306a36Sopenharmony_ci		memcpy(extra, wl->essid, wl->essid_len);
99162306a36Sopenharmony_ci		data->essid.length = wl->essid_len;
99262306a36Sopenharmony_ci		data->essid.flags = 1;
99362306a36Sopenharmony_ci	} else
99462306a36Sopenharmony_ci		data->essid.flags = 0;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	mutex_unlock(&wl->assoc_stat_lock);
99762306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
99862306a36Sopenharmony_ci	pr_debug("%s: -> len=%d\n", __func__, data->essid.length);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	return 0;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci/* SIO{S,G}IWENCODE */
100462306a36Sopenharmony_cistatic int gelic_wl_set_encode(struct net_device *netdev,
100562306a36Sopenharmony_ci			       struct iw_request_info *info,
100662306a36Sopenharmony_ci			       union iwreq_data *data, char *extra)
100762306a36Sopenharmony_ci{
100862306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
100962306a36Sopenharmony_ci	struct iw_point *enc = &data->encoding;
101062306a36Sopenharmony_ci	__u16 flags;
101162306a36Sopenharmony_ci	unsigned long irqflag;
101262306a36Sopenharmony_ci	int key_index, index_specified;
101362306a36Sopenharmony_ci	int ret = 0;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
101662306a36Sopenharmony_ci	flags = enc->flags & IW_ENCODE_FLAGS;
101762306a36Sopenharmony_ci	key_index = enc->flags & IW_ENCODE_INDEX;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	pr_debug("%s: key_index = %d\n", __func__, key_index);
102062306a36Sopenharmony_ci	pr_debug("%s: key_len = %d\n", __func__, enc->length);
102162306a36Sopenharmony_ci	pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (GELIC_WEP_KEYS < key_index)
102462306a36Sopenharmony_ci		return -EINVAL;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
102762306a36Sopenharmony_ci	if (key_index) {
102862306a36Sopenharmony_ci		index_specified = 1;
102962306a36Sopenharmony_ci		key_index--;
103062306a36Sopenharmony_ci	} else {
103162306a36Sopenharmony_ci		index_specified = 0;
103262306a36Sopenharmony_ci		key_index = wl->current_key;
103362306a36Sopenharmony_ci	}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (flags & IW_ENCODE_NOKEY) {
103662306a36Sopenharmony_ci		/* if just IW_ENCODE_NOKEY, change current key index */
103762306a36Sopenharmony_ci		if (!flags && index_specified) {
103862306a36Sopenharmony_ci			wl->current_key = key_index;
103962306a36Sopenharmony_ci			goto done;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		if (flags & IW_ENCODE_DISABLED) {
104362306a36Sopenharmony_ci			if (!index_specified) {
104462306a36Sopenharmony_ci				/* disable encryption */
104562306a36Sopenharmony_ci				wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
104662306a36Sopenharmony_ci				wl->pairwise_cipher_method =
104762306a36Sopenharmony_ci					GELIC_WL_CIPHER_NONE;
104862306a36Sopenharmony_ci				/* invalidate all key */
104962306a36Sopenharmony_ci				wl->key_enabled = 0;
105062306a36Sopenharmony_ci			} else
105162306a36Sopenharmony_ci				clear_bit(key_index, &wl->key_enabled);
105262306a36Sopenharmony_ci		}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci		if (flags & IW_ENCODE_OPEN)
105562306a36Sopenharmony_ci			wl->auth_method = GELIC_EURUS_AUTH_OPEN;
105662306a36Sopenharmony_ci		if (flags & IW_ENCODE_RESTRICTED) {
105762306a36Sopenharmony_ci			pr_info("%s: shared key mode enabled\n", __func__);
105862306a36Sopenharmony_ci			wl->auth_method = GELIC_EURUS_AUTH_SHARED;
105962306a36Sopenharmony_ci		}
106062306a36Sopenharmony_ci	} else {
106162306a36Sopenharmony_ci		if (IW_ENCODING_TOKEN_MAX < enc->length) {
106262306a36Sopenharmony_ci			ret = -EINVAL;
106362306a36Sopenharmony_ci			goto done;
106462306a36Sopenharmony_ci		}
106562306a36Sopenharmony_ci		wl->key_len[key_index] = enc->length;
106662306a36Sopenharmony_ci		memcpy(wl->key[key_index], extra, enc->length);
106762306a36Sopenharmony_ci		set_bit(key_index, &wl->key_enabled);
106862306a36Sopenharmony_ci		wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
106962306a36Sopenharmony_ci		wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci	set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
107262306a36Sopenharmony_cidone:
107362306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
107462306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
107562306a36Sopenharmony_ci	return ret;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_cistatic int gelic_wl_get_encode(struct net_device *netdev,
107962306a36Sopenharmony_ci			       struct iw_request_info *info,
108062306a36Sopenharmony_ci			       union iwreq_data *data, char *extra)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
108362306a36Sopenharmony_ci	struct iw_point *enc = &data->encoding;
108462306a36Sopenharmony_ci	unsigned long irqflag;
108562306a36Sopenharmony_ci	unsigned int key_index;
108662306a36Sopenharmony_ci	int ret = 0;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
108962306a36Sopenharmony_ci	key_index = enc->flags & IW_ENCODE_INDEX;
109062306a36Sopenharmony_ci	pr_debug("%s: flag=%#x point=%p len=%d extra=%p\n", __func__,
109162306a36Sopenharmony_ci		 enc->flags, enc->pointer, enc->length, extra);
109262306a36Sopenharmony_ci	if (GELIC_WEP_KEYS < key_index)
109362306a36Sopenharmony_ci		return -EINVAL;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
109662306a36Sopenharmony_ci	if (key_index)
109762306a36Sopenharmony_ci		key_index--;
109862306a36Sopenharmony_ci	else
109962306a36Sopenharmony_ci		key_index = wl->current_key;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
110262306a36Sopenharmony_ci		switch (wl->auth_method) {
110362306a36Sopenharmony_ci		case GELIC_EURUS_AUTH_OPEN:
110462306a36Sopenharmony_ci			enc->flags = IW_ENCODE_OPEN;
110562306a36Sopenharmony_ci			break;
110662306a36Sopenharmony_ci		case GELIC_EURUS_AUTH_SHARED:
110762306a36Sopenharmony_ci			enc->flags = IW_ENCODE_RESTRICTED;
110862306a36Sopenharmony_ci			break;
110962306a36Sopenharmony_ci		}
111062306a36Sopenharmony_ci	} else
111162306a36Sopenharmony_ci		enc->flags = IW_ENCODE_DISABLED;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (test_bit(key_index, &wl->key_enabled)) {
111462306a36Sopenharmony_ci		if (enc->length < wl->key_len[key_index]) {
111562306a36Sopenharmony_ci			ret = -EINVAL;
111662306a36Sopenharmony_ci			goto done;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci		enc->length = wl->key_len[key_index];
111962306a36Sopenharmony_ci		memcpy(extra, wl->key[key_index], wl->key_len[key_index]);
112062306a36Sopenharmony_ci	} else {
112162306a36Sopenharmony_ci		enc->length = 0;
112262306a36Sopenharmony_ci		enc->flags |= IW_ENCODE_NOKEY;
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci	enc->flags |= key_index + 1;
112562306a36Sopenharmony_ci	pr_debug("%s: -> flag=%x len=%d\n", __func__,
112662306a36Sopenharmony_ci		 enc->flags, enc->length);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cidone:
112962306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
113062306a36Sopenharmony_ci	return ret;
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci/* SIOC{S,G}IWAP */
113462306a36Sopenharmony_cistatic int gelic_wl_set_ap(struct net_device *netdev,
113562306a36Sopenharmony_ci			   struct iw_request_info *info,
113662306a36Sopenharmony_ci			   union iwreq_data *data, char *extra)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
113962306a36Sopenharmony_ci	unsigned long irqflag;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
114262306a36Sopenharmony_ci	if (data->ap_addr.sa_family != ARPHRD_ETHER)
114362306a36Sopenharmony_ci		return -EINVAL;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
114662306a36Sopenharmony_ci	if (is_valid_ether_addr(data->ap_addr.sa_data)) {
114762306a36Sopenharmony_ci		memcpy(wl->bssid, data->ap_addr.sa_data,
114862306a36Sopenharmony_ci		       ETH_ALEN);
114962306a36Sopenharmony_ci		set_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
115062306a36Sopenharmony_ci		set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
115162306a36Sopenharmony_ci		pr_debug("%s: bss=%pM\n", __func__, wl->bssid);
115262306a36Sopenharmony_ci	} else {
115362306a36Sopenharmony_ci		pr_debug("%s: clear bssid\n", __func__);
115462306a36Sopenharmony_ci		clear_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
115562306a36Sopenharmony_ci		eth_zero_addr(wl->bssid);
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
115862306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
115962306a36Sopenharmony_ci	return 0;
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_cistatic int gelic_wl_get_ap(struct net_device *netdev,
116362306a36Sopenharmony_ci			   struct iw_request_info *info,
116462306a36Sopenharmony_ci			   union iwreq_data *data, char *extra)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
116762306a36Sopenharmony_ci	unsigned long irqflag;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
117062306a36Sopenharmony_ci	mutex_lock(&wl->assoc_stat_lock);
117162306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
117262306a36Sopenharmony_ci	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) {
117362306a36Sopenharmony_ci		data->ap_addr.sa_family = ARPHRD_ETHER;
117462306a36Sopenharmony_ci		memcpy(data->ap_addr.sa_data, wl->active_bssid,
117562306a36Sopenharmony_ci		       ETH_ALEN);
117662306a36Sopenharmony_ci	} else
117762306a36Sopenharmony_ci		eth_zero_addr(data->ap_addr.sa_data);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
118062306a36Sopenharmony_ci	mutex_unlock(&wl->assoc_stat_lock);
118162306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
118262306a36Sopenharmony_ci	return 0;
118362306a36Sopenharmony_ci}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci/* SIOC{S,G}IWENCODEEXT */
118662306a36Sopenharmony_cistatic int gelic_wl_set_encodeext(struct net_device *netdev,
118762306a36Sopenharmony_ci				  struct iw_request_info *info,
118862306a36Sopenharmony_ci				  union iwreq_data *data, char *extra)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
119162306a36Sopenharmony_ci	struct iw_point *enc = &data->encoding;
119262306a36Sopenharmony_ci	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
119362306a36Sopenharmony_ci	__u16 alg;
119462306a36Sopenharmony_ci	__u16 flags;
119562306a36Sopenharmony_ci	unsigned long irqflag;
119662306a36Sopenharmony_ci	int key_index;
119762306a36Sopenharmony_ci	int ret = 0;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
120062306a36Sopenharmony_ci	flags = enc->flags & IW_ENCODE_FLAGS;
120162306a36Sopenharmony_ci	alg = ext->alg;
120262306a36Sopenharmony_ci	key_index = enc->flags & IW_ENCODE_INDEX;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	pr_debug("%s: key_index = %d\n", __func__, key_index);
120562306a36Sopenharmony_ci	pr_debug("%s: key_len = %d\n", __func__, enc->length);
120662306a36Sopenharmony_ci	pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
120762306a36Sopenharmony_ci	pr_debug("%s: ext_flag=%x\n", __func__, ext->ext_flags);
120862306a36Sopenharmony_ci	pr_debug("%s: ext_key_len=%x\n", __func__, ext->key_len);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	if (GELIC_WEP_KEYS < key_index)
121162306a36Sopenharmony_ci		return -EINVAL;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
121462306a36Sopenharmony_ci	if (key_index)
121562306a36Sopenharmony_ci		key_index--;
121662306a36Sopenharmony_ci	else
121762306a36Sopenharmony_ci		key_index = wl->current_key;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (!enc->length && (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
122062306a36Sopenharmony_ci		/* request to change default key index */
122162306a36Sopenharmony_ci		pr_debug("%s: request to change default key to %d\n",
122262306a36Sopenharmony_ci			 __func__, key_index);
122362306a36Sopenharmony_ci		wl->current_key = key_index;
122462306a36Sopenharmony_ci		goto done;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (alg == IW_ENCODE_ALG_NONE || (flags & IW_ENCODE_DISABLED)) {
122862306a36Sopenharmony_ci		pr_debug("%s: alg disabled\n", __func__);
122962306a36Sopenharmony_ci		wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
123062306a36Sopenharmony_ci		wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
123162306a36Sopenharmony_ci		wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
123262306a36Sopenharmony_ci		wl->auth_method = GELIC_EURUS_AUTH_OPEN; /* should be open */
123362306a36Sopenharmony_ci	} else if (alg == IW_ENCODE_ALG_WEP) {
123462306a36Sopenharmony_ci		pr_debug("%s: WEP requested\n", __func__);
123562306a36Sopenharmony_ci		if (flags & IW_ENCODE_OPEN) {
123662306a36Sopenharmony_ci			pr_debug("%s: open key mode\n", __func__);
123762306a36Sopenharmony_ci			wl->auth_method = GELIC_EURUS_AUTH_OPEN;
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci		if (flags & IW_ENCODE_RESTRICTED) {
124062306a36Sopenharmony_ci			pr_debug("%s: shared key mode\n", __func__);
124162306a36Sopenharmony_ci			wl->auth_method = GELIC_EURUS_AUTH_SHARED;
124262306a36Sopenharmony_ci		}
124362306a36Sopenharmony_ci		if (IW_ENCODING_TOKEN_MAX < ext->key_len) {
124462306a36Sopenharmony_ci			pr_info("%s: key is too long %d\n", __func__,
124562306a36Sopenharmony_ci				ext->key_len);
124662306a36Sopenharmony_ci			ret = -EINVAL;
124762306a36Sopenharmony_ci			goto done;
124862306a36Sopenharmony_ci		}
124962306a36Sopenharmony_ci		/* OK, update the key */
125062306a36Sopenharmony_ci		wl->key_len[key_index] = ext->key_len;
125162306a36Sopenharmony_ci		memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX);
125262306a36Sopenharmony_ci		memcpy(wl->key[key_index], ext->key, ext->key_len);
125362306a36Sopenharmony_ci		set_bit(key_index, &wl->key_enabled);
125462306a36Sopenharmony_ci		/* remember wep info changed */
125562306a36Sopenharmony_ci		set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
125662306a36Sopenharmony_ci	} else if (alg == IW_ENCODE_ALG_PMK) {
125762306a36Sopenharmony_ci		if (ext->key_len != WPA_PSK_LEN) {
125862306a36Sopenharmony_ci			pr_err("%s: PSK length wrong %d\n", __func__,
125962306a36Sopenharmony_ci			       ext->key_len);
126062306a36Sopenharmony_ci			ret = -EINVAL;
126162306a36Sopenharmony_ci			goto done;
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci		memset(wl->psk, 0, sizeof(wl->psk));
126462306a36Sopenharmony_ci		memcpy(wl->psk, ext->key, ext->key_len);
126562306a36Sopenharmony_ci		wl->psk_len = ext->key_len;
126662306a36Sopenharmony_ci		wl->psk_type = GELIC_EURUS_WPA_PSK_BIN;
126762306a36Sopenharmony_ci		/* remember PSK configured */
126862306a36Sopenharmony_ci		set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat);
126962306a36Sopenharmony_ci	}
127062306a36Sopenharmony_cidone:
127162306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
127262306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
127362306a36Sopenharmony_ci	return ret;
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic int gelic_wl_get_encodeext(struct net_device *netdev,
127762306a36Sopenharmony_ci				  struct iw_request_info *info,
127862306a36Sopenharmony_ci				  union iwreq_data *data, char *extra)
127962306a36Sopenharmony_ci{
128062306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
128162306a36Sopenharmony_ci	struct iw_point *enc = &data->encoding;
128262306a36Sopenharmony_ci	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
128362306a36Sopenharmony_ci	unsigned long irqflag;
128462306a36Sopenharmony_ci	int key_index;
128562306a36Sopenharmony_ci	int ret = 0;
128662306a36Sopenharmony_ci	int max_key_len;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	max_key_len = enc->length - sizeof(struct iw_encode_ext);
129162306a36Sopenharmony_ci	if (max_key_len < 0)
129262306a36Sopenharmony_ci		return -EINVAL;
129362306a36Sopenharmony_ci	key_index = enc->flags & IW_ENCODE_INDEX;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	pr_debug("%s: key_index = %d\n", __func__, key_index);
129662306a36Sopenharmony_ci	pr_debug("%s: key_len = %d\n", __func__, enc->length);
129762306a36Sopenharmony_ci	pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (GELIC_WEP_KEYS < key_index)
130062306a36Sopenharmony_ci		return -EINVAL;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
130362306a36Sopenharmony_ci	if (key_index)
130462306a36Sopenharmony_ci		key_index--;
130562306a36Sopenharmony_ci	else
130662306a36Sopenharmony_ci		key_index = wl->current_key;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	memset(ext, 0, sizeof(struct iw_encode_ext));
130962306a36Sopenharmony_ci	switch (wl->group_cipher_method) {
131062306a36Sopenharmony_ci	case GELIC_WL_CIPHER_WEP:
131162306a36Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_WEP;
131262306a36Sopenharmony_ci		enc->flags |= IW_ENCODE_ENABLED;
131362306a36Sopenharmony_ci		break;
131462306a36Sopenharmony_ci	case GELIC_WL_CIPHER_TKIP:
131562306a36Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_TKIP;
131662306a36Sopenharmony_ci		enc->flags |= IW_ENCODE_ENABLED;
131762306a36Sopenharmony_ci		break;
131862306a36Sopenharmony_ci	case GELIC_WL_CIPHER_AES:
131962306a36Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_CCMP;
132062306a36Sopenharmony_ci		enc->flags |= IW_ENCODE_ENABLED;
132162306a36Sopenharmony_ci		break;
132262306a36Sopenharmony_ci	case GELIC_WL_CIPHER_NONE:
132362306a36Sopenharmony_ci	default:
132462306a36Sopenharmony_ci		ext->alg = IW_ENCODE_ALG_NONE;
132562306a36Sopenharmony_ci		enc->flags |= IW_ENCODE_NOKEY;
132662306a36Sopenharmony_ci		break;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (!(enc->flags & IW_ENCODE_NOKEY)) {
133062306a36Sopenharmony_ci		if (max_key_len < wl->key_len[key_index]) {
133162306a36Sopenharmony_ci			ret = -E2BIG;
133262306a36Sopenharmony_ci			goto out;
133362306a36Sopenharmony_ci		}
133462306a36Sopenharmony_ci		if (test_bit(key_index, &wl->key_enabled))
133562306a36Sopenharmony_ci			memcpy(ext->key, wl->key[key_index],
133662306a36Sopenharmony_ci			       wl->key_len[key_index]);
133762306a36Sopenharmony_ci		else
133862306a36Sopenharmony_ci			pr_debug("%s: disabled key requested ix=%d\n",
133962306a36Sopenharmony_ci				 __func__, key_index);
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ciout:
134262306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
134362306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
134462306a36Sopenharmony_ci	return ret;
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci/* SIOC{S,G}IWMODE */
134762306a36Sopenharmony_cistatic int gelic_wl_set_mode(struct net_device *netdev,
134862306a36Sopenharmony_ci			     struct iw_request_info *info,
134962306a36Sopenharmony_ci			     union iwreq_data *data, char *extra)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	__u32 mode = data->mode;
135262306a36Sopenharmony_ci	int ret;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
135562306a36Sopenharmony_ci	if (mode == IW_MODE_INFRA)
135662306a36Sopenharmony_ci		ret = 0;
135762306a36Sopenharmony_ci	else
135862306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
135962306a36Sopenharmony_ci	pr_debug("%s: -> %d\n", __func__, ret);
136062306a36Sopenharmony_ci	return ret;
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_cistatic int gelic_wl_get_mode(struct net_device *netdev,
136462306a36Sopenharmony_ci			     struct iw_request_info *info,
136562306a36Sopenharmony_ci			     union iwreq_data *data, char *extra)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	__u32 *mode = &data->mode;
136862306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
136962306a36Sopenharmony_ci	*mode = IW_MODE_INFRA;
137062306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
137162306a36Sopenharmony_ci	return 0;
137262306a36Sopenharmony_ci}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci/* SIOCGIWNICKN */
137562306a36Sopenharmony_cistatic int gelic_wl_get_nick(struct net_device *net_dev,
137662306a36Sopenharmony_ci				  struct iw_request_info *info,
137762306a36Sopenharmony_ci				  union iwreq_data *data, char *extra)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	strcpy(extra, "gelic_wl");
138062306a36Sopenharmony_ci	data->data.length = strlen(extra);
138162306a36Sopenharmony_ci	data->data.flags = 1;
138262306a36Sopenharmony_ci	return 0;
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci/* --- */
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic struct iw_statistics *gelic_wl_get_wireless_stats(
138962306a36Sopenharmony_ci	struct net_device *netdev)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
139362306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
139462306a36Sopenharmony_ci	struct iw_statistics *is;
139562306a36Sopenharmony_ci	struct gelic_eurus_rssi_info *rssi;
139662306a36Sopenharmony_ci	void *buf;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	buf = (void *)__get_free_page(GFP_KERNEL);
140162306a36Sopenharmony_ci	if (!buf)
140262306a36Sopenharmony_ci		return NULL;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	is = &wl->iwstat;
140562306a36Sopenharmony_ci	memset(is, 0, sizeof(*is));
140662306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_RSSI_CFG,
140762306a36Sopenharmony_ci				   buf, sizeof(*rssi));
140862306a36Sopenharmony_ci	if (cmd && !cmd->status && !cmd->cmd_status) {
140962306a36Sopenharmony_ci		rssi = buf;
141062306a36Sopenharmony_ci		is->qual.level = be16_to_cpu(rssi->rssi);
141162306a36Sopenharmony_ci		is->qual.updated = IW_QUAL_LEVEL_UPDATED |
141262306a36Sopenharmony_ci			IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
141362306a36Sopenharmony_ci	} else
141462306a36Sopenharmony_ci		/* not associated */
141562306a36Sopenharmony_ci		is->qual.updated = IW_QUAL_ALL_INVALID;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	kfree(cmd);
141862306a36Sopenharmony_ci	free_page((unsigned long)buf);
141962306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
142062306a36Sopenharmony_ci	return is;
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci/*
142462306a36Sopenharmony_ci *  scanning helpers
142562306a36Sopenharmony_ci */
142662306a36Sopenharmony_cistatic int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan,
142762306a36Sopenharmony_ci			       u8 *essid, size_t essid_len)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
143062306a36Sopenharmony_ci	int ret = 0;
143162306a36Sopenharmony_ci	void *buf = NULL;
143262306a36Sopenharmony_ci	size_t len;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	pr_debug("%s: <- always=%d\n", __func__, always_scan);
143562306a36Sopenharmony_ci	if (mutex_lock_interruptible(&wl->scan_lock))
143662306a36Sopenharmony_ci		return -ERESTARTSYS;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/*
143962306a36Sopenharmony_ci	 * If already a scan in progress, do not trigger more
144062306a36Sopenharmony_ci	 */
144162306a36Sopenharmony_ci	if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) {
144262306a36Sopenharmony_ci		pr_debug("%s: scanning now\n", __func__);
144362306a36Sopenharmony_ci		goto out;
144462306a36Sopenharmony_ci	}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	init_completion(&wl->scan_done);
144762306a36Sopenharmony_ci	/*
144862306a36Sopenharmony_ci	 * If we have already a bss list, don't try to get new
144962306a36Sopenharmony_ci	 * unless we are doing an ESSID scan
145062306a36Sopenharmony_ci	 */
145162306a36Sopenharmony_ci	if ((!essid_len && !always_scan)
145262306a36Sopenharmony_ci	    && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) {
145362306a36Sopenharmony_ci		pr_debug("%s: already has the list\n", __func__);
145462306a36Sopenharmony_ci		complete(&wl->scan_done);
145562306a36Sopenharmony_ci		goto out;
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	/* ESSID scan ? */
145962306a36Sopenharmony_ci	if (essid_len && essid) {
146062306a36Sopenharmony_ci		buf = (void *)__get_free_page(GFP_KERNEL);
146162306a36Sopenharmony_ci		if (!buf) {
146262306a36Sopenharmony_ci			ret = -ENOMEM;
146362306a36Sopenharmony_ci			goto out;
146462306a36Sopenharmony_ci		}
146562306a36Sopenharmony_ci		len = IW_ESSID_MAX_SIZE; /* hypervisor always requires 32 */
146662306a36Sopenharmony_ci		memset(buf, 0, len);
146762306a36Sopenharmony_ci		memcpy(buf, essid, essid_len);
146862306a36Sopenharmony_ci		pr_debug("%s: essid scan='%s'\n", __func__, (char *)buf);
146962306a36Sopenharmony_ci	} else
147062306a36Sopenharmony_ci		len = 0;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	/*
147362306a36Sopenharmony_ci	 * issue start scan request
147462306a36Sopenharmony_ci	 */
147562306a36Sopenharmony_ci	wl->scan_stat = GELIC_WL_SCAN_STAT_SCANNING;
147662306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_START_SCAN,
147762306a36Sopenharmony_ci				   buf, len);
147862306a36Sopenharmony_ci	if (!cmd || cmd->status || cmd->cmd_status) {
147962306a36Sopenharmony_ci		wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
148062306a36Sopenharmony_ci		complete(&wl->scan_done);
148162306a36Sopenharmony_ci		ret = -ENOMEM;
148262306a36Sopenharmony_ci		goto out;
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci	kfree(cmd);
148562306a36Sopenharmony_ciout:
148662306a36Sopenharmony_ci	free_page((unsigned long)buf);
148762306a36Sopenharmony_ci	mutex_unlock(&wl->scan_lock);
148862306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
148962306a36Sopenharmony_ci	return ret;
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci/*
149362306a36Sopenharmony_ci * retrieve scan result from the chip (hypervisor)
149462306a36Sopenharmony_ci * this function is invoked by schedule work.
149562306a36Sopenharmony_ci */
149662306a36Sopenharmony_cistatic void gelic_wl_scan_complete_event(struct gelic_wl_info *wl)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd = NULL;
149962306a36Sopenharmony_ci	struct gelic_wl_scan_info *target, *tmp;
150062306a36Sopenharmony_ci	struct gelic_wl_scan_info *oldest = NULL;
150162306a36Sopenharmony_ci	struct gelic_eurus_scan_info *scan_info;
150262306a36Sopenharmony_ci	unsigned int scan_info_size;
150362306a36Sopenharmony_ci	union iwreq_data data;
150462306a36Sopenharmony_ci	unsigned long this_time = jiffies;
150562306a36Sopenharmony_ci	unsigned int data_len, i, found, r;
150662306a36Sopenharmony_ci	void *buf;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	pr_debug("%s:start\n", __func__);
150962306a36Sopenharmony_ci	mutex_lock(&wl->scan_lock);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	buf = (void *)__get_free_page(GFP_KERNEL);
151262306a36Sopenharmony_ci	if (!buf) {
151362306a36Sopenharmony_ci		pr_info("%s: scan buffer alloc failed\n", __func__);
151462306a36Sopenharmony_ci		goto out;
151562306a36Sopenharmony_ci	}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	if (wl->scan_stat != GELIC_WL_SCAN_STAT_SCANNING) {
151862306a36Sopenharmony_ci		/*
151962306a36Sopenharmony_ci		 * stop() may be called while scanning, ignore result
152062306a36Sopenharmony_ci		 */
152162306a36Sopenharmony_ci		pr_debug("%s: scan complete when stat != scanning(%d)\n",
152262306a36Sopenharmony_ci			 __func__, wl->scan_stat);
152362306a36Sopenharmony_ci		goto out;
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_SCAN,
152762306a36Sopenharmony_ci				   buf, PAGE_SIZE);
152862306a36Sopenharmony_ci	if (!cmd || cmd->status || cmd->cmd_status) {
152962306a36Sopenharmony_ci		wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
153062306a36Sopenharmony_ci		pr_info("%s:cmd failed\n", __func__);
153162306a36Sopenharmony_ci		kfree(cmd);
153262306a36Sopenharmony_ci		goto out;
153362306a36Sopenharmony_ci	}
153462306a36Sopenharmony_ci	data_len = cmd->size;
153562306a36Sopenharmony_ci	pr_debug("%s: data_len = %d\n", __func__, data_len);
153662306a36Sopenharmony_ci	kfree(cmd);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	/* OK, bss list retrieved */
153962306a36Sopenharmony_ci	wl->scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/* mark all entries are old */
154262306a36Sopenharmony_ci	list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
154362306a36Sopenharmony_ci		target->valid = 0;
154462306a36Sopenharmony_ci		/* expire too old entries */
154562306a36Sopenharmony_ci		if (time_before(target->last_scanned + wl->scan_age,
154662306a36Sopenharmony_ci				this_time)) {
154762306a36Sopenharmony_ci			kfree(target->hwinfo);
154862306a36Sopenharmony_ci			target->hwinfo = NULL;
154962306a36Sopenharmony_ci			list_move_tail(&target->list, &wl->network_free_list);
155062306a36Sopenharmony_ci		}
155162306a36Sopenharmony_ci	}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	/* put them in the network_list */
155462306a36Sopenharmony_ci	for (i = 0, scan_info_size = 0, scan_info = buf;
155562306a36Sopenharmony_ci	     scan_info_size < data_len;
155662306a36Sopenharmony_ci	     i++, scan_info_size += be16_to_cpu(scan_info->size),
155762306a36Sopenharmony_ci	     scan_info = (void *)scan_info + be16_to_cpu(scan_info->size)) {
155862306a36Sopenharmony_ci		pr_debug("%s:size=%d bssid=%pM scan_info=%p\n", __func__,
155962306a36Sopenharmony_ci			 be16_to_cpu(scan_info->size),
156062306a36Sopenharmony_ci			 &scan_info->bssid[2], scan_info);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci		/*
156362306a36Sopenharmony_ci		 * The wireless firmware may return invalid channel 0 and/or
156462306a36Sopenharmony_ci		 * invalid rate if the AP emits zero length SSID ie. As this
156562306a36Sopenharmony_ci		 * scan information is useless, ignore it
156662306a36Sopenharmony_ci		 */
156762306a36Sopenharmony_ci		if (!be16_to_cpu(scan_info->channel) || !scan_info->rate[0]) {
156862306a36Sopenharmony_ci			pr_debug("%s: invalid scan info\n", __func__);
156962306a36Sopenharmony_ci			continue;
157062306a36Sopenharmony_ci		}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci		found = 0;
157362306a36Sopenharmony_ci		oldest = NULL;
157462306a36Sopenharmony_ci		list_for_each_entry(target, &wl->network_list, list) {
157562306a36Sopenharmony_ci			if (ether_addr_equal(&target->hwinfo->bssid[2],
157662306a36Sopenharmony_ci					     &scan_info->bssid[2])) {
157762306a36Sopenharmony_ci				found = 1;
157862306a36Sopenharmony_ci				pr_debug("%s: same BBS found scanned list\n",
157962306a36Sopenharmony_ci					 __func__);
158062306a36Sopenharmony_ci				break;
158162306a36Sopenharmony_ci			}
158262306a36Sopenharmony_ci			if (!oldest ||
158362306a36Sopenharmony_ci			    (target->last_scanned < oldest->last_scanned))
158462306a36Sopenharmony_ci				oldest = target;
158562306a36Sopenharmony_ci		}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci		if (!found) {
158862306a36Sopenharmony_ci			/* not found in the list */
158962306a36Sopenharmony_ci			if (list_empty(&wl->network_free_list)) {
159062306a36Sopenharmony_ci				/* expire oldest */
159162306a36Sopenharmony_ci				target = oldest;
159262306a36Sopenharmony_ci			} else {
159362306a36Sopenharmony_ci				target = list_entry(wl->network_free_list.next,
159462306a36Sopenharmony_ci						    struct gelic_wl_scan_info,
159562306a36Sopenharmony_ci						    list);
159662306a36Sopenharmony_ci			}
159762306a36Sopenharmony_ci		}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci		/* update the item */
160062306a36Sopenharmony_ci		target->last_scanned = this_time;
160162306a36Sopenharmony_ci		target->valid = 1;
160262306a36Sopenharmony_ci		target->eurus_index = i;
160362306a36Sopenharmony_ci		kfree(target->hwinfo);
160462306a36Sopenharmony_ci		target->hwinfo = kmemdup(scan_info,
160562306a36Sopenharmony_ci					 be16_to_cpu(scan_info->size),
160662306a36Sopenharmony_ci					 GFP_KERNEL);
160762306a36Sopenharmony_ci		if (!target->hwinfo)
160862306a36Sopenharmony_ci			continue;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci		/* copy hw scan info */
161162306a36Sopenharmony_ci		target->essid_len = strnlen(scan_info->essid,
161262306a36Sopenharmony_ci					    sizeof(scan_info->essid));
161362306a36Sopenharmony_ci		target->rate_len = 0;
161462306a36Sopenharmony_ci		for (r = 0; r < 12; r++)
161562306a36Sopenharmony_ci			if (scan_info->rate[r])
161662306a36Sopenharmony_ci				target->rate_len++;
161762306a36Sopenharmony_ci		if (8 < target->rate_len)
161862306a36Sopenharmony_ci			pr_info("%s: AP returns %d rates\n", __func__,
161962306a36Sopenharmony_ci				target->rate_len);
162062306a36Sopenharmony_ci		target->rate_ext_len = 0;
162162306a36Sopenharmony_ci		for (r = 0; r < 16; r++)
162262306a36Sopenharmony_ci			if (scan_info->ext_rate[r])
162362306a36Sopenharmony_ci				target->rate_ext_len++;
162462306a36Sopenharmony_ci		list_move_tail(&target->list, &wl->network_list);
162562306a36Sopenharmony_ci	}
162662306a36Sopenharmony_ci	memset(&data, 0, sizeof(data));
162762306a36Sopenharmony_ci	wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWSCAN, &data,
162862306a36Sopenharmony_ci			    NULL);
162962306a36Sopenharmony_ciout:
163062306a36Sopenharmony_ci	free_page((unsigned long)buf);
163162306a36Sopenharmony_ci	complete(&wl->scan_done);
163262306a36Sopenharmony_ci	mutex_unlock(&wl->scan_lock);
163362306a36Sopenharmony_ci	pr_debug("%s:end\n", __func__);
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci/*
163762306a36Sopenharmony_ci * Select an appropriate bss from current scan list regarding
163862306a36Sopenharmony_ci * current settings from userspace.
163962306a36Sopenharmony_ci * The caller must hold wl->scan_lock,
164062306a36Sopenharmony_ci * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
164162306a36Sopenharmony_ci */
164262306a36Sopenharmony_cistatic void update_best(struct gelic_wl_scan_info **best,
164362306a36Sopenharmony_ci			struct gelic_wl_scan_info *candid,
164462306a36Sopenharmony_ci			int *best_weight,
164562306a36Sopenharmony_ci			int *weight)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	if (*best_weight < ++(*weight)) {
164862306a36Sopenharmony_ci		*best_weight = *weight;
164962306a36Sopenharmony_ci		*best = candid;
165062306a36Sopenharmony_ci	}
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cistatic
165462306a36Sopenharmony_cistruct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	struct gelic_wl_scan_info *scan_info;
165762306a36Sopenharmony_ci	struct gelic_wl_scan_info *best_bss;
165862306a36Sopenharmony_ci	int weight, best_weight;
165962306a36Sopenharmony_ci	u16 security;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	best_bss = NULL;
166462306a36Sopenharmony_ci	best_weight = 0;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	list_for_each_entry(scan_info, &wl->network_list, list) {
166762306a36Sopenharmony_ci		pr_debug("%s: station %p\n", __func__, scan_info);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci		if (!scan_info->valid) {
167062306a36Sopenharmony_ci			pr_debug("%s: station invalid\n", __func__);
167162306a36Sopenharmony_ci			continue;
167262306a36Sopenharmony_ci		}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci		/* If bss specified, check it only */
167562306a36Sopenharmony_ci		if (test_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat)) {
167662306a36Sopenharmony_ci			if (ether_addr_equal(&scan_info->hwinfo->bssid[2],
167762306a36Sopenharmony_ci					     wl->bssid)) {
167862306a36Sopenharmony_ci				best_bss = scan_info;
167962306a36Sopenharmony_ci				pr_debug("%s: bssid matched\n", __func__);
168062306a36Sopenharmony_ci				break;
168162306a36Sopenharmony_ci			} else {
168262306a36Sopenharmony_ci				pr_debug("%s: bssid unmatched\n", __func__);
168362306a36Sopenharmony_ci				continue;
168462306a36Sopenharmony_ci			}
168562306a36Sopenharmony_ci		}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci		weight = 0;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci		/* security */
169062306a36Sopenharmony_ci		security = be16_to_cpu(scan_info->hwinfo->security) &
169162306a36Sopenharmony_ci			GELIC_EURUS_SCAN_SEC_MASK;
169262306a36Sopenharmony_ci		if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) {
169362306a36Sopenharmony_ci			if (security == GELIC_EURUS_SCAN_SEC_WPA2)
169462306a36Sopenharmony_ci				update_best(&best_bss, scan_info,
169562306a36Sopenharmony_ci					    &best_weight, &weight);
169662306a36Sopenharmony_ci			else
169762306a36Sopenharmony_ci				continue;
169862306a36Sopenharmony_ci		} else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA) {
169962306a36Sopenharmony_ci			if (security == GELIC_EURUS_SCAN_SEC_WPA)
170062306a36Sopenharmony_ci				update_best(&best_bss, scan_info,
170162306a36Sopenharmony_ci					    &best_weight, &weight);
170262306a36Sopenharmony_ci			else
170362306a36Sopenharmony_ci				continue;
170462306a36Sopenharmony_ci		} else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_NONE &&
170562306a36Sopenharmony_ci			   wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
170662306a36Sopenharmony_ci			if (security == GELIC_EURUS_SCAN_SEC_WEP)
170762306a36Sopenharmony_ci				update_best(&best_bss, scan_info,
170862306a36Sopenharmony_ci					    &best_weight, &weight);
170962306a36Sopenharmony_ci			else
171062306a36Sopenharmony_ci				continue;
171162306a36Sopenharmony_ci		}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci		/* If ESSID is set, check it */
171462306a36Sopenharmony_ci		if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) {
171562306a36Sopenharmony_ci			if ((scan_info->essid_len == wl->essid_len) &&
171662306a36Sopenharmony_ci			    !strncmp(wl->essid,
171762306a36Sopenharmony_ci				     scan_info->hwinfo->essid,
171862306a36Sopenharmony_ci				     scan_info->essid_len))
171962306a36Sopenharmony_ci				update_best(&best_bss, scan_info,
172062306a36Sopenharmony_ci					    &best_weight, &weight);
172162306a36Sopenharmony_ci			else
172262306a36Sopenharmony_ci				continue;
172362306a36Sopenharmony_ci		}
172462306a36Sopenharmony_ci	}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci#ifdef DEBUG
172762306a36Sopenharmony_ci	pr_debug("%s: -> bss=%p\n", __func__, best_bss);
172862306a36Sopenharmony_ci	if (best_bss) {
172962306a36Sopenharmony_ci		pr_debug("%s:addr=%pM\n", __func__,
173062306a36Sopenharmony_ci			 &best_bss->hwinfo->bssid[2]);
173162306a36Sopenharmony_ci	}
173262306a36Sopenharmony_ci#endif
173362306a36Sopenharmony_ci	return best_bss;
173462306a36Sopenharmony_ci}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci/*
173762306a36Sopenharmony_ci * Setup WEP configuration to the chip
173862306a36Sopenharmony_ci * The caller must hold wl->scan_lock,
173962306a36Sopenharmony_ci * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
174062306a36Sopenharmony_ci */
174162306a36Sopenharmony_cistatic int gelic_wl_do_wep_setup(struct gelic_wl_info *wl)
174262306a36Sopenharmony_ci{
174362306a36Sopenharmony_ci	unsigned int i;
174462306a36Sopenharmony_ci	struct gelic_eurus_wep_cfg *wep;
174562306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
174662306a36Sopenharmony_ci	int wep104 = 0;
174762306a36Sopenharmony_ci	int have_key = 0;
174862306a36Sopenharmony_ci	int ret = 0;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
175162306a36Sopenharmony_ci	/* we can assume no one should uses the buffer */
175262306a36Sopenharmony_ci	wep = (struct gelic_eurus_wep_cfg *)__get_free_page(GFP_KERNEL);
175362306a36Sopenharmony_ci	if (!wep)
175462306a36Sopenharmony_ci		return -ENOMEM;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	memset(wep, 0, sizeof(*wep));
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
175962306a36Sopenharmony_ci		pr_debug("%s: WEP mode\n", __func__);
176062306a36Sopenharmony_ci		for (i = 0; i < GELIC_WEP_KEYS; i++) {
176162306a36Sopenharmony_ci			if (!test_bit(i, &wl->key_enabled))
176262306a36Sopenharmony_ci				continue;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci			pr_debug("%s: key#%d enabled\n", __func__, i);
176562306a36Sopenharmony_ci			have_key = 1;
176662306a36Sopenharmony_ci			if (wl->key_len[i] == 13)
176762306a36Sopenharmony_ci				wep104 = 1;
176862306a36Sopenharmony_ci			else if (wl->key_len[i] != 5) {
176962306a36Sopenharmony_ci				pr_info("%s: wrong wep key[%d]=%d\n",
177062306a36Sopenharmony_ci					__func__, i, wl->key_len[i]);
177162306a36Sopenharmony_ci				ret = -EINVAL;
177262306a36Sopenharmony_ci				goto out;
177362306a36Sopenharmony_ci			}
177462306a36Sopenharmony_ci			memcpy(wep->key[i], wl->key[i], wl->key_len[i]);
177562306a36Sopenharmony_ci		}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci		if (!have_key) {
177862306a36Sopenharmony_ci			pr_info("%s: all wep key disabled\n", __func__);
177962306a36Sopenharmony_ci			ret = -EINVAL;
178062306a36Sopenharmony_ci			goto out;
178162306a36Sopenharmony_ci		}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci		if (wep104) {
178462306a36Sopenharmony_ci			pr_debug("%s: 104bit key\n", __func__);
178562306a36Sopenharmony_ci			wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_104BIT);
178662306a36Sopenharmony_ci		} else {
178762306a36Sopenharmony_ci			pr_debug("%s: 40bit key\n", __func__);
178862306a36Sopenharmony_ci			wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_40BIT);
178962306a36Sopenharmony_ci		}
179062306a36Sopenharmony_ci	} else {
179162306a36Sopenharmony_ci		pr_debug("%s: NO encryption\n", __func__);
179262306a36Sopenharmony_ci		wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_NONE);
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/* issue wep setup */
179662306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WEP_CFG,
179762306a36Sopenharmony_ci				   wep, sizeof(*wep));
179862306a36Sopenharmony_ci	if (!cmd)
179962306a36Sopenharmony_ci		ret = -ENOMEM;
180062306a36Sopenharmony_ci	else if (cmd->status || cmd->cmd_status)
180162306a36Sopenharmony_ci		ret = -ENXIO;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	kfree(cmd);
180462306a36Sopenharmony_ciout:
180562306a36Sopenharmony_ci	free_page((unsigned long)wep);
180662306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
180762306a36Sopenharmony_ci	return ret;
180862306a36Sopenharmony_ci}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci#ifdef DEBUG
181162306a36Sopenharmony_cistatic const char *wpasecstr(enum gelic_eurus_wpa_security sec)
181262306a36Sopenharmony_ci{
181362306a36Sopenharmony_ci	switch (sec) {
181462306a36Sopenharmony_ci	case GELIC_EURUS_WPA_SEC_NONE:
181562306a36Sopenharmony_ci		return "NONE";
181662306a36Sopenharmony_ci	case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP:
181762306a36Sopenharmony_ci		return "WPA_TKIP_TKIP";
181862306a36Sopenharmony_ci	case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES:
181962306a36Sopenharmony_ci		return "WPA_TKIP_AES";
182062306a36Sopenharmony_ci	case GELIC_EURUS_WPA_SEC_WPA_AES_AES:
182162306a36Sopenharmony_ci		return "WPA_AES_AES";
182262306a36Sopenharmony_ci	case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP:
182362306a36Sopenharmony_ci		return "WPA2_TKIP_TKIP";
182462306a36Sopenharmony_ci	case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES:
182562306a36Sopenharmony_ci		return "WPA2_TKIP_AES";
182662306a36Sopenharmony_ci	case GELIC_EURUS_WPA_SEC_WPA2_AES_AES:
182762306a36Sopenharmony_ci		return "WPA2_AES_AES";
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci	return "";
183062306a36Sopenharmony_ci};
183162306a36Sopenharmony_ci#endif
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_cistatic int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci	struct gelic_eurus_wpa_cfg *wpa;
183662306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
183762306a36Sopenharmony_ci	u16 security;
183862306a36Sopenharmony_ci	int ret = 0;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
184162306a36Sopenharmony_ci	/* we can assume no one should uses the buffer */
184262306a36Sopenharmony_ci	wpa = (struct gelic_eurus_wpa_cfg *)__get_free_page(GFP_KERNEL);
184362306a36Sopenharmony_ci	if (!wpa)
184462306a36Sopenharmony_ci		return -ENOMEM;
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	memset(wpa, 0, sizeof(*wpa));
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	if (!test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat))
184962306a36Sopenharmony_ci		pr_info("%s: PSK not configured yet\n", __func__);
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	/* copy key */
185262306a36Sopenharmony_ci	memcpy(wpa->psk, wl->psk, wl->psk_len);
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	/* set security level */
185562306a36Sopenharmony_ci	if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) {
185662306a36Sopenharmony_ci		if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) {
185762306a36Sopenharmony_ci			security = GELIC_EURUS_WPA_SEC_WPA2_AES_AES;
185862306a36Sopenharmony_ci		} else {
185962306a36Sopenharmony_ci			if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES &&
186062306a36Sopenharmony_ci			    precise_ie())
186162306a36Sopenharmony_ci				security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES;
186262306a36Sopenharmony_ci			else
186362306a36Sopenharmony_ci				security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP;
186462306a36Sopenharmony_ci		}
186562306a36Sopenharmony_ci	} else {
186662306a36Sopenharmony_ci		if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) {
186762306a36Sopenharmony_ci			security = GELIC_EURUS_WPA_SEC_WPA_AES_AES;
186862306a36Sopenharmony_ci		} else {
186962306a36Sopenharmony_ci			if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES &&
187062306a36Sopenharmony_ci			    precise_ie())
187162306a36Sopenharmony_ci				security = GELIC_EURUS_WPA_SEC_WPA_TKIP_AES;
187262306a36Sopenharmony_ci			else
187362306a36Sopenharmony_ci				security = GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP;
187462306a36Sopenharmony_ci		}
187562306a36Sopenharmony_ci	}
187662306a36Sopenharmony_ci	wpa->security = cpu_to_be16(security);
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	/* PSK type */
187962306a36Sopenharmony_ci	wpa->psk_type = cpu_to_be16(wl->psk_type);
188062306a36Sopenharmony_ci#ifdef DEBUG
188162306a36Sopenharmony_ci	pr_debug("%s: sec=%s psktype=%s\n", __func__,
188262306a36Sopenharmony_ci		 wpasecstr(wpa->security),
188362306a36Sopenharmony_ci		 (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ?
188462306a36Sopenharmony_ci		 "BIN" : "passphrase");
188562306a36Sopenharmony_ci#if 0
188662306a36Sopenharmony_ci	/*
188762306a36Sopenharmony_ci	 * don't enable here if you plan to submit
188862306a36Sopenharmony_ci	 * the debug log because this dumps your precious
188962306a36Sopenharmony_ci	 * passphrase/key.
189062306a36Sopenharmony_ci	 */
189162306a36Sopenharmony_ci	pr_debug("%s: psk=%s\n", __func__,
189262306a36Sopenharmony_ci		 (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ?
189362306a36Sopenharmony_ci		 "N/A" : wpa->psk);
189462306a36Sopenharmony_ci#endif
189562306a36Sopenharmony_ci#endif
189662306a36Sopenharmony_ci	/* issue wpa setup */
189762306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WPA_CFG,
189862306a36Sopenharmony_ci				   wpa, sizeof(*wpa));
189962306a36Sopenharmony_ci	if (!cmd)
190062306a36Sopenharmony_ci		ret = -ENOMEM;
190162306a36Sopenharmony_ci	else if (cmd->status || cmd->cmd_status)
190262306a36Sopenharmony_ci		ret = -ENXIO;
190362306a36Sopenharmony_ci	kfree(cmd);
190462306a36Sopenharmony_ci	free_page((unsigned long)wpa);
190562306a36Sopenharmony_ci	pr_debug("%s: --> %d\n", __func__, ret);
190662306a36Sopenharmony_ci	return ret;
190762306a36Sopenharmony_ci}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci/*
191062306a36Sopenharmony_ci * Start association. caller must hold assoc_stat_lock
191162306a36Sopenharmony_ci */
191262306a36Sopenharmony_cistatic int gelic_wl_associate_bss(struct gelic_wl_info *wl,
191362306a36Sopenharmony_ci				  struct gelic_wl_scan_info *bss)
191462306a36Sopenharmony_ci{
191562306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
191662306a36Sopenharmony_ci	struct gelic_eurus_common_cfg *common;
191762306a36Sopenharmony_ci	int ret = 0;
191862306a36Sopenharmony_ci	unsigned long rc;
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	/* do common config */
192362306a36Sopenharmony_ci	common = (struct gelic_eurus_common_cfg *)__get_free_page(GFP_KERNEL);
192462306a36Sopenharmony_ci	if (!common)
192562306a36Sopenharmony_ci		return -ENOMEM;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	memset(common, 0, sizeof(*common));
192862306a36Sopenharmony_ci	common->bss_type = cpu_to_be16(GELIC_EURUS_BSS_INFRA);
192962306a36Sopenharmony_ci	common->op_mode = cpu_to_be16(GELIC_EURUS_OPMODE_11BG);
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	common->scan_index = cpu_to_be16(bss->eurus_index);
193262306a36Sopenharmony_ci	switch (wl->auth_method) {
193362306a36Sopenharmony_ci	case GELIC_EURUS_AUTH_OPEN:
193462306a36Sopenharmony_ci		common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_OPEN);
193562306a36Sopenharmony_ci		break;
193662306a36Sopenharmony_ci	case GELIC_EURUS_AUTH_SHARED:
193762306a36Sopenharmony_ci		common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_SHARED);
193862306a36Sopenharmony_ci		break;
193962306a36Sopenharmony_ci	}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci#ifdef DEBUG
194262306a36Sopenharmony_ci	scan_list_dump(wl);
194362306a36Sopenharmony_ci#endif
194462306a36Sopenharmony_ci	pr_debug("%s: common cfg index=%d bsstype=%d auth=%d\n", __func__,
194562306a36Sopenharmony_ci		 be16_to_cpu(common->scan_index),
194662306a36Sopenharmony_ci		 be16_to_cpu(common->bss_type),
194762306a36Sopenharmony_ci		 be16_to_cpu(common->auth_method));
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_COMMON_CFG,
195062306a36Sopenharmony_ci				   common, sizeof(*common));
195162306a36Sopenharmony_ci	if (!cmd || cmd->status || cmd->cmd_status) {
195262306a36Sopenharmony_ci		ret = -ENOMEM;
195362306a36Sopenharmony_ci		kfree(cmd);
195462306a36Sopenharmony_ci		goto out;
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci	kfree(cmd);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	/* WEP/WPA */
195962306a36Sopenharmony_ci	switch (wl->wpa_level) {
196062306a36Sopenharmony_ci	case GELIC_WL_WPA_LEVEL_NONE:
196162306a36Sopenharmony_ci		/* If WEP or no security, setup WEP config */
196262306a36Sopenharmony_ci		ret = gelic_wl_do_wep_setup(wl);
196362306a36Sopenharmony_ci		break;
196462306a36Sopenharmony_ci	case GELIC_WL_WPA_LEVEL_WPA:
196562306a36Sopenharmony_ci	case GELIC_WL_WPA_LEVEL_WPA2:
196662306a36Sopenharmony_ci		ret = gelic_wl_do_wpa_setup(wl);
196762306a36Sopenharmony_ci		break;
196862306a36Sopenharmony_ci	}
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	if (ret) {
197162306a36Sopenharmony_ci		pr_debug("%s: WEP/WPA setup failed %d\n", __func__,
197262306a36Sopenharmony_ci			 ret);
197362306a36Sopenharmony_ci		ret = -EPERM;
197462306a36Sopenharmony_ci		gelic_wl_send_iwap_event(wl, NULL);
197562306a36Sopenharmony_ci		goto out;
197662306a36Sopenharmony_ci	}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	/* start association */
197962306a36Sopenharmony_ci	init_completion(&wl->assoc_done);
198062306a36Sopenharmony_ci	wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATING;
198162306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_ASSOC,
198262306a36Sopenharmony_ci				   NULL, 0);
198362306a36Sopenharmony_ci	if (!cmd || cmd->status || cmd->cmd_status) {
198462306a36Sopenharmony_ci		pr_debug("%s: assoc request failed\n", __func__);
198562306a36Sopenharmony_ci		wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
198662306a36Sopenharmony_ci		kfree(cmd);
198762306a36Sopenharmony_ci		ret = -ENOMEM;
198862306a36Sopenharmony_ci		gelic_wl_send_iwap_event(wl, NULL);
198962306a36Sopenharmony_ci		goto out;
199062306a36Sopenharmony_ci	}
199162306a36Sopenharmony_ci	kfree(cmd);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	/* wait for connected event */
199462306a36Sopenharmony_ci	rc = wait_for_completion_timeout(&wl->assoc_done, HZ * 4);/*FIXME*/
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	if (!rc) {
199762306a36Sopenharmony_ci		/* timeouted.  Maybe key or cyrpt mode is wrong */
199862306a36Sopenharmony_ci		pr_info("%s: connect timeout\n", __func__);
199962306a36Sopenharmony_ci		cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC,
200062306a36Sopenharmony_ci					   NULL, 0);
200162306a36Sopenharmony_ci		kfree(cmd);
200262306a36Sopenharmony_ci		wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
200362306a36Sopenharmony_ci		gelic_wl_send_iwap_event(wl, NULL);
200462306a36Sopenharmony_ci		ret = -ENXIO;
200562306a36Sopenharmony_ci	} else {
200662306a36Sopenharmony_ci		wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATED;
200762306a36Sopenharmony_ci		/* copy bssid */
200862306a36Sopenharmony_ci		memcpy(wl->active_bssid, &bss->hwinfo->bssid[2], ETH_ALEN);
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci		/* send connect event */
201162306a36Sopenharmony_ci		gelic_wl_send_iwap_event(wl, wl->active_bssid);
201262306a36Sopenharmony_ci		pr_info("%s: connected\n", __func__);
201362306a36Sopenharmony_ci	}
201462306a36Sopenharmony_ciout:
201562306a36Sopenharmony_ci	free_page((unsigned long)common);
201662306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
201762306a36Sopenharmony_ci	return ret;
201862306a36Sopenharmony_ci}
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci/*
202162306a36Sopenharmony_ci * connected event
202262306a36Sopenharmony_ci */
202362306a36Sopenharmony_cistatic void gelic_wl_connected_event(struct gelic_wl_info *wl,
202462306a36Sopenharmony_ci				     u64 event)
202562306a36Sopenharmony_ci{
202662306a36Sopenharmony_ci	u64 desired_event = 0;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	switch (wl->wpa_level) {
202962306a36Sopenharmony_ci	case GELIC_WL_WPA_LEVEL_NONE:
203062306a36Sopenharmony_ci		desired_event = GELIC_LV1_WL_EVENT_CONNECTED;
203162306a36Sopenharmony_ci		break;
203262306a36Sopenharmony_ci	case GELIC_WL_WPA_LEVEL_WPA:
203362306a36Sopenharmony_ci	case GELIC_WL_WPA_LEVEL_WPA2:
203462306a36Sopenharmony_ci		desired_event = GELIC_LV1_WL_EVENT_WPA_CONNECTED;
203562306a36Sopenharmony_ci		break;
203662306a36Sopenharmony_ci	}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	if (desired_event == event) {
203962306a36Sopenharmony_ci		pr_debug("%s: completed\n", __func__);
204062306a36Sopenharmony_ci		complete(&wl->assoc_done);
204162306a36Sopenharmony_ci		netif_carrier_on(port_to_netdev(wl_port(wl)));
204262306a36Sopenharmony_ci	} else
204362306a36Sopenharmony_ci		pr_debug("%s: event %#llx under wpa\n",
204462306a36Sopenharmony_ci				 __func__, event);
204562306a36Sopenharmony_ci}
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci/*
204862306a36Sopenharmony_ci * disconnect event
204962306a36Sopenharmony_ci */
205062306a36Sopenharmony_cistatic void gelic_wl_disconnect_event(struct gelic_wl_info *wl,
205162306a36Sopenharmony_ci				      u64 event)
205262306a36Sopenharmony_ci{
205362306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
205462306a36Sopenharmony_ci	int lock;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	/*
205762306a36Sopenharmony_ci	 * If we fall here in the middle of association,
205862306a36Sopenharmony_ci	 * associate_bss() should be waiting for complation of
205962306a36Sopenharmony_ci	 * wl->assoc_done.
206062306a36Sopenharmony_ci	 * As it waits with timeout, just leave assoc_done
206162306a36Sopenharmony_ci	 * uncompleted, then it terminates with timeout
206262306a36Sopenharmony_ci	 */
206362306a36Sopenharmony_ci	if (!mutex_trylock(&wl->assoc_stat_lock)) {
206462306a36Sopenharmony_ci		pr_debug("%s: already locked\n", __func__);
206562306a36Sopenharmony_ci		lock = 0;
206662306a36Sopenharmony_ci	} else {
206762306a36Sopenharmony_ci		pr_debug("%s: obtain lock\n", __func__);
206862306a36Sopenharmony_ci		lock = 1;
206962306a36Sopenharmony_ci	}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0);
207262306a36Sopenharmony_ci	kfree(cmd);
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	/* send disconnected event to the supplicant */
207562306a36Sopenharmony_ci	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
207662306a36Sopenharmony_ci		gelic_wl_send_iwap_event(wl, NULL);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
207962306a36Sopenharmony_ci	netif_carrier_off(port_to_netdev(wl_port(wl)));
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	if (lock)
208262306a36Sopenharmony_ci		mutex_unlock(&wl->assoc_stat_lock);
208362306a36Sopenharmony_ci}
208462306a36Sopenharmony_ci/*
208562306a36Sopenharmony_ci * event worker
208662306a36Sopenharmony_ci */
208762306a36Sopenharmony_ci#ifdef DEBUG
208862306a36Sopenharmony_cistatic const char *eventstr(enum gelic_lv1_wl_event event)
208962306a36Sopenharmony_ci{
209062306a36Sopenharmony_ci	static char buf[32];
209162306a36Sopenharmony_ci	char *ret;
209262306a36Sopenharmony_ci	if (event & GELIC_LV1_WL_EVENT_DEVICE_READY)
209362306a36Sopenharmony_ci		ret = "EURUS_READY";
209462306a36Sopenharmony_ci	else if (event & GELIC_LV1_WL_EVENT_SCAN_COMPLETED)
209562306a36Sopenharmony_ci		ret = "SCAN_COMPLETED";
209662306a36Sopenharmony_ci	else if (event & GELIC_LV1_WL_EVENT_DEAUTH)
209762306a36Sopenharmony_ci		ret = "DEAUTH";
209862306a36Sopenharmony_ci	else if (event & GELIC_LV1_WL_EVENT_BEACON_LOST)
209962306a36Sopenharmony_ci		ret = "BEACON_LOST";
210062306a36Sopenharmony_ci	else if (event & GELIC_LV1_WL_EVENT_CONNECTED)
210162306a36Sopenharmony_ci		ret = "CONNECTED";
210262306a36Sopenharmony_ci	else if (event & GELIC_LV1_WL_EVENT_WPA_CONNECTED)
210362306a36Sopenharmony_ci		ret = "WPA_CONNECTED";
210462306a36Sopenharmony_ci	else if (event & GELIC_LV1_WL_EVENT_WPA_ERROR)
210562306a36Sopenharmony_ci		ret = "WPA_ERROR";
210662306a36Sopenharmony_ci	else {
210762306a36Sopenharmony_ci		sprintf(buf, "Unknown(%#x)", event);
210862306a36Sopenharmony_ci		ret = buf;
210962306a36Sopenharmony_ci	}
211062306a36Sopenharmony_ci	return ret;
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci#else
211362306a36Sopenharmony_cistatic const char *eventstr(enum gelic_lv1_wl_event event)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	return NULL;
211662306a36Sopenharmony_ci}
211762306a36Sopenharmony_ci#endif
211862306a36Sopenharmony_cistatic void gelic_wl_event_worker(struct work_struct *work)
211962306a36Sopenharmony_ci{
212062306a36Sopenharmony_ci	struct gelic_wl_info *wl;
212162306a36Sopenharmony_ci	struct gelic_port *port;
212262306a36Sopenharmony_ci	u64 event, tmp;
212362306a36Sopenharmony_ci	int status;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	pr_debug("%s:start\n", __func__);
212662306a36Sopenharmony_ci	wl = container_of(work, struct gelic_wl_info, event_work.work);
212762306a36Sopenharmony_ci	port = wl_port(wl);
212862306a36Sopenharmony_ci	while (1) {
212962306a36Sopenharmony_ci		status = lv1_net_control(bus_id(port->card), dev_id(port->card),
213062306a36Sopenharmony_ci					 GELIC_LV1_GET_WLAN_EVENT, 0, 0, 0,
213162306a36Sopenharmony_ci					 &event, &tmp);
213262306a36Sopenharmony_ci		if (status) {
213362306a36Sopenharmony_ci			if (status != LV1_NO_ENTRY)
213462306a36Sopenharmony_ci				pr_debug("%s:wlan event failed %d\n",
213562306a36Sopenharmony_ci					 __func__, status);
213662306a36Sopenharmony_ci			/* got all events */
213762306a36Sopenharmony_ci			pr_debug("%s:end\n", __func__);
213862306a36Sopenharmony_ci			return;
213962306a36Sopenharmony_ci		}
214062306a36Sopenharmony_ci		pr_debug("%s: event=%s\n", __func__, eventstr(event));
214162306a36Sopenharmony_ci		switch (event) {
214262306a36Sopenharmony_ci		case GELIC_LV1_WL_EVENT_SCAN_COMPLETED:
214362306a36Sopenharmony_ci			gelic_wl_scan_complete_event(wl);
214462306a36Sopenharmony_ci			break;
214562306a36Sopenharmony_ci		case GELIC_LV1_WL_EVENT_BEACON_LOST:
214662306a36Sopenharmony_ci		case GELIC_LV1_WL_EVENT_DEAUTH:
214762306a36Sopenharmony_ci			gelic_wl_disconnect_event(wl, event);
214862306a36Sopenharmony_ci			break;
214962306a36Sopenharmony_ci		case GELIC_LV1_WL_EVENT_CONNECTED:
215062306a36Sopenharmony_ci		case GELIC_LV1_WL_EVENT_WPA_CONNECTED:
215162306a36Sopenharmony_ci			gelic_wl_connected_event(wl, event);
215262306a36Sopenharmony_ci			break;
215362306a36Sopenharmony_ci		default:
215462306a36Sopenharmony_ci			break;
215562306a36Sopenharmony_ci		}
215662306a36Sopenharmony_ci	} /* while */
215762306a36Sopenharmony_ci}
215862306a36Sopenharmony_ci/*
215962306a36Sopenharmony_ci * association worker
216062306a36Sopenharmony_ci */
216162306a36Sopenharmony_cistatic void gelic_wl_assoc_worker(struct work_struct *work)
216262306a36Sopenharmony_ci{
216362306a36Sopenharmony_ci	struct gelic_wl_info *wl;
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	struct gelic_wl_scan_info *best_bss;
216662306a36Sopenharmony_ci	int ret;
216762306a36Sopenharmony_ci	unsigned long irqflag;
216862306a36Sopenharmony_ci	u8 *essid;
216962306a36Sopenharmony_ci	size_t essid_len;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	wl = container_of(work, struct gelic_wl_info, assoc_work.work);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	mutex_lock(&wl->assoc_stat_lock);
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	if (wl->assoc_stat != GELIC_WL_ASSOC_STAT_DISCONN)
217662306a36Sopenharmony_ci		goto out;
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	spin_lock_irqsave(&wl->lock, irqflag);
217962306a36Sopenharmony_ci	if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) {
218062306a36Sopenharmony_ci		pr_debug("%s: assoc ESSID configured %s\n", __func__,
218162306a36Sopenharmony_ci			 wl->essid);
218262306a36Sopenharmony_ci		essid = wl->essid;
218362306a36Sopenharmony_ci		essid_len = wl->essid_len;
218462306a36Sopenharmony_ci	} else {
218562306a36Sopenharmony_ci		essid = NULL;
218662306a36Sopenharmony_ci		essid_len = 0;
218762306a36Sopenharmony_ci	}
218862306a36Sopenharmony_ci	spin_unlock_irqrestore(&wl->lock, irqflag);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	ret = gelic_wl_start_scan(wl, 0, essid, essid_len);
219162306a36Sopenharmony_ci	if (ret == -ERESTARTSYS) {
219262306a36Sopenharmony_ci		pr_debug("%s: scan start failed association\n", __func__);
219362306a36Sopenharmony_ci		schedule_delayed_work(&wl->assoc_work, HZ/10); /*FIXME*/
219462306a36Sopenharmony_ci		goto out;
219562306a36Sopenharmony_ci	} else if (ret) {
219662306a36Sopenharmony_ci		pr_info("%s: scan prerequisite failed\n", __func__);
219762306a36Sopenharmony_ci		goto out;
219862306a36Sopenharmony_ci	}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	/*
220162306a36Sopenharmony_ci	 * Wait for bss scan completion
220262306a36Sopenharmony_ci	 * If we have scan list already, gelic_wl_start_scan()
220362306a36Sopenharmony_ci	 * returns OK and raises the complete.  Thus,
220462306a36Sopenharmony_ci	 * it's ok to wait unconditionally here
220562306a36Sopenharmony_ci	 */
220662306a36Sopenharmony_ci	wait_for_completion(&wl->scan_done);
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	pr_debug("%s: scan done\n", __func__);
220962306a36Sopenharmony_ci	mutex_lock(&wl->scan_lock);
221062306a36Sopenharmony_ci	if (wl->scan_stat != GELIC_WL_SCAN_STAT_GOT_LIST) {
221162306a36Sopenharmony_ci		gelic_wl_send_iwap_event(wl, NULL);
221262306a36Sopenharmony_ci		pr_info("%s: no scan list. association failed\n", __func__);
221362306a36Sopenharmony_ci		goto scan_lock_out;
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	/* find best matching bss */
221762306a36Sopenharmony_ci	best_bss = gelic_wl_find_best_bss(wl);
221862306a36Sopenharmony_ci	if (!best_bss) {
221962306a36Sopenharmony_ci		gelic_wl_send_iwap_event(wl, NULL);
222062306a36Sopenharmony_ci		pr_info("%s: no bss matched. association failed\n", __func__);
222162306a36Sopenharmony_ci		goto scan_lock_out;
222262306a36Sopenharmony_ci	}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	/* ok, do association */
222562306a36Sopenharmony_ci	ret = gelic_wl_associate_bss(wl, best_bss);
222662306a36Sopenharmony_ci	if (ret)
222762306a36Sopenharmony_ci		pr_info("%s: association failed %d\n", __func__, ret);
222862306a36Sopenharmony_ciscan_lock_out:
222962306a36Sopenharmony_ci	mutex_unlock(&wl->scan_lock);
223062306a36Sopenharmony_ciout:
223162306a36Sopenharmony_ci	mutex_unlock(&wl->assoc_stat_lock);
223262306a36Sopenharmony_ci}
223362306a36Sopenharmony_ci/*
223462306a36Sopenharmony_ci * Interrupt handler
223562306a36Sopenharmony_ci * Called from the ethernet interrupt handler
223662306a36Sopenharmony_ci * Processes wireless specific virtual interrupts only
223762306a36Sopenharmony_ci */
223862306a36Sopenharmony_civoid gelic_wl_interrupt(struct net_device *netdev, u64 status)
223962306a36Sopenharmony_ci{
224062306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	if (status & GELIC_CARD_WLAN_COMMAND_COMPLETED) {
224362306a36Sopenharmony_ci		pr_debug("%s:cmd complete\n", __func__);
224462306a36Sopenharmony_ci		complete(&wl->cmd_done_intr);
224562306a36Sopenharmony_ci	}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	if (status & GELIC_CARD_WLAN_EVENT_RECEIVED) {
224862306a36Sopenharmony_ci		pr_debug("%s:event received\n", __func__);
224962306a36Sopenharmony_ci		queue_delayed_work(wl->event_queue, &wl->event_work, 0);
225062306a36Sopenharmony_ci	}
225162306a36Sopenharmony_ci}
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci/*
225462306a36Sopenharmony_ci * driver helpers
225562306a36Sopenharmony_ci */
225662306a36Sopenharmony_cistatic const iw_handler gelic_wl_wext_handler[] =
225762306a36Sopenharmony_ci{
225862306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name),
225962306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range),
226062306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan),
226162306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan),
226262306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth),
226362306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth),
226462306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid),
226562306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid),
226662306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode),
226762306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode),
226862306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap),
226962306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap),
227062306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext),
227162306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext),
227262306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode),
227362306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode),
227462306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick),
227562306a36Sopenharmony_ci};
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_cistatic const struct iw_handler_def gelic_wl_wext_handler_def = {
227862306a36Sopenharmony_ci	.num_standard		= ARRAY_SIZE(gelic_wl_wext_handler),
227962306a36Sopenharmony_ci	.standard		= gelic_wl_wext_handler,
228062306a36Sopenharmony_ci	.get_wireless_stats	= gelic_wl_get_wireless_stats,
228162306a36Sopenharmony_ci};
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_cistatic struct net_device *gelic_wl_alloc(struct gelic_card *card)
228462306a36Sopenharmony_ci{
228562306a36Sopenharmony_ci	struct net_device *netdev;
228662306a36Sopenharmony_ci	struct gelic_port *port;
228762306a36Sopenharmony_ci	struct gelic_wl_info *wl;
228862306a36Sopenharmony_ci	unsigned int i;
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	pr_debug("%s:start\n", __func__);
229162306a36Sopenharmony_ci	netdev = alloc_etherdev(sizeof(struct gelic_port) +
229262306a36Sopenharmony_ci				sizeof(struct gelic_wl_info));
229362306a36Sopenharmony_ci	pr_debug("%s: netdev =%p card=%p\n", __func__, netdev, card);
229462306a36Sopenharmony_ci	if (!netdev)
229562306a36Sopenharmony_ci		return NULL;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	strcpy(netdev->name, "wlan%d");
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	port = netdev_priv(netdev);
230062306a36Sopenharmony_ci	port->netdev = netdev;
230162306a36Sopenharmony_ci	port->card = card;
230262306a36Sopenharmony_ci	port->type = GELIC_PORT_WIRELESS;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	wl = port_wl(port);
230562306a36Sopenharmony_ci	pr_debug("%s: wl=%p port=%p\n", __func__, wl, port);
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	/* allocate scan list */
230862306a36Sopenharmony_ci	wl->networks = kcalloc(GELIC_WL_BSS_MAX_ENT,
230962306a36Sopenharmony_ci			       sizeof(struct gelic_wl_scan_info),
231062306a36Sopenharmony_ci			       GFP_KERNEL);
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	if (!wl->networks)
231362306a36Sopenharmony_ci		goto fail_bss;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	wl->eurus_cmd_queue = create_singlethread_workqueue("gelic_cmd");
231662306a36Sopenharmony_ci	if (!wl->eurus_cmd_queue)
231762306a36Sopenharmony_ci		goto fail_cmd_workqueue;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci	wl->event_queue = create_singlethread_workqueue("gelic_event");
232062306a36Sopenharmony_ci	if (!wl->event_queue)
232162306a36Sopenharmony_ci		goto fail_event_workqueue;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	INIT_LIST_HEAD(&wl->network_free_list);
232462306a36Sopenharmony_ci	INIT_LIST_HEAD(&wl->network_list);
232562306a36Sopenharmony_ci	for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++)
232662306a36Sopenharmony_ci		list_add_tail(&wl->networks[i].list,
232762306a36Sopenharmony_ci			      &wl->network_free_list);
232862306a36Sopenharmony_ci	init_completion(&wl->cmd_done_intr);
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	INIT_DELAYED_WORK(&wl->event_work, gelic_wl_event_worker);
233162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&wl->assoc_work, gelic_wl_assoc_worker);
233262306a36Sopenharmony_ci	mutex_init(&wl->scan_lock);
233362306a36Sopenharmony_ci	mutex_init(&wl->assoc_stat_lock);
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	init_completion(&wl->scan_done);
233662306a36Sopenharmony_ci	/* for the case that no scan request is issued and stop() is called */
233762306a36Sopenharmony_ci	complete(&wl->scan_done);
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	spin_lock_init(&wl->lock);
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	wl->scan_age = 5*HZ; /* FIXME */
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	/* buffer for receiving scanned list etc */
234462306a36Sopenharmony_ci	BUILD_BUG_ON(PAGE_SIZE <
234562306a36Sopenharmony_ci		     sizeof(struct gelic_eurus_scan_info) *
234662306a36Sopenharmony_ci		     GELIC_EURUS_MAX_SCAN);
234762306a36Sopenharmony_ci	pr_debug("%s:end\n", __func__);
234862306a36Sopenharmony_ci	return netdev;
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_cifail_event_workqueue:
235162306a36Sopenharmony_ci	destroy_workqueue(wl->eurus_cmd_queue);
235262306a36Sopenharmony_cifail_cmd_workqueue:
235362306a36Sopenharmony_ci	kfree(wl->networks);
235462306a36Sopenharmony_cifail_bss:
235562306a36Sopenharmony_ci	free_netdev(netdev);
235662306a36Sopenharmony_ci	pr_debug("%s:end error\n", __func__);
235762306a36Sopenharmony_ci	return NULL;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci}
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_cistatic void gelic_wl_free(struct gelic_wl_info *wl)
236262306a36Sopenharmony_ci{
236362306a36Sopenharmony_ci	struct gelic_wl_scan_info *scan_info;
236462306a36Sopenharmony_ci	unsigned int i;
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	pr_debug("%s: destroy queues\n", __func__);
236962306a36Sopenharmony_ci	destroy_workqueue(wl->eurus_cmd_queue);
237062306a36Sopenharmony_ci	destroy_workqueue(wl->event_queue);
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	scan_info = wl->networks;
237362306a36Sopenharmony_ci	for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++, scan_info++)
237462306a36Sopenharmony_ci		kfree(scan_info->hwinfo);
237562306a36Sopenharmony_ci	kfree(wl->networks);
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	free_netdev(port_to_netdev(wl_port(wl)));
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	pr_debug("%s: ->\n", __func__);
238062306a36Sopenharmony_ci}
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_cistatic int gelic_wl_try_associate(struct net_device *netdev)
238362306a36Sopenharmony_ci{
238462306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
238562306a36Sopenharmony_ci	int ret = -1;
238662306a36Sopenharmony_ci	unsigned int i;
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	pr_debug("%s: <-\n", __func__);
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	/* check constraits for start association */
239162306a36Sopenharmony_ci	/* for no access restriction AP */
239262306a36Sopenharmony_ci	if (wl->group_cipher_method == GELIC_WL_CIPHER_NONE) {
239362306a36Sopenharmony_ci		if (test_bit(GELIC_WL_STAT_CONFIGURED,
239462306a36Sopenharmony_ci			     &wl->stat))
239562306a36Sopenharmony_ci			goto do_associate;
239662306a36Sopenharmony_ci		else {
239762306a36Sopenharmony_ci			pr_debug("%s: no wep, not configured\n", __func__);
239862306a36Sopenharmony_ci			return ret;
239962306a36Sopenharmony_ci		}
240062306a36Sopenharmony_ci	}
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	/* for WEP, one of four keys should be set */
240362306a36Sopenharmony_ci	if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
240462306a36Sopenharmony_ci		/* one of keys set */
240562306a36Sopenharmony_ci		for (i = 0; i < GELIC_WEP_KEYS; i++) {
240662306a36Sopenharmony_ci			if (test_bit(i, &wl->key_enabled))
240762306a36Sopenharmony_ci			    goto do_associate;
240862306a36Sopenharmony_ci		}
240962306a36Sopenharmony_ci		pr_debug("%s: WEP, but no key specified\n", __func__);
241062306a36Sopenharmony_ci		return ret;
241162306a36Sopenharmony_ci	}
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	/* for WPA[2], psk should be set */
241462306a36Sopenharmony_ci	if ((wl->group_cipher_method == GELIC_WL_CIPHER_TKIP) ||
241562306a36Sopenharmony_ci	    (wl->group_cipher_method == GELIC_WL_CIPHER_AES)) {
241662306a36Sopenharmony_ci		if (test_bit(GELIC_WL_STAT_WPA_PSK_SET,
241762306a36Sopenharmony_ci			     &wl->stat))
241862306a36Sopenharmony_ci			goto do_associate;
241962306a36Sopenharmony_ci		else {
242062306a36Sopenharmony_ci			pr_debug("%s: AES/TKIP, but PSK not configured\n",
242162306a36Sopenharmony_ci				 __func__);
242262306a36Sopenharmony_ci			return ret;
242362306a36Sopenharmony_ci		}
242462306a36Sopenharmony_ci	}
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_cido_associate:
242762306a36Sopenharmony_ci	ret = schedule_delayed_work(&wl->assoc_work, 0);
242862306a36Sopenharmony_ci	pr_debug("%s: start association work %d\n", __func__, ret);
242962306a36Sopenharmony_ci	return ret;
243062306a36Sopenharmony_ci}
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci/*
243362306a36Sopenharmony_ci * netdev handlers
243462306a36Sopenharmony_ci */
243562306a36Sopenharmony_cistatic int gelic_wl_open(struct net_device *netdev)
243662306a36Sopenharmony_ci{
243762306a36Sopenharmony_ci	struct gelic_card *card = netdev_card(netdev);
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	pr_debug("%s:->%p\n", __func__, netdev);
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	gelic_card_up(card);
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	/* try to associate */
244462306a36Sopenharmony_ci	gelic_wl_try_associate(netdev);
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	netif_start_queue(netdev);
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	pr_debug("%s:<-\n", __func__);
244962306a36Sopenharmony_ci	return 0;
245062306a36Sopenharmony_ci}
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci/*
245362306a36Sopenharmony_ci * reset state machine
245462306a36Sopenharmony_ci */
245562306a36Sopenharmony_cistatic int gelic_wl_reset_state(struct gelic_wl_info *wl)
245662306a36Sopenharmony_ci{
245762306a36Sopenharmony_ci	struct gelic_wl_scan_info *target;
245862306a36Sopenharmony_ci	struct gelic_wl_scan_info *tmp;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	/* empty scan list */
246162306a36Sopenharmony_ci	list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
246262306a36Sopenharmony_ci		list_move_tail(&target->list, &wl->network_free_list);
246362306a36Sopenharmony_ci	}
246462306a36Sopenharmony_ci	wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	/* clear configuration */
246762306a36Sopenharmony_ci	wl->auth_method = GELIC_EURUS_AUTH_OPEN;
246862306a36Sopenharmony_ci	wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
246962306a36Sopenharmony_ci	wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
247062306a36Sopenharmony_ci	wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	wl->key_enabled = 0;
247362306a36Sopenharmony_ci	wl->current_key = 0;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE;
247662306a36Sopenharmony_ci	wl->psk_len = 0;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	wl->essid_len = 0;
247962306a36Sopenharmony_ci	memset(wl->essid, 0, sizeof(wl->essid));
248062306a36Sopenharmony_ci	memset(wl->bssid, 0, sizeof(wl->bssid));
248162306a36Sopenharmony_ci	memset(wl->active_bssid, 0, sizeof(wl->active_bssid));
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	memset(&wl->iwstat, 0, sizeof(wl->iwstat));
248662306a36Sopenharmony_ci	/* all status bit clear */
248762306a36Sopenharmony_ci	wl->stat = 0;
248862306a36Sopenharmony_ci	return 0;
248962306a36Sopenharmony_ci}
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci/*
249262306a36Sopenharmony_ci * Tell eurus to terminate association
249362306a36Sopenharmony_ci */
249462306a36Sopenharmony_cistatic void gelic_wl_disconnect(struct net_device *netdev)
249562306a36Sopenharmony_ci{
249662306a36Sopenharmony_ci	struct gelic_port *port = netdev_priv(netdev);
249762306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(port);
249862306a36Sopenharmony_ci	struct gelic_eurus_cmd *cmd;
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	/*
250162306a36Sopenharmony_ci	 * If scann process is running on chip,
250262306a36Sopenharmony_ci	 * further requests will be rejected
250362306a36Sopenharmony_ci	 */
250462306a36Sopenharmony_ci	if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING)
250562306a36Sopenharmony_ci		wait_for_completion_timeout(&wl->scan_done, HZ);
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0);
250862306a36Sopenharmony_ci	kfree(cmd);
250962306a36Sopenharmony_ci	gelic_wl_send_iwap_event(wl, NULL);
251062306a36Sopenharmony_ci};
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_cistatic int gelic_wl_stop(struct net_device *netdev)
251362306a36Sopenharmony_ci{
251462306a36Sopenharmony_ci	struct gelic_port *port = netdev_priv(netdev);
251562306a36Sopenharmony_ci	struct gelic_wl_info *wl = port_wl(port);
251662306a36Sopenharmony_ci	struct gelic_card *card = netdev_card(netdev);
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	pr_debug("%s:<-\n", __func__);
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	/*
252162306a36Sopenharmony_ci	 * Cancel pending association work.
252262306a36Sopenharmony_ci	 * event work can run after netdev down
252362306a36Sopenharmony_ci	 */
252462306a36Sopenharmony_ci	cancel_delayed_work(&wl->assoc_work);
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
252762306a36Sopenharmony_ci		gelic_wl_disconnect(netdev);
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	/* reset our state machine */
253062306a36Sopenharmony_ci	gelic_wl_reset_state(wl);
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	netif_stop_queue(netdev);
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	gelic_card_down(card);
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	pr_debug("%s:->\n", __func__);
253762306a36Sopenharmony_ci	return 0;
253862306a36Sopenharmony_ci}
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci/* -- */
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_cistatic const struct net_device_ops gelic_wl_netdevice_ops = {
254362306a36Sopenharmony_ci	.ndo_open = gelic_wl_open,
254462306a36Sopenharmony_ci	.ndo_stop = gelic_wl_stop,
254562306a36Sopenharmony_ci	.ndo_start_xmit = gelic_net_xmit,
254662306a36Sopenharmony_ci	.ndo_set_rx_mode = gelic_net_set_multi,
254762306a36Sopenharmony_ci	.ndo_tx_timeout = gelic_net_tx_timeout,
254862306a36Sopenharmony_ci	.ndo_set_mac_address = eth_mac_addr,
254962306a36Sopenharmony_ci	.ndo_validate_addr = eth_validate_addr,
255062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
255162306a36Sopenharmony_ci	.ndo_poll_controller = gelic_net_poll_controller,
255262306a36Sopenharmony_ci#endif
255362306a36Sopenharmony_ci};
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_cistatic const struct ethtool_ops gelic_wl_ethtool_ops = {
255662306a36Sopenharmony_ci	.get_drvinfo	= gelic_net_get_drvinfo,
255762306a36Sopenharmony_ci	.get_link	= gelic_wl_get_link,
255862306a36Sopenharmony_ci};
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_cistatic void gelic_wl_setup_netdev_ops(struct net_device *netdev)
256162306a36Sopenharmony_ci{
256262306a36Sopenharmony_ci	struct gelic_wl_info *wl;
256362306a36Sopenharmony_ci	wl = port_wl(netdev_priv(netdev));
256462306a36Sopenharmony_ci	BUG_ON(!wl);
256562306a36Sopenharmony_ci	netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT;
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	netdev->ethtool_ops = &gelic_wl_ethtool_ops;
256862306a36Sopenharmony_ci	netdev->netdev_ops = &gelic_wl_netdevice_ops;
256962306a36Sopenharmony_ci	netdev->wireless_data = &wl->wireless_data;
257062306a36Sopenharmony_ci	netdev->wireless_handlers = &gelic_wl_wext_handler_def;
257162306a36Sopenharmony_ci}
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci/*
257462306a36Sopenharmony_ci * driver probe/remove
257562306a36Sopenharmony_ci */
257662306a36Sopenharmony_ciint gelic_wl_driver_probe(struct gelic_card *card)
257762306a36Sopenharmony_ci{
257862306a36Sopenharmony_ci	int ret;
257962306a36Sopenharmony_ci	struct net_device *netdev;
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	pr_debug("%s:start\n", __func__);
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	if (ps3_compare_firmware_version(1, 6, 0) < 0)
258462306a36Sopenharmony_ci		return 0;
258562306a36Sopenharmony_ci	if (!card->vlan[GELIC_PORT_WIRELESS].tx)
258662306a36Sopenharmony_ci		return 0;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	/* alloc netdevice for wireless */
258962306a36Sopenharmony_ci	netdev = gelic_wl_alloc(card);
259062306a36Sopenharmony_ci	if (!netdev)
259162306a36Sopenharmony_ci		return -ENOMEM;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	/* setup net_device structure */
259462306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &card->dev->core);
259562306a36Sopenharmony_ci	gelic_wl_setup_netdev_ops(netdev);
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	/* setup some of net_device and register it */
259862306a36Sopenharmony_ci	ret = gelic_net_setup_netdev(netdev, card);
259962306a36Sopenharmony_ci	if (ret)
260062306a36Sopenharmony_ci		goto fail_setup;
260162306a36Sopenharmony_ci	card->netdev[GELIC_PORT_WIRELESS] = netdev;
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_ci	/* add enable wireless interrupt */
260462306a36Sopenharmony_ci	card->irq_mask |= GELIC_CARD_WLAN_EVENT_RECEIVED |
260562306a36Sopenharmony_ci		GELIC_CARD_WLAN_COMMAND_COMPLETED;
260662306a36Sopenharmony_ci	/* to allow wireless commands while both interfaces are down */
260762306a36Sopenharmony_ci	gelic_card_set_irq_mask(card, GELIC_CARD_WLAN_EVENT_RECEIVED |
260862306a36Sopenharmony_ci				GELIC_CARD_WLAN_COMMAND_COMPLETED);
260962306a36Sopenharmony_ci	pr_debug("%s:end\n", __func__);
261062306a36Sopenharmony_ci	return 0;
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_cifail_setup:
261362306a36Sopenharmony_ci	gelic_wl_free(port_wl(netdev_port(netdev)));
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	return ret;
261662306a36Sopenharmony_ci}
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ciint gelic_wl_driver_remove(struct gelic_card *card)
261962306a36Sopenharmony_ci{
262062306a36Sopenharmony_ci	struct gelic_wl_info *wl;
262162306a36Sopenharmony_ci	struct net_device *netdev;
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	pr_debug("%s:start\n", __func__);
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	if (ps3_compare_firmware_version(1, 6, 0) < 0)
262662306a36Sopenharmony_ci		return 0;
262762306a36Sopenharmony_ci	if (!card->vlan[GELIC_PORT_WIRELESS].tx)
262862306a36Sopenharmony_ci		return 0;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	netdev = card->netdev[GELIC_PORT_WIRELESS];
263162306a36Sopenharmony_ci	wl = port_wl(netdev_priv(netdev));
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	/* if the interface was not up, but associated */
263462306a36Sopenharmony_ci	if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
263562306a36Sopenharmony_ci		gelic_wl_disconnect(netdev);
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	complete(&wl->cmd_done_intr);
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	/* cancel all work queue */
264062306a36Sopenharmony_ci	cancel_delayed_work(&wl->assoc_work);
264162306a36Sopenharmony_ci	cancel_delayed_work(&wl->event_work);
264262306a36Sopenharmony_ci	flush_workqueue(wl->eurus_cmd_queue);
264362306a36Sopenharmony_ci	flush_workqueue(wl->event_queue);
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	unregister_netdev(netdev);
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	/* disable wireless interrupt */
264862306a36Sopenharmony_ci	pr_debug("%s: disable intr\n", __func__);
264962306a36Sopenharmony_ci	card->irq_mask &= ~(GELIC_CARD_WLAN_EVENT_RECEIVED |
265062306a36Sopenharmony_ci			    GELIC_CARD_WLAN_COMMAND_COMPLETED);
265162306a36Sopenharmony_ci	/* free bss list, netdev*/
265262306a36Sopenharmony_ci	gelic_wl_free(wl);
265362306a36Sopenharmony_ci	pr_debug("%s:end\n", __func__);
265462306a36Sopenharmony_ci	return 0;
265562306a36Sopenharmony_ci}
2656