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-prev] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 01 Jun 2007 12:35:49 -0400
From:	Dan Williams <dcbw@...hat.com>
To:	MOKUNO Masakazu <mokuno@...sony.co.jp>
Cc:	netdev@...r.kernel.org, Geoff Levand <geoffrey.levand@...sony.com>,
	Geert Uytterhoeven <Geert.Uytterhoeven@...ycom.com>
Subject: Re: [PATCH 2/2] ps3: add wireless LAN support

On Fri, 2007-06-01 at 15:10 +0900, MOKUNO Masakazu wrote: 
> 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

Does the firmware have a supplicant in it?

> +#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));

Need endian notations here if it touches hw/fw, otherwise get rid of the
packed attribute.

> +
> +/* 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));

Need endian notations here if it touches hw/fw, otherwise get rid of the
packed attribute.

> +
> +/* 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));

Need endian notations here if it touches hw/fw, otherwise get rid of the
packed attribute.

> +/* 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

This looks like a bitfield.  Make it one, then you can construct
synthetic RSN & WPA IEs more easily (see the end of the mail).

> +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));

Need endian notations here if it touches hw/fw, otherwise get rid of the
packed attribute.

> +/* rssi_descriptor */
> +struct rssi_desc {
> +	u16 rssi; /* max rssi = 100 */
> +} __attribute__ ((packed));

Need endian notations here if it touches hw/fw, otherwise get rid of the
packed attribute.

> +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 */

Typo, should be "supported"

> +	u8 wireless_mode; /* 11b/g */

Is this switch (and the IEEE_B/IEEE_G constants) for some sort of
compatibility mode versus full G mode or something?  I don't think any
of the other cards Linux supports have this distinction.

> +	u8 channel; /* current ch */
> +	u8 iw_mode; /* INFRA or Ad-hoc */

iw_mode seems wierd here, best to just rename it "mode" and
differentiate it from "wireless_mode" by making wireless_mode more
descriptive.  We're much more likely to care about infra/adhoc than b/g
separately.

> +	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];

Don't use a static array; use a list structure and age the scan results
accordingly.  Keep a pointer to the current BSS rather than an index.
You then get to use list_for_each_entry() to iterate over BSSs.

> +};
> +
>  /* 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;
> +	}

Once you've got the endian notations in common_config, you'll need to
use cpu_to_XXX calls around stuff when building up the command
structure.

> +	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;

Thinking about it, the passphrase stuff should just be killed.
Userspace tools take care of the hashing from passhprase to hex PSK.
Assuming, of course, that we're talking about the standard WPA PSK hash
algorithm here, right?

Speaking of which, does this device support 802.1x?  WPA[2] Enterprise?
What happens when you try to use wpa_supplicant with it?

> +		} 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];

Again, use a list and not a static array.  Age the BSSs in the list
accordingly and don't blow the list away every single scan.  When the
next scan request comes back, cull old BSSs from the list here and
re-use the element.

See airo and ipw2x00 for how scan result handling should be done.

> +
> +		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) {

Use constants from ieee80211.h here instead of magic numbers.  You want,
for example, WLAN_CAPABILITY_ESS and WLAN_CAPABILITY_IBSS.

You also need endian conversions for most of these since it apparently
comes from hardware?

> +			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;

Need endian conversions here for everything coming from hardware.

> +	}
> +	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;

use compare_ether_addr()

> +	/* 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;

Can you explain the "avoid frequent scanning" thing a bit more?

> +	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");

I'll talk a bit about scanning below...  I assume this is supposed to be
background scanning for a specific SSID, but the scanning has to be more
flexible.

> +		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 check is bogus.  You should be able to set frequency while
associated.  Basically, the different WEXT options have two states;
locked and ANY.  If you set to any, the driver/card is free to decided
what to use.  If locked, the card needs to honor that setting, no matter
what the state.

In this case, if the card is associated, and the user sets the
frequency, the card needs to disassociate from the current AP (assuming
the current AP is not on the frequency specified), and attempt to
reassociate with a BSS on the new frequency using the rest of the
parameters previously configured.

> +	/* 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;

What are your quality levels reported in?  If they are in dBm, set the
dBm flag here.  Or, if the quality is truly arbitrary RSSI, are you
_sure_ it is a linear value?  'qual' must be linear in progression.

> +
> +	/* 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);

Use compare_ether_addr for all of these BSSID comparisons.

> +	/* 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);

Would be nice to have the specific ESSID scan, since the device is
already doing background scans for the configured SSID.

> +	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)

Nice use of the constant :)  But it should also be used above (where
noted) when translating from the hardware scan result buffer to the
driver BSS structure too.

> +		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);

What units is RSSI actually in again?  What's the hardware's max and min
RSSI, and how does that map to dBm?

> +	/* 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);

This is horribly, horribly wrong.  If the driver supports WPA is _MUST_
return the WPA and RSN information elements using the IWEVGENIE tag.  If
you cannot get those IEs from the firmware, then I'd NAK WPA support in
the driver completely.  The way this is now, userspace tools have no
idea what security APs actually support in any standard, meaningful way.

> +	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;

This is wrong.  If there are scan results, return them.  Also, use the
aged scan result list so that the list is never completely cleared, but
BSSs are only culled when new scan results for that BSS come in from the
firmware, or if the BSS is timed out.

> +		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);

Seems wrong; don't start scans from the get_scan function.

> +	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);

Wouldn't this break 802.1x with dynamic WEP keys?  Just setting the WEP
key should trigger any sort of disassociation from the current AP unless
you're using Shared Key encryption.

> +	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;

So just how much WPA[2] _does_ this device support?  Does
wpa_supplicant's 'wext' driver work with this device?

> +	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]);
> +	}

More key validation should be done here.  For example, encodeext needs
to support WEP too, and therefore needs to do key validation and take
the key type into account.  encodeext should be returning errors to
userspace on invalid keys and such without punting the validation to a
delayed work function.

> +	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);

Nope, this private function needs to go away.  It's functionality is
provided by ENCODEEXT.

> +	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);

Again, this needs to go away.  Its functionality is provided by
ENCODEEXT.

> +	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,
> +};

What about SIOC[G|S}IWGENIE ?  Goes back to the question about how much
WPA this device actually supports.

> +/*
> + * Private wireless handlers
> + */
> +enum {
> +	GELICW_PRIV_SET_AUTH  = SIOCIWFIRSTPRIV,
> +	GELICW_PRIV_GET_AUTH
> +};

Need to be killed.

> +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"
> +	},
> +};

Needs to be killed.

> +static const iw_handler gelicw_private_handler[] =
> +{
> +	gelicw_priv_set_alg_mode,
> +	gelicw_priv_get_alg_mode,
> +};

Needs to be killed.

> +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
> +};

In any case, code looks pretty clean.  Nice job.  There are a few basic
issues, rehashed here:

1) Need endian annotations and conversions.

2) How much WPA does the device really support?  If it only supports
WPA[2]-PSK, I guess that's OK, but the driver _MUST_ return the WPA and
RSN information elements from each BSS that provides them using the
IWEVGENIE tag from the get scan handler.  A hack might be to construct a
suitable synthetic IE depending on the contents of the 'security' field
the firmware passes back.  But userspace needs to know that a particular
BSS supports WPA in a standard manner.

3) Should remove the WPA passphrase bits from the encode functions,
these functions should operate only on raw key material.  Userspace
tools do the hashing.

4) I'd like an explanation of the scanning behavior too, looks fishy.

5) Should be using a list of scanned BSSs, should be aging and culling
them, instead of a static array.

6) The private ioctl needs to die.

7) Does the device work with wpa_supplicant's 'wext' driver?

And more I probably haven't thought of, but this is a start.

Cheers,
Dan


-
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