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 PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Fri, 01 Jun 2007 12:39:56 -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. Also, any further patches you send should be sent to: linux-wireless@...r.kernel.org which is the list for wireless stuff. Dan > 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 - 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