[<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