162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * WL3501 Wireless LAN PCMCIA Card Driver for Linux
462306a36Sopenharmony_ci * Written originally for Linux 2.0.30 by Fox Chen, mhchen@golf.ccl.itri.org.tw
562306a36Sopenharmony_ci * Ported to 2.2, 2.4 & 2.5 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
662306a36Sopenharmony_ci * Wireless extensions in 2.4 by Gustavo Niemeyer <niemeyer@conectiva.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * References used by Fox Chen while writing the original driver for 2.0.30:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *   1. WL24xx packet drivers (tooasm.asm)
1162306a36Sopenharmony_ci *   2. Access Point Firmware Interface Specification for IEEE 802.11 SUTRO
1262306a36Sopenharmony_ci *   3. IEEE 802.11
1362306a36Sopenharmony_ci *   4. Linux network driver (/usr/src/linux/drivers/net)
1462306a36Sopenharmony_ci *   5. ISA card driver - wl24.c
1562306a36Sopenharmony_ci *   6. Linux PCMCIA skeleton driver - skeleton.c
1662306a36Sopenharmony_ci *   7. Linux PCMCIA 3c589 network driver - 3c589_cs.c
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Tested with WL2400 firmware 1.2, Linux 2.0.30, and pcmcia-cs-2.9.12
1962306a36Sopenharmony_ci *   1. Performance: about 165 Kbytes/sec in TCP/IP with Ad-Hoc mode.
2062306a36Sopenharmony_ci *      rsh 192.168.1.3 "dd if=/dev/zero bs=1k count=1000" > /dev/null
2162306a36Sopenharmony_ci *      (Specification 2M bits/sec. is about 250 Kbytes/sec., but we must deduct
2262306a36Sopenharmony_ci *       ETHER/IP/UDP/TCP header, and acknowledgement overhead)
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * Tested with Planet AP in 2.4.17, 184 Kbytes/s in UDP in Infrastructure mode,
2562306a36Sopenharmony_ci * 173 Kbytes/s in TCP.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * Tested with Planet AP in 2.5.73-bk, 216 Kbytes/s in Infrastructure mode
2862306a36Sopenharmony_ci * with a SMP machine (dual pentium 100), using pktgen, 432 pps (pkt_size = 60)
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <linux/delay.h>
3262306a36Sopenharmony_ci#include <linux/types.h>
3362306a36Sopenharmony_ci#include <linux/interrupt.h>
3462306a36Sopenharmony_ci#include <linux/in.h>
3562306a36Sopenharmony_ci#include <linux/kernel.h>
3662306a36Sopenharmony_ci#include <linux/module.h>
3762306a36Sopenharmony_ci#include <linux/fcntl.h>
3862306a36Sopenharmony_ci#include <linux/if_arp.h>
3962306a36Sopenharmony_ci#include <linux/ioport.h>
4062306a36Sopenharmony_ci#include <linux/netdevice.h>
4162306a36Sopenharmony_ci#include <linux/etherdevice.h>
4262306a36Sopenharmony_ci#include <linux/skbuff.h>
4362306a36Sopenharmony_ci#include <linux/slab.h>
4462306a36Sopenharmony_ci#include <linux/string.h>
4562306a36Sopenharmony_ci#include <linux/wireless.h>
4662306a36Sopenharmony_ci#include <net/cfg80211.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include <net/iw_handler.h>
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#include <pcmcia/cistpl.h>
5162306a36Sopenharmony_ci#include <pcmcia/cisreg.h>
5262306a36Sopenharmony_ci#include <pcmcia/ds.h>
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#include <asm/io.h>
5562306a36Sopenharmony_ci#include <linux/uaccess.h>
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#include "wl3501.h"
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#ifndef __i386__
6062306a36Sopenharmony_ci#define slow_down_io()
6162306a36Sopenharmony_ci#endif
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* For rough constant delay */
6462306a36Sopenharmony_ci#define WL3501_NOPLOOP(n) { int x = 0; while (x++ < n) slow_down_io(); }
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define wl3501_outb(a, b) { outb(a, b); slow_down_io(); }
6962306a36Sopenharmony_ci#define wl3501_outb_p(a, b) { outb_p(a, b); slow_down_io(); }
7062306a36Sopenharmony_ci#define wl3501_outsb(a, b, c) { outsb(a, b, c); slow_down_io(); }
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define WL3501_RELEASE_TIMEOUT (25 * HZ)
7362306a36Sopenharmony_ci#define WL3501_MAX_ADHOC_TRIES 16
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define WL3501_RESUME	0
7662306a36Sopenharmony_ci#define WL3501_SUSPEND	1
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int wl3501_config(struct pcmcia_device *link);
7962306a36Sopenharmony_cistatic void wl3501_release(struct pcmcia_device *link);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic const struct {
8262306a36Sopenharmony_ci	int reg_domain;
8362306a36Sopenharmony_ci	int min, max, deflt;
8462306a36Sopenharmony_ci} iw_channel_table[] = {
8562306a36Sopenharmony_ci	{
8662306a36Sopenharmony_ci		.reg_domain = IW_REG_DOMAIN_FCC,
8762306a36Sopenharmony_ci		.min	    = 1,
8862306a36Sopenharmony_ci		.max	    = 11,
8962306a36Sopenharmony_ci		.deflt	    = 1,
9062306a36Sopenharmony_ci	},
9162306a36Sopenharmony_ci	{
9262306a36Sopenharmony_ci		.reg_domain = IW_REG_DOMAIN_DOC,
9362306a36Sopenharmony_ci		.min	    = 1,
9462306a36Sopenharmony_ci		.max	    = 11,
9562306a36Sopenharmony_ci		.deflt	    = 1,
9662306a36Sopenharmony_ci	},
9762306a36Sopenharmony_ci	{
9862306a36Sopenharmony_ci		.reg_domain = IW_REG_DOMAIN_ETSI,
9962306a36Sopenharmony_ci		.min	    = 1,
10062306a36Sopenharmony_ci		.max	    = 13,
10162306a36Sopenharmony_ci		.deflt	    = 1,
10262306a36Sopenharmony_ci	},
10362306a36Sopenharmony_ci	{
10462306a36Sopenharmony_ci		.reg_domain = IW_REG_DOMAIN_SPAIN,
10562306a36Sopenharmony_ci		.min	    = 10,
10662306a36Sopenharmony_ci		.max	    = 11,
10762306a36Sopenharmony_ci		.deflt	    = 10,
10862306a36Sopenharmony_ci	},
10962306a36Sopenharmony_ci	{
11062306a36Sopenharmony_ci		.reg_domain = IW_REG_DOMAIN_FRANCE,
11162306a36Sopenharmony_ci		.min	    = 10,
11262306a36Sopenharmony_ci		.max	    = 13,
11362306a36Sopenharmony_ci		.deflt	    = 10,
11462306a36Sopenharmony_ci	},
11562306a36Sopenharmony_ci	{
11662306a36Sopenharmony_ci		.reg_domain = IW_REG_DOMAIN_MKK,
11762306a36Sopenharmony_ci		.min	    = 14,
11862306a36Sopenharmony_ci		.max	    = 14,
11962306a36Sopenharmony_ci		.deflt	    = 14,
12062306a36Sopenharmony_ci	},
12162306a36Sopenharmony_ci	{
12262306a36Sopenharmony_ci		.reg_domain = IW_REG_DOMAIN_MKK1,
12362306a36Sopenharmony_ci		.min	    = 1,
12462306a36Sopenharmony_ci		.max	    = 14,
12562306a36Sopenharmony_ci		.deflt	    = 1,
12662306a36Sopenharmony_ci	},
12762306a36Sopenharmony_ci	{
12862306a36Sopenharmony_ci		.reg_domain = IW_REG_DOMAIN_ISRAEL,
12962306a36Sopenharmony_ci		.min	    = 3,
13062306a36Sopenharmony_ci		.max	    = 9,
13162306a36Sopenharmony_ci		.deflt	    = 9,
13262306a36Sopenharmony_ci	},
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/**
13662306a36Sopenharmony_ci * iw_valid_channel - validate channel in regulatory domain
13762306a36Sopenharmony_ci * @reg_domain: regulatory domain
13862306a36Sopenharmony_ci * @channel: channel to validate
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * Returns 0 if invalid in the specified regulatory domain, non-zero if valid.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_cistatic int iw_valid_channel(int reg_domain, int channel)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	int i, rc = 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++)
14762306a36Sopenharmony_ci		if (reg_domain == iw_channel_table[i].reg_domain) {
14862306a36Sopenharmony_ci			rc = channel >= iw_channel_table[i].min &&
14962306a36Sopenharmony_ci			     channel <= iw_channel_table[i].max;
15062306a36Sopenharmony_ci			break;
15162306a36Sopenharmony_ci		}
15262306a36Sopenharmony_ci	return rc;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * iw_default_channel - get default channel for a regulatory domain
15762306a36Sopenharmony_ci * @reg_domain: regulatory domain
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * Returns the default channel for a regulatory domain
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic int iw_default_channel(int reg_domain)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	int i, rc = 1;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++)
16662306a36Sopenharmony_ci		if (reg_domain == iw_channel_table[i].reg_domain) {
16762306a36Sopenharmony_ci			rc = iw_channel_table[i].deflt;
16862306a36Sopenharmony_ci			break;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	return rc;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void iw_set_mgmt_info_element(enum iw_mgmt_info_element_ids id,
17462306a36Sopenharmony_ci				     struct iw_mgmt_info_element *el,
17562306a36Sopenharmony_ci				     void *value, int len)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	el->id  = id;
17862306a36Sopenharmony_ci	el->len = len;
17962306a36Sopenharmony_ci	memcpy(el->data, value, len);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void iw_copy_mgmt_info_element(struct iw_mgmt_info_element *to,
18362306a36Sopenharmony_ci				      struct iw_mgmt_info_element *from)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	iw_set_mgmt_info_element(from->id, to, from->data, from->len);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic inline void wl3501_switch_page(struct wl3501_card *this, u8 page)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	wl3501_outb(page, this->base_addr + WL3501_NIC_BSS);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/*
19462306a36Sopenharmony_ci * Get Ethernet MAC address.
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * WARNING: We switch to FPAGE0 and switc back again.
19762306a36Sopenharmony_ci *          Making sure there is no other WL function beening called by ISR.
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_cistatic int wl3501_get_flash_mac_addr(struct wl3501_card *this)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	int base_addr = this->base_addr;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* get MAC addr */
20462306a36Sopenharmony_ci	wl3501_outb(WL3501_BSS_FPAGE3, base_addr + WL3501_NIC_BSS); /* BSS */
20562306a36Sopenharmony_ci	wl3501_outb(0x00, base_addr + WL3501_NIC_LMAL);	/* LMAL */
20662306a36Sopenharmony_ci	wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH);	/* LMAH */
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* wait for reading EEPROM */
20962306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
21062306a36Sopenharmony_ci	this->mac_addr[0] = inb(base_addr + WL3501_NIC_IODPA);
21162306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
21262306a36Sopenharmony_ci	this->mac_addr[1] = inb(base_addr + WL3501_NIC_IODPA);
21362306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
21462306a36Sopenharmony_ci	this->mac_addr[2] = inb(base_addr + WL3501_NIC_IODPA);
21562306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
21662306a36Sopenharmony_ci	this->mac_addr[3] = inb(base_addr + WL3501_NIC_IODPA);
21762306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
21862306a36Sopenharmony_ci	this->mac_addr[4] = inb(base_addr + WL3501_NIC_IODPA);
21962306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
22062306a36Sopenharmony_ci	this->mac_addr[5] = inb(base_addr + WL3501_NIC_IODPA);
22162306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
22262306a36Sopenharmony_ci	this->reg_domain = inb(base_addr + WL3501_NIC_IODPA);
22362306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
22462306a36Sopenharmony_ci	wl3501_outb(WL3501_BSS_FPAGE0, base_addr + WL3501_NIC_BSS);
22562306a36Sopenharmony_ci	wl3501_outb(0x04, base_addr + WL3501_NIC_LMAL);
22662306a36Sopenharmony_ci	wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH);
22762306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
22862306a36Sopenharmony_ci	this->version[0] = inb(base_addr + WL3501_NIC_IODPA);
22962306a36Sopenharmony_ci	WL3501_NOPLOOP(100);
23062306a36Sopenharmony_ci	this->version[1] = inb(base_addr + WL3501_NIC_IODPA);
23162306a36Sopenharmony_ci	/* switch to SRAM Page 0 (for safety) */
23262306a36Sopenharmony_ci	wl3501_switch_page(this, WL3501_BSS_SPAGE0);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* The MAC addr should be 00:60:... */
23562306a36Sopenharmony_ci	return this->mac_addr[0] == 0x00 && this->mac_addr[1] == 0x60;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/**
23962306a36Sopenharmony_ci * wl3501_set_to_wla - Move 'size' bytes from PC to card
24062306a36Sopenharmony_ci * @this: Card
24162306a36Sopenharmony_ci * @dest: Card addressing space
24262306a36Sopenharmony_ci * @src: PC addressing space
24362306a36Sopenharmony_ci * @size: Bytes to move
24462306a36Sopenharmony_ci *
24562306a36Sopenharmony_ci * Move 'size' bytes from PC to card. (Shouldn't be interrupted)
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src,
24862306a36Sopenharmony_ci			      int size)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	/* switch to SRAM Page 0 */
25162306a36Sopenharmony_ci	wl3501_switch_page(this, (dest & 0x8000) ? WL3501_BSS_SPAGE1 :
25262306a36Sopenharmony_ci						   WL3501_BSS_SPAGE0);
25362306a36Sopenharmony_ci	/* set LMAL and LMAH */
25462306a36Sopenharmony_ci	wl3501_outb(dest & 0xff, this->base_addr + WL3501_NIC_LMAL);
25562306a36Sopenharmony_ci	wl3501_outb(((dest >> 8) & 0x7f), this->base_addr + WL3501_NIC_LMAH);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* rep out to Port A */
25862306a36Sopenharmony_ci	wl3501_outsb(this->base_addr + WL3501_NIC_IODPA, src, size);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/**
26262306a36Sopenharmony_ci * wl3501_get_from_wla - Move 'size' bytes from card to PC
26362306a36Sopenharmony_ci * @this: Card
26462306a36Sopenharmony_ci * @src: Card addressing space
26562306a36Sopenharmony_ci * @dest: PC addressing space
26662306a36Sopenharmony_ci * @size: Bytes to move
26762306a36Sopenharmony_ci *
26862306a36Sopenharmony_ci * Move 'size' bytes from card to PC. (Shouldn't be interrupted)
26962306a36Sopenharmony_ci */
27062306a36Sopenharmony_cistatic void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,
27162306a36Sopenharmony_ci				int size)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	/* switch to SRAM Page 0 */
27462306a36Sopenharmony_ci	wl3501_switch_page(this, (src & 0x8000) ? WL3501_BSS_SPAGE1 :
27562306a36Sopenharmony_ci						  WL3501_BSS_SPAGE0);
27662306a36Sopenharmony_ci	/* set LMAL and LMAH */
27762306a36Sopenharmony_ci	wl3501_outb(src & 0xff, this->base_addr + WL3501_NIC_LMAL);
27862306a36Sopenharmony_ci	wl3501_outb((src >> 8) & 0x7f, this->base_addr + WL3501_NIC_LMAH);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* rep get from Port A */
28162306a36Sopenharmony_ci	insb(this->base_addr + WL3501_NIC_IODPA, dest, size);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci/*
28562306a36Sopenharmony_ci * Get/Allocate a free Tx Data Buffer
28662306a36Sopenharmony_ci *
28762306a36Sopenharmony_ci *  *--------------*-----------------*----------------------------------*
28862306a36Sopenharmony_ci *  |    PLCP      |    MAC Header   |  DST  SRC         Data ...       |
28962306a36Sopenharmony_ci *  |  (24 bytes)  |    (30 bytes)   |  (6)  (6)  (Ethernet Row Data)   |
29062306a36Sopenharmony_ci *  *--------------*-----------------*----------------------------------*
29162306a36Sopenharmony_ci *  \               \- IEEE 802.11 -/ \-------------- len --------------/
29262306a36Sopenharmony_ci *   \-struct wl3501_80211_tx_hdr--/   \-------- Ethernet Frame -------/
29362306a36Sopenharmony_ci *
29462306a36Sopenharmony_ci * Return = Position in Card
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_cistatic u16 wl3501_get_tx_buffer(struct wl3501_card *this, u16 len)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	u16 next, blk_cnt = 0, zero = 0;
29962306a36Sopenharmony_ci	u16 full_len = sizeof(struct wl3501_80211_tx_hdr) + len;
30062306a36Sopenharmony_ci	u16 ret = 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (full_len > this->tx_buffer_cnt * 254)
30362306a36Sopenharmony_ci		goto out;
30462306a36Sopenharmony_ci	ret = this->tx_buffer_head;
30562306a36Sopenharmony_ci	while (full_len) {
30662306a36Sopenharmony_ci		if (full_len < 254)
30762306a36Sopenharmony_ci			full_len = 0;
30862306a36Sopenharmony_ci		else
30962306a36Sopenharmony_ci			full_len -= 254;
31062306a36Sopenharmony_ci		wl3501_get_from_wla(this, this->tx_buffer_head, &next,
31162306a36Sopenharmony_ci				    sizeof(next));
31262306a36Sopenharmony_ci		if (!full_len)
31362306a36Sopenharmony_ci			wl3501_set_to_wla(this, this->tx_buffer_head, &zero,
31462306a36Sopenharmony_ci					  sizeof(zero));
31562306a36Sopenharmony_ci		this->tx_buffer_head = next;
31662306a36Sopenharmony_ci		blk_cnt++;
31762306a36Sopenharmony_ci		/* if buffer is not enough */
31862306a36Sopenharmony_ci		if (!next && full_len) {
31962306a36Sopenharmony_ci			this->tx_buffer_head = ret;
32062306a36Sopenharmony_ci			ret = 0;
32162306a36Sopenharmony_ci			goto out;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci	this->tx_buffer_cnt -= blk_cnt;
32562306a36Sopenharmony_ciout:
32662306a36Sopenharmony_ci	return ret;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/*
33062306a36Sopenharmony_ci * Free an allocated Tx Buffer. ptr must be correct position.
33162306a36Sopenharmony_ci */
33262306a36Sopenharmony_cistatic void wl3501_free_tx_buffer(struct wl3501_card *this, u16 ptr)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	/* check if all space is not free */
33562306a36Sopenharmony_ci	if (!this->tx_buffer_head)
33662306a36Sopenharmony_ci		this->tx_buffer_head = ptr;
33762306a36Sopenharmony_ci	else
33862306a36Sopenharmony_ci		wl3501_set_to_wla(this, this->tx_buffer_tail,
33962306a36Sopenharmony_ci				  &ptr, sizeof(ptr));
34062306a36Sopenharmony_ci	while (ptr) {
34162306a36Sopenharmony_ci		u16 next;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		this->tx_buffer_cnt++;
34462306a36Sopenharmony_ci		wl3501_get_from_wla(this, ptr, &next, sizeof(next));
34562306a36Sopenharmony_ci		this->tx_buffer_tail = ptr;
34662306a36Sopenharmony_ci		ptr = next;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int wl3501_esbq_req_test(struct wl3501_card *this)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	u8 tmp = 0;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	wl3501_get_from_wla(this, this->esbq_req_head + 3, &tmp, sizeof(tmp));
35562306a36Sopenharmony_ci	return tmp & 0x80;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void wl3501_esbq_req(struct wl3501_card *this, u16 *ptr)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	u16 tmp = 0;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	wl3501_set_to_wla(this, this->esbq_req_head, ptr, 2);
36362306a36Sopenharmony_ci	wl3501_set_to_wla(this, this->esbq_req_head + 2, &tmp, sizeof(tmp));
36462306a36Sopenharmony_ci	this->esbq_req_head += 4;
36562306a36Sopenharmony_ci	if (this->esbq_req_head >= this->esbq_req_end)
36662306a36Sopenharmony_ci		this->esbq_req_head = this->esbq_req_start;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int wl3501_esbq_exec(struct wl3501_card *this, void *sig, int sig_size)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	int rc = -EIO;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (wl3501_esbq_req_test(this)) {
37462306a36Sopenharmony_ci		u16 ptr = wl3501_get_tx_buffer(this, sig_size);
37562306a36Sopenharmony_ci		if (ptr) {
37662306a36Sopenharmony_ci			wl3501_set_to_wla(this, ptr, sig, sig_size);
37762306a36Sopenharmony_ci			wl3501_esbq_req(this, &ptr);
37862306a36Sopenharmony_ci			rc = 0;
37962306a36Sopenharmony_ci		}
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci	return rc;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int wl3501_request_mib(struct wl3501_card *this, u8 index, void *bf)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct wl3501_get_req sig = {
38762306a36Sopenharmony_ci		.sig_id	    = WL3501_SIG_GET_REQ,
38862306a36Sopenharmony_ci		.mib_attrib = index,
38962306a36Sopenharmony_ci	};
39062306a36Sopenharmony_ci	unsigned long flags;
39162306a36Sopenharmony_ci	int rc = -EIO;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	spin_lock_irqsave(&this->lock, flags);
39462306a36Sopenharmony_ci	if (wl3501_esbq_req_test(this)) {
39562306a36Sopenharmony_ci		u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig));
39662306a36Sopenharmony_ci		if (ptr) {
39762306a36Sopenharmony_ci			wl3501_set_to_wla(this, ptr, &sig, sizeof(sig));
39862306a36Sopenharmony_ci			wl3501_esbq_req(this, &ptr);
39962306a36Sopenharmony_ci			this->sig_get_confirm.mib_status = 255;
40062306a36Sopenharmony_ci			rc = 0;
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci	spin_unlock_irqrestore(&this->lock, flags);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return rc;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int wl3501_get_mib_value(struct wl3501_card *this, u8 index,
40962306a36Sopenharmony_ci				void *bf, int size)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	int rc;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	rc = wl3501_request_mib(this, index, bf);
41462306a36Sopenharmony_ci	if (rc)
41562306a36Sopenharmony_ci		return rc;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	rc = wait_event_interruptible(this->wait,
41862306a36Sopenharmony_ci		this->sig_get_confirm.mib_status != 255);
41962306a36Sopenharmony_ci	if (rc)
42062306a36Sopenharmony_ci		return rc;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	memcpy(bf, this->sig_get_confirm.mib_value, size);
42362306a36Sopenharmony_ci	return 0;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int wl3501_pwr_mgmt(struct wl3501_card *this, int suspend)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct wl3501_pwr_mgmt_req sig = {
42962306a36Sopenharmony_ci		.sig_id		= WL3501_SIG_PWR_MGMT_REQ,
43062306a36Sopenharmony_ci		.pwr_save	= suspend,
43162306a36Sopenharmony_ci		.wake_up	= !suspend,
43262306a36Sopenharmony_ci		.receive_dtims	= 10,
43362306a36Sopenharmony_ci	};
43462306a36Sopenharmony_ci	unsigned long flags;
43562306a36Sopenharmony_ci	int rc = -EIO;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	spin_lock_irqsave(&this->lock, flags);
43862306a36Sopenharmony_ci	if (wl3501_esbq_req_test(this)) {
43962306a36Sopenharmony_ci		u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig));
44062306a36Sopenharmony_ci		if (ptr) {
44162306a36Sopenharmony_ci			wl3501_set_to_wla(this, ptr, &sig, sizeof(sig));
44262306a36Sopenharmony_ci			wl3501_esbq_req(this, &ptr);
44362306a36Sopenharmony_ci			this->sig_pwr_mgmt_confirm.status = 255;
44462306a36Sopenharmony_ci			spin_unlock_irqrestore(&this->lock, flags);
44562306a36Sopenharmony_ci			rc = wait_event_interruptible(this->wait,
44662306a36Sopenharmony_ci				this->sig_pwr_mgmt_confirm.status != 255);
44762306a36Sopenharmony_ci			printk(KERN_INFO "%s: %s status=%d\n", __func__,
44862306a36Sopenharmony_ci			       suspend ? "suspend" : "resume",
44962306a36Sopenharmony_ci			       this->sig_pwr_mgmt_confirm.status);
45062306a36Sopenharmony_ci			goto out;
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci	spin_unlock_irqrestore(&this->lock, flags);
45462306a36Sopenharmony_ciout:
45562306a36Sopenharmony_ci	return rc;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/**
45962306a36Sopenharmony_ci * wl3501_send_pkt - Send a packet.
46062306a36Sopenharmony_ci * @this: Card
46162306a36Sopenharmony_ci * @data: Ethernet raw frame.  (e.g. data[0] - data[5] is Dest MAC Addr,
46262306a36Sopenharmony_ci *                                   data[6] - data[11] is Src MAC Addr)
46362306a36Sopenharmony_ci * @len: Packet length
46462306a36Sopenharmony_ci * Ref: IEEE 802.11
46562306a36Sopenharmony_ci */
46662306a36Sopenharmony_cistatic int wl3501_send_pkt(struct wl3501_card *this, u8 *data, u16 len)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	u16 bf, sig_bf, next, tmplen, pktlen;
46962306a36Sopenharmony_ci	struct wl3501_md_req sig = {
47062306a36Sopenharmony_ci		.sig_id = WL3501_SIG_MD_REQ,
47162306a36Sopenharmony_ci	};
47262306a36Sopenharmony_ci	size_t sig_addr_len = sizeof(sig.addr);
47362306a36Sopenharmony_ci	u8 *pdata = (char *)data;
47462306a36Sopenharmony_ci	int rc = -EIO;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (wl3501_esbq_req_test(this)) {
47762306a36Sopenharmony_ci		sig_bf = wl3501_get_tx_buffer(this, sizeof(sig));
47862306a36Sopenharmony_ci		rc = -ENOMEM;
47962306a36Sopenharmony_ci		if (!sig_bf)	/* No free buffer available */
48062306a36Sopenharmony_ci			goto out;
48162306a36Sopenharmony_ci		bf = wl3501_get_tx_buffer(this, len + 26 + 24);
48262306a36Sopenharmony_ci		if (!bf) {
48362306a36Sopenharmony_ci			/* No free buffer available */
48462306a36Sopenharmony_ci			wl3501_free_tx_buffer(this, sig_bf);
48562306a36Sopenharmony_ci			goto out;
48662306a36Sopenharmony_ci		}
48762306a36Sopenharmony_ci		rc = 0;
48862306a36Sopenharmony_ci		memcpy(&sig.addr, pdata, sig_addr_len);
48962306a36Sopenharmony_ci		pktlen = len - sig_addr_len;
49062306a36Sopenharmony_ci		pdata += sig_addr_len;
49162306a36Sopenharmony_ci		sig.data = bf;
49262306a36Sopenharmony_ci		if (((*pdata) * 256 + (*(pdata + 1))) > 1500) {
49362306a36Sopenharmony_ci			u8 addr4[ETH_ALEN] = {
49462306a36Sopenharmony_ci				[0] = 0xAA, [1] = 0xAA, [2] = 0x03, [4] = 0x00,
49562306a36Sopenharmony_ci			};
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci			wl3501_set_to_wla(this, bf + 2 +
49862306a36Sopenharmony_ci					  offsetof(struct wl3501_tx_hdr, addr4),
49962306a36Sopenharmony_ci					  addr4, sizeof(addr4));
50062306a36Sopenharmony_ci			sig.size = pktlen + 24 + 4 + 6;
50162306a36Sopenharmony_ci			if (pktlen > (254 - sizeof(struct wl3501_tx_hdr))) {
50262306a36Sopenharmony_ci				tmplen = 254 - sizeof(struct wl3501_tx_hdr);
50362306a36Sopenharmony_ci				pktlen -= tmplen;
50462306a36Sopenharmony_ci			} else {
50562306a36Sopenharmony_ci				tmplen = pktlen;
50662306a36Sopenharmony_ci				pktlen = 0;
50762306a36Sopenharmony_ci			}
50862306a36Sopenharmony_ci			wl3501_set_to_wla(this,
50962306a36Sopenharmony_ci					  bf + 2 + sizeof(struct wl3501_tx_hdr),
51062306a36Sopenharmony_ci					  pdata, tmplen);
51162306a36Sopenharmony_ci			pdata += tmplen;
51262306a36Sopenharmony_ci			wl3501_get_from_wla(this, bf, &next, sizeof(next));
51362306a36Sopenharmony_ci			bf = next;
51462306a36Sopenharmony_ci		} else {
51562306a36Sopenharmony_ci			sig.size = pktlen + 24 + 4 - 2;
51662306a36Sopenharmony_ci			pdata += 2;
51762306a36Sopenharmony_ci			pktlen -= 2;
51862306a36Sopenharmony_ci			if (pktlen > (254 - sizeof(struct wl3501_tx_hdr) + 6)) {
51962306a36Sopenharmony_ci				tmplen = 254 - sizeof(struct wl3501_tx_hdr) + 6;
52062306a36Sopenharmony_ci				pktlen -= tmplen;
52162306a36Sopenharmony_ci			} else {
52262306a36Sopenharmony_ci				tmplen = pktlen;
52362306a36Sopenharmony_ci				pktlen = 0;
52462306a36Sopenharmony_ci			}
52562306a36Sopenharmony_ci			wl3501_set_to_wla(this, bf + 2 +
52662306a36Sopenharmony_ci					  offsetof(struct wl3501_tx_hdr, addr4),
52762306a36Sopenharmony_ci					  pdata, tmplen);
52862306a36Sopenharmony_ci			pdata += tmplen;
52962306a36Sopenharmony_ci			wl3501_get_from_wla(this, bf, &next, sizeof(next));
53062306a36Sopenharmony_ci			bf = next;
53162306a36Sopenharmony_ci		}
53262306a36Sopenharmony_ci		while (pktlen > 0) {
53362306a36Sopenharmony_ci			if (pktlen > 254) {
53462306a36Sopenharmony_ci				tmplen = 254;
53562306a36Sopenharmony_ci				pktlen -= 254;
53662306a36Sopenharmony_ci			} else {
53762306a36Sopenharmony_ci				tmplen = pktlen;
53862306a36Sopenharmony_ci				pktlen = 0;
53962306a36Sopenharmony_ci			}
54062306a36Sopenharmony_ci			wl3501_set_to_wla(this, bf + 2, pdata, tmplen);
54162306a36Sopenharmony_ci			pdata += tmplen;
54262306a36Sopenharmony_ci			wl3501_get_from_wla(this, bf, &next, sizeof(next));
54362306a36Sopenharmony_ci			bf = next;
54462306a36Sopenharmony_ci		}
54562306a36Sopenharmony_ci		wl3501_set_to_wla(this, sig_bf, &sig, sizeof(sig));
54662306a36Sopenharmony_ci		wl3501_esbq_req(this, &sig_bf);
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ciout:
54962306a36Sopenharmony_ci	return rc;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic int wl3501_mgmt_resync(struct wl3501_card *this)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct wl3501_resync_req sig = {
55562306a36Sopenharmony_ci		.sig_id = WL3501_SIG_RESYNC_REQ,
55662306a36Sopenharmony_ci	};
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return wl3501_esbq_exec(this, &sig, sizeof(sig));
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic inline int wl3501_fw_bss_type(struct wl3501_card *this)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	return this->net_type == IW_MODE_INFRA ? WL3501_NET_TYPE_INFRA :
56462306a36Sopenharmony_ci						 WL3501_NET_TYPE_ADHOC;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic inline int wl3501_fw_cap_info(struct wl3501_card *this)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	return this->net_type == IW_MODE_INFRA ? WL3501_MGMT_CAPABILITY_ESS :
57062306a36Sopenharmony_ci						 WL3501_MGMT_CAPABILITY_IBSS;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int wl3501_mgmt_scan(struct wl3501_card *this, u16 chan_time)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct wl3501_scan_req sig = {
57662306a36Sopenharmony_ci		.sig_id		= WL3501_SIG_SCAN_REQ,
57762306a36Sopenharmony_ci		.scan_type	= WL3501_SCAN_TYPE_ACTIVE,
57862306a36Sopenharmony_ci		.probe_delay	= 0x10,
57962306a36Sopenharmony_ci		.min_chan_time	= chan_time,
58062306a36Sopenharmony_ci		.max_chan_time	= chan_time,
58162306a36Sopenharmony_ci		.bss_type	= wl3501_fw_bss_type(this),
58262306a36Sopenharmony_ci	};
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	this->bss_cnt = this->join_sta_bss = 0;
58562306a36Sopenharmony_ci	return wl3501_esbq_exec(this, &sig, sizeof(sig));
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int wl3501_mgmt_join(struct wl3501_card *this, u16 stas)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct wl3501_join_req sig = {
59162306a36Sopenharmony_ci		.sig_id		  = WL3501_SIG_JOIN_REQ,
59262306a36Sopenharmony_ci		.timeout	  = 10,
59362306a36Sopenharmony_ci		.req.ds_pset = {
59462306a36Sopenharmony_ci			.el = {
59562306a36Sopenharmony_ci				.id  = IW_MGMT_INFO_ELEMENT_DS_PARAMETER_SET,
59662306a36Sopenharmony_ci				.len = 1,
59762306a36Sopenharmony_ci			},
59862306a36Sopenharmony_ci			.chan	= this->chan,
59962306a36Sopenharmony_ci		},
60062306a36Sopenharmony_ci	};
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	memcpy(&sig.req, &this->bss_set[stas].req, sizeof(sig.req));
60362306a36Sopenharmony_ci	return wl3501_esbq_exec(this, &sig, sizeof(sig));
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic int wl3501_mgmt_start(struct wl3501_card *this)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct wl3501_start_req sig = {
60962306a36Sopenharmony_ci		.sig_id			= WL3501_SIG_START_REQ,
61062306a36Sopenharmony_ci		.beacon_period		= 400,
61162306a36Sopenharmony_ci		.dtim_period		= 1,
61262306a36Sopenharmony_ci		.ds_pset = {
61362306a36Sopenharmony_ci			.el = {
61462306a36Sopenharmony_ci				.id  = IW_MGMT_INFO_ELEMENT_DS_PARAMETER_SET,
61562306a36Sopenharmony_ci				.len = 1,
61662306a36Sopenharmony_ci			},
61762306a36Sopenharmony_ci			.chan	= this->chan,
61862306a36Sopenharmony_ci		},
61962306a36Sopenharmony_ci		.bss_basic_rset	= {
62062306a36Sopenharmony_ci			.el = {
62162306a36Sopenharmony_ci				.id	= IW_MGMT_INFO_ELEMENT_SUPPORTED_RATES,
62262306a36Sopenharmony_ci				.len = 2,
62362306a36Sopenharmony_ci			},
62462306a36Sopenharmony_ci			.data_rate_labels = {
62562306a36Sopenharmony_ci				[0] = IW_MGMT_RATE_LABEL_MANDATORY |
62662306a36Sopenharmony_ci				      IW_MGMT_RATE_LABEL_1MBIT,
62762306a36Sopenharmony_ci				[1] = IW_MGMT_RATE_LABEL_MANDATORY |
62862306a36Sopenharmony_ci				      IW_MGMT_RATE_LABEL_2MBIT,
62962306a36Sopenharmony_ci			},
63062306a36Sopenharmony_ci		},
63162306a36Sopenharmony_ci		.operational_rset	= {
63262306a36Sopenharmony_ci			.el = {
63362306a36Sopenharmony_ci				.id	= IW_MGMT_INFO_ELEMENT_SUPPORTED_RATES,
63462306a36Sopenharmony_ci				.len = 2,
63562306a36Sopenharmony_ci			},
63662306a36Sopenharmony_ci			.data_rate_labels = {
63762306a36Sopenharmony_ci				[0] = IW_MGMT_RATE_LABEL_MANDATORY |
63862306a36Sopenharmony_ci				      IW_MGMT_RATE_LABEL_1MBIT,
63962306a36Sopenharmony_ci				[1] = IW_MGMT_RATE_LABEL_MANDATORY |
64062306a36Sopenharmony_ci				      IW_MGMT_RATE_LABEL_2MBIT,
64162306a36Sopenharmony_ci			},
64262306a36Sopenharmony_ci		},
64362306a36Sopenharmony_ci		.ibss_pset		= {
64462306a36Sopenharmony_ci			.el = {
64562306a36Sopenharmony_ci				.id	 = IW_MGMT_INFO_ELEMENT_IBSS_PARAMETER_SET,
64662306a36Sopenharmony_ci				.len     = 2,
64762306a36Sopenharmony_ci			},
64862306a36Sopenharmony_ci			.atim_window = 10,
64962306a36Sopenharmony_ci		},
65062306a36Sopenharmony_ci		.bss_type		= wl3501_fw_bss_type(this),
65162306a36Sopenharmony_ci		.cap_info		= wl3501_fw_cap_info(this),
65262306a36Sopenharmony_ci	};
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	iw_copy_mgmt_info_element(&sig.ssid.el, &this->essid.el);
65562306a36Sopenharmony_ci	iw_copy_mgmt_info_element(&this->keep_essid.el, &this->essid.el);
65662306a36Sopenharmony_ci	return wl3501_esbq_exec(this, &sig, sizeof(sig));
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	u16 i = 0;
66262306a36Sopenharmony_ci	int matchflag = 0;
66362306a36Sopenharmony_ci	struct wl3501_scan_confirm sig;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	pr_debug("entry");
66662306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
66762306a36Sopenharmony_ci	if (sig.status == WL3501_STATUS_SUCCESS) {
66862306a36Sopenharmony_ci		pr_debug("success");
66962306a36Sopenharmony_ci		if ((this->net_type == IW_MODE_INFRA &&
67062306a36Sopenharmony_ci		     (sig.req.cap_info & WL3501_MGMT_CAPABILITY_ESS)) ||
67162306a36Sopenharmony_ci		    (this->net_type == IW_MODE_ADHOC &&
67262306a36Sopenharmony_ci		     (sig.req.cap_info & WL3501_MGMT_CAPABILITY_IBSS)) ||
67362306a36Sopenharmony_ci		    this->net_type == IW_MODE_AUTO) {
67462306a36Sopenharmony_ci			if (!this->essid.el.len)
67562306a36Sopenharmony_ci				matchflag = 1;
67662306a36Sopenharmony_ci			else if (this->essid.el.len == 3 &&
67762306a36Sopenharmony_ci				 !memcmp(this->essid.essid, "ANY", 3))
67862306a36Sopenharmony_ci				matchflag = 1;
67962306a36Sopenharmony_ci			else if (this->essid.el.len != sig.req.ssid.el.len)
68062306a36Sopenharmony_ci				matchflag = 0;
68162306a36Sopenharmony_ci			else if (memcmp(this->essid.essid, sig.req.ssid.essid,
68262306a36Sopenharmony_ci					this->essid.el.len))
68362306a36Sopenharmony_ci				matchflag = 0;
68462306a36Sopenharmony_ci			else
68562306a36Sopenharmony_ci				matchflag = 1;
68662306a36Sopenharmony_ci			if (matchflag) {
68762306a36Sopenharmony_ci				for (i = 0; i < this->bss_cnt; i++) {
68862306a36Sopenharmony_ci					if (ether_addr_equal_unaligned(this->bss_set[i].req.bssid,
68962306a36Sopenharmony_ci								       sig.req.bssid)) {
69062306a36Sopenharmony_ci						matchflag = 0;
69162306a36Sopenharmony_ci						break;
69262306a36Sopenharmony_ci					}
69362306a36Sopenharmony_ci				}
69462306a36Sopenharmony_ci			}
69562306a36Sopenharmony_ci			if (matchflag && (i < 20)) {
69662306a36Sopenharmony_ci				memcpy(&this->bss_set[i].req,
69762306a36Sopenharmony_ci				       &sig.req, sizeof(sig.req));
69862306a36Sopenharmony_ci				this->bss_cnt++;
69962306a36Sopenharmony_ci				this->rssi = sig.rssi;
70062306a36Sopenharmony_ci				this->bss_set[i].rssi = sig.rssi;
70162306a36Sopenharmony_ci			}
70262306a36Sopenharmony_ci		}
70362306a36Sopenharmony_ci	} else if (sig.status == WL3501_STATUS_TIMEOUT) {
70462306a36Sopenharmony_ci		pr_debug("timeout");
70562306a36Sopenharmony_ci		this->join_sta_bss = 0;
70662306a36Sopenharmony_ci		for (i = this->join_sta_bss; i < this->bss_cnt; i++)
70762306a36Sopenharmony_ci			if (!wl3501_mgmt_join(this, i))
70862306a36Sopenharmony_ci				break;
70962306a36Sopenharmony_ci		this->join_sta_bss = i;
71062306a36Sopenharmony_ci		if (this->join_sta_bss == this->bss_cnt) {
71162306a36Sopenharmony_ci			if (this->net_type == IW_MODE_INFRA)
71262306a36Sopenharmony_ci				wl3501_mgmt_scan(this, 100);
71362306a36Sopenharmony_ci			else {
71462306a36Sopenharmony_ci				this->adhoc_times++;
71562306a36Sopenharmony_ci				if (this->adhoc_times > WL3501_MAX_ADHOC_TRIES)
71662306a36Sopenharmony_ci					wl3501_mgmt_start(this);
71762306a36Sopenharmony_ci				else
71862306a36Sopenharmony_ci					wl3501_mgmt_scan(this, 100);
71962306a36Sopenharmony_ci			}
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci/**
72562306a36Sopenharmony_ci * wl3501_block_interrupt - Mask interrupt from SUTRO
72662306a36Sopenharmony_ci * @this: Card
72762306a36Sopenharmony_ci *
72862306a36Sopenharmony_ci * Mask interrupt from SUTRO. (i.e. SUTRO cannot interrupt the HOST)
72962306a36Sopenharmony_ci * Return: 1 if interrupt is originally enabled
73062306a36Sopenharmony_ci */
73162306a36Sopenharmony_cistatic int wl3501_block_interrupt(struct wl3501_card *this)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	u8 old = inb(this->base_addr + WL3501_NIC_GCR);
73462306a36Sopenharmony_ci	u8 new = old & (~(WL3501_GCR_ECINT | WL3501_GCR_INT2EC |
73562306a36Sopenharmony_ci			WL3501_GCR_ENECINT));
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	wl3501_outb(new, this->base_addr + WL3501_NIC_GCR);
73862306a36Sopenharmony_ci	return old & WL3501_GCR_ENECINT;
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci/**
74262306a36Sopenharmony_ci * wl3501_unblock_interrupt - Enable interrupt from SUTRO
74362306a36Sopenharmony_ci * @this: Card
74462306a36Sopenharmony_ci *
74562306a36Sopenharmony_ci * Enable interrupt from SUTRO. (i.e. SUTRO can interrupt the HOST)
74662306a36Sopenharmony_ci * Return: 1 if interrupt is originally enabled
74762306a36Sopenharmony_ci */
74862306a36Sopenharmony_cistatic int wl3501_unblock_interrupt(struct wl3501_card *this)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	u8 old = inb(this->base_addr + WL3501_NIC_GCR);
75162306a36Sopenharmony_ci	u8 new = (old & ~(WL3501_GCR_ECINT | WL3501_GCR_INT2EC)) |
75262306a36Sopenharmony_ci		  WL3501_GCR_ENECINT;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	wl3501_outb(new, this->base_addr + WL3501_NIC_GCR);
75562306a36Sopenharmony_ci	return old & WL3501_GCR_ENECINT;
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci/**
75962306a36Sopenharmony_ci * wl3501_receive - Receive data from Receive Queue.
76062306a36Sopenharmony_ci *
76162306a36Sopenharmony_ci * Receive data from Receive Queue.
76262306a36Sopenharmony_ci *
76362306a36Sopenharmony_ci * @this: card
76462306a36Sopenharmony_ci * @bf: address of host
76562306a36Sopenharmony_ci * @size: size of buffer.
76662306a36Sopenharmony_ci */
76762306a36Sopenharmony_cistatic u16 wl3501_receive(struct wl3501_card *this, u8 *bf, u16 size)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	u16 next_addr, next_addr1;
77062306a36Sopenharmony_ci	u8 *data = bf + 12;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	size -= 12;
77362306a36Sopenharmony_ci	wl3501_get_from_wla(this, this->start_seg + 2,
77462306a36Sopenharmony_ci			    &next_addr, sizeof(next_addr));
77562306a36Sopenharmony_ci	if (size > WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr)) {
77662306a36Sopenharmony_ci		wl3501_get_from_wla(this,
77762306a36Sopenharmony_ci				    this->start_seg +
77862306a36Sopenharmony_ci					sizeof(struct wl3501_rx_hdr), data,
77962306a36Sopenharmony_ci				    WL3501_BLKSZ -
78062306a36Sopenharmony_ci					sizeof(struct wl3501_rx_hdr));
78162306a36Sopenharmony_ci		size -= WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr);
78262306a36Sopenharmony_ci		data += WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr);
78362306a36Sopenharmony_ci	} else {
78462306a36Sopenharmony_ci		wl3501_get_from_wla(this,
78562306a36Sopenharmony_ci				    this->start_seg +
78662306a36Sopenharmony_ci					sizeof(struct wl3501_rx_hdr),
78762306a36Sopenharmony_ci				    data, size);
78862306a36Sopenharmony_ci		size = 0;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci	while (size > 0) {
79162306a36Sopenharmony_ci		if (size > WL3501_BLKSZ - 5) {
79262306a36Sopenharmony_ci			wl3501_get_from_wla(this, next_addr + 5, data,
79362306a36Sopenharmony_ci					    WL3501_BLKSZ - 5);
79462306a36Sopenharmony_ci			size -= WL3501_BLKSZ - 5;
79562306a36Sopenharmony_ci			data += WL3501_BLKSZ - 5;
79662306a36Sopenharmony_ci			wl3501_get_from_wla(this, next_addr + 2, &next_addr1,
79762306a36Sopenharmony_ci					    sizeof(next_addr1));
79862306a36Sopenharmony_ci			next_addr = next_addr1;
79962306a36Sopenharmony_ci		} else {
80062306a36Sopenharmony_ci			wl3501_get_from_wla(this, next_addr + 5, data, size);
80162306a36Sopenharmony_ci			size = 0;
80262306a36Sopenharmony_ci		}
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci	return 0;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic void wl3501_esbq_req_free(struct wl3501_card *this)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	u8 tmp;
81062306a36Sopenharmony_ci	u16 addr;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (this->esbq_req_head == this->esbq_req_tail)
81362306a36Sopenharmony_ci		goto out;
81462306a36Sopenharmony_ci	wl3501_get_from_wla(this, this->esbq_req_tail + 3, &tmp, sizeof(tmp));
81562306a36Sopenharmony_ci	if (!(tmp & 0x80))
81662306a36Sopenharmony_ci		goto out;
81762306a36Sopenharmony_ci	wl3501_get_from_wla(this, this->esbq_req_tail, &addr, sizeof(addr));
81862306a36Sopenharmony_ci	wl3501_free_tx_buffer(this, addr);
81962306a36Sopenharmony_ci	this->esbq_req_tail += 4;
82062306a36Sopenharmony_ci	if (this->esbq_req_tail >= this->esbq_req_end)
82162306a36Sopenharmony_ci		this->esbq_req_tail = this->esbq_req_start;
82262306a36Sopenharmony_ciout:
82362306a36Sopenharmony_ci	return;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic int wl3501_esbq_confirm(struct wl3501_card *this)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	u8 tmp;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	wl3501_get_from_wla(this, this->esbq_confirm + 3, &tmp, sizeof(tmp));
83162306a36Sopenharmony_ci	return tmp & 0x80;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic void wl3501_online(struct net_device *dev)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	printk(KERN_INFO "%s: Wireless LAN online. BSSID: %pM\n",
83962306a36Sopenharmony_ci	       dev->name, this->bssid);
84062306a36Sopenharmony_ci	netif_wake_queue(dev);
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic void wl3501_esbq_confirm_done(struct wl3501_card *this)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	u8 tmp = 0;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	wl3501_set_to_wla(this, this->esbq_confirm + 3, &tmp, sizeof(tmp));
84862306a36Sopenharmony_ci	this->esbq_confirm += 4;
84962306a36Sopenharmony_ci	if (this->esbq_confirm >= this->esbq_confirm_end)
85062306a36Sopenharmony_ci		this->esbq_confirm = this->esbq_confirm_start;
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic int wl3501_mgmt_auth(struct wl3501_card *this)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct wl3501_auth_req sig = {
85662306a36Sopenharmony_ci		.sig_id	 = WL3501_SIG_AUTH_REQ,
85762306a36Sopenharmony_ci		.type	 = WL3501_SYS_TYPE_OPEN,
85862306a36Sopenharmony_ci		.timeout = 1000,
85962306a36Sopenharmony_ci	};
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	pr_debug("entry");
86262306a36Sopenharmony_ci	memcpy(sig.mac_addr, this->bssid, ETH_ALEN);
86362306a36Sopenharmony_ci	return wl3501_esbq_exec(this, &sig, sizeof(sig));
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_cistatic int wl3501_mgmt_association(struct wl3501_card *this)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct wl3501_assoc_req sig = {
86962306a36Sopenharmony_ci		.sig_id		 = WL3501_SIG_ASSOC_REQ,
87062306a36Sopenharmony_ci		.timeout	 = 1000,
87162306a36Sopenharmony_ci		.listen_interval = 5,
87262306a36Sopenharmony_ci		.cap_info	 = this->cap_info,
87362306a36Sopenharmony_ci	};
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	pr_debug("entry");
87662306a36Sopenharmony_ci	memcpy(sig.mac_addr, this->bssid, ETH_ALEN);
87762306a36Sopenharmony_ci	return wl3501_esbq_exec(this, &sig, sizeof(sig));
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic void wl3501_mgmt_join_confirm(struct net_device *dev, u16 addr)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
88362306a36Sopenharmony_ci	struct wl3501_join_confirm sig;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	pr_debug("entry");
88662306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
88762306a36Sopenharmony_ci	if (sig.status == WL3501_STATUS_SUCCESS) {
88862306a36Sopenharmony_ci		if (this->net_type == IW_MODE_INFRA) {
88962306a36Sopenharmony_ci			if (this->join_sta_bss < this->bss_cnt) {
89062306a36Sopenharmony_ci				const int i = this->join_sta_bss;
89162306a36Sopenharmony_ci				memcpy(this->bssid,
89262306a36Sopenharmony_ci				       this->bss_set[i].req.bssid, ETH_ALEN);
89362306a36Sopenharmony_ci				this->chan = this->bss_set[i].req.ds_pset.chan;
89462306a36Sopenharmony_ci				iw_copy_mgmt_info_element(&this->keep_essid.el,
89562306a36Sopenharmony_ci						     &this->bss_set[i].req.ssid.el);
89662306a36Sopenharmony_ci				wl3501_mgmt_auth(this);
89762306a36Sopenharmony_ci			}
89862306a36Sopenharmony_ci		} else {
89962306a36Sopenharmony_ci			const int i = this->join_sta_bss;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci			memcpy(&this->bssid, &this->bss_set[i].req.bssid, ETH_ALEN);
90262306a36Sopenharmony_ci			this->chan = this->bss_set[i].req.ds_pset.chan;
90362306a36Sopenharmony_ci			iw_copy_mgmt_info_element(&this->keep_essid.el,
90462306a36Sopenharmony_ci						  &this->bss_set[i].req.ssid.el);
90562306a36Sopenharmony_ci			wl3501_online(dev);
90662306a36Sopenharmony_ci		}
90762306a36Sopenharmony_ci	} else {
90862306a36Sopenharmony_ci		int i;
90962306a36Sopenharmony_ci		this->join_sta_bss++;
91062306a36Sopenharmony_ci		for (i = this->join_sta_bss; i < this->bss_cnt; i++)
91162306a36Sopenharmony_ci			if (!wl3501_mgmt_join(this, i))
91262306a36Sopenharmony_ci				break;
91362306a36Sopenharmony_ci		this->join_sta_bss = i;
91462306a36Sopenharmony_ci		if (this->join_sta_bss == this->bss_cnt) {
91562306a36Sopenharmony_ci			if (this->net_type == IW_MODE_INFRA)
91662306a36Sopenharmony_ci				wl3501_mgmt_scan(this, 100);
91762306a36Sopenharmony_ci			else {
91862306a36Sopenharmony_ci				this->adhoc_times++;
91962306a36Sopenharmony_ci				if (this->adhoc_times > WL3501_MAX_ADHOC_TRIES)
92062306a36Sopenharmony_ci					wl3501_mgmt_start(this);
92162306a36Sopenharmony_ci				else
92262306a36Sopenharmony_ci					wl3501_mgmt_scan(this, 100);
92362306a36Sopenharmony_ci			}
92462306a36Sopenharmony_ci		}
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic inline void wl3501_alarm_interrupt(struct net_device *dev,
92962306a36Sopenharmony_ci					  struct wl3501_card *this)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	if (this->net_type == IW_MODE_INFRA) {
93262306a36Sopenharmony_ci		printk(KERN_INFO "Wireless LAN offline\n");
93362306a36Sopenharmony_ci		netif_stop_queue(dev);
93462306a36Sopenharmony_ci		wl3501_mgmt_resync(this);
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_cistatic inline void wl3501_md_confirm_interrupt(struct net_device *dev,
93962306a36Sopenharmony_ci					       struct wl3501_card *this,
94062306a36Sopenharmony_ci					       u16 addr)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	struct wl3501_md_confirm sig;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	pr_debug("entry");
94562306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
94662306a36Sopenharmony_ci	wl3501_free_tx_buffer(this, sig.data);
94762306a36Sopenharmony_ci	if (netif_queue_stopped(dev))
94862306a36Sopenharmony_ci		netif_wake_queue(dev);
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic inline void wl3501_md_ind_interrupt(struct net_device *dev,
95262306a36Sopenharmony_ci					   struct wl3501_card *this, u16 addr)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct wl3501_md_ind sig;
95562306a36Sopenharmony_ci	struct sk_buff *skb;
95662306a36Sopenharmony_ci	u8 rssi, addr4[ETH_ALEN];
95762306a36Sopenharmony_ci	u16 pkt_len;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
96062306a36Sopenharmony_ci	this->start_seg = sig.data;
96162306a36Sopenharmony_ci	wl3501_get_from_wla(this,
96262306a36Sopenharmony_ci			    sig.data + offsetof(struct wl3501_rx_hdr, rssi),
96362306a36Sopenharmony_ci			    &rssi, sizeof(rssi));
96462306a36Sopenharmony_ci	this->rssi = rssi <= 63 ? (rssi * 100) / 64 : 255;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	wl3501_get_from_wla(this,
96762306a36Sopenharmony_ci			    sig.data +
96862306a36Sopenharmony_ci				offsetof(struct wl3501_rx_hdr, addr4),
96962306a36Sopenharmony_ci			    &addr4, sizeof(addr4));
97062306a36Sopenharmony_ci	if (!(addr4[0] == 0xAA && addr4[1] == 0xAA &&
97162306a36Sopenharmony_ci	      addr4[2] == 0x03 && addr4[4] == 0x00)) {
97262306a36Sopenharmony_ci		printk(KERN_INFO "Unsupported packet type!\n");
97362306a36Sopenharmony_ci		return;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci	pkt_len = sig.size + 12 - 24 - 4 - 6;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	skb = dev_alloc_skb(pkt_len + 5);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	if (!skb) {
98062306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Can't alloc a sk_buff of size %d.\n",
98162306a36Sopenharmony_ci		       dev->name, pkt_len);
98262306a36Sopenharmony_ci		dev->stats.rx_dropped++;
98362306a36Sopenharmony_ci	} else {
98462306a36Sopenharmony_ci		skb->dev = dev;
98562306a36Sopenharmony_ci		skb_reserve(skb, 2); /* IP headers on 16 bytes boundaries */
98662306a36Sopenharmony_ci		skb_copy_to_linear_data(skb, (unsigned char *)&sig.addr,
98762306a36Sopenharmony_ci					sizeof(sig.addr));
98862306a36Sopenharmony_ci		wl3501_receive(this, skb->data, pkt_len);
98962306a36Sopenharmony_ci		skb_put(skb, pkt_len);
99062306a36Sopenharmony_ci		skb->protocol	= eth_type_trans(skb, dev);
99162306a36Sopenharmony_ci		dev->stats.rx_packets++;
99262306a36Sopenharmony_ci		dev->stats.rx_bytes += skb->len;
99362306a36Sopenharmony_ci		netif_rx(skb);
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic inline void wl3501_get_confirm_interrupt(struct wl3501_card *this,
99862306a36Sopenharmony_ci						u16 addr, void *sig, int size)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	pr_debug("entry");
100162306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr, &this->sig_get_confirm,
100262306a36Sopenharmony_ci			    sizeof(this->sig_get_confirm));
100362306a36Sopenharmony_ci	wake_up(&this->wait);
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic inline void wl3501_start_confirm_interrupt(struct net_device *dev,
100762306a36Sopenharmony_ci						  struct wl3501_card *this,
100862306a36Sopenharmony_ci						  u16 addr)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct wl3501_start_confirm sig;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	pr_debug("entry");
101362306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
101462306a36Sopenharmony_ci	if (sig.status == WL3501_STATUS_SUCCESS)
101562306a36Sopenharmony_ci		netif_wake_queue(dev);
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic inline void wl3501_assoc_confirm_interrupt(struct net_device *dev,
101962306a36Sopenharmony_ci						  u16 addr)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
102262306a36Sopenharmony_ci	struct wl3501_assoc_confirm sig;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	pr_debug("entry");
102562306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	if (sig.status == WL3501_STATUS_SUCCESS)
102862306a36Sopenharmony_ci		wl3501_online(dev);
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic inline void wl3501_auth_confirm_interrupt(struct wl3501_card *this,
103262306a36Sopenharmony_ci						 u16 addr)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	struct wl3501_auth_confirm sig;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	pr_debug("entry");
103762306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (sig.status == WL3501_STATUS_SUCCESS)
104062306a36Sopenharmony_ci		wl3501_mgmt_association(this);
104162306a36Sopenharmony_ci	else
104262306a36Sopenharmony_ci		wl3501_mgmt_resync(this);
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic inline void wl3501_rx_interrupt(struct net_device *dev)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	int morepkts;
104862306a36Sopenharmony_ci	u16 addr;
104962306a36Sopenharmony_ci	u8 sig_id;
105062306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	pr_debug("entry");
105362306a36Sopenharmony_ciloop:
105462306a36Sopenharmony_ci	morepkts = 0;
105562306a36Sopenharmony_ci	if (!wl3501_esbq_confirm(this))
105662306a36Sopenharmony_ci		goto free;
105762306a36Sopenharmony_ci	wl3501_get_from_wla(this, this->esbq_confirm, &addr, sizeof(addr));
105862306a36Sopenharmony_ci	wl3501_get_from_wla(this, addr + 2, &sig_id, sizeof(sig_id));
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	switch (sig_id) {
106162306a36Sopenharmony_ci	case WL3501_SIG_DEAUTH_IND:
106262306a36Sopenharmony_ci	case WL3501_SIG_DISASSOC_IND:
106362306a36Sopenharmony_ci	case WL3501_SIG_ALARM:
106462306a36Sopenharmony_ci		wl3501_alarm_interrupt(dev, this);
106562306a36Sopenharmony_ci		break;
106662306a36Sopenharmony_ci	case WL3501_SIG_MD_CONFIRM:
106762306a36Sopenharmony_ci		wl3501_md_confirm_interrupt(dev, this, addr);
106862306a36Sopenharmony_ci		break;
106962306a36Sopenharmony_ci	case WL3501_SIG_MD_IND:
107062306a36Sopenharmony_ci		wl3501_md_ind_interrupt(dev, this, addr);
107162306a36Sopenharmony_ci		break;
107262306a36Sopenharmony_ci	case WL3501_SIG_GET_CONFIRM:
107362306a36Sopenharmony_ci		wl3501_get_confirm_interrupt(this, addr,
107462306a36Sopenharmony_ci					     &this->sig_get_confirm,
107562306a36Sopenharmony_ci					     sizeof(this->sig_get_confirm));
107662306a36Sopenharmony_ci		break;
107762306a36Sopenharmony_ci	case WL3501_SIG_PWR_MGMT_CONFIRM:
107862306a36Sopenharmony_ci		wl3501_get_confirm_interrupt(this, addr,
107962306a36Sopenharmony_ci					     &this->sig_pwr_mgmt_confirm,
108062306a36Sopenharmony_ci					    sizeof(this->sig_pwr_mgmt_confirm));
108162306a36Sopenharmony_ci		break;
108262306a36Sopenharmony_ci	case WL3501_SIG_START_CONFIRM:
108362306a36Sopenharmony_ci		wl3501_start_confirm_interrupt(dev, this, addr);
108462306a36Sopenharmony_ci		break;
108562306a36Sopenharmony_ci	case WL3501_SIG_SCAN_CONFIRM:
108662306a36Sopenharmony_ci		wl3501_mgmt_scan_confirm(this, addr);
108762306a36Sopenharmony_ci		break;
108862306a36Sopenharmony_ci	case WL3501_SIG_JOIN_CONFIRM:
108962306a36Sopenharmony_ci		wl3501_mgmt_join_confirm(dev, addr);
109062306a36Sopenharmony_ci		break;
109162306a36Sopenharmony_ci	case WL3501_SIG_ASSOC_CONFIRM:
109262306a36Sopenharmony_ci		wl3501_assoc_confirm_interrupt(dev, addr);
109362306a36Sopenharmony_ci		break;
109462306a36Sopenharmony_ci	case WL3501_SIG_AUTH_CONFIRM:
109562306a36Sopenharmony_ci		wl3501_auth_confirm_interrupt(this, addr);
109662306a36Sopenharmony_ci		break;
109762306a36Sopenharmony_ci	case WL3501_SIG_RESYNC_CONFIRM:
109862306a36Sopenharmony_ci		wl3501_mgmt_resync(this); /* FIXME: should be resync_confirm */
109962306a36Sopenharmony_ci		break;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci	wl3501_esbq_confirm_done(this);
110262306a36Sopenharmony_ci	morepkts = 1;
110362306a36Sopenharmony_ci	/* free request if necessary */
110462306a36Sopenharmony_cifree:
110562306a36Sopenharmony_ci	wl3501_esbq_req_free(this);
110662306a36Sopenharmony_ci	if (morepkts)
110762306a36Sopenharmony_ci		goto loop;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic inline void wl3501_ack_interrupt(struct wl3501_card *this)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	wl3501_outb(WL3501_GCR_ECINT, this->base_addr + WL3501_NIC_GCR);
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci/**
111662306a36Sopenharmony_ci * wl3501_interrupt - Hardware interrupt from card.
111762306a36Sopenharmony_ci * @irq: Interrupt number
111862306a36Sopenharmony_ci * @dev_id: net_device
111962306a36Sopenharmony_ci *
112062306a36Sopenharmony_ci * We must acknowledge the interrupt as soon as possible, and block the
112162306a36Sopenharmony_ci * interrupt from the same card immediately to prevent re-entry.
112262306a36Sopenharmony_ci *
112362306a36Sopenharmony_ci * Before accessing the Control_Status_Block, we must lock SUTRO first.
112462306a36Sopenharmony_ci * On the other hand, to prevent SUTRO from malfunctioning, we must
112562306a36Sopenharmony_ci * unlock the SUTRO as soon as possible.
112662306a36Sopenharmony_ci */
112762306a36Sopenharmony_cistatic irqreturn_t wl3501_interrupt(int irq, void *dev_id)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	struct net_device *dev = dev_id;
113062306a36Sopenharmony_ci	struct wl3501_card *this;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	this = netdev_priv(dev);
113362306a36Sopenharmony_ci	spin_lock(&this->lock);
113462306a36Sopenharmony_ci	wl3501_ack_interrupt(this);
113562306a36Sopenharmony_ci	wl3501_block_interrupt(this);
113662306a36Sopenharmony_ci	wl3501_rx_interrupt(dev);
113762306a36Sopenharmony_ci	wl3501_unblock_interrupt(this);
113862306a36Sopenharmony_ci	spin_unlock(&this->lock);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	return IRQ_HANDLED;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic int wl3501_reset_board(struct wl3501_card *this)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	u8 tmp = 0;
114662306a36Sopenharmony_ci	int i, rc = 0;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	/* Coreset */
114962306a36Sopenharmony_ci	wl3501_outb_p(WL3501_GCR_CORESET, this->base_addr + WL3501_NIC_GCR);
115062306a36Sopenharmony_ci	wl3501_outb_p(0, this->base_addr + WL3501_NIC_GCR);
115162306a36Sopenharmony_ci	wl3501_outb_p(WL3501_GCR_CORESET, this->base_addr + WL3501_NIC_GCR);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	/* Reset SRAM 0x480 to zero */
115462306a36Sopenharmony_ci	wl3501_set_to_wla(this, 0x480, &tmp, sizeof(tmp));
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	/* Start up */
115762306a36Sopenharmony_ci	wl3501_outb_p(0, this->base_addr + WL3501_NIC_GCR);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	WL3501_NOPLOOP(1024 * 50);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	wl3501_unblock_interrupt(this);	/* acme: was commented */
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/* Polling Self_Test_Status */
116462306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
116562306a36Sopenharmony_ci		wl3501_get_from_wla(this, 0x480, &tmp, sizeof(tmp));
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci		if (tmp == 'W') {
116862306a36Sopenharmony_ci			/* firmware complete all test successfully */
116962306a36Sopenharmony_ci			tmp = 'A';
117062306a36Sopenharmony_ci			wl3501_set_to_wla(this, 0x480, &tmp, sizeof(tmp));
117162306a36Sopenharmony_ci			goto out;
117262306a36Sopenharmony_ci		}
117362306a36Sopenharmony_ci		WL3501_NOPLOOP(10);
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci	printk(KERN_WARNING "%s: failed to reset the board!\n", __func__);
117662306a36Sopenharmony_ci	rc = -ENODEV;
117762306a36Sopenharmony_ciout:
117862306a36Sopenharmony_ci	return rc;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic int wl3501_init_firmware(struct wl3501_card *this)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	u16 ptr, next;
118462306a36Sopenharmony_ci	int rc = wl3501_reset_board(this);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	if (rc)
118762306a36Sopenharmony_ci		goto fail;
118862306a36Sopenharmony_ci	this->card_name[0] = '\0';
118962306a36Sopenharmony_ci	wl3501_get_from_wla(this, 0x1a00,
119062306a36Sopenharmony_ci			    this->card_name, sizeof(this->card_name));
119162306a36Sopenharmony_ci	this->card_name[sizeof(this->card_name) - 1] = '\0';
119262306a36Sopenharmony_ci	this->firmware_date[0] = '\0';
119362306a36Sopenharmony_ci	wl3501_get_from_wla(this, 0x1a40,
119462306a36Sopenharmony_ci			    this->firmware_date, sizeof(this->firmware_date));
119562306a36Sopenharmony_ci	this->firmware_date[sizeof(this->firmware_date) - 1] = '\0';
119662306a36Sopenharmony_ci	/* Switch to SRAM Page 0 */
119762306a36Sopenharmony_ci	wl3501_switch_page(this, WL3501_BSS_SPAGE0);
119862306a36Sopenharmony_ci	/* Read parameter from card */
119962306a36Sopenharmony_ci	wl3501_get_from_wla(this, 0x482, &this->esbq_req_start, 2);
120062306a36Sopenharmony_ci	wl3501_get_from_wla(this, 0x486, &this->esbq_req_end, 2);
120162306a36Sopenharmony_ci	wl3501_get_from_wla(this, 0x488, &this->esbq_confirm_start, 2);
120262306a36Sopenharmony_ci	wl3501_get_from_wla(this, 0x48c, &this->esbq_confirm_end, 2);
120362306a36Sopenharmony_ci	wl3501_get_from_wla(this, 0x48e, &this->tx_buffer_head, 2);
120462306a36Sopenharmony_ci	wl3501_get_from_wla(this, 0x492, &this->tx_buffer_size, 2);
120562306a36Sopenharmony_ci	this->esbq_req_tail	= this->esbq_req_head = this->esbq_req_start;
120662306a36Sopenharmony_ci	this->esbq_req_end     += this->esbq_req_start;
120762306a36Sopenharmony_ci	this->esbq_confirm	= this->esbq_confirm_start;
120862306a36Sopenharmony_ci	this->esbq_confirm_end += this->esbq_confirm_start;
120962306a36Sopenharmony_ci	/* Initial Tx Buffer */
121062306a36Sopenharmony_ci	this->tx_buffer_cnt = 1;
121162306a36Sopenharmony_ci	ptr = this->tx_buffer_head;
121262306a36Sopenharmony_ci	next = ptr + WL3501_BLKSZ;
121362306a36Sopenharmony_ci	while ((next - this->tx_buffer_head) < this->tx_buffer_size) {
121462306a36Sopenharmony_ci		this->tx_buffer_cnt++;
121562306a36Sopenharmony_ci		wl3501_set_to_wla(this, ptr, &next, sizeof(next));
121662306a36Sopenharmony_ci		ptr = next;
121762306a36Sopenharmony_ci		next = ptr + WL3501_BLKSZ;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci	rc = 0;
122062306a36Sopenharmony_ci	next = 0;
122162306a36Sopenharmony_ci	wl3501_set_to_wla(this, ptr, &next, sizeof(next));
122262306a36Sopenharmony_ci	this->tx_buffer_tail = ptr;
122362306a36Sopenharmony_ciout:
122462306a36Sopenharmony_ci	return rc;
122562306a36Sopenharmony_cifail:
122662306a36Sopenharmony_ci	printk(KERN_WARNING "%s: failed!\n", __func__);
122762306a36Sopenharmony_ci	goto out;
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic int wl3501_close(struct net_device *dev)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
123362306a36Sopenharmony_ci	unsigned long flags;
123462306a36Sopenharmony_ci	struct pcmcia_device *link;
123562306a36Sopenharmony_ci	link = this->p_dev;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	spin_lock_irqsave(&this->lock, flags);
123862306a36Sopenharmony_ci	link->open--;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	/* Stop wl3501_hard_start_xmit() from now on */
124162306a36Sopenharmony_ci	netif_stop_queue(dev);
124262306a36Sopenharmony_ci	wl3501_ack_interrupt(this);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	/* Mask interrupts from the SUTRO */
124562306a36Sopenharmony_ci	wl3501_block_interrupt(this);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	printk(KERN_INFO "%s: WL3501 closed\n", dev->name);
124862306a36Sopenharmony_ci	spin_unlock_irqrestore(&this->lock, flags);
124962306a36Sopenharmony_ci	return 0;
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci/**
125362306a36Sopenharmony_ci * wl3501_reset - Reset the SUTRO.
125462306a36Sopenharmony_ci * @dev: network device
125562306a36Sopenharmony_ci *
125662306a36Sopenharmony_ci * It is almost the same as wl3501_open(). In fact, we may just wl3501_close()
125762306a36Sopenharmony_ci * and wl3501_open() again, but I wouldn't like to free_irq() when the driver
125862306a36Sopenharmony_ci * is running. It seems to be dangerous.
125962306a36Sopenharmony_ci */
126062306a36Sopenharmony_cistatic int wl3501_reset(struct net_device *dev)
126162306a36Sopenharmony_ci{
126262306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
126362306a36Sopenharmony_ci	int rc = -ENODEV;
126462306a36Sopenharmony_ci	unsigned long flags;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	spin_lock_irqsave(&this->lock, flags);
126762306a36Sopenharmony_ci	wl3501_block_interrupt(this);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (wl3501_init_firmware(this)) {
127062306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Can't initialize Firmware!\n",
127162306a36Sopenharmony_ci		       dev->name);
127262306a36Sopenharmony_ci		/* Free IRQ, and mark IRQ as unused */
127362306a36Sopenharmony_ci		free_irq(dev->irq, dev);
127462306a36Sopenharmony_ci		goto out;
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	/*
127862306a36Sopenharmony_ci	 * Queue has to be started only when the Card is Started
127962306a36Sopenharmony_ci	 */
128062306a36Sopenharmony_ci	netif_stop_queue(dev);
128162306a36Sopenharmony_ci	this->adhoc_times = 0;
128262306a36Sopenharmony_ci	wl3501_ack_interrupt(this);
128362306a36Sopenharmony_ci	wl3501_unblock_interrupt(this);
128462306a36Sopenharmony_ci	wl3501_mgmt_scan(this, 100);
128562306a36Sopenharmony_ci	pr_debug("%s: device reset", dev->name);
128662306a36Sopenharmony_ci	rc = 0;
128762306a36Sopenharmony_ciout:
128862306a36Sopenharmony_ci	spin_unlock_irqrestore(&this->lock, flags);
128962306a36Sopenharmony_ci	return rc;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cistatic void wl3501_tx_timeout(struct net_device *dev, unsigned int txqueue)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
129562306a36Sopenharmony_ci	int rc;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	stats->tx_errors++;
129862306a36Sopenharmony_ci	rc = wl3501_reset(dev);
129962306a36Sopenharmony_ci	if (rc)
130062306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n",
130162306a36Sopenharmony_ci		       dev->name, rc);
130262306a36Sopenharmony_ci	else {
130362306a36Sopenharmony_ci		netif_trans_update(dev); /* prevent tx timeout */
130462306a36Sopenharmony_ci		netif_wake_queue(dev);
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci/*
130962306a36Sopenharmony_ci * Return : 0 - OK
131062306a36Sopenharmony_ci *	    1 - Could not transmit (dev_queue_xmit will queue it)
131162306a36Sopenharmony_ci *		and try to sent it later
131262306a36Sopenharmony_ci */
131362306a36Sopenharmony_cistatic netdev_tx_t wl3501_hard_start_xmit(struct sk_buff *skb,
131462306a36Sopenharmony_ci						struct net_device *dev)
131562306a36Sopenharmony_ci{
131662306a36Sopenharmony_ci	int enabled, rc;
131762306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
131862306a36Sopenharmony_ci	unsigned long flags;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	spin_lock_irqsave(&this->lock, flags);
132162306a36Sopenharmony_ci	enabled = wl3501_block_interrupt(this);
132262306a36Sopenharmony_ci	rc = wl3501_send_pkt(this, skb->data, skb->len);
132362306a36Sopenharmony_ci	if (enabled)
132462306a36Sopenharmony_ci		wl3501_unblock_interrupt(this);
132562306a36Sopenharmony_ci	if (rc) {
132662306a36Sopenharmony_ci		++dev->stats.tx_dropped;
132762306a36Sopenharmony_ci		netif_stop_queue(dev);
132862306a36Sopenharmony_ci	} else {
132962306a36Sopenharmony_ci		++dev->stats.tx_packets;
133062306a36Sopenharmony_ci		dev->stats.tx_bytes += skb->len;
133162306a36Sopenharmony_ci		dev_kfree_skb_irq(skb);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci		if (this->tx_buffer_cnt < 2)
133462306a36Sopenharmony_ci			netif_stop_queue(dev);
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci	spin_unlock_irqrestore(&this->lock, flags);
133762306a36Sopenharmony_ci	return NETDEV_TX_OK;
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_cistatic int wl3501_open(struct net_device *dev)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	int rc = -ENODEV;
134362306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
134462306a36Sopenharmony_ci	unsigned long flags;
134562306a36Sopenharmony_ci	struct pcmcia_device *link;
134662306a36Sopenharmony_ci	link = this->p_dev;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	spin_lock_irqsave(&this->lock, flags);
134962306a36Sopenharmony_ci	if (!pcmcia_dev_present(link))
135062306a36Sopenharmony_ci		goto out;
135162306a36Sopenharmony_ci	netif_device_attach(dev);
135262306a36Sopenharmony_ci	link->open++;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/* Initial WL3501 firmware */
135562306a36Sopenharmony_ci	pr_debug("%s: Initialize WL3501 firmware...", dev->name);
135662306a36Sopenharmony_ci	if (wl3501_init_firmware(this))
135762306a36Sopenharmony_ci		goto fail;
135862306a36Sopenharmony_ci	/* Initial device variables */
135962306a36Sopenharmony_ci	this->adhoc_times = 0;
136062306a36Sopenharmony_ci	/* Acknowledge Interrupt, for cleaning last state */
136162306a36Sopenharmony_ci	wl3501_ack_interrupt(this);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	/* Enable interrupt from card after all */
136462306a36Sopenharmony_ci	wl3501_unblock_interrupt(this);
136562306a36Sopenharmony_ci	wl3501_mgmt_scan(this, 100);
136662306a36Sopenharmony_ci	rc = 0;
136762306a36Sopenharmony_ci	pr_debug("%s: WL3501 opened", dev->name);
136862306a36Sopenharmony_ci	printk(KERN_INFO "%s: Card Name: %s\n"
136962306a36Sopenharmony_ci			 "%s: Firmware Date: %s\n",
137062306a36Sopenharmony_ci			 dev->name, this->card_name,
137162306a36Sopenharmony_ci			 dev->name, this->firmware_date);
137262306a36Sopenharmony_ciout:
137362306a36Sopenharmony_ci	spin_unlock_irqrestore(&this->lock, flags);
137462306a36Sopenharmony_ci	return rc;
137562306a36Sopenharmony_cifail:
137662306a36Sopenharmony_ci	printk(KERN_WARNING "%s: Can't initialize firmware!\n", dev->name);
137762306a36Sopenharmony_ci	goto out;
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
138362306a36Sopenharmony_ci	struct iw_statistics *wstats = &this->wstats;
138462306a36Sopenharmony_ci	u32 value; /* size checked: it is u32 */
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	memset(wstats, 0, sizeof(*wstats));
138762306a36Sopenharmony_ci	wstats->status = netif_running(dev);
138862306a36Sopenharmony_ci	if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_ICV_ERROR_COUNT,
138962306a36Sopenharmony_ci				  &value, sizeof(value)))
139062306a36Sopenharmony_ci		wstats->discard.code += value;
139162306a36Sopenharmony_ci	if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_UNDECRYPTABLE_COUNT,
139262306a36Sopenharmony_ci				  &value, sizeof(value)))
139362306a36Sopenharmony_ci		wstats->discard.code += value;
139462306a36Sopenharmony_ci	if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_EXCLUDED_COUNT,
139562306a36Sopenharmony_ci				  &value, sizeof(value)))
139662306a36Sopenharmony_ci		wstats->discard.code += value;
139762306a36Sopenharmony_ci	if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_RETRY_COUNT,
139862306a36Sopenharmony_ci				  &value, sizeof(value)))
139962306a36Sopenharmony_ci		wstats->discard.retries	= value;
140062306a36Sopenharmony_ci	if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_FAILED_COUNT,
140162306a36Sopenharmony_ci				  &value, sizeof(value)))
140262306a36Sopenharmony_ci		wstats->discard.misc += value;
140362306a36Sopenharmony_ci	if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_RTS_FAILURE_COUNT,
140462306a36Sopenharmony_ci				  &value, sizeof(value)))
140562306a36Sopenharmony_ci		wstats->discard.misc += value;
140662306a36Sopenharmony_ci	if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_ACK_FAILURE_COUNT,
140762306a36Sopenharmony_ci				  &value, sizeof(value)))
140862306a36Sopenharmony_ci		wstats->discard.misc += value;
140962306a36Sopenharmony_ci	if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_FRAME_DUPLICATE_COUNT,
141062306a36Sopenharmony_ci				  &value, sizeof(value)))
141162306a36Sopenharmony_ci		wstats->discard.misc += value;
141262306a36Sopenharmony_ci	return wstats;
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci/**
141662306a36Sopenharmony_ci * wl3501_detach - deletes a driver "instance"
141762306a36Sopenharmony_ci * @link: FILL_IN
141862306a36Sopenharmony_ci *
141962306a36Sopenharmony_ci * This deletes a driver "instance". The device is de-registered with Card
142062306a36Sopenharmony_ci * Services. If it has been released, all local data structures are freed.
142162306a36Sopenharmony_ci * Otherwise, the structures will be freed when the device is released.
142262306a36Sopenharmony_ci */
142362306a36Sopenharmony_cistatic void wl3501_detach(struct pcmcia_device *link)
142462306a36Sopenharmony_ci{
142562306a36Sopenharmony_ci	struct net_device *dev = link->priv;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	/* If the device is currently configured and active, we won't actually
142862306a36Sopenharmony_ci	 * delete it yet.  Instead, it is marked so that when the release()
142962306a36Sopenharmony_ci	 * function is called, that will trigger a proper detach(). */
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	while (link->open > 0)
143262306a36Sopenharmony_ci		wl3501_close(dev);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	netif_device_detach(dev);
143562306a36Sopenharmony_ci	wl3501_release(link);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	unregister_netdev(dev);
143862306a36Sopenharmony_ci	free_netdev(dev);
143962306a36Sopenharmony_ci}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_cistatic int wl3501_get_name(struct net_device *dev, struct iw_request_info *info,
144262306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
144362306a36Sopenharmony_ci{
144462306a36Sopenharmony_ci	strscpy(wrqu->name, "IEEE 802.11-DS", sizeof(wrqu->name));
144562306a36Sopenharmony_ci	return 0;
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_cistatic int wl3501_set_freq(struct net_device *dev, struct iw_request_info *info,
144962306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
145262306a36Sopenharmony_ci	int channel = wrqu->freq.m;
145362306a36Sopenharmony_ci	int rc = -EINVAL;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (iw_valid_channel(this->reg_domain, channel)) {
145662306a36Sopenharmony_ci		this->chan = channel;
145762306a36Sopenharmony_ci		rc = wl3501_reset(dev);
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci	return rc;
146062306a36Sopenharmony_ci}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cistatic int wl3501_get_freq(struct net_device *dev, struct iw_request_info *info,
146362306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
146462306a36Sopenharmony_ci{
146562306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	wrqu->freq.m = 100000 *
146862306a36Sopenharmony_ci		ieee80211_channel_to_frequency(this->chan, NL80211_BAND_2GHZ);
146962306a36Sopenharmony_ci	wrqu->freq.e = 1;
147062306a36Sopenharmony_ci	return 0;
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic int wl3501_set_mode(struct net_device *dev, struct iw_request_info *info,
147462306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	int rc = -EINVAL;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	if (wrqu->mode == IW_MODE_INFRA ||
147962306a36Sopenharmony_ci	    wrqu->mode == IW_MODE_ADHOC ||
148062306a36Sopenharmony_ci	    wrqu->mode == IW_MODE_AUTO) {
148162306a36Sopenharmony_ci		struct wl3501_card *this = netdev_priv(dev);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci		this->net_type = wrqu->mode;
148462306a36Sopenharmony_ci		rc = wl3501_reset(dev);
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci	return rc;
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic int wl3501_get_mode(struct net_device *dev, struct iw_request_info *info,
149062306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
149162306a36Sopenharmony_ci{
149262306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	wrqu->mode = this->net_type;
149562306a36Sopenharmony_ci	return 0;
149662306a36Sopenharmony_ci}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistatic int wl3501_get_sens(struct net_device *dev, struct iw_request_info *info,
149962306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	wrqu->sens.value = this->rssi;
150462306a36Sopenharmony_ci	wrqu->sens.disabled = !wrqu->sens.value;
150562306a36Sopenharmony_ci	wrqu->sens.fixed = 1;
150662306a36Sopenharmony_ci	return 0;
150762306a36Sopenharmony_ci}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_cistatic int wl3501_get_range(struct net_device *dev,
151062306a36Sopenharmony_ci			    struct iw_request_info *info,
151162306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *extra)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	struct iw_range *range = (struct iw_range *)extra;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	/* Set the length (very important for backward compatibility) */
151662306a36Sopenharmony_ci	wrqu->data.length = sizeof(*range);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	/* Set all the info we don't care or don't know about to zero */
151962306a36Sopenharmony_ci	memset(range, 0, sizeof(*range));
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	/* Set the Wireless Extension versions */
152262306a36Sopenharmony_ci	range->we_version_compiled	= WIRELESS_EXT;
152362306a36Sopenharmony_ci	range->we_version_source	= 1;
152462306a36Sopenharmony_ci	range->throughput		= 2 * 1000 * 1000;     /* ~2 Mb/s */
152562306a36Sopenharmony_ci	/* FIXME: study the code to fill in more fields... */
152662306a36Sopenharmony_ci	return 0;
152762306a36Sopenharmony_ci}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_cistatic int wl3501_set_wap(struct net_device *dev, struct iw_request_info *info,
153062306a36Sopenharmony_ci			  union iwreq_data *wrqu, char *extra)
153162306a36Sopenharmony_ci{
153262306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
153362306a36Sopenharmony_ci	int rc = -EINVAL;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	/* FIXME: we support other ARPHRDs...*/
153662306a36Sopenharmony_ci	if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
153762306a36Sopenharmony_ci		goto out;
153862306a36Sopenharmony_ci	if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data)) {
153962306a36Sopenharmony_ci		/* FIXME: rescan? */
154062306a36Sopenharmony_ci	} else
154162306a36Sopenharmony_ci		memcpy(this->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
154262306a36Sopenharmony_ci		/* FIXME: rescan? deassoc & scan? */
154362306a36Sopenharmony_ci	rc = 0;
154462306a36Sopenharmony_ciout:
154562306a36Sopenharmony_ci	return rc;
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cistatic int wl3501_get_wap(struct net_device *dev, struct iw_request_info *info,
154962306a36Sopenharmony_ci			  union iwreq_data *wrqu, char *extra)
155062306a36Sopenharmony_ci{
155162306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	wrqu->ap_addr.sa_family = ARPHRD_ETHER;
155462306a36Sopenharmony_ci	memcpy(wrqu->ap_addr.sa_data, this->bssid, ETH_ALEN);
155562306a36Sopenharmony_ci	return 0;
155662306a36Sopenharmony_ci}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_cistatic int wl3501_set_scan(struct net_device *dev, struct iw_request_info *info,
155962306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
156062306a36Sopenharmony_ci{
156162306a36Sopenharmony_ci	/*
156262306a36Sopenharmony_ci	 * FIXME: trigger scanning with a reset, yes, I'm lazy
156362306a36Sopenharmony_ci	 */
156462306a36Sopenharmony_ci	return wl3501_reset(dev);
156562306a36Sopenharmony_ci}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_cistatic int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info,
156862306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
157162306a36Sopenharmony_ci	int i;
157262306a36Sopenharmony_ci	char *current_ev = extra;
157362306a36Sopenharmony_ci	struct iw_event iwe;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	for (i = 0; i < this->bss_cnt; ++i) {
157662306a36Sopenharmony_ci		iwe.cmd			= SIOCGIWAP;
157762306a36Sopenharmony_ci		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
157862306a36Sopenharmony_ci		memcpy(iwe.u.ap_addr.sa_data, this->bss_set[i].req.bssid, ETH_ALEN);
157962306a36Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev,
158062306a36Sopenharmony_ci						  extra + IW_SCAN_MAX_DATA,
158162306a36Sopenharmony_ci						  &iwe, IW_EV_ADDR_LEN);
158262306a36Sopenharmony_ci		iwe.cmd		  = SIOCGIWESSID;
158362306a36Sopenharmony_ci		iwe.u.data.flags  = 1;
158462306a36Sopenharmony_ci		iwe.u.data.length = this->bss_set[i].req.ssid.el.len;
158562306a36Sopenharmony_ci		current_ev = iwe_stream_add_point(info, current_ev,
158662306a36Sopenharmony_ci						  extra + IW_SCAN_MAX_DATA,
158762306a36Sopenharmony_ci						  &iwe,
158862306a36Sopenharmony_ci						  this->bss_set[i].req.ssid.essid);
158962306a36Sopenharmony_ci		iwe.cmd	   = SIOCGIWMODE;
159062306a36Sopenharmony_ci		iwe.u.mode = this->bss_set[i].req.bss_type;
159162306a36Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev,
159262306a36Sopenharmony_ci						  extra + IW_SCAN_MAX_DATA,
159362306a36Sopenharmony_ci						  &iwe, IW_EV_UINT_LEN);
159462306a36Sopenharmony_ci		iwe.cmd = SIOCGIWFREQ;
159562306a36Sopenharmony_ci		iwe.u.freq.m = this->bss_set[i].req.ds_pset.chan;
159662306a36Sopenharmony_ci		iwe.u.freq.e = 0;
159762306a36Sopenharmony_ci		current_ev = iwe_stream_add_event(info, current_ev,
159862306a36Sopenharmony_ci						  extra + IW_SCAN_MAX_DATA,
159962306a36Sopenharmony_ci						  &iwe, IW_EV_FREQ_LEN);
160062306a36Sopenharmony_ci		iwe.cmd = SIOCGIWENCODE;
160162306a36Sopenharmony_ci		if (this->bss_set[i].req.cap_info & WL3501_MGMT_CAPABILITY_PRIVACY)
160262306a36Sopenharmony_ci			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
160362306a36Sopenharmony_ci		else
160462306a36Sopenharmony_ci			iwe.u.data.flags = IW_ENCODE_DISABLED;
160562306a36Sopenharmony_ci		iwe.u.data.length = 0;
160662306a36Sopenharmony_ci		current_ev = iwe_stream_add_point(info, current_ev,
160762306a36Sopenharmony_ci						  extra + IW_SCAN_MAX_DATA,
160862306a36Sopenharmony_ci						  &iwe, NULL);
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci	/* Length of data */
161162306a36Sopenharmony_ci	wrqu->data.length = (current_ev - extra);
161262306a36Sopenharmony_ci	wrqu->data.flags = 0; /* FIXME: set properly these flags */
161362306a36Sopenharmony_ci	return 0;
161462306a36Sopenharmony_ci}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_cistatic int wl3501_set_essid(struct net_device *dev,
161762306a36Sopenharmony_ci			    struct iw_request_info *info,
161862306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *extra)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	if (wrqu->data.flags) {
162362306a36Sopenharmony_ci		iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID,
162462306a36Sopenharmony_ci					 &this->essid.el,
162562306a36Sopenharmony_ci					 extra, wrqu->data.length);
162662306a36Sopenharmony_ci	} else { /* We accept any ESSID */
162762306a36Sopenharmony_ci		iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID,
162862306a36Sopenharmony_ci					 &this->essid.el, "ANY", 3);
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci	return wl3501_reset(dev);
163162306a36Sopenharmony_ci}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_cistatic int wl3501_get_essid(struct net_device *dev,
163462306a36Sopenharmony_ci			    struct iw_request_info *info,
163562306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *extra)
163662306a36Sopenharmony_ci{
163762306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
163862306a36Sopenharmony_ci	unsigned long flags;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	spin_lock_irqsave(&this->lock, flags);
164162306a36Sopenharmony_ci	wrqu->essid.flags  = 1;
164262306a36Sopenharmony_ci	wrqu->essid.length = this->essid.el.len;
164362306a36Sopenharmony_ci	memcpy(extra, this->essid.essid, this->essid.el.len);
164462306a36Sopenharmony_ci	spin_unlock_irqrestore(&this->lock, flags);
164562306a36Sopenharmony_ci	return 0;
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic int wl3501_set_nick(struct net_device *dev, struct iw_request_info *info,
164962306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
165062306a36Sopenharmony_ci{
165162306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	if (wrqu->data.length > sizeof(this->nick))
165462306a36Sopenharmony_ci		return -E2BIG;
165562306a36Sopenharmony_ci	strscpy(this->nick, extra, wrqu->data.length);
165662306a36Sopenharmony_ci	return 0;
165762306a36Sopenharmony_ci}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_cistatic int wl3501_get_nick(struct net_device *dev, struct iw_request_info *info,
166062306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	strscpy(extra, this->nick, 32);
166562306a36Sopenharmony_ci	wrqu->data.length = strlen(extra);
166662306a36Sopenharmony_ci	return 0;
166762306a36Sopenharmony_ci}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic int wl3501_get_rate(struct net_device *dev, struct iw_request_info *info,
167062306a36Sopenharmony_ci			   union iwreq_data *wrqu, char *extra)
167162306a36Sopenharmony_ci{
167262306a36Sopenharmony_ci	/*
167362306a36Sopenharmony_ci	 * FIXME: have to see from where to get this info, perhaps this card
167462306a36Sopenharmony_ci	 * works at 1 Mbit/s too... for now leave at 2 Mbit/s that is the most
167562306a36Sopenharmony_ci	 * common with the Planet Access Points. -acme
167662306a36Sopenharmony_ci	 */
167762306a36Sopenharmony_ci	wrqu->bitrate.value = 2000000;
167862306a36Sopenharmony_ci	wrqu->bitrate.fixed = 1;
167962306a36Sopenharmony_ci	return 0;
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_cistatic int wl3501_get_rts_threshold(struct net_device *dev,
168362306a36Sopenharmony_ci				    struct iw_request_info *info,
168462306a36Sopenharmony_ci				    union iwreq_data *wrqu, char *extra)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	u16 threshold; /* size checked: it is u16 */
168762306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
168862306a36Sopenharmony_ci	int rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_RTS_THRESHOLD,
168962306a36Sopenharmony_ci				      &threshold, sizeof(threshold));
169062306a36Sopenharmony_ci	if (!rc) {
169162306a36Sopenharmony_ci		wrqu->rts.value = threshold;
169262306a36Sopenharmony_ci		wrqu->rts.disabled = threshold >= 2347;
169362306a36Sopenharmony_ci		wrqu->rts.fixed = 1;
169462306a36Sopenharmony_ci	}
169562306a36Sopenharmony_ci	return rc;
169662306a36Sopenharmony_ci}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic int wl3501_get_frag_threshold(struct net_device *dev,
169962306a36Sopenharmony_ci				     struct iw_request_info *info,
170062306a36Sopenharmony_ci				     union iwreq_data *wrqu, char *extra)
170162306a36Sopenharmony_ci{
170262306a36Sopenharmony_ci	u16 threshold; /* size checked: it is u16 */
170362306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
170462306a36Sopenharmony_ci	int rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_FRAG_THRESHOLD,
170562306a36Sopenharmony_ci				      &threshold, sizeof(threshold));
170662306a36Sopenharmony_ci	if (!rc) {
170762306a36Sopenharmony_ci		wrqu->frag.value = threshold;
170862306a36Sopenharmony_ci		wrqu->frag.disabled = threshold >= 2346;
170962306a36Sopenharmony_ci		wrqu->frag.fixed = 1;
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci	return rc;
171262306a36Sopenharmony_ci}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_cistatic int wl3501_get_txpow(struct net_device *dev,
171562306a36Sopenharmony_ci			    struct iw_request_info *info,
171662306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *extra)
171762306a36Sopenharmony_ci{
171862306a36Sopenharmony_ci	u16 txpow;
171962306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
172062306a36Sopenharmony_ci	int rc = wl3501_get_mib_value(this,
172162306a36Sopenharmony_ci				      WL3501_MIB_ATTR_CURRENT_TX_PWR_LEVEL,
172262306a36Sopenharmony_ci				      &txpow, sizeof(txpow));
172362306a36Sopenharmony_ci	if (!rc) {
172462306a36Sopenharmony_ci		wrqu->txpower.value = txpow;
172562306a36Sopenharmony_ci		wrqu->txpower.disabled = 0;
172662306a36Sopenharmony_ci		/*
172762306a36Sopenharmony_ci		 * From the MIB values I think this can be configurable,
172862306a36Sopenharmony_ci		 * as it lists several tx power levels -acme
172962306a36Sopenharmony_ci		 */
173062306a36Sopenharmony_ci		wrqu->txpower.fixed = 0;
173162306a36Sopenharmony_ci		wrqu->txpower.flags = IW_TXPOW_MWATT;
173262306a36Sopenharmony_ci	}
173362306a36Sopenharmony_ci	return rc;
173462306a36Sopenharmony_ci}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_cistatic int wl3501_get_retry(struct net_device *dev,
173762306a36Sopenharmony_ci			    struct iw_request_info *info,
173862306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *extra)
173962306a36Sopenharmony_ci{
174062306a36Sopenharmony_ci	u8 retry; /* size checked: it is u8 */
174162306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
174262306a36Sopenharmony_ci	int rc = wl3501_get_mib_value(this,
174362306a36Sopenharmony_ci				      WL3501_MIB_ATTR_LONG_RETRY_LIMIT,
174462306a36Sopenharmony_ci				      &retry, sizeof(retry));
174562306a36Sopenharmony_ci	if (rc)
174662306a36Sopenharmony_ci		goto out;
174762306a36Sopenharmony_ci	if (wrqu->retry.flags & IW_RETRY_LONG) {
174862306a36Sopenharmony_ci		wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
174962306a36Sopenharmony_ci		goto set_value;
175062306a36Sopenharmony_ci	}
175162306a36Sopenharmony_ci	rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_SHORT_RETRY_LIMIT,
175262306a36Sopenharmony_ci				  &retry, sizeof(retry));
175362306a36Sopenharmony_ci	if (rc)
175462306a36Sopenharmony_ci		goto out;
175562306a36Sopenharmony_ci	wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
175662306a36Sopenharmony_ciset_value:
175762306a36Sopenharmony_ci	wrqu->retry.value = retry;
175862306a36Sopenharmony_ci	wrqu->retry.disabled = 0;
175962306a36Sopenharmony_ciout:
176062306a36Sopenharmony_ci	return rc;
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_cistatic int wl3501_get_encode(struct net_device *dev,
176462306a36Sopenharmony_ci			     struct iw_request_info *info,
176562306a36Sopenharmony_ci			     union iwreq_data *wrqu, char *extra)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	u8 implemented, restricted, keys[100], len_keys, tocopy;
176862306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
176962306a36Sopenharmony_ci	int rc = wl3501_get_mib_value(this,
177062306a36Sopenharmony_ci				      WL3501_MIB_ATTR_PRIV_OPT_IMPLEMENTED,
177162306a36Sopenharmony_ci				      &implemented, sizeof(implemented));
177262306a36Sopenharmony_ci	if (rc)
177362306a36Sopenharmony_ci		goto out;
177462306a36Sopenharmony_ci	if (!implemented) {
177562306a36Sopenharmony_ci		wrqu->encoding.flags = IW_ENCODE_DISABLED;
177662306a36Sopenharmony_ci		goto out;
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci	rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_EXCLUDE_UNENCRYPTED,
177962306a36Sopenharmony_ci				  &restricted, sizeof(restricted));
178062306a36Sopenharmony_ci	if (rc)
178162306a36Sopenharmony_ci		goto out;
178262306a36Sopenharmony_ci	wrqu->encoding.flags = restricted ? IW_ENCODE_RESTRICTED :
178362306a36Sopenharmony_ci					    IW_ENCODE_OPEN;
178462306a36Sopenharmony_ci	rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_KEY_MAPPINGS_LEN,
178562306a36Sopenharmony_ci				  &len_keys, sizeof(len_keys));
178662306a36Sopenharmony_ci	if (rc)
178762306a36Sopenharmony_ci		goto out;
178862306a36Sopenharmony_ci	rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_KEY_MAPPINGS,
178962306a36Sopenharmony_ci				  keys, len_keys);
179062306a36Sopenharmony_ci	if (rc)
179162306a36Sopenharmony_ci		goto out;
179262306a36Sopenharmony_ci	tocopy = min_t(u16, len_keys, wrqu->encoding.length);
179362306a36Sopenharmony_ci	tocopy = min_t(u8, tocopy, 100);
179462306a36Sopenharmony_ci	wrqu->encoding.length = tocopy;
179562306a36Sopenharmony_ci	memcpy(extra, keys, tocopy);
179662306a36Sopenharmony_ciout:
179762306a36Sopenharmony_ci	return rc;
179862306a36Sopenharmony_ci}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cistatic int wl3501_get_power(struct net_device *dev,
180162306a36Sopenharmony_ci			    struct iw_request_info *info,
180262306a36Sopenharmony_ci			    union iwreq_data *wrqu, char *extra)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	u8 pwr_state;
180562306a36Sopenharmony_ci	struct wl3501_card *this = netdev_priv(dev);
180662306a36Sopenharmony_ci	int rc = wl3501_get_mib_value(this,
180762306a36Sopenharmony_ci				      WL3501_MIB_ATTR_CURRENT_PWR_STATE,
180862306a36Sopenharmony_ci				      &pwr_state, sizeof(pwr_state));
180962306a36Sopenharmony_ci	if (rc)
181062306a36Sopenharmony_ci		goto out;
181162306a36Sopenharmony_ci	wrqu->power.disabled = !pwr_state;
181262306a36Sopenharmony_ci	wrqu->power.flags = IW_POWER_ON;
181362306a36Sopenharmony_ciout:
181462306a36Sopenharmony_ci	return rc;
181562306a36Sopenharmony_ci}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_cistatic const iw_handler	wl3501_handler[] = {
181862306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWNAME, wl3501_get_name),
181962306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWFREQ, wl3501_set_freq),
182062306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWFREQ, wl3501_get_freq),
182162306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWMODE, wl3501_set_mode),
182262306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWMODE, wl3501_get_mode),
182362306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWSENS, wl3501_get_sens),
182462306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRANGE, wl3501_get_range),
182562306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
182662306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
182762306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
182862306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
182962306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWAP, wl3501_set_wap),
183062306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWAP, wl3501_get_wap),
183162306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWSCAN, wl3501_set_scan),
183262306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWSCAN, wl3501_get_scan),
183362306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWESSID, wl3501_set_essid),
183462306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWESSID, wl3501_get_essid),
183562306a36Sopenharmony_ci	IW_HANDLER(SIOCSIWNICKN, wl3501_set_nick),
183662306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWNICKN, wl3501_get_nick),
183762306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRATE, wl3501_get_rate),
183862306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRTS, wl3501_get_rts_threshold),
183962306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWFRAG, wl3501_get_frag_threshold),
184062306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWTXPOW, wl3501_get_txpow),
184162306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWRETRY, wl3501_get_retry),
184262306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWENCODE, wl3501_get_encode),
184362306a36Sopenharmony_ci	IW_HANDLER(SIOCGIWPOWER, wl3501_get_power),
184462306a36Sopenharmony_ci};
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic const struct iw_handler_def wl3501_handler_def = {
184762306a36Sopenharmony_ci	.num_standard	= ARRAY_SIZE(wl3501_handler),
184862306a36Sopenharmony_ci	.standard	= (iw_handler *)wl3501_handler,
184962306a36Sopenharmony_ci	.get_wireless_stats = wl3501_get_wireless_stats,
185062306a36Sopenharmony_ci};
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_cistatic const struct net_device_ops wl3501_netdev_ops = {
185362306a36Sopenharmony_ci	.ndo_open		= wl3501_open,
185462306a36Sopenharmony_ci	.ndo_stop		= wl3501_close,
185562306a36Sopenharmony_ci	.ndo_start_xmit		= wl3501_hard_start_xmit,
185662306a36Sopenharmony_ci	.ndo_tx_timeout		= wl3501_tx_timeout,
185762306a36Sopenharmony_ci	.ndo_set_mac_address 	= eth_mac_addr,
185862306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
185962306a36Sopenharmony_ci};
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_cistatic int wl3501_probe(struct pcmcia_device *p_dev)
186262306a36Sopenharmony_ci{
186362306a36Sopenharmony_ci	struct net_device *dev;
186462306a36Sopenharmony_ci	struct wl3501_card *this;
186562306a36Sopenharmony_ci	int ret;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	/* The io structure describes IO port mapping */
186862306a36Sopenharmony_ci	p_dev->resource[0]->end	= 16;
186962306a36Sopenharmony_ci	p_dev->resource[0]->flags	= IO_DATA_PATH_WIDTH_8;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	/* General socket configuration */
187262306a36Sopenharmony_ci	p_dev->config_flags	= CONF_ENABLE_IRQ;
187362306a36Sopenharmony_ci	p_dev->config_index	= 1;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct wl3501_card));
187662306a36Sopenharmony_ci	if (!dev)
187762306a36Sopenharmony_ci		return -ENOMEM;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	dev->netdev_ops		= &wl3501_netdev_ops;
188062306a36Sopenharmony_ci	dev->watchdog_timeo	= 5 * HZ;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	this = netdev_priv(dev);
188362306a36Sopenharmony_ci	this->wireless_data.spy_data = &this->spy_data;
188462306a36Sopenharmony_ci	this->p_dev = p_dev;
188562306a36Sopenharmony_ci	dev->wireless_data	= &this->wireless_data;
188662306a36Sopenharmony_ci	dev->wireless_handlers	= &wl3501_handler_def;
188762306a36Sopenharmony_ci	netif_stop_queue(dev);
188862306a36Sopenharmony_ci	p_dev->priv = dev;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	ret = wl3501_config(p_dev);
189162306a36Sopenharmony_ci	if (ret)
189262306a36Sopenharmony_ci		goto out_free_etherdev;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	return 0;
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ciout_free_etherdev:
189762306a36Sopenharmony_ci	free_netdev(dev);
189862306a36Sopenharmony_ci	return ret;
189962306a36Sopenharmony_ci}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_cistatic int wl3501_config(struct pcmcia_device *link)
190262306a36Sopenharmony_ci{
190362306a36Sopenharmony_ci	struct net_device *dev = link->priv;
190462306a36Sopenharmony_ci	int i = 0, j, ret;
190562306a36Sopenharmony_ci	struct wl3501_card *this;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	/* Try allocating IO ports.  This tries a few fixed addresses.  If you
190862306a36Sopenharmony_ci	 * want, you can also read the card's config table to pick addresses --
190962306a36Sopenharmony_ci	 * see the serial driver for an example. */
191062306a36Sopenharmony_ci	link->io_lines = 5;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	for (j = 0x280; j < 0x400; j += 0x20) {
191362306a36Sopenharmony_ci		/* The '^0x300' is so that we probe 0x300-0x3ff first, then
191462306a36Sopenharmony_ci		 * 0x200-0x2ff, and so on, because this seems safer */
191562306a36Sopenharmony_ci		link->resource[0]->start = j;
191662306a36Sopenharmony_ci		link->resource[1]->start = link->resource[0]->start + 0x10;
191762306a36Sopenharmony_ci		i = pcmcia_request_io(link);
191862306a36Sopenharmony_ci		if (i == 0)
191962306a36Sopenharmony_ci			break;
192062306a36Sopenharmony_ci	}
192162306a36Sopenharmony_ci	if (i != 0)
192262306a36Sopenharmony_ci		goto failed;
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	/* Now allocate an interrupt line. Note that this does not actually
192562306a36Sopenharmony_ci	 * assign a handler to the interrupt. */
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	ret = pcmcia_request_irq(link, wl3501_interrupt);
192862306a36Sopenharmony_ci	if (ret)
192962306a36Sopenharmony_ci		goto failed;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	ret = pcmcia_enable_device(link);
193262306a36Sopenharmony_ci	if (ret)
193362306a36Sopenharmony_ci		goto failed;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	dev->irq = link->irq;
193662306a36Sopenharmony_ci	dev->base_addr = link->resource[0]->start;
193762306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &link->dev);
193862306a36Sopenharmony_ci	if (register_netdev(dev)) {
193962306a36Sopenharmony_ci		printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n");
194062306a36Sopenharmony_ci		goto failed;
194162306a36Sopenharmony_ci	}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	this = netdev_priv(dev);
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	this->base_addr = dev->base_addr;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	if (!wl3501_get_flash_mac_addr(this)) {
194862306a36Sopenharmony_ci		printk(KERN_WARNING "%s: Can't read MAC addr in flash ROM?\n",
194962306a36Sopenharmony_ci		       dev->name);
195062306a36Sopenharmony_ci		unregister_netdev(dev);
195162306a36Sopenharmony_ci		goto failed;
195262306a36Sopenharmony_ci	}
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	eth_hw_addr_set(dev, this->mac_addr);
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	/* print probe information */
195762306a36Sopenharmony_ci	printk(KERN_INFO "%s: wl3501 @ 0x%3.3x, IRQ %d, "
195862306a36Sopenharmony_ci	       "MAC addr in flash ROM:%pM\n",
195962306a36Sopenharmony_ci	       dev->name, this->base_addr, (int)dev->irq,
196062306a36Sopenharmony_ci	       dev->dev_addr);
196162306a36Sopenharmony_ci	/*
196262306a36Sopenharmony_ci	 * Initialize card parameters - added by jss
196362306a36Sopenharmony_ci	 */
196462306a36Sopenharmony_ci	this->net_type		= IW_MODE_INFRA;
196562306a36Sopenharmony_ci	this->bss_cnt		= 0;
196662306a36Sopenharmony_ci	this->join_sta_bss	= 0;
196762306a36Sopenharmony_ci	this->adhoc_times	= 0;
196862306a36Sopenharmony_ci	iw_set_mgmt_info_element(IW_MGMT_INFO_ELEMENT_SSID, &this->essid.el,
196962306a36Sopenharmony_ci				 "ANY", 3);
197062306a36Sopenharmony_ci	this->card_name[0]	= '\0';
197162306a36Sopenharmony_ci	this->firmware_date[0]	= '\0';
197262306a36Sopenharmony_ci	this->rssi		= 255;
197362306a36Sopenharmony_ci	this->chan		= iw_default_channel(this->reg_domain);
197462306a36Sopenharmony_ci	strscpy(this->nick, "Planet WL3501", sizeof(this->nick));
197562306a36Sopenharmony_ci	spin_lock_init(&this->lock);
197662306a36Sopenharmony_ci	init_waitqueue_head(&this->wait);
197762306a36Sopenharmony_ci	netif_start_queue(dev);
197862306a36Sopenharmony_ci	return 0;
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_cifailed:
198162306a36Sopenharmony_ci	wl3501_release(link);
198262306a36Sopenharmony_ci	return -ENODEV;
198362306a36Sopenharmony_ci}
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_cistatic void wl3501_release(struct pcmcia_device *link)
198662306a36Sopenharmony_ci{
198762306a36Sopenharmony_ci	pcmcia_disable_device(link);
198862306a36Sopenharmony_ci}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_cistatic int wl3501_suspend(struct pcmcia_device *link)
199162306a36Sopenharmony_ci{
199262306a36Sopenharmony_ci	struct net_device *dev = link->priv;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	wl3501_pwr_mgmt(netdev_priv(dev), WL3501_SUSPEND);
199562306a36Sopenharmony_ci	if (link->open)
199662306a36Sopenharmony_ci		netif_device_detach(dev);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	return 0;
199962306a36Sopenharmony_ci}
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_cistatic int wl3501_resume(struct pcmcia_device *link)
200262306a36Sopenharmony_ci{
200362306a36Sopenharmony_ci	struct net_device *dev = link->priv;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	wl3501_pwr_mgmt(netdev_priv(dev), WL3501_RESUME);
200662306a36Sopenharmony_ci	if (link->open) {
200762306a36Sopenharmony_ci		wl3501_reset(dev);
200862306a36Sopenharmony_ci		netif_device_attach(dev);
200962306a36Sopenharmony_ci	}
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	return 0;
201262306a36Sopenharmony_ci}
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_cistatic const struct pcmcia_device_id wl3501_ids[] = {
201662306a36Sopenharmony_ci	PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0001),
201762306a36Sopenharmony_ci	PCMCIA_DEVICE_NULL
201862306a36Sopenharmony_ci};
201962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pcmcia, wl3501_ids);
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_cistatic struct pcmcia_driver wl3501_driver = {
202262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
202362306a36Sopenharmony_ci	.name		= "wl3501_cs",
202462306a36Sopenharmony_ci	.probe		= wl3501_probe,
202562306a36Sopenharmony_ci	.remove		= wl3501_detach,
202662306a36Sopenharmony_ci	.id_table	= wl3501_ids,
202762306a36Sopenharmony_ci	.suspend	= wl3501_suspend,
202862306a36Sopenharmony_ci	.resume		= wl3501_resume,
202962306a36Sopenharmony_ci};
203062306a36Sopenharmony_cimodule_pcmcia_driver(wl3501_driver);
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ciMODULE_AUTHOR("Fox Chen <mhchen@golf.ccl.itri.org.tw>, "
203362306a36Sopenharmony_ci	      "Arnaldo Carvalho de Melo <acme@conectiva.com.br>,"
203462306a36Sopenharmony_ci	      "Gustavo Niemeyer <niemeyer@conectiva.com>");
203562306a36Sopenharmony_ciMODULE_DESCRIPTION("Planet wl3501 wireless driver");
203662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2037