lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20070601150802.2C3D.MOKUNO@sm.sony.co.jp>
Date:	Fri, 01 Jun 2007 15:10:35 +0900
From:	MOKUNO Masakazu <mokuno@...sony.co.jp>
To:	netdev@...r.kernel.org
Cc:	Geoff Levand <geoffrey.levand@...sony.com>,
	Geert Uytterhoeven <Geert.Uytterhoeven@...ycom.com>
Subject: [PATCH 2/2] ps3: add wireless LAN support

Add support of the internal wireless LAN of PS3.
 
Signed-off-by: Masakazu Mokuno <mokuno@...sony.co.jp>
---
 drivers/net/Kconfig          |    7 
 drivers/net/Makefile         |    4 
 drivers/net/gelic_net.c      |   30 
 drivers/net/gelic_net.h      |  227 ++++
 drivers/net/gelic_wireless.c | 2132 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 2396 insertions(+), 4 deletions(-)

--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2274,6 +2274,13 @@ config GELIC_NET
 	  To compile this driver as a module, choose M here: the
 	  module will be called gelic_net.
 
+config GELIC_WIRELESS
+	bool "Gelic net Wireless Extension"
+	depends on GELIC_NET
+	select WIRELESS_EXT
+	help
+	  Wireless Extension support for Gelic Gigabit Ethernet driver
+
 config GIANFAR
 	tristate "Gianfar Ethernet"
 	depends on 85xx || 83xx || PPC_86xx
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -60,7 +60,9 @@ obj-$(CONFIG_TIGON3) += tg3.o
 obj-$(CONFIG_BNX2) += bnx2.o
 spidernet-y += spider_net.o spider_net_ethtool.o
 obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o
-obj-$(CONFIG_GELIC_NET) += gelic_net.o
+obj-$(CONFIG_GELIC_NET) += ps3_gelic.o
+gelic-$(CONFIG_GELIC_WIRELESS) += gelic_wireless.o
+ps3_gelic-objs += gelic_net.o $(gelic-y)
 obj-$(CONFIG_TC35815) += tc35815.o
 obj-$(CONFIG_SKGE) += skge.o
 obj-$(CONFIG_SKY2) += sky2.o
--- a/drivers/net/gelic_net.c
+++ b/drivers/net/gelic_net.c
@@ -550,6 +550,9 @@ static int gelic_net_stop(struct net_dev
 {
 	struct gelic_net_card *card = netdev_priv(netdev);
 
+#ifdef CONFIG_GELIC_WIRELESS
+	gelicw_down(netdev);
+#endif
 	netif_poll_disable(netdev);
 	netif_stop_queue(netdev);
 
@@ -1024,6 +1027,9 @@ static irqreturn_t gelic_net_interrupt(i
 		/* start pending DMA */
 		gelic_net_xmit(NULL, netdev);
 	}
+#ifdef CONFIG_GELIC_WIRELESS
+	gelicw_interrupt(netdev, status);
+#endif
 	return IRQ_HANDLED;
 }
 
@@ -1120,13 +1126,18 @@ static int gelic_net_open(struct net_dev
 
 	card->tx_dma_progress = 0;
 	card->ghiintmask = GELIC_NET_RXINT | GELIC_NET_TXINT;
-
+#ifdef CONFIG_GELIC_WIRELESS
+	card->ghiintmask |= GELICW_DEVICE_CMD_COMP | GELICW_DEVICE_EVENT_RECV;
+#endif
 	gelic_net_set_irq_mask(card, card->ghiintmask);
 	gelic_net_enable_rxdmac(card);
 
 	netif_start_queue(netdev);
 	netif_carrier_on(netdev);
 	netif_poll_enable(netdev);
+#ifdef CONFIG_GELIC_WIRELESS
+	gelicw_up(netdev);
+#endif
 
 	return 0;
 
@@ -1205,7 +1216,12 @@ static u32 gelic_net_get_link(struct net
 		link = 1;
 	else
 		link = 0;
-
+#ifdef CONFIG_GELIC_WIRELESS
+	/* (v1 & GELIC_NET_LINK_UP) is always 0 in wireless mode */
+	if (gelicw_is_associated(netdev)) {
+		link = 1;
+	}
+#endif
 	return link;
 }
 
@@ -1396,7 +1412,12 @@ static int gelic_net_setup_netdev(struct
 	}
 	if (card->vlan_id[GELIC_NET_VLAN_WIRED - 1])
 		card->vlan_index = GELIC_NET_VLAN_WIRED - 1;
-
+#ifdef CONFIG_GELIC_WIRELESS
+	card->w.card = card;
+	/* init wireless extension */
+	/* No wireless vlan_index:-1 */
+	gelicw_setup_netdev(netdev, card->vlan_index);
+#endif
 	status = register_netdev(netdev);
 	if (status) {
 		dev_err(ctodev(card), "%s:Couldn't register net_device: %d\n",
@@ -1530,6 +1551,9 @@ static int ps3_gelic_driver_remove (stru
 
 	card = ps3_system_bus_get_driver_data(dev);
 
+#ifdef CONFIG_GELIC_WIRELESS
+	gelicw_remove(card->netdev);
+#endif
 	wait_event(card->waitq,
 		   atomic_read(&card->tx_timeout_task_counter) == 0);
 
--- a/drivers/net/gelic_net.h
+++ b/drivers/net/gelic_net.h
@@ -30,6 +30,9 @@
 #ifndef _GELIC_NET_H
 #define _GELIC_NET_H
 
+#include <linux/wireless.h>
+#include <net/ieee80211.h>
+
 #define GELIC_NET_DRV_NAME "Gelic Network Driver"
 #define GELIC_NET_DRV_VERSION "1.0"
 
@@ -170,6 +173,220 @@ enum gelic_net_descr_status {
 
 #define GELIC_NET_PORT                          2 /* for port status */
 
+/* wireless */
+#define GELICW_WIRELESS_NOT_EXIST	0
+#define GELICW_WIRELESS_SUPPORTED	1
+#define GELICW_WIRELESS_ON		2
+#define GELICW_WIRELESS_SHUTDOWN	3
+/* state */
+#define GELICW_STATE_DOWN		0
+#define GELICW_STATE_UP			1
+#define GELICW_STATE_SCANNING		2
+#define GELICW_STATE_SCAN_DONE		3
+#define GELICW_STATE_ASSOCIATED		4
+
+/* cmd_send_flg */
+#define GELICW_CMD_SEND_NONE		0x00
+#define GELICW_CMD_SEND_COMMON		0x01
+#define GELICW_CMD_SEND_ENCODE		0x02
+#define GELICW_CMD_SEND_SCAN		0x04
+#define GELICW_CMD_SEND_ALL		(GELICW_CMD_SEND_COMMON \
+					| GELICW_CMD_SEND_ENCODE \
+					| GELICW_CMD_SEND_SCAN)
+
+#define GELICW_SCAN_INTERVAL		(HZ)
+
+#ifdef DEBUG
+#define CH_INFO_FAIL 0x0600 /* debug */
+#else
+#define CH_INFO_FAIL 0
+#endif
+
+
+/* net_control command */
+#define GELICW_SET_PORT			3 /* control Ether port */
+#define GELICW_GET_INFO			6 /* get supported channels */
+#define GELICW_SET_CMD			9 /* set configuration */
+#define GELICW_GET_RES			10 /* get command response */
+#define GELICW_GET_EVENT		11 /* get event from device */
+/* net_control command data buffer */
+#define GELICW_DATA_BUF_SIZE		0x1000
+
+/* GELICW_SET_CMD params */
+#define GELICW_CMD_START		1
+#define GELICW_CMD_STOP			2
+#define GELICW_CMD_SCAN			3
+#define GELICW_CMD_GET_SCAN		4
+#define GELICW_CMD_SET_CONFIG		5
+#define GELICW_CMD_GET_CONFIG		6
+#define GELICW_CMD_SET_WEP		7
+#define GELICW_CMD_GET_WEP		8
+#define GELICW_CMD_SET_WPA		9
+#define GELICW_CMD_GET_WPA		10
+#define GELICW_CMD_GET_RSSI		11
+
+/* GELICW_SET_PORT params */
+#define GELICW_ETHER_PORT		2
+#define GELICW_PORT_DOWN		0 /* Ether port off */
+#define GELICW_PORT_UP			4 /* Ether port on (auto neg) */
+
+/* interrupt status bit */
+#define GELICW_DEVICE_CMD_COMP		(1UL << 31)
+#define GELICW_DEVICE_EVENT_RECV	(1UL << 30)
+
+/* GELICW_GET_EVENT ID */
+#define GELICW_EVENT_UNKNOWN		0x00
+#define GELICW_EVENT_DEVICE_READY	0x01
+#define GELICW_EVENT_SCAN_COMPLETED	0x02
+#define GELICW_EVENT_DEAUTH		0x04
+#define GELICW_EVENT_BEACON_LOST	0x08
+#define GELICW_EVENT_CONNECTED		0x10
+#define GELICW_EVENT_WPA_CONNECTED	0x20
+#define GELICW_EVENT_WPA_ERROR		0x40
+#define GELICW_EVENT_NO_ENTRY		(-6)
+
+#define MAX_IW_PRIV_SIZE		32
+
+/* structure of data buffer for lv1_net_contol */
+/* wep_config: sec */
+#define GELICW_WEP_SEC_NONE		0
+#define GELICW_WEP_SEC_40BIT		1
+#define GELICW_WEP_SEC_104BIT		2
+struct wep_config {
+	u16 sec;
+	u8  key[4][16];
+} __attribute__ ((packed));
+
+/* wpa_config: sec */
+#define GELICW_WPA_SEC_NONE		0
+#define GELICW_WPA_SEC_TKIP		1
+#define GELICW_WPA_SEC_AES		2
+/* wpa_config: psk_type */
+#define GELICW_PSK_PASSPHRASE		0
+#define GELICW_PSK_64HEX		1
+struct wpa_config {
+	u16 sec;
+	u16 psk_type;
+	u8  psk_material[64]; /* key */
+} __attribute__ ((packed));
+
+/* common_config: bss_type */
+#define GELICW_BSS_INFRA		0
+#define GELICW_BSS_ADHOC		1
+/* common_config: auth_method */
+#define GELICW_AUTH_OPEN		0
+#define GELICW_AUTH_SHARED		1
+/* common_config: op_mode */
+#define GELICW_OP_MODE_11BG		0
+#define GELICW_OP_MODE_11B		1
+#define GELICW_OP_MODE_11G		2
+struct common_config {
+	u16 scan_index; /* index of scan_desc list */
+	u16 bss_type;
+	u16 auth_method;
+	u16 op_mode;
+} __attribute__ ((packed));
+
+/* scan_descriptor: security */
+#define GELICW_SEC_TYPE_NONE		0x0000
+#define GELICW_SEC_TYPE_WEP		0x0100
+#define GELICW_SEC_TYPE_WEP40		0x0101
+#define GELICW_SEC_TYPE_WEP104		0x0102
+#define GELICW_SEC_TYPE_TKIP		0x0201
+#define GELICW_SEC_TYPE_AES		0x0202
+#define GELICW_SEC_TYPE_WEP_MASK	0xFF00
+struct scan_desc {
+	u16 size;
+	u16 rssi;
+	u16 channel;
+	u16 beacon_period;
+	u16 capability;
+	u16 security;
+	u64 bssid;
+	u8  essid[32];
+	u8  rate[16];
+	u8  ext_rate[16];
+	u32 reserved1;
+	u32 reserved2;
+	u32 reserved3;
+	u32 reserved4;
+} __attribute__ ((packed));
+
+/* rssi_descriptor */
+struct rssi_desc {
+	u16 rssi; /* max rssi = 100 */
+} __attribute__ ((packed));
+
+
+struct gelicw_bss {
+	u8 bssid[ETH_ALEN];
+	u8 channel;
+	u8 mode;
+	u8 essid_len;
+	u8 essid[IW_ESSID_MAX_SIZE + 1]; /* null terminated for debug msg */
+
+	u16 capability;
+	u16 beacon_interval;
+
+	u8 rates_len;
+	u8 rates[MAX_RATES_LENGTH];
+	u8 rates_ex_len;
+	u8 rates_ex[MAX_RATES_EX_LENGTH];
+	u8 rssi;
+
+	/* scan results have sec_info instead of rsn_ie or wpa_ie */
+	u16 sec_info;
+};
+
+/* max station count of station list which hvc returns */
+#define MAX_SCAN_BSS	(16)
+
+struct gelic_wireless {
+	struct gelic_net_card *card;
+	struct completion cmd_done, rssi_done;
+	struct work_struct work_event, work_start_done;
+	struct delayed_work work_rssi, work_scan_all, work_scan_essid;
+	struct delayed_work work_common, work_encode;
+	struct delayed_work work_start, work_stop, work_roam;
+	wait_queue_head_t waitq_cmd, waitq_scan;
+
+	u64 cmd_tag, cmd_id;
+	u8 cmd_send_flg;
+
+	struct iw_public_data wireless_data;
+	u8 *data_buf; /* data buffer for lv1_net_control */
+
+	u8 wireless; /* wireless support */
+	u8 state;
+	u8 scan_all; /* essid scan or all scan */
+	u8 essid_search; /* essid background scan */
+	u8 is_assoc;
+
+	u16 ch_info; /* supoprted channels */
+	u8 wireless_mode; /* 11b/g */
+	u8 channel; /* current ch */
+	u8 iw_mode; /* INFRA or Ad-hoc */
+	u8 rssi;
+	u8 essid_len;
+	u8 essid[IW_ESSID_MAX_SIZE + 1]; /* null terminated for debug msg */
+	u8 nick[IW_ESSID_MAX_SIZE + 1];
+	u8 bssid[ETH_ALEN];
+
+	u8 key_index;
+	u8 key[WEP_KEYS][IW_ENCODING_TOKEN_MAX]; /* 4 * 64byte */
+	u8 key_len[WEP_KEYS];
+	u8 key_alg; /* key algorithm  */
+	u8 auth_mode; /* authenticaton mode */
+
+	u8 bss_index; /* current bss in bss_list */
+	u8 num_bss_list;
+	u8 bss_key_alg; /* key alg of bss */
+	u8 wap_bssid[ETH_ALEN];
+	unsigned long last_scan; /* last scan time */
+	struct gelicw_bss current_bss;
+	struct gelicw_bss bss_list[MAX_SCAN_BSS];
+};
+
 /* size of hardware part of gelic descriptor */
 #define GELIC_NET_DESCR_SIZE	(32)
 struct gelic_net_descr {
@@ -225,9 +442,19 @@ struct gelic_net_card {
 	wait_queue_head_t waitq;
 
 	struct gelic_net_descr *tx_top, *rx_top;
+#ifdef CONFIG_GELIC_WIRELESS
+	struct gelic_wireless w;
+#endif
 	struct gelic_net_descr descr[0];
 };
 
 
 extern unsigned long p_to_lp(long pa);
+extern int gelicw_setup_netdev(struct net_device *netdev, int wi);
+extern void gelicw_up(struct net_device *netdev);
+extern int gelicw_down(struct net_device *netdev);
+extern void gelicw_remove(struct net_device *netdev);
+extern void gelicw_interrupt(struct net_device *netdev, u64 status);
+extern int gelicw_is_associated(struct net_device *netdev);
+
 #endif //_GELIC_NET_H
--- /dev/null
+++ b/drivers/net/gelic_wireless.c
@@ -0,0 +1,2132 @@
+/*
+ * gelic_wireless.c: wireless extension for gelic_net
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/completion.h>
+#include <linux/ctype.h>
+#include <asm/ps3.h>
+#include <asm/lv1call.h>
+
+#include "gelic_net.h"
+
+static struct iw_handler_def gelicw_handler_def;
+
+/*
+ * Data tables
+ */
+static const u32 freq_list[] = {
+	2412, 2417, 2422, 2427, 2432,
+	2437, 2442, 2447, 2452, 2457,
+	2462, 2467, 2472, 2484 };
+
+static const u32 bitrate_list[] = {
+	 1000000,  2000000,  5500000, 11000000, /* 11b */
+	 6000000,  9000000, 12000000, 18000000,
+	24000000, 36000000, 48000000, 54000000
+};
+#define GELICW_NUM_11B_BITRATES 4 /* 802.11b: 1 ~ 11 Mb/s */
+
+static inline struct device * ntodev(struct net_device *netdev)
+{
+	return &((struct gelic_net_card *)netdev_priv(netdev))->dev->core;
+}
+
+static inline struct device * wtodev(struct gelic_wireless *w)
+{
+	return &w->card->dev->core;
+}
+static inline struct gelic_wireless *gelicw_priv(struct net_device *netdev)
+{
+	struct gelic_net_card *card = netdev_priv(netdev);
+	return &card->w;
+}
+static inline unsigned int bus_id(struct gelic_wireless *w)
+{
+	return w->card->dev->did.bus_id;
+}
+static inline unsigned int dev_id(struct gelic_wireless *w)
+{
+	return w->card->dev->did.dev_id;
+}
+
+/* control wired or wireless */
+static void gelicw_vlan_mode(struct net_device *netdev, int mode)
+{
+	struct gelic_net_card *card = netdev_priv(netdev);
+
+	if ((mode < GELIC_NET_VLAN_WIRED) ||
+	    (mode > GELIC_NET_VLAN_WIRELESS))
+		return;
+
+	card->vlan_index = mode - 1;
+}
+
+/* wireless_send_event */
+static void notify_assoc_event(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	union iwreq_data wrqu;
+
+	cancel_delayed_work(&w->work_scan_all);
+	cancel_delayed_work(&w->work_scan_essid);
+
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	if (w->state < GELICW_STATE_ASSOCIATED) {
+		dev_dbg(ntodev(netdev), "notify disassociated\n");
+		w->is_assoc = 0;
+		memset(w->bssid, 0, ETH_ALEN);
+	} else {
+		dev_dbg(ntodev(netdev), "notify associated\n");
+		w->channel = w->current_bss.channel;
+		w->is_assoc = 1;
+		memcpy(w->bssid, w->current_bss.bssid, ETH_ALEN);
+	}
+	memcpy(wrqu.ap_addr.sa_data, w->bssid, ETH_ALEN);
+	wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL);
+}
+
+/* association/disassociation */
+static void gelicw_assoc(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	if (w->state == GELICW_STATE_ASSOCIATED)
+		return;
+	schedule_delayed_work(&w->work_start, 0);
+}
+
+static int gelicw_disassoc(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	memset(w->bssid, 0, ETH_ALEN); /* clear current bssid */
+	if (w->state < GELICW_STATE_ASSOCIATED)
+		return 0;
+
+	schedule_delayed_work(&w->work_stop, 0);
+	w->state = GELICW_STATE_SCAN_DONE;
+	/* notify disassociation */
+	notify_assoc_event(netdev);
+
+	return 0;
+}
+
+static void gelicw_reassoc(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	if (w->cmd_send_flg != GELICW_CMD_SEND_ALL)
+		return;
+
+	if (!gelicw_disassoc(netdev))
+		gelicw_assoc(netdev);
+}
+
+
+/*
+ * lv1_net_control command
+ */
+/* control Ether port */
+static int gelicw_cmd_set_port(struct net_device *netdev, int mode)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 tag, val;
+	int status;
+
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_SET_PORT, GELICW_ETHER_PORT, mode, 0,
+			&tag, &val);
+	if (status)
+		dev_dbg(ntodev(netdev), "GELICW_SET_PORT failed:%d\n", status);
+	return status;
+}
+
+/* check support channels */
+static int gelicw_cmd_get_ch_info(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 ch_info, val;
+	int status;
+
+	if (w->state < GELICW_STATE_UP)
+		return -EIO;
+
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_GET_INFO, 0, 0 , 0,
+			&ch_info, &val);
+	if (status) {
+		dev_dbg(ntodev(netdev), "GELICW_GET_INFO failed:%d\n", status);
+		w->ch_info = CH_INFO_FAIL;
+	} else {
+		dev_dbg(ntodev(netdev), "ch_info:%lx val:%lx\n", ch_info, val);
+		w->ch_info = ch_info >> 48; /* MSB 16bit shows supported channnels */
+	}
+	return status;
+}
+
+
+/*
+ * lv1_net_control GELICW_SET_CMD command
+ * queued using schedule_work()
+ */
+/* association */
+static void gelicw_cmd_start(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 val;
+	int status;
+
+	if (w->state < GELICW_STATE_SCAN_DONE)
+		return;
+
+	dev_dbg(ntodev(netdev), "GELICW_CMD_START\n");
+	w->cmd_id = GELICW_CMD_START;
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_SET_CMD, w->cmd_id, 0, 0,
+			&w->cmd_tag, &val);
+	if (status) {
+		w->cmd_tag = 0;
+		dev_dbg(ntodev(netdev), "GELICW_CMD_START failed:%d\n", status);
+	}
+}
+
+/* association done */
+static void gelicw_cmd_start_done(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 res, val;
+	int status;
+
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_GET_RES, w->cmd_tag, 0, 0,
+			&res, &val);
+	w->cmd_tag = 0;
+	wake_up_interruptible(&w->waitq_cmd);
+
+	if (status || res)
+		dev_dbg(ntodev(netdev), "GELICW_CMD_START res:%d,%ld\n", status, res);
+}
+
+/* disassociation */
+static void gelicw_cmd_stop(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 res, val;
+	int status;
+
+	if (w->state < GELICW_STATE_SCAN_DONE)
+		return;
+
+	dev_dbg(ntodev(netdev), "GELICW_CMD_STOP\n");
+	init_completion(&w->cmd_done);
+	w->cmd_id = GELICW_CMD_STOP;
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_SET_CMD, w->cmd_id, 0, 0,
+			&w->cmd_tag, &val);
+	if (status) {
+		w->cmd_tag = 0;
+		dev_dbg(ntodev(netdev), "GELICW_CMD_STOP failed:%d\n", status);
+		return;
+	}
+	wait_for_completion_interruptible(&w->cmd_done);
+
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_GET_RES, w->cmd_tag, 0, 0,
+			&res, &val);
+	w->cmd_tag = 0;
+	if (status || res) {
+		dev_dbg(ntodev(netdev), "GELICW_CMD_STOP res:%d,%ld\n", status, res);
+		return;
+	}
+}
+
+/* get rssi */
+static void gelicw_cmd_rssi(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 lpar, res, val;
+	int status;
+	struct rssi_desc *rssi;
+
+	if (w->state < GELICW_STATE_ASSOCIATED) {
+		w->rssi = 0;
+		complete(&w->rssi_done);
+		return;
+	}
+
+	lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+	init_completion(&w->cmd_done);
+	w->cmd_id = GELICW_CMD_GET_RSSI;
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_SET_CMD, w->cmd_id, 0, 0,
+			&w->cmd_tag, &val);
+	if (status) {
+		w->cmd_tag = 0;
+		w->rssi = 0;
+		dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI failed:%d\n", status);
+	} else {
+		wait_for_completion_interruptible(&w->cmd_done);
+		status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_GET_RES, w->cmd_tag, lpar, sizeof(*rssi),
+				&res, &val);
+		w->cmd_tag = 0;
+		if (status || res) {
+			w->rssi = 0;
+			dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI res:%d,%ld\n", status, res);
+		}
+		rssi = (struct rssi_desc *)w->data_buf;
+		w->rssi = rssi->rssi;
+		dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI:%d\n", rssi->rssi);
+	}
+
+	complete(&w->rssi_done);
+}
+
+/* set common configuration */
+static int gelicw_cmd_common(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 lpar, res, val;
+	int status;
+	struct common_config *config;
+
+	if (w->state < GELICW_STATE_SCAN_DONE)
+		return -EIO;
+
+	lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+	config = (struct common_config *)w->data_buf;
+	config->scan_index = w->bss_index;
+	config->bss_type = (w->iw_mode == IW_MODE_ADHOC) ?
+				GELICW_BSS_ADHOC : GELICW_BSS_INFRA;
+	config->auth_method = (w->auth_mode == IW_AUTH_ALG_SHARED_KEY) ?
+				GELICW_AUTH_SHARED : GELICW_AUTH_OPEN;
+	switch (w->wireless_mode) {
+	case IEEE_B:
+		config->op_mode = GELICW_OP_MODE_11B;
+		break;
+	case IEEE_G:
+		config->op_mode = GELICW_OP_MODE_11G;
+		break;
+	case IEEE_B | IEEE_G:
+	default:
+		/* default 11bg mode */
+		config->op_mode = GELICW_OP_MODE_11BG;
+		break;
+	}
+
+	dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG: index:%d type:%d auth:%d mode:%d\n",\
+		config->scan_index, config->bss_type,\
+		config->auth_method,config->op_mode);
+	init_completion(&w->cmd_done);
+	w->cmd_id = GELICW_CMD_SET_CONFIG;
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_SET_CMD, w->cmd_id,
+			lpar, sizeof(struct common_config),
+			&w->cmd_tag, &val);
+	if (status) {
+		w->cmd_tag = 0;
+		dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG failed:%d\n", status);
+		return status;
+	}
+	wait_for_completion_interruptible(&w->cmd_done);
+
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_GET_RES, w->cmd_tag, 0, 0,
+			&res, &val);
+	w->cmd_tag = 0;
+	if (status || res) {
+		dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG res:%d,%ld\n", status, res);
+		return -EFAULT;
+	}
+
+	w->cmd_send_flg |= GELICW_CMD_SEND_COMMON;
+
+	return 0;
+}
+
+#define h2i(c)    (isdigit(c) ? c - '0' : toupper(c) - 'A' + 10)
+/* send WEP/WPA configuration */
+static int gelicw_cmd_encode(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 res, val, lpar;
+	int status;
+	struct wep_config *config;
+	struct wpa_config *wpa_config;
+
+	u8 *key, key_len;
+
+	if (w->state < GELICW_STATE_SCAN_DONE)
+		return -EIO;
+
+	lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+
+	if (w->key_alg == IW_ENCODE_ALG_WEP ||
+	    w->key_alg == IW_ENCODE_ALG_NONE) {
+		/* WEP */
+		config = (struct wep_config *)w->data_buf;
+		memset(config, 0, sizeof(struct wep_config));
+
+		/* check key len */
+		key_len = w->key_len[w->key_index];
+		key = w->key[w->key_index];
+		if (w->key_alg == IW_ENCODE_ALG_NONE)
+			config->sec = GELICW_WEP_SEC_NONE;
+		else
+			config->sec = (key_len == 5) ? GELICW_WEP_SEC_40BIT :
+							GELICW_WEP_SEC_104BIT;
+		/* copy key */
+		memcpy(config->key[w->key_index], key, key_len);
+
+		/* send wep config */
+		dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP\n");
+		init_completion(&w->cmd_done);
+		w->cmd_id = GELICW_CMD_SET_WEP;
+		status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_SET_CMD, w->cmd_id,
+			lpar, sizeof(struct wep_config),
+			&w->cmd_tag, &val);
+		if (status) {
+			w->cmd_tag = 0;
+			dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP failed:%d\n", status);
+			goto err;
+		}
+		wait_for_completion_interruptible(&w->cmd_done);
+
+		status = lv1_net_control(bus_id(w), dev_id(w),
+				GELICW_GET_RES, w->cmd_tag, 0, 0,
+				&res, &val);
+		w->cmd_tag = 0;
+		if (status || res) {
+			dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP res:%d,%ld\n", status, res);
+			status = -EFAULT;
+			goto err;
+		}
+	} else {
+		/* WPA */
+		wpa_config = (struct wpa_config *)w->data_buf;
+		memset(wpa_config, 0, sizeof(struct wpa_config));
+
+		switch (w->key_alg) {
+		case IW_ENCODE_ALG_TKIP:
+			wpa_config->sec = GELICW_WPA_SEC_TKIP;
+			break;
+		default:
+		case IW_ENCODE_ALG_CCMP:
+			wpa_config->sec = GELICW_WPA_SEC_AES;
+			break;
+		}
+		/* check key len */
+		key = w->key[w->key_index];
+		key_len = w->key_len[w->key_index];
+
+		if (key_len > 64) key_len = 64;
+		if (key_len != 64) {
+			/* if key_len isn't 64byte ,it should be passphrase */
+			/* pass phrase */
+			memcpy(wpa_config->psk_material, key, key_len);
+			wpa_config->psk_type = GELICW_PSK_PASSPHRASE;
+		} else {
+			int i;
+			/* 64 hex */
+			for (i = 0; i < 32; i++)
+				wpa_config->psk_material[i] =
+						h2i(key[2 * i]) * 16
+						+ h2i(key[2 * i + 1]);
+			wpa_config->psk_type = GELICW_PSK_64HEX;
+		}
+
+		/* send wpa config */
+		dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA:type:%d\n", wpa_config->psk_type);
+		init_completion(&w->cmd_done);
+		w->cmd_id = GELICW_CMD_SET_WPA;
+		status = lv1_net_control(bus_id(w), dev_id(w),
+				GELICW_SET_CMD, w->cmd_id,
+				lpar, sizeof(struct wpa_config),
+				&w->cmd_tag, &val);
+		if (status) {
+			w->cmd_tag = 0;
+			dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA failed:%d\n", status);
+			goto err;
+		}
+		wait_for_completion_interruptible(&w->cmd_done);
+
+		status = lv1_net_control(bus_id(w), dev_id(w),
+				GELICW_GET_RES, w->cmd_tag, 0, 0, &res, &val);
+		w->cmd_tag = 0;
+		if (status || res) {
+			dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA res:%d,%ld\n", status, res);
+			status = -EFAULT;
+			goto err;
+		}
+	}
+
+	w->cmd_send_flg |= GELICW_CMD_SEND_ENCODE;
+	/* (re)associate */
+	gelicw_reassoc(netdev);
+
+	return 0;
+err:
+	gelicw_disassoc(netdev);
+	return status;
+}
+
+static int gelicw_is_ap_11b(struct gelicw_bss *list)
+{
+	if (list->rates_len + list->rates_ex_len == GELICW_NUM_11B_BITRATES)
+		return 1;
+	else
+		return 0;
+}
+
+/* get scan results */
+static int gelicw_cmd_get_scan(struct gelic_wireless *w)
+{
+	u64 lpar, res, val;
+	int status;
+	struct scan_desc *desc;
+	int i, j;
+	u8 *p;
+
+
+	/* get scan */
+	dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN\n");
+	init_completion(&w->cmd_done);
+	w->cmd_id = GELICW_CMD_GET_SCAN;
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_SET_CMD, w->cmd_id, 0, 0,
+			&w->cmd_tag, &val);
+	if (status) {
+		w->cmd_tag = 0;
+		dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN failed:%d\n", status);
+		return status;
+	}
+	wait_for_completion_interruptible(&w->cmd_done);
+
+	lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_GET_RES, w->cmd_tag, lpar, PAGE_SIZE,
+			&res, &val);
+	w->cmd_tag = 0;
+	if (status || res) {
+		dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN res:%d,%ld\n",
+			status, res);
+		return -EFAULT;
+	}
+
+	desc = (struct scan_desc *)w->data_buf;
+	for (i = 0;
+	     i < val / sizeof(struct scan_desc) && i < MAX_SCAN_BSS;
+	     i++) {
+		struct gelicw_bss *bss = &w->bss_list[i];
+
+		bss->rates_len = 0;
+		for (j = 0; j < MAX_RATES_LENGTH; j++)
+			if (desc[i].rate[j])
+				bss->rates[bss->rates_len++] = desc[i].rate[j];
+		bss->rates_ex_len = 0;
+		for (j = 0; j < MAX_RATES_EX_LENGTH; j++)
+			if (desc[i].ext_rate[j])
+				bss->rates_ex[bss->rates_ex_len++]
+						= desc[i].ext_rate[j];
+
+		if (desc[i].capability & 0x3) {
+			if (desc[i].capability & 0x1)
+				bss->mode = IW_MODE_INFRA;
+			else
+				bss->mode = IW_MODE_ADHOC;
+		}
+		bss->channel = desc[i].channel;
+		bss->essid_len = strnlen(desc[i].essid, IW_ESSID_MAX_SIZE);
+		bss->rssi = (u8)desc[i].rssi;
+		bss->capability = desc[i].capability;
+		bss->beacon_interval = desc[i].beacon_period;
+		memset(bss->essid, 0, sizeof(bss->essid));
+		memcpy(bss->essid, desc[i].essid, bss->essid_len);
+		p = (u8 *)&desc[i].bssid;
+		memcpy(bss->bssid, &p[2], ETH_ALEN);/* bssid:64bit in desc */
+		bss->sec_info = desc[i].security;
+	}
+	w->num_bss_list = i;
+
+	if (w->num_bss_list)
+		return 0; /* ap found */
+	else
+		return -1; /* no ap found */
+}
+
+/* search bssid in bss list */
+static int gelicw_search_bss_list(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	int i;
+	int check_bss = 0, check_11g = 0, found_bss, found_11g;
+
+	if (!w->num_bss_list)
+		return -1;	/* no bss list */
+
+	if (memcmp(off, w->wap_bssid, ETH_ALEN))
+		check_bss = 1;
+
+	/* wireless op_mode seems not working with CMD_SET_CONFIG */
+	if (w->wireless_mode == IEEE_G)
+		check_11g = 1;
+
+	if (!check_bss && !check_11g)
+		return 0;	/* no check bssid, wmode */
+
+	for (i = 0; i < w->num_bss_list; i++) {
+		found_bss = found_11g = 1;
+		if (check_bss &&
+		    memcmp(w->bss_list[i].bssid, w->wap_bssid, ETH_ALEN))
+			found_bss = 0; /* not found */
+
+		if (check_11g &&
+		    gelicw_is_ap_11b(&w->bss_list[i]))
+			found_11g = 0; /* not found */
+
+		if (found_bss && found_11g)
+			break;
+	}
+
+	if (i == w->num_bss_list)
+		return -1; /* not found */
+	else
+		return i;
+}
+
+/* scan done */
+static void gelicw_scan_complete(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	int res;
+	int bss_index;
+
+	/* get scan results */
+	res = gelicw_cmd_get_scan(w);
+	if (w->is_assoc)
+		w->state = GELICW_STATE_ASSOCIATED;
+	else
+		w->state = GELICW_STATE_SCAN_DONE;
+
+	if (res) {
+		/* No AP found */
+		if (!w->scan_all) {
+			/* no specified AP */
+			gelicw_disassoc(netdev);
+			/* rescan */
+			if (w->essid_search && w->essid_len)
+				schedule_delayed_work(&w->work_scan_essid,
+							GELICW_SCAN_INTERVAL);
+			return;
+		}
+	}
+
+	if (w->scan_all) {
+		/* all params should be set again after scan */
+		w->cmd_send_flg = 0;
+		return;
+	}
+
+	bss_index = gelicw_search_bss_list(netdev);
+	if (bss_index < 0) {
+		/* no wap_bssid in bss_list */
+		if (w->essid_search && w->essid_len)
+			schedule_delayed_work(&w->work_scan_essid,
+						GELICW_SCAN_INTERVAL);
+		return;
+	}
+	w->bss_index = (u8)bss_index;
+	w->current_bss = w->bss_list[w->bss_index];
+
+	/* essid search complete */
+	w->essid_search = 0;
+	w->cmd_send_flg |= GELICW_CMD_SEND_SCAN;
+
+	/* (re)connect to AP */
+	if (w->is_assoc) {
+		/* notify disassociation */
+		w->state = GELICW_STATE_SCAN_DONE;
+		notify_assoc_event(netdev);
+	}
+	schedule_delayed_work(&w->work_common, 0);
+	schedule_delayed_work(&w->work_encode, 0);
+}
+
+/* start scan */
+static int gelicw_cmd_set_scan(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 res, val, lpar;
+	int status;
+	u8 *p;
+
+	if (w->state < GELICW_STATE_UP) {
+		w->scan_all = 0;
+		return -EIO;
+	}
+	if (!w->scan_all && !w->essid_len)
+		return -EINVAL; /* unsupported essid ANY */
+
+	/* set device wired to wireless when essid is set */
+	if (!w->scan_all && w->wireless < GELICW_WIRELESS_ON) {
+		gelicw_vlan_mode(netdev, GELIC_NET_VLAN_WIRELESS);
+		gelicw_cmd_set_port(netdev, GELICW_PORT_DOWN);
+		w->wireless = GELICW_WIRELESS_ON;
+	}
+
+	p = (u8 *)w->data_buf;
+	lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf));
+
+	/* avoid frequent scanning */
+	if (!w->essid_search && /* background scan off */
+	    w->scan_all &&
+	    (time_before64(get_jiffies_64(), w->last_scan + 5 * HZ)))
+		return 0;
+
+	w->bss_key_alg = IW_ENCODE_ALG_NONE;
+
+	init_completion(&w->cmd_done);
+	w->state = GELICW_STATE_SCANNING;
+	w->cmd_id = GELICW_CMD_SCAN;
+
+	if (w->scan_all) {
+		/* scan all ch */
+		dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN all\n");
+		w->last_scan = get_jiffies_64(); /* last scan time */
+		status = lv1_net_control(bus_id(w), dev_id(w),
+				GELICW_SET_CMD, w->cmd_id, 0, 0,
+				&w->cmd_tag, &val);
+	} else {
+		/* scan essid */
+		memset(p, 0, 32);
+		memcpy(p, w->essid, w->essid_len);
+		dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN essid\n");
+		status = lv1_net_control(bus_id(w), dev_id(w),
+				GELICW_SET_CMD, w->cmd_id, lpar, 32,
+				&w->cmd_tag, &val);
+	}
+
+	if (status) {
+		w->cmd_tag = 0;
+		dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN failed:%d\n", status);
+		return status;
+	}
+	wait_for_completion_interruptible(&w->cmd_done);
+
+	status = lv1_net_control(bus_id(w), dev_id(w),
+			GELICW_GET_RES, w->cmd_tag, 0, 0,
+			&res, &val);
+	w->cmd_tag = 0;
+	if (status || res) {
+		dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN res:%d,%ld\n", status, res);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static void gelicw_send_common_config(struct net_device *netdev,
+					u8 *cur, u8 mode)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	if (*cur != mode) {
+		*cur = mode;
+		if (w->state < GELICW_STATE_SCAN_DONE)
+			return;
+
+		if (!(w->cmd_send_flg & GELICW_CMD_SEND_SCAN) &&
+		    w->essid_len)
+			/* scan essid and set other params */
+			schedule_delayed_work(&w->work_scan_essid, 0);
+		else {
+			schedule_delayed_work(&w->work_common, 0);
+			if (w->cmd_send_flg
+			    & GELICW_CMD_SEND_ENCODE)
+				/* (re)send encode key */
+				schedule_delayed_work(&w->work_encode, 0);
+		}
+	}
+}
+
+
+/*
+ * work queue
+ */
+static void gelicw_work_rssi(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_rssi.work);
+	struct net_device *netdev = w->card->netdev;
+
+	if (w->cmd_tag) {
+		schedule_delayed_work(&w->work_rssi, HZ / 5);
+		return;
+	}
+
+	gelicw_cmd_rssi(netdev);
+}
+
+static void gelicw_work_scan_all(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_scan_all.work);
+	struct net_device *netdev = w->card->netdev;
+
+	if (w->cmd_tag || w->state == GELICW_STATE_SCANNING) {
+		schedule_delayed_work(&w->work_scan_all, HZ / 5);
+		return;
+	}
+
+	w->scan_all = 1;
+	gelicw_cmd_set_scan(netdev);
+}
+
+static void gelicw_work_scan_essid(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_scan_essid.work);
+	struct net_device *netdev = w->card->netdev;
+
+	if (w->cmd_tag || w->scan_all || w->state == GELICW_STATE_SCANNING) {
+		schedule_delayed_work(&w->work_scan_essid, HZ / 5);
+		return;
+	}
+	w->bss_index = 0;
+	w->scan_all = 0;
+	gelicw_cmd_set_scan(netdev);
+}
+
+static void gelicw_work_common(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_common.work);
+	struct net_device *netdev = w->card->netdev;
+
+	if (w->cmd_tag) {
+		schedule_delayed_work(&w->work_common, HZ / 5);
+		return;
+	}
+	gelicw_cmd_common(netdev);
+}
+
+static void gelicw_work_encode(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_encode.work);
+	struct net_device *netdev = w->card->netdev;
+
+	if (w->cmd_tag) {
+		schedule_delayed_work(&w->work_encode, HZ / 5);
+		return;
+	}
+	gelicw_cmd_encode(netdev);
+}
+
+static void gelicw_work_start(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_start.work);
+	struct net_device *netdev = w->card->netdev;
+
+	if (w->cmd_tag) {
+		schedule_delayed_work(&w->work_start, HZ / 5);
+		return;
+	}
+	gelicw_cmd_start(netdev);
+}
+
+static void gelicw_work_start_done(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_start_done);
+	struct net_device *netdev = w->card->netdev;
+
+	gelicw_cmd_start_done(netdev);
+}
+
+static void gelicw_work_stop(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_stop.work);
+	struct net_device *netdev = w->card->netdev;
+
+	if (w->cmd_tag) {
+		schedule_delayed_work(&w->work_stop, HZ / 5);
+		return;
+	}
+	gelicw_cmd_stop(netdev);
+}
+
+static void gelicw_work_roam(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_roam.work);
+	struct net_device *netdev = w->card->netdev;
+
+	if (w->cmd_tag || w->scan_all || w->state == GELICW_STATE_SCANNING) {
+		schedule_delayed_work(&w->work_roam, HZ / 5);
+		return;
+	}
+	gelicw_cmd_stop(netdev);
+	w->bss_index = 0;
+	w->scan_all = 0;
+	gelicw_cmd_set_scan(netdev);
+}
+
+/*
+ * Event handler
+ */
+#define GELICW_EVENT_LOOP_MAX 16
+static void gelicw_event(struct work_struct *work)
+{
+	struct gelic_wireless *w =
+		container_of(work, struct gelic_wireless, work_event);
+	struct net_device *netdev = w->card->netdev;
+	u64 event_type, val;
+	int i, status;
+
+	for (i = 0; i < GELICW_EVENT_LOOP_MAX; i++) {
+		status = lv1_net_control(bus_id(w), dev_id(w),
+				GELICW_GET_EVENT, 0, 0 , 0,
+				&event_type, &val);
+		if (status == GELICW_EVENT_NO_ENTRY)
+			/* got all events */
+			break;
+		else if (status){
+			dev_dbg(ntodev(netdev), "GELICW_GET_EVENT failed:%d\n", status);
+			return;
+		}
+		switch(event_type) {
+		case GELICW_EVENT_DEVICE_READY:
+			dev_dbg(ntodev(netdev), "  GELICW_EVENT_DEVICE_READY\n");
+			break;
+		case GELICW_EVENT_SCAN_COMPLETED:
+			dev_dbg(ntodev(netdev), "  GELICW_EVENT_SCAN_COMPLETED\n");
+			gelicw_scan_complete(netdev);
+			break;
+		case GELICW_EVENT_BEACON_LOST:
+			dev_dbg(ntodev(netdev), "  GELICW_EVENT_BEACON_LOST\n");
+			w->state = GELICW_STATE_SCAN_DONE;
+			notify_assoc_event(netdev);
+			/* roaming */
+			w->essid_search = 1;
+			schedule_delayed_work(&w->work_roam, 0);
+			break;
+		case GELICW_EVENT_CONNECTED:
+		{
+			u16 ap_sec;
+			dev_dbg(ntodev(netdev), "  GELICW_EVENT_CONNECTED\n");
+			/* this event ocuured with any key_alg */
+			ap_sec = w->current_bss.sec_info;
+			if (w->key_alg == IW_ENCODE_ALG_NONE) {
+				/* no encryption */
+				if (ap_sec == 0) {
+					w->state = GELICW_STATE_ASSOCIATED;
+					notify_assoc_event(netdev);
+				}
+			} else if (w->key_alg == IW_ENCODE_ALG_WEP){
+				if ((ap_sec & GELICW_SEC_TYPE_WEP_MASK)
+				    == GELICW_SEC_TYPE_WEP) {
+					/* wep */
+					w->state = GELICW_STATE_ASSOCIATED;
+					notify_assoc_event(netdev);
+				}
+			}
+			break;
+		}
+		case GELICW_EVENT_WPA_CONNECTED:
+			dev_dbg(ntodev(netdev), "  GELICW_EVENT_WPA_CONNECTED\n");
+			w->state = GELICW_STATE_ASSOCIATED;
+			notify_assoc_event(netdev);
+			break;
+		case GELICW_EVENT_WPA_ERROR:
+			dev_dbg(ntodev(netdev), "  GELICW_EVENT_WPA_ERROR\n");
+			break;
+		default:
+			dev_dbg(ntodev(netdev), "  GELICW_EVENT_UNKNOWN\n");
+			break;
+		}
+	}
+}
+
+static void gelicw_clear_event(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u64 event_type, val;
+	int i, status;
+
+	for (i = 0; i < GELICW_EVENT_LOOP_MAX; i++) {
+		status = lv1_net_control(bus_id(w), dev_id(w),
+				GELICW_GET_EVENT, 0, 0 , 0,
+				&event_type, &val);
+		if (status)
+			return;/* got all events */
+
+		switch(event_type) {
+		case GELICW_EVENT_SCAN_COMPLETED:
+			w->state = GELICW_STATE_SCAN_DONE;
+			wake_up_interruptible(&w->waitq_scan);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * gelic_net support function
+ */
+static void gelicw_clear_params(struct gelic_wireless *w)
+{
+	int i;
+
+	/* clear status */
+	w->state = GELICW_STATE_DOWN;
+	w->cmd_send_flg = 0;
+	w->scan_all = 0;
+	w->is_assoc = 0;
+	w->essid_search = 0;
+	w->cmd_tag = 0;
+	w->cmd_id = 0;
+	w->last_scan = 0;
+
+	/* default mode and settings */
+	w->essid_len = 0;
+	w->essid[0] = '\0';
+	w->nick[0] = '\0';
+	w->iw_mode = IW_MODE_INFRA;
+	w->auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+	w->wireless_mode = IEEE_B | IEEE_G;
+	w->bss_index = 0;
+	memset(w->bssid, 0, ETH_ALEN);
+	memset(w->wap_bssid, 0, ETH_ALEN);
+
+	/* init key */
+	w->key_index = 0;
+	for (i = 0; i < WEP_KEYS; i++) {
+		w->key[i][0] = '\0';
+		w->key_len[i] = 0;
+	}
+	w->key_alg = IW_ENCODE_ALG_NONE;
+	w->bss_key_alg = IW_ENCODE_ALG_NONE;
+}
+
+int gelicw_setup_netdev(struct net_device *netdev, int wi)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	union ps3_firmware_version ver;
+	union ps3_firmware_version initial_ver;
+
+	if (wi < 0) {
+		/* PS3 low model has no wireless */
+		dev_info(ntodev(netdev), "No wireless dvice in this system\n");
+		w->wireless = 0;
+		return 0;
+	}
+	/* version check */
+	initial_ver.raw = 0;
+	initial_ver.major = 1;
+	initial_ver.minor = 6;
+	if (ps3_get_firmware_version(&ver) ||
+	    (ver.raw < initial_ver.raw)) {
+		dev_info(ntodev(netdev),
+			 "firmware %d.%d is too old for wireless.\n",
+			 ver.major, ver.minor);
+		w->wireless = 0;
+		return 0;
+	}
+	/* we need 4K aligned, 16 units of scan_desc sized */
+	BUILD_BUG_ON(PAGE_SIZE < sizeof(struct scan_desc) * MAX_SCAN_BSS);
+	w->data_buf = (u8*)get_zeroed_page(GFP_KERNEL);
+	if (!w->data_buf) {
+		w->wireless = 0;
+		dev_info(ntodev(netdev), "%s:get_page failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	w->wireless = GELICW_WIRELESS_SUPPORTED;
+
+	w->ch_info = 0;
+	w->channel = 0;
+	netdev->wireless_data = &w->wireless_data;
+	netdev->wireless_handlers = &gelicw_handler_def;
+	INIT_WORK(&w->work_event, gelicw_event);
+	INIT_WORK(&w->work_start_done, gelicw_work_start_done);
+	INIT_DELAYED_WORK(&w->work_rssi, gelicw_work_rssi);
+	INIT_DELAYED_WORK(&w->work_scan_all, gelicw_work_scan_all);
+	INIT_DELAYED_WORK(&w->work_scan_essid, gelicw_work_scan_essid);
+	INIT_DELAYED_WORK(&w->work_common, gelicw_work_common);
+	INIT_DELAYED_WORK(&w->work_encode, gelicw_work_encode);
+	INIT_DELAYED_WORK(&w->work_start, gelicw_work_start);
+	INIT_DELAYED_WORK(&w->work_stop, gelicw_work_stop);
+	INIT_DELAYED_WORK(&w->work_roam, gelicw_work_roam);
+	init_waitqueue_head(&w->waitq_cmd);
+	init_waitqueue_head(&w->waitq_scan);
+
+	gelicw_clear_params(w);
+
+	return 0;
+}
+
+void gelicw_up(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	if (!w->wireless)
+		return;
+
+	dev_dbg(ntodev(netdev), "gelicw_up\n");
+	if (w->state < GELICW_STATE_UP)
+		w->state = GELICW_STATE_UP;
+
+	/* start essid scanning */
+	if (w->essid_len)
+		schedule_delayed_work(&w->work_scan_essid, 0);
+}
+
+int gelicw_down(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	if (!w->wireless || w->state == GELICW_STATE_DOWN)
+		return 0;
+
+	dev_dbg(ntodev(netdev), "gelicw_down\n");
+	w->wireless = GELICW_WIRELESS_SHUTDOWN;
+	flush_scheduled_work();
+
+	/* check cmd_tag of CMD_START */
+	if (w->cmd_id == GELICW_CMD_START)
+		wait_event_interruptible(w->waitq_cmd, !w->cmd_tag);
+	/* wait scan done */
+	if (w->state == GELICW_STATE_SCANNING) {
+		wait_event_interruptible(w->waitq_scan,
+					w->state != GELICW_STATE_SCANNING);
+		gelicw_cmd_get_scan(w);
+	}
+
+	gelicw_cmd_stop(netdev);
+	if (w->is_assoc) {
+		w->state = GELICW_STATE_DOWN;
+		notify_assoc_event(netdev);
+	}
+	gelicw_clear_params(w);
+
+	/* set device wireless to wired */
+	gelicw_vlan_mode(netdev, GELIC_NET_VLAN_WIRED);
+	gelicw_cmd_set_port(netdev, GELICW_PORT_UP);
+	w->wireless = GELICW_WIRELESS_SUPPORTED;
+
+	return 0;
+}
+
+void gelicw_remove(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	if (!w->wireless)
+		return;
+
+	dev_dbg(ntodev(netdev), "gelicw_remove\n");
+	gelicw_down(netdev);
+	w->wireless = 0;
+	netdev->wireless_handlers = NULL;
+	free_page((unsigned long)w->data_buf);
+}
+
+void gelicw_interrupt(struct net_device *netdev, u64 status)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	if (!w->wireless)
+		return;
+
+	if (status & GELICW_DEVICE_CMD_COMP) {
+		dev_dbg(ntodev(netdev), "GELICW_DEVICE_CMD_COMP\n");
+		if (w->cmd_id == GELICW_CMD_START)
+			schedule_work(&w->work_start_done);
+		else
+			complete(&w->cmd_done);
+	}
+	if (status & GELICW_DEVICE_EVENT_RECV) {
+		dev_dbg(ntodev(netdev), "GELICW_DEVICE_EVENT_RECV\n");
+		if (w->wireless == GELICW_WIRELESS_SHUTDOWN)
+			gelicw_clear_event(netdev);
+		else
+			schedule_work(&w->work_event);
+	}
+}
+
+int gelicw_is_associated(struct net_device *netdev)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	if (!w->wireless)
+		return 0;
+
+	return w->is_assoc;
+}
+
+
+/*
+ * Wireless externsions
+ */
+static int gelicw_get_name(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx: get_name\n");
+	if (w->state < GELICW_STATE_UP) {
+		strcpy(wrqu->name, "radio off");
+		return 0;
+	}
+
+	if (w->wireless_mode == IEEE_B ||
+	    (w->is_assoc && gelicw_is_ap_11b(&w->current_bss)))
+		strcpy(wrqu->name, "IEEE 802.11b");
+	else {
+		switch (w->wireless_mode) {
+		case IEEE_G:
+			strcpy(wrqu->name, "IEEE 802.11g");
+			break;
+		case IEEE_B | IEEE_G:
+		default:
+			strcpy(wrqu->name, "IEEE 802.11bg");
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int gelicw_set_freq(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	struct iw_freq *fwrq = &wrqu->freq;
+	int ch;
+
+	dev_dbg(ntodev(netdev), "wx: set_freq e:%d m:%d\n", fwrq->e, fwrq->m);
+	if (w->is_assoc || w->state < GELICW_STATE_UP)
+		return 0;
+
+	/* this setting has no effect for INFRA mode */
+	if (fwrq->e == 1) {
+		u32 f = fwrq->m / 100000;
+		int i;
+		for (i = 0; i < ARRAY_SIZE(freq_list); i++)
+			if (freq_list[i] == f)
+				break;
+		if (i == ARRAY_SIZE(freq_list))
+			return -EINVAL;
+		fwrq->m = i + 1; /* ch number */
+		fwrq->e = 0;
+	}
+	if (fwrq->e > 0)
+		return -EINVAL;
+
+	ch = fwrq->m;
+	if (ch < 1)
+		w->channel = 0; /* auto */
+	else if (ch > ARRAY_SIZE(freq_list))
+		return -EINVAL;
+	else {
+		/* check supported channnel */
+		if (!w->ch_info)
+			gelicw_cmd_get_ch_info(netdev);
+		if (w->ch_info & (1 << (ch - 1)))
+			w->channel = ch;
+		else
+			return -EINVAL;
+	}
+	dev_dbg(ntodev(netdev), " set cnannel: %d\n", w->channel);
+
+	return 0;
+}
+
+static int gelicw_get_freq(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx: get_freq:%d\n", w->channel);
+	if (w->channel == 0)
+		wrqu->freq.m = 0;
+	else
+		wrqu->freq.m = freq_list[w->channel - 1] * 100000;
+	wrqu->freq.e = 1;
+
+	return 0;
+}
+
+static int gelicw_set_mode(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	int mode = wrqu->mode;
+	u8 iw_mode = IW_MODE_INFRA;
+
+	dev_dbg(ntodev(netdev), "wx: set_mode:%x\n",mode);
+	switch (mode) {
+	case IW_MODE_ADHOC:
+		dev_dbg(ntodev(netdev), "IW_MODE_ADHOC\n");
+		iw_mode = mode;
+		return -EOPNOTSUPP; /* adhoc not supported */
+	case IW_MODE_INFRA:
+	default:
+		dev_dbg(ntodev(netdev), "IW_MODE_INFRA\n");
+		iw_mode = IW_MODE_INFRA;
+		break;
+	}
+
+	/* send common config */
+	gelicw_send_common_config(netdev, &w->iw_mode, iw_mode);
+
+	return 0;
+}
+
+static int gelicw_get_mode(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx: get_mode\n");
+	wrqu->mode = w->iw_mode;
+
+	return 0;
+}
+
+static inline int gelicw_qual2level(int qual)
+{
+	return (qual * 4 - 820)/10; /* FIXME: dummy */
+}
+
+static int gelicw_get_range(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	struct iw_range *range = (struct iw_range *)extra;
+	int num_ch, i;
+
+	dev_dbg(ntodev(netdev), "wx: get_range\n");
+	wrqu->data.length = sizeof(*range);
+	memset(range, 0, sizeof(*range));
+
+	/* wireless extension */
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = 19;
+
+	/* supported bitrates */
+	if (w->wireless_mode == IEEE_B)
+		range->num_bitrates = GELICW_NUM_11B_BITRATES;
+	else
+		range->num_bitrates = ARRAY_SIZE(bitrate_list);
+	range->throughput = bitrate_list[range->num_bitrates -1] / 2; /* half */
+	for (i = 0; i < range->num_bitrates; i++)
+		range->bitrate[i] = bitrate_list[i];
+
+	range->max_qual.qual = 100; /* relative value */
+	range->max_qual.level = 0;
+	range->avg_qual.qual = 50;
+	range->avg_qual.level = 0;
+	range->sensitivity = 0;
+
+	/* encryption capabilities */
+	range->encoding_size[0] = 5;	/* 40bit WEP */
+	range->encoding_size[1] = 13;	/* 104bit WEP */
+	range->encoding_size[2] = 64;	/* WPA-PSK */
+	range->num_encoding_sizes = 3;
+	range->max_encoding_tokens = WEP_KEYS;
+	range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+			  IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+	/* freq */
+	if (!w->ch_info)
+		gelicw_cmd_get_ch_info(netdev); /* get supported freq */
+
+	num_ch = 0;
+	for (i = 0; i < ARRAY_SIZE(freq_list); i++)
+		if (w->ch_info & (1 << i)) {
+			range->freq[num_ch].i = i + 1;
+			range->freq[num_ch].m = freq_list[i] * 100000;
+			range->freq[num_ch].e = 1;
+			if (++num_ch == IW_MAX_FREQUENCIES)
+				break;
+		}
+
+	range->num_channels = num_ch;
+	range->num_frequency = num_ch;
+
+	/* event capabilities */
+	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+				IW_EVENT_CAPA_MASK(SIOCGIWAP));
+	range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+	return 0;
+}
+
+static int gelicw_set_wap(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	static const u8 any[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+	static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+	dev_dbg(ntodev(netdev), "wx: set_wap\n");
+	if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+		return -EINVAL;
+
+	if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
+	    !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+		if (!memcmp(off, w->wap_bssid, ETH_ALEN))
+			return 0; /* ap off, no change */
+		else {
+			memset(w->wap_bssid, 0, ETH_ALEN);
+			/* start scan */
+		}
+	} else if (!memcmp(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN))
+		/* no change */
+		return 0;
+	else if (!memcmp(w->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+		/* current bss */
+		memcpy(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
+		return 0;
+	} else
+		memcpy(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+	/* start scan */
+	if (w->essid_len && w->state >= GELICW_STATE_SCAN_DONE) {
+		gelicw_disassoc(netdev);
+		/* scan essid */
+		cancel_delayed_work(&w->work_scan_all);
+		cancel_delayed_work(&w->work_scan_essid);
+		w->essid_search = 1;
+		schedule_delayed_work(&w->work_scan_essid, 0);
+	}
+
+	return 0;
+}
+
+static int gelicw_get_wap(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx: get_wap\n");
+	wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(wrqu->ap_addr.sa_data, w->bssid, ETH_ALEN);
+
+	return 0;
+}
+
+static int gelicw_set_scan(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx: set_scan\n");
+	if (w->state < GELICW_STATE_UP)
+		return -EIO;
+
+	/* cancel scan */
+	cancel_delayed_work(&w->work_scan_all);
+	cancel_delayed_work(&w->work_scan_essid);
+
+	schedule_delayed_work(&w->work_scan_all, 0);
+
+	return 0;
+}
+
+#define MAX_CUSTOM_LEN 64
+static char *gelicw_translate_scan(struct net_device *netdev,
+				char *start, char *stop,
+				struct gelicw_bss *list)
+{
+	char custom[MAX_CUSTOM_LEN];
+	struct iw_event iwe;
+	int i;
+	char *p, *current_val;
+
+	/* BSSID */
+	iwe.cmd = SIOCGIWAP;
+	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(iwe.u.ap_addr.sa_data, list->bssid, ETH_ALEN);
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
+
+	/* ESSID */
+	iwe.cmd = SIOCGIWESSID;
+	iwe.u.data.flags = 1;
+	iwe.u.data.length = list->essid_len;
+	start = iwe_stream_add_point(start, stop, &iwe, list->essid);
+
+	/* protocol name */
+	iwe.cmd = SIOCGIWNAME;
+	if (gelicw_is_ap_11b(list))
+		snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b");
+	else
+		snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bg");
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
+
+	/* MODE */
+	iwe.cmd = SIOCGIWMODE;
+	iwe.u.mode = list->mode;
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
+
+	/* FREQ */
+	iwe.cmd = SIOCGIWFREQ;
+	iwe.u.freq.m = list->channel;
+	iwe.u.freq.e = 0;
+	iwe.u.freq.i = 0;
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
+
+	/* ENCODE */
+	iwe.cmd = SIOCGIWENCODE;
+	if (list->capability & WLAN_CAPABILITY_PRIVACY)
+		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+	else
+		iwe.u.data.flags = IW_ENCODE_DISABLED;
+	iwe.u.data.length = 0;
+	start = iwe_stream_add_point(start, stop, &iwe, list->essid);
+
+	/* QUAL */
+	iwe.cmd = IWEVQUAL;
+	iwe.u.qual.updated  = IW_QUAL_QUAL_UPDATED |
+			IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID;
+	iwe.u.qual.qual = list->rssi;
+	iwe.u.qual.level = gelicw_qual2level(list->rssi);
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
+
+	/* RATE */
+	current_val = start + IW_EV_LCP_LEN;
+	iwe.cmd = SIOCGIWRATE;
+	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+	for (i = 0; i < list->rates_len; i++) {
+		iwe.u.bitrate.value = ((list->rates[i] & 0x7f) * 500000);
+		current_val = iwe_stream_add_value(start, current_val, stop,
+					&iwe, IW_EV_PARAM_LEN);
+	}
+	for (i = 0; i < list->rates_ex_len; i++) {
+		iwe.u.bitrate.value = ((list->rates_ex[i] & 0x7f) * 500000);
+		current_val = iwe_stream_add_value(start, current_val, stop,
+					&iwe, IW_EV_PARAM_LEN);
+	}
+	if ((current_val - start) > IW_EV_LCP_LEN)
+		start = current_val;
+
+	/* Extra */
+	/* BEACON */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = IWEVCUSTOM;
+	p = custom;
+	p += snprintf(p, MAX_CUSTOM_LEN, "bcn_int=%d", list->beacon_interval);
+	iwe.u.data.length = p - custom;
+	start = iwe_stream_add_point(start, stop, &iwe, custom);
+
+	/* AP security */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = IWEVCUSTOM;
+	p = custom;
+	p += snprintf(p, MAX_CUSTOM_LEN, "ap_sec=%04X", list->sec_info);
+	iwe.u.data.length = p - custom;
+	start = iwe_stream_add_point(start, stop, &iwe, custom);
+
+	return start;
+}
+
+static int gelicw_get_scan(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	int i;
+	char *ev = extra;
+	char *stop = ev + wrqu->data.length;
+
+	dev_dbg(ntodev(netdev), "wx: get_scan \n");
+	switch (w->state) {
+	case GELICW_STATE_DOWN:
+	case GELICW_STATE_UP:
+		return 0; /* no scan results */
+	case GELICW_STATE_SCANNING:
+		return -EAGAIN; /* now scanning */
+	case GELICW_STATE_SCAN_DONE:
+		if (!w->scan_all) /* essid scan */
+			return -EAGAIN;
+		break;
+	default:
+		break;
+	}
+
+	w->scan_all = 0;
+	for (i = 0; i < w->num_bss_list; i++)
+		ev = gelicw_translate_scan(netdev, ev, stop, &w->bss_list[i]);
+	wrqu->data.length = ev - extra;
+	wrqu->data.flags = 0;
+
+	/* start background scan */
+	if (w->essid_search)
+		schedule_delayed_work(&w->work_scan_essid,
+					GELICW_SCAN_INTERVAL);
+
+	return 0;
+}
+
+static int gelicw_set_essid(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u16 length = 0;
+
+	dev_dbg(ntodev(netdev), "wx:set_essid\n");
+	/* cancel scan */
+	w->essid_search = 0;
+	cancel_delayed_work(&w->work_scan_all);
+	cancel_delayed_work(&w->work_scan_essid);
+
+	if (wrqu->essid.flags && wrqu->essid.length)
+		length = wrqu->essid.length;
+
+	if (length == 0) {
+		/* essid ANY scan not supported */
+		dev_dbg(ntodev(netdev), "ESSID ANY\n");
+		w->essid_len = 0; /* clear essid */
+		w->essid[0] = '\0';
+		return 0;
+	} else {
+		/* check essid */
+		if (length > IW_ESSID_MAX_SIZE)
+			return -EINVAL;
+		if (w->essid_len == length &&
+		    !strncmp(w->essid, extra, length)) {
+			/* same essid */
+			if (w->is_assoc)
+				return 0;
+		} else {
+			/* set new essid */
+			w->essid_len = length;
+			memcpy(w->essid, extra, length);
+		}
+	}
+	/* start essid scan */
+	w->essid_search = 1;
+	schedule_delayed_work(&w->work_scan_essid, 0);
+
+	return 0;
+}
+
+static int gelicw_get_essid(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx:get_essid\n");
+	if (w->essid_len) {
+		memcpy(extra, w->essid, w->essid_len);
+		wrqu->essid.length = w->essid_len;
+		wrqu->essid.flags = 1;
+	} else {
+		wrqu->essid.length = 0;
+		wrqu->essid.flags = 0;
+	}
+
+	return 0;
+}
+
+static int gelicw_set_nick(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	u32 len = wrqu->data.length;
+
+	dev_dbg(ntodev(netdev), "wx:set_nick\n");
+	if (len > IW_ESSID_MAX_SIZE)
+		return -EINVAL;
+
+	memset(w->nick, 0, sizeof(w->nick));
+	memcpy(w->nick, extra, len);
+
+	return 0;
+}
+
+static int gelicw_get_nick(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx:get_nick\n");
+	wrqu->data.length = strlen(w->nick);
+	memcpy(extra, w->nick, wrqu->data.length);
+	wrqu->data.flags = 1;
+
+	return 0;
+}
+
+static int gelicw_set_rate(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	dev_dbg(ntodev(netdev), "wx:set_rate:%d\n", wrqu->bitrate.value);
+	if (wrqu->bitrate.value == -1)
+		return 0;	/* auto rate only */
+
+	return -EOPNOTSUPP;
+}
+
+static int gelicw_get_rate(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx:get_rate\n");
+
+	if (w->wireless_mode == IEEE_B ||
+	    (w->is_assoc && gelicw_is_ap_11b(&w->current_bss)))
+		wrqu->bitrate.value = bitrate_list[GELICW_NUM_11B_BITRATES -1];
+	else
+		wrqu->bitrate.value = bitrate_list[ARRAY_SIZE(bitrate_list) -1];
+
+	wrqu->bitrate.fixed = 0;
+
+	return 0;
+}
+
+static int gelicw_set_encode(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	struct iw_point *enc = &wrqu->encoding;
+	int i, index, key_index;
+
+	dev_dbg(ntodev(netdev), "wx:set_encode: flags:%x\n", enc->flags );
+	index = enc->flags & IW_ENCODE_INDEX;
+	if (index < 0 || index > WEP_KEYS)
+		return -EINVAL;
+	index--;
+
+	if (enc->length > IW_ENCODING_TOKEN_MAX)
+		return -EINVAL;
+
+	if (index != -1)
+		w->key_index = index;
+	key_index = w->key_index;
+
+	if (enc->flags & IW_ENCODE_DISABLED) {
+		/* disable encryption */
+		if (index == -1) {
+			/* disable all */
+			w->key_alg = IW_ENCODE_ALG_NONE;
+			for (i = 0; i < WEP_KEYS; i++)
+				w->key_len[i] = 0;
+		} else
+			w->key_len[key_index] = 0;
+	} else if (enc->flags & IW_ENCODE_NOKEY) {
+		/* key not changed */
+		if (w->key_alg == IW_ENCODE_ALG_NONE)
+			w->key_alg = IW_ENCODE_ALG_WEP; /* default wep */
+	} else {
+		/* enable encryption */
+		w->key_len[key_index] = enc->length;
+		if (w->key_alg == IW_ENCODE_ALG_NONE)
+			w->key_alg = IW_ENCODE_ALG_WEP; /* default wep */
+		memcpy(w->key[key_index], extra, w->key_len[key_index]);
+	}
+	dev_dbg(ntodev(netdev), "key %d len:%d alg:%x\n",\
+		key_index, w->key_len[key_index], w->key_alg);
+
+	if (w->state >= GELICW_STATE_SCAN_DONE &&
+	    w->cmd_send_flg == 0 && w->essid_len)
+		/* scan essid and set other params */
+		schedule_delayed_work(&w->work_scan_essid, 0);
+	else
+		schedule_delayed_work(&w->work_encode, 0);
+
+	return 0;
+}
+
+static int gelicw_get_encode(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	struct iw_point *enc = &wrqu->encoding;
+	int index, key_index;
+
+	dev_dbg(ntodev(netdev), "wx:get_encode\n");
+	index = enc->flags & IW_ENCODE_INDEX;
+	if (index < 0 || index > WEP_KEYS)
+		return -EINVAL;
+
+	index--;
+	key_index = (index == -1 ? w->key_index : index);
+	enc->flags = key_index + 1;
+
+	if (w->key_alg == IW_ENCODE_ALG_NONE || !w->key_len[key_index]) {
+		/* no encryption */
+		enc->flags |= IW_ENCODE_DISABLED;
+		enc->length = 0;
+	} else {
+		enc->flags |= IW_ENCODE_NOKEY;
+		enc->length = w->key_len[key_index];
+		memset(extra, 0, w->key_len[key_index]);
+	}
+
+	return 0;
+}
+
+static int gelicw_set_auth(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	struct iw_param *param = &wrqu->param;
+	int value = param->value;
+	int ret = 0;
+
+	dev_dbg(ntodev(netdev), "wx:set_auth:%x\n", param->flags & IW_AUTH_INDEX);
+	switch(param->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+	case IW_AUTH_CIPHER_PAIRWISE:
+	case IW_AUTH_CIPHER_GROUP:
+	case IW_AUTH_KEY_MGMT:
+	case IW_AUTH_TKIP_COUNTERMEASURES:
+	case IW_AUTH_DROP_UNENCRYPTED:
+	case IW_AUTH_WPA_ENABLED:
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+	case IW_AUTH_ROAMING_CONTROL:
+	case IW_AUTH_PRIVACY_INVOKED:
+		/* ignore */
+		dev_dbg(ntodev(netdev), "IW_AUTH(%x)\n", param->flags & IW_AUTH_INDEX);
+		break;
+	case IW_AUTH_80211_AUTH_ALG:
+		dev_dbg(ntodev(netdev), "IW_AUTH_80211_AUTH_ALG:\n");
+		if (value & IW_AUTH_ALG_SHARED_KEY)
+			w->auth_mode = IW_AUTH_ALG_SHARED_KEY;
+		else if (value & IW_AUTH_ALG_OPEN_SYSTEM)
+			w->auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+		else
+			ret = -EINVAL;
+		break;
+	default:
+		dev_dbg(ntodev(netdev), "IW_AUTH_UNKNOWN flags:%x\n", param->flags);
+		ret = -EOPNOTSUPP;
+	}
+
+	return ret;
+}
+
+static int gelicw_get_auth(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	struct iw_param *param = &wrqu->param;
+
+	dev_dbg(ntodev(netdev), "wx:get_auth\n");
+	switch(param->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_80211_AUTH_ALG:
+		param->value = w->auth_mode;
+		break;
+	case IW_AUTH_WPA_ENABLED:
+		if ((w->key_alg & IW_ENCODE_ALG_TKIP) ||
+		    (w->key_alg & IW_ENCODE_ALG_CCMP))
+			param->value = 1;
+		else
+			param->value = 0;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int gelicw_set_encodeext(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	struct iw_point *enc = &wrqu->encoding;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int i, index, key_index;
+
+	dev_dbg(ntodev(netdev), "wx:set_encodeext\n");
+	index = enc->flags & IW_ENCODE_INDEX;
+	if (index < 0 || index > WEP_KEYS)
+		return -EINVAL;
+
+	index--;
+	if (ext->key_len > IW_ENCODING_TOKEN_MAX)
+		return -EINVAL;
+
+	if (index != -1)
+		w->key_index = index;
+	key_index = w->key_index;
+
+	if (enc->flags & IW_ENCODE_DISABLED) {
+		/* disable encryption */
+		if (index == -1) {
+			/* disable all */
+			w->key_alg = IW_ENCODE_ALG_NONE;
+			for (i = 0; i < WEP_KEYS; i++)
+				w->key_len[i] = 0;
+		} else
+			w->key_len[key_index] = 0;
+	} else if (enc->flags & IW_ENCODE_NOKEY)
+		/* key not changed */
+		w->key_alg = ext->alg;
+	else {
+		w->key_len[key_index] = ext->key_len;
+		w->key_alg = ext->alg;
+		if (w->key_alg != IW_ENCODE_ALG_NONE && w->key_len[key_index])
+			memcpy(w->key[key_index], ext->key, w->key_len[key_index]);
+	}
+	dev_dbg(ntodev(netdev), "key %d len:%d alg:%x\n",\
+		key_index, w->key_len[key_index], w->key_alg);
+
+	if (w->state >= GELICW_STATE_SCAN_DONE &&
+	    w->cmd_send_flg == 0 && w->essid_len)
+		/* scan essid and set other params */
+		schedule_delayed_work(&w->work_scan_essid, 0);
+	else
+		schedule_delayed_work(&w->work_encode, 0);
+
+	return 0;
+}
+
+static int gelicw_get_encodeext(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	struct iw_point *enc = &wrqu->encoding;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int index, key_index, key_len;
+
+	dev_dbg(ntodev(netdev), "wx:get_encodeext\n");
+	key_len = enc->length - sizeof(*ext);
+	if (key_len < 0)
+		return -EINVAL;
+
+	index = enc->flags & IW_ENCODE_INDEX;
+	if (index < 0 || index > WEP_KEYS)
+		return -EINVAL;
+
+	index--;
+	key_index = (index == -1 ? w->key_index : index);
+
+	memset(ext, 0, sizeof(*ext));
+	enc->flags = key_index + 1;
+
+	if (w->key_alg == IW_ENCODE_ALG_NONE || !w->key_len[key_index]) {
+		/* no encryption */
+		enc->flags |= IW_ENCODE_DISABLED;
+		ext->alg = IW_ENCODE_ALG_NONE;
+		ext->key_len = 0;
+	} else {
+		enc->flags |= IW_ENCODE_NOKEY;
+		ext->alg = w->key_alg;
+		ext->key_len = w->key_len[key_index];
+	}
+
+	return 0;
+}
+
+/*
+ * wireless stats
+ */
+static struct iw_statistics *gelicw_get_wireless_stats(struct net_device *netdev)
+{
+	static struct iw_statistics wstats;
+	struct gelic_wireless *w = gelicw_priv(netdev);
+
+	dev_dbg(ntodev(netdev), "wx:wireless_stats\n");
+	if (w->state < GELICW_STATE_ASSOCIATED) {
+		wstats.qual.updated  = IW_QUAL_QUAL_UPDATED |
+				IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID;
+		wstats.qual.qual = 0;
+		wstats.qual.level = 0;
+		return &wstats;
+	}
+	init_completion(&w->rssi_done);
+	schedule_delayed_work(&w->work_rssi, 0);
+
+	wait_for_completion_interruptible(&w->rssi_done);
+	wstats.qual.updated  = IW_QUAL_QUAL_UPDATED |
+			IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID;
+	wstats.qual.qual = w->rssi;
+	wstats.qual.level = gelicw_qual2level(w->rssi);
+
+	return &wstats;
+}
+
+/*
+ * private handler
+ */
+static int gelicw_priv_set_alg_mode(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	int mode = *(int *)extra;
+
+	dev_dbg(ntodev(netdev), "wx:priv_set_alg\n");
+	switch (mode) {
+	case IW_ENCODE_ALG_NONE:
+	case IW_ENCODE_ALG_WEP:
+	case IW_ENCODE_ALG_TKIP:
+	case IW_ENCODE_ALG_CCMP:
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* send common config */
+	gelicw_send_common_config(netdev, &w->key_alg, (u8)mode);
+
+	return 0;
+}
+
+static int gelicw_priv_get_alg_mode(struct net_device *netdev,
+			   struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct gelic_wireless *w = gelicw_priv(netdev);
+	char *p;
+
+	dev_dbg(ntodev(netdev), "wx:priv_get_alg\n");
+	switch (w->key_alg) {
+	case IW_ENCODE_ALG_NONE:
+		strncpy(extra, "OFF", MAX_IW_PRIV_SIZE);
+		break;
+	case IW_ENCODE_ALG_WEP:
+		strncpy(extra, "WEP", MAX_IW_PRIV_SIZE);
+		break;
+	case IW_ENCODE_ALG_TKIP:
+		strncpy(extra, "TKIP", MAX_IW_PRIV_SIZE);
+		break;
+	case IW_ENCODE_ALG_CCMP:
+		strncpy(extra, "AES-CCMP", MAX_IW_PRIV_SIZE);
+		break;
+	default:
+		break;
+	}
+	p = extra + strlen(extra);
+
+	if (w->key_alg == IW_ENCODE_ALG_TKIP ||
+	    w->key_alg == IW_ENCODE_ALG_CCMP) {
+		if (w->key_len[w->key_index] == 64) /* current key index */
+			strncpy(p, " hex", MAX_IW_PRIV_SIZE);
+		else
+			strncpy(p, " passphrase", MAX_IW_PRIV_SIZE);
+	}
+	wrqu->data.length = strlen(extra);
+
+	return 0;
+}
+
+
+/*
+ * Wireless handlers
+ */
+static const iw_handler gelicw_handler[] =
+{
+	[IW_IOCTL_IDX(SIOCGIWNAME)]      = gelicw_get_name,
+	[IW_IOCTL_IDX(SIOCSIWFREQ)]      = gelicw_set_freq,
+	[IW_IOCTL_IDX(SIOCGIWFREQ)]      = gelicw_get_freq,
+	[IW_IOCTL_IDX(SIOCSIWMODE)]      = gelicw_set_mode,
+	[IW_IOCTL_IDX(SIOCGIWMODE)]      = gelicw_get_mode,
+	[IW_IOCTL_IDX(SIOCGIWRANGE)]     = gelicw_get_range,
+	[IW_IOCTL_IDX(SIOCSIWAP)]        = gelicw_set_wap,
+	[IW_IOCTL_IDX(SIOCGIWAP)]        = gelicw_get_wap,
+	[IW_IOCTL_IDX(SIOCSIWSCAN)]      = gelicw_set_scan,
+	[IW_IOCTL_IDX(SIOCGIWSCAN)]      = gelicw_get_scan,
+	[IW_IOCTL_IDX(SIOCSIWESSID)]     = gelicw_set_essid,
+	[IW_IOCTL_IDX(SIOCGIWESSID)]     = gelicw_get_essid,
+	[IW_IOCTL_IDX(SIOCSIWNICKN)]     = gelicw_set_nick,
+	[IW_IOCTL_IDX(SIOCGIWNICKN)]     = gelicw_get_nick,
+	[IW_IOCTL_IDX(SIOCSIWRATE)]      = gelicw_set_rate,
+	[IW_IOCTL_IDX(SIOCGIWRATE)]      = gelicw_get_rate,
+	[IW_IOCTL_IDX(SIOCSIWENCODE)]    = gelicw_set_encode,
+	[IW_IOCTL_IDX(SIOCGIWENCODE)]    = gelicw_get_encode,
+	[IW_IOCTL_IDX(SIOCSIWAUTH)]      = gelicw_set_auth,
+	[IW_IOCTL_IDX(SIOCGIWAUTH)]      = gelicw_get_auth,
+	[IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = gelicw_set_encodeext,
+	[IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = gelicw_get_encodeext,
+};
+
+/*
+ * Private wireless handlers
+ */
+enum {
+	GELICW_PRIV_SET_AUTH  = SIOCIWFIRSTPRIV,
+	GELICW_PRIV_GET_AUTH
+};
+
+static struct iw_priv_args gelicw_private_args[] = {
+	{
+	 .cmd = GELICW_PRIV_SET_AUTH,
+	 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	 .name = "set_alg"
+	},
+	{
+	 .cmd = GELICW_PRIV_GET_AUTH,
+	 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_IW_PRIV_SIZE,
+	 .name = "get_alg"
+	},
+};
+
+static const iw_handler gelicw_private_handler[] =
+{
+	gelicw_priv_set_alg_mode,
+	gelicw_priv_get_alg_mode,
+};
+
+static struct iw_handler_def gelicw_handler_def =
+{
+	.num_standard	= ARRAY_SIZE(gelicw_handler),
+	.num_private	= ARRAY_SIZE(gelicw_private_handler),
+	.num_private_args = ARRAY_SIZE(gelicw_private_args),
+	.standard	= (iw_handler *)gelicw_handler,
+	.private	= (iw_handler *)gelicw_private_handler,
+	.private_args	= (struct iw_priv_args *)gelicw_private_args,
+	.get_wireless_stats = gelicw_get_wireless_stats
+};

--
Masakazu MOKUNO

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ