o wl3501: initial batch of support for wireless extensions and ethtool

parent 73fca214
...@@ -301,6 +301,8 @@ config PCMCIA_WL3501 ...@@ -301,6 +301,8 @@ config PCMCIA_WL3501
depends on NET_RADIO && EXPERIMENTAL && PCMCIA depends on NET_RADIO && EXPERIMENTAL && PCMCIA
---help--- ---help---
A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet.
It has basic support for Linux wireless extensions and initial
micro support for ethtool.
# yes, this works even when no drivers are selected # yes, this works even when no drivers are selected
config NET_WIRELESS config NET_WIRELESS
......
...@@ -63,8 +63,8 @@ enum wl3501_signals { ...@@ -63,8 +63,8 @@ enum wl3501_signals {
}; };
enum wl3501_net_type { enum wl3501_net_type {
WL3501_NET_TYPE_INFRASTRUCTURE, WL3501_NET_TYPE_INFRA,
WL3501_NET_TYPE_INDEPENDENT, WL3501_NET_TYPE_ADHOC,
WL3501_NET_TYPE_ANY_BSS, WL3501_NET_TYPE_ANY_BSS,
}; };
...@@ -84,12 +84,6 @@ enum wl3501_sys_type { ...@@ -84,12 +84,6 @@ enum wl3501_sys_type {
WL3501_SYS_TYPE_SHARE_KEY, WL3501_SYS_TYPE_SHARE_KEY,
}; };
enum wl3501_pkt_type {
WL3501_PKT_TYPE_ETHERII,
WL3501_PKT_TYPE_ETHER802_3E,
WL3501_PKT_TYPE_ETHER802_3F,
};
enum wl3501_status { enum wl3501_status {
WL3501_STATUS_SUCCESS, WL3501_STATUS_SUCCESS,
WL3501_STATUS_INVALID, WL3501_STATUS_INVALID,
...@@ -429,10 +423,10 @@ struct wl3501_card { ...@@ -429,10 +423,10 @@ struct wl3501_card {
u16 esbq_confirm; u16 esbq_confirm;
struct wl3501_mac_addr bssid; struct wl3501_mac_addr bssid;
u8 llc_type; u8 llc_type;
enum wl3501_net_type net_type; int net_type;
u8 essid[34]; u8 essid[34];
u8 keep_essid[34]; u8 keep_essid[34];
u8 ether_type; int ether_type;
u8 chan; u8 chan;
u8 def_chan; u8 def_chan;
u16 start_seg; u16 start_seg;
...@@ -447,6 +441,7 @@ struct wl3501_card { ...@@ -447,6 +441,7 @@ struct wl3501_card {
u8 version[2]; u8 version[2];
struct net_device_stats stats; struct net_device_stats stats;
struct iw_statistics wstats; struct iw_statistics wstats;
struct iw_spy_data spy_data;
struct dev_node_t node; struct dev_node_t node;
}; };
...@@ -476,7 +471,7 @@ struct wl3501_ioctl_parm { ...@@ -476,7 +471,7 @@ struct wl3501_ioctl_parm {
}; };
enum wl3501_ioctl_cmd { enum wl3501_ioctl_cmd {
WL3501_IOCTL_CMD_GET_PARAMETER, WL3501_IOCTL_CMD_GET_PARAMETER = SIOCIWFIRSTPRIV,
WL3501_IOCTL_CMD_SET_PARAMETER, WL3501_IOCTL_CMD_SET_PARAMETER,
WL3501_IOCTL_CMD_WRITE_FLASH, WL3501_IOCTL_CMD_WRITE_FLASH,
WL3501_IOCTL_CMD_SET_RESET, WL3501_IOCTL_CMD_SET_RESET,
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Ported to 2.2, 2.4 & 2.5 by Arnaldo Carvalho de Melo <acme@conectiva.com.br> * Ported to 2.2, 2.4 & 2.5 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
* Wireless extensions in 2.4 by Gustavo Niemeyer <niemeyer@conectiva.com> * Wireless extensions in 2.4 by Gustavo Niemeyer <niemeyer@conectiva.com>
* *
* References: * References used by Fox Chen while writing the original driver for 2.0.30:
* *
* 1. WL24xx packet drivers (tooasm.asm) * 1. WL24xx packet drivers (tooasm.asm)
* 2. Access Point Firmware Interface Specification for IEEE 802.11 SUTRO * 2. Access Point Firmware Interface Specification for IEEE 802.11 SUTRO
...@@ -24,30 +24,27 @@ ...@@ -24,30 +24,27 @@
* 173 KiB/s in TCP. * 173 KiB/s in TCP.
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/delay.h>
#include <linux/kernel.h> #include <linux/types.h>
#include <linux/ethtool.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/delay.h> #include <linux/kernel.h>
#include <asm/uaccess.h> #include <linux/module.h>
#include <asm/io.h> #include <linux/fcntl.h>
#include <asm/system.h> #include <linux/if_arp.h>
#include <asm/bitops.h> #include <linux/ioport.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/if_arp.h> #include <linux/slab.h>
#include <linux/ioport.h> #include <linux/string.h>
#include <linux/fcntl.h> #include <linux/timer.h>
#include <linux/wireless.h> #include <linux/wireless.h>
#include <net/iw_handler.h>
#include <pcmcia/version.h> #include <pcmcia/version.h>
#include <pcmcia/cs_types.h> #include <pcmcia/cs_types.h>
#include <pcmcia/cs.h> #include <pcmcia/cs.h>
...@@ -55,6 +52,10 @@ ...@@ -55,6 +52,10 @@
#include <pcmcia/cisreg.h> #include <pcmcia/cisreg.h>
#include <pcmcia/ds.h> #include <pcmcia/ds.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include "wl3501.h" #include "wl3501.h"
/* /*
...@@ -95,14 +96,6 @@ static u8 wl3501_fpage[] = { ...@@ -95,14 +96,6 @@ static u8 wl3501_fpage[] = {
static unsigned long wl3501_irq_mask = 0xdeb8; static unsigned long wl3501_irq_mask = 0xdeb8;
static int wl3501_irq_list[4] = { -1 }; static int wl3501_irq_list[4] = { -1 };
MODULE_PARM(wl3501_irq_mask, "i");
MODULE_PARM(wl3501_irq_list, "1-4i");
MODULE_AUTHOR("Fox Chen <mhchen@golf.ccl.itri.org.tw>, "
"Arnaldo Carvalho de Melo <acme@conectiva.com.br>,"
"Gustavo Niemeyer <niemeyer@conectiva.com>");
MODULE_DESCRIPTION("Planet wl3501 wireless driver");
MODULE_LICENSE("GPL");
/* /*
* The event() function is this driver's Card Services event handler. It will * The event() function is this driver's Card Services event handler. It will
* be called by Card Services when an appropriate card status event is * be called by Card Services when an appropriate card status event is
...@@ -676,10 +669,10 @@ static int wl3501_mgmt_scan(struct wl3501_card *this, u16 chan_time) ...@@ -676,10 +669,10 @@ static int wl3501_mgmt_scan(struct wl3501_card *this, u16 chan_time)
signal.min_chan_time = chan_time; signal.min_chan_time = chan_time;
signal.max_chan_time = chan_time; signal.max_chan_time = chan_time;
if (this->net_type == WL3501_NET_TYPE_INFRASTRUCTURE) if (this->net_type == IW_MODE_INFRA)
signal.bss_type = WL3501_NET_TYPE_INFRASTRUCTURE; signal.bss_type = WL3501_NET_TYPE_INFRA;
else else
signal.bss_type = WL3501_NET_TYPE_INDEPENDENT; signal.bss_type = WL3501_NET_TYPE_ADHOC;
this->bss_cnt = this->join_sta_bss = 0; this->bss_cnt = this->join_sta_bss = 0;
...@@ -726,7 +719,8 @@ static int wl3501_mgmt_start(struct wl3501_card *this) ...@@ -726,7 +719,8 @@ static int wl3501_mgmt_start(struct wl3501_card *this)
signal.sig_id = WL3501_SIG_START_REQ; signal.sig_id = WL3501_SIG_START_REQ;
memcpy((char *)signal.ssid, (char *)this->essid, 34); memcpy((char *)signal.ssid, (char *)this->essid, 34);
memcpy((char *)this->keep_essid, (char *)this->essid, 34); memcpy((char *)this->keep_essid, (char *)this->essid, 34);
signal.bss_type = WL3501_NET_TYPE_INDEPENDENT; signal.bss_type = this->net_type = IW_MODE_INFRA ?
WL3501_NET_TYPE_INFRA : WL3501_NET_TYPE_INFRA;
signal.beacon_period = 400; signal.beacon_period = 400;
signal.dtim_period = 1; signal.dtim_period = 1;
signal.phy_pset[0] = 3; signal.phy_pset[0] = 3;
...@@ -777,11 +771,11 @@ static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr) ...@@ -777,11 +771,11 @@ static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr)
(char *)&(signal.beacon_period), 73); (char *)&(signal.beacon_period), 73);
this->bss_cnt++; this->bss_cnt++;
} }
} else if ((this->net_type == WL3501_NET_TYPE_INFRASTRUCTURE && } else if ((this->net_type == IW_MODE_INFRA &&
(signal.cap_info & 0x01)) || (signal.cap_info & 0x01)) ||
(this->net_type == WL3501_NET_TYPE_INDEPENDENT && (this->net_type == IW_MODE_ADHOC &&
(signal.cap_info & 0x02)) || (signal.cap_info & 0x02)) ||
this->net_type == WL3501_NET_TYPE_ANY_BSS) { this->net_type == IW_MODE_AUTO) {
if (!this->essid[1]) if (!this->essid[1])
matchflag = 1; matchflag = 1;
else if (this->essid[1] == 3 && else if (this->essid[1] == 3 &&
...@@ -820,7 +814,7 @@ static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr) ...@@ -820,7 +814,7 @@ static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr)
break; break;
this->join_sta_bss = j; this->join_sta_bss = j;
if (this->join_sta_bss == this->bss_cnt) { if (this->join_sta_bss == this->bss_cnt) {
if (this->net_type == WL3501_NET_TYPE_INFRASTRUCTURE) if (this->net_type == IW_MODE_INFRA)
wl3501_mgmt_scan(this, 100); wl3501_mgmt_scan(this, 100);
else { else {
this->adhoc_times++; this->adhoc_times++;
...@@ -886,7 +880,7 @@ static u16 wl3501_receive(struct wl3501_card *this, u8 *bf, u16 size) ...@@ -886,7 +880,7 @@ static u16 wl3501_receive(struct wl3501_card *this, u8 *bf, u16 size)
wl3501_get_from_wla(this, this->start_seg + 2, wl3501_get_from_wla(this, this->start_seg + 2,
&next_addr, sizeof(next_addr)); &next_addr, sizeof(next_addr));
if (this->llc_type == 1) { if (this->llc_type == 1) {
if (this->ether_type == WL3501_PKT_TYPE_ETHERII) { if (this->ether_type == ARPHRD_ETHER) {
if (size > if (size >
WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr)) { WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr)) {
wl3501_get_from_wla(this, this->start_seg + wl3501_get_from_wla(this, this->start_seg +
...@@ -1064,7 +1058,7 @@ static void wl3501_mgmt_join_confirm(struct net_device *dev, ...@@ -1064,7 +1058,7 @@ static void wl3501_mgmt_join_confirm(struct net_device *dev,
wl3501_get_from_wla(this, addr, &sig, sizeof(sig)); wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS) { if (sig.status == WL3501_STATUS_SUCCESS) {
if (this->net_type == WL3501_NET_TYPE_INFRASTRUCTURE) { if (this->net_type == IW_MODE_INFRA) {
if (this->join_sta_bss < this->bss_cnt) { if (this->join_sta_bss < this->bss_cnt) {
i = this->join_sta_bss; i = this->join_sta_bss;
memcpy((char *)&(this->bssid), memcpy((char *)&(this->bssid),
...@@ -1091,7 +1085,7 @@ static void wl3501_mgmt_join_confirm(struct net_device *dev, ...@@ -1091,7 +1085,7 @@ static void wl3501_mgmt_join_confirm(struct net_device *dev,
break; break;
this->join_sta_bss = j; this->join_sta_bss = j;
if (this->join_sta_bss == this->bss_cnt) { if (this->join_sta_bss == this->bss_cnt) {
if (this->net_type == WL3501_NET_TYPE_INFRASTRUCTURE) if (this->net_type == IW_MODE_INFRA)
wl3501_mgmt_scan(this, 100); wl3501_mgmt_scan(this, 100);
else { else {
this->adhoc_times++; this->adhoc_times++;
...@@ -1112,7 +1106,7 @@ static void wl3501_mgmt_join_confirm(struct net_device *dev, ...@@ -1112,7 +1106,7 @@ static void wl3501_mgmt_join_confirm(struct net_device *dev,
static inline void wl3501_alarm_interrupt(struct net_device *dev, static inline void wl3501_alarm_interrupt(struct net_device *dev,
struct wl3501_card *this) struct wl3501_card *this)
{ {
if (this->net_type == WL3501_NET_TYPE_INFRASTRUCTURE) { if (this->net_type == IW_MODE_INFRA) {
printk(KERN_INFO "Wireless LAN offline\n"); printk(KERN_INFO "Wireless LAN offline\n");
netif_stop_queue(dev); netif_stop_queue(dev);
wl3501_mgmt_resync(this); wl3501_mgmt_resync(this);
...@@ -1148,13 +1142,13 @@ static inline void wl3501_md_ind_interrupt(struct net_device *dev, ...@@ -1148,13 +1142,13 @@ static inline void wl3501_md_ind_interrupt(struct net_device *dev,
&tmp, sizeof(tmp)); &tmp, sizeof(tmp));
if (tmp == 0xaaaa) { if (tmp == 0xaaaa) {
pkt_len = sig.size + 12 - 24 - 4 - 6; pkt_len = sig.size + 12 - 24 - 4 - 6;
this->ether_type = WL3501_PKT_TYPE_ETHERII; this->ether_type = ARPHRD_ETHER;
} else if (tmp == 0xe0e0) { } else if (tmp == 0xe0e0) {
pkt_len = sig.size + 12 - 24 - 4 + 2; pkt_len = sig.size + 12 - 24 - 4 + 2;
this->ether_type = WL3501_PKT_TYPE_ETHER802_3E; this->ether_type = ARPHRD_IEEE80211; /* FIXME */
} else { } else {
pkt_len = sig.size + 12 - 24 - 4 + 2; pkt_len = sig.size + 12 - 24 - 4 + 2;
this->ether_type = WL3501_PKT_TYPE_ETHER802_3F; this->ether_type = ARPHRD_IEEE80211; /* FIXME */
} }
} else } else
pkt_len = sig.size - 24 - 4; pkt_len = sig.size - 24 - 4;
...@@ -1466,11 +1460,9 @@ static int wl3501_close(struct net_device *dev) ...@@ -1466,11 +1460,9 @@ static int wl3501_close(struct net_device *dev)
*/ */
static int wl3501_reset(struct net_device *dev) static int wl3501_reset(struct net_device *dev)
{ {
unsigned long flags;
struct wl3501_card *this = (struct wl3501_card *)dev->priv; struct wl3501_card *this = (struct wl3501_card *)dev->priv;
int rc = -ENODEV; int rc = -ENODEV;
spin_lock_irqsave(&this->lock, flags);
/* Stop processing interrupt from the card */ /* Stop processing interrupt from the card */
wl3501_block_interrupt(this); wl3501_block_interrupt(this);
...@@ -1498,7 +1490,6 @@ static int wl3501_reset(struct net_device *dev) ...@@ -1498,7 +1490,6 @@ static int wl3501_reset(struct net_device *dev)
printk(KERN_INFO "%s: device reset\n", dev->name); printk(KERN_INFO "%s: device reset\n", dev->name);
rc = 0; rc = 0;
out: out:
spin_unlock_irqrestore(&this->lock, flags);
return rc; return rc;
} }
...@@ -1683,6 +1674,29 @@ static void wl3501_set_multicast_list(struct net_device *dev) ...@@ -1683,6 +1674,29 @@ static void wl3501_set_multicast_list(struct net_device *dev)
#endif #endif
} }
static inline int wl3501_ethtool_ioctl(struct net_device *dev, void *uaddr)
{
u32 ethcmd;
int rc = -EFAULT;
if (copy_from_user(&ethcmd, uaddr, sizeof(ethcmd)))
goto out;
switch (ethcmd) {
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = { .cmd = ETHTOOL_GDRVINFO, };
strlcpy(info.driver, wl3501_dev_info, sizeof(info.driver));
rc = copy_to_user(uaddr, &info, sizeof(info)) ? -EFAULT : 1;
}
default:
rc = -EOPNOTSUPP;
break;
}
out:
return rc;
}
/** /**
* wl3501_ioctl - Perform IOCTL call functions * wl3501_ioctl - Perform IOCTL call functions
* @dev - network device * @dev - network device
...@@ -1696,27 +1710,33 @@ static void wl3501_set_multicast_list(struct net_device *dev) ...@@ -1696,27 +1710,33 @@ static void wl3501_set_multicast_list(struct net_device *dev)
* *
* CAUTION: To prevent interrupted by wl3501_interrupt() and timer-based * CAUTION: To prevent interrupted by wl3501_interrupt() and timer-based
* wl3501_hard_start_xmit() from other interrupts, this should be run * wl3501_hard_start_xmit() from other interrupts, this should be run
* single-threaded. This function is expected to be a rare operation, and it's * single-threaded.
* simpler to just use cli() to disable ALL interrupts.
*/ */
static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{ {
int rc = -ENODEV; int rc = -ENODEV;
struct wl3501_card *this = (struct wl3501_card *)dev->priv;
struct wl3501_ioctl_parm parm;
struct wl3501_ioctl_blk *blk = (struct wl3501_ioctl_blk *)&rq->ifr_data;
if (!netif_device_present(dev)) if (!netif_device_present(dev))
goto out; goto out;
switch (blk->cmd) { switch (cmd) {
case WL3501_IOCTL_CMD_SET_RESET: /* Reset drv - needed after set */ case SIOCETHTOOL:
rc = wl3501_ethtool_ioctl(dev, (void *)rq->ifr_data);
break;
#if 0
/* FIXME: has to go to the private stuff in iw_handler_def */
/*
* Private IOCTLs
*/
case WL3501_IOCTL_CMD_SET_RESET:
rc = -EPERM; rc = -EPERM;
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
break; break;
spin_lock_irqsave(&this->lock, flags);
rc = wl3501_reset(dev); rc = wl3501_reset(dev);
spin_unlock_irqrestore(&this->lock, flags);
break; break;
case WL3501_IOCTL_CMD_WRITE_FLASH: { /* Write firmware into Flash */ case WL3501_IOCTL_CMD_WRITE_FLASH: {
unsigned char bf[1028]; unsigned char bf[1028];
rc = -EPERM; rc = -EPERM;
...@@ -1728,10 +1748,13 @@ static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -1728,10 +1748,13 @@ static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
rc = -EFAULT; rc = -EFAULT;
if (copy_from_user(bf, blk->data, blk->len)) if (copy_from_user(bf, blk->data, blk->len))
break; break;
spin_lock_irqsave(&this->lock, flags);
rc = wl3501_write_flash(this, bf, blk->len); rc = wl3501_write_flash(this, bf, blk->len);
spin_unlock_irqrestore(&this->lock, flags);
} }
break; break;
case WL3501_IOCTL_CMD_GET_PARAMETER: case WL3501_IOCTL_CMD_GET_PARAMETER:
spin_lock_irqsave(&this->lock, flags);
parm.def_chan = this->def_chan; parm.def_chan = this->def_chan;
parm.chan = this->chan; parm.chan = this->chan;
parm.net_type = this->net_type; parm.net_type = this->net_type;
...@@ -1741,6 +1764,7 @@ static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -1741,6 +1764,7 @@ static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
memcpy((char *)&(parm.keep_essid[0]), memcpy((char *)&(parm.keep_essid[0]),
(char *)&(this->keep_essid[0]), 34); (char *)&(this->keep_essid[0]), 34);
memcpy((char *)&(parm.essid[0]), (char *)&(this->essid[0]), 34); memcpy((char *)&(parm.essid[0]), (char *)&(this->essid[0]), 34);
spin_unlock_irqrestore(&this->lock, flags);
blk->len = sizeof(parm); blk->len = sizeof(parm);
rc = -EFAULT; rc = -EFAULT;
if (copy_to_user(blk->data, &parm, blk->len)) if (copy_to_user(blk->data, &parm, blk->len))
...@@ -1754,11 +1778,14 @@ static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ...@@ -1754,11 +1778,14 @@ static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
rc = -EFAULT; rc = -EFAULT;
if (copy_from_user(&parm, blk->data, sizeof(parm))) if (copy_from_user(&parm, blk->data, sizeof(parm)))
break; break;
spin_lock_irqsave(&this->lock, flags);
this->def_chan = parm.def_chan; this->def_chan = parm.def_chan;
this->net_type = parm.net_type; this->net_type = parm.net_type;
memcpy((char *)&(this->essid[0]), (char *)&(parm.essid[0]), 34); memcpy((char *)&(this->essid[0]), (char *)&(parm.essid[0]), 34);
rc = wl3501_reset(dev); rc = wl3501_reset(dev);
spin_unlock_irqrestore(&this->lock, flags);
break; break;
#endif
default: default:
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
} }
...@@ -1828,6 +1855,130 @@ static void wl3501_flush_stale_links(void) ...@@ -1828,6 +1855,130 @@ static void wl3501_flush_stale_links(void)
} }
} }
static int wl3501_get_name(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
strlcpy(wrqu->name, "IEEE 802.11-FH", sizeof(wrqu->name));
return 0;
}
static int wl3501_set_mode(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = (struct wl3501_card *)dev->priv;
unsigned long flags;
int rc;
spin_lock_irqsave(&this->lock, flags);
if (wrqu->mode == IW_MODE_INFRA ||
wrqu->mode == IW_MODE_ADHOC ||
wrqu->mode == IW_MODE_AUTO) {
this->net_type = wrqu->mode;
rc = wl3501_mgmt_start(this);
} else
rc = -EINVAL;
spin_unlock_irqrestore(&this->lock, flags);
return rc;
}
static int wl3501_get_mode(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = (struct wl3501_card *)dev->priv;
wrqu->mode = this->net_type;
return 0;
}
static int wl3501_get_range(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct iw_range *range = (struct iw_range *)extra;
/* Set the length (very important for backward compatibility) */
wrqu->data.length = sizeof(*range);
/* Set all the info we don't care or don't know about to zero */
memset(range, 0, sizeof(*range));
/* Set the Wireless Extension versions */
range->we_version_compiled = WIRELESS_EXT;
range->we_version_source = 1;
range->throughput = 2 * 1000 * 1000; /* ~2 Mb/s */
/* FIXME: study the code to fill in more fields... */
return 0;
}
static int wl3501_get_wap(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = (struct wl3501_card *)dev->priv;
wrqu->ap_addr.sa_family = this->ether_type;
memcpy(wrqu->ap_addr.sa_data, &this->bssid, ETH_ALEN);
return 0;
}
static int wl3501_set_essid(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = (struct wl3501_card *)dev->priv;
unsigned long flags;
spin_lock_irqsave(&this->lock, flags);
if (wrqu->data.flags)
strlcpy(this->essid, extra, min_t(u16, wrqu->data.length,
IW_ESSID_MAX_SIZE));
spin_unlock_irqrestore(&this->lock, flags);
return 0;
}
static int wl3501_get_essid(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = (struct wl3501_card *)dev->priv;
unsigned long flags;
spin_lock_irqsave(&this->lock, flags);
wrqu->essid.flags = 1;
wrqu->essid.length = IW_ESSID_MAX_SIZE;
strlcpy(extra, this->essid, IW_ESSID_MAX_SIZE);
spin_unlock_irqrestore(&this->lock, flags);
return 0;
}
static const iw_handler wl3501_handler[] = {
[SIOCGIWNAME - SIOCSIWCOMMIT] = wl3501_get_name,
//[SIOCSIWNWID - SIOCSIWCOMMIT] = wl3501_set_nwid,
//[SIOCGIWNWID - SIOCSIWCOMMIT] = wl3501_get_nwid,
//[SIOCSIWFREQ - SIOCSIWCOMMIT] = wl3501_set_freq,
//[SIOCGIWFREQ - SIOCSIWCOMMIT] = wl3501_get_freq,
[SIOCSIWMODE - SIOCSIWCOMMIT] = wl3501_set_mode,
[SIOCGIWMODE - SIOCSIWCOMMIT] = wl3501_get_mode,
//[SIOCSIWSENS - SIOCSIWCOMMIT] = wl3501_set_sens,
//[SIOCGIWSENS - SIOCSIWCOMMIT] = wl3501_get_sens,
[SIOCGIWRANGE - SIOCSIWCOMMIT] = wl3501_get_range,
//[SIOCSIWSPY - SIOCSIWCOMMIT] = iw_handler_set_spy,
//[SIOCGIWSPY - SIOCSIWCOMMIT] = iw_handler_get_spy,
[SIOCSIWTHRSPY - SIOCSIWCOMMIT] = iw_handler_set_thrspy,
[SIOCGIWTHRSPY - SIOCSIWCOMMIT] = iw_handler_get_thrspy,
//[SIOCSIWAP - SIOCSIWCOMMIT] = wl3501_set_wap,
[SIOCGIWAP - SIOCSIWCOMMIT] = wl3501_get_wap,
[SIOCSIWESSID - SIOCSIWCOMMIT] = wl3501_set_essid,
[SIOCGIWESSID - SIOCSIWCOMMIT] = wl3501_get_essid,
//[SIOCSIWENCODE - SIOCSIWCOMMIT] = wl3501_set_encode,
//[SIOCGIWENCODE - SIOCSIWCOMMIT] = wl3501_get_encode,
};
static const struct iw_handler_def wl3501_handler_def = {
.num_standard = sizeof(wl3501_handler) / sizeof(iw_handler),
.standard = (iw_handler *)wl3501_handler,
.spy_offset = offsetof(struct wl3501_card, spy_data),
};
/** /**
* wl3501_attach - creates an "instance" of the driver * wl3501_attach - creates an "instance" of the driver
* *
...@@ -1894,6 +2045,7 @@ static dev_link_t *wl3501_attach(void) ...@@ -1894,6 +2045,7 @@ static dev_link_t *wl3501_attach(void)
dev->get_wireless_stats = wl3501_get_wireless_stats; dev->get_wireless_stats = wl3501_get_wireless_stats;
dev->set_multicast_list = wl3501_set_multicast_list; dev->set_multicast_list = wl3501_set_multicast_list;
dev->do_ioctl = wl3501_ioctl; dev->do_ioctl = wl3501_ioctl;
dev->wireless_handlers = (struct iw_handler_def *)&wl3501_handler_def;
netif_stop_queue(dev); netif_stop_queue(dev);
link->priv = link->irq.Instance = dev; link->priv = link->irq.Instance = dev;
...@@ -2021,8 +2173,8 @@ static void wl3501_config(dev_link_t *link) ...@@ -2021,8 +2173,8 @@ static void wl3501_config(dev_link_t *link)
printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
} }
printk("\n"); printk("\n");
/* initialize card parameter - add by jss */ /* initialize card parameter - added by jss */
this->net_type = WL3501_NET_TYPE_INFRASTRUCTURE; this->net_type = IW_MODE_INFRA;
this->llc_type = 1; this->llc_type = 1;
this->def_chan = 1; this->def_chan = 1;
this->bss_cnt = 0; this->bss_cnt = 0;
...@@ -2071,7 +2223,7 @@ static void wl3501_config(dev_link_t *link) ...@@ -2071,7 +2223,7 @@ static void wl3501_config(dev_link_t *link)
*/ */
static void wl3501_release(unsigned long arg) static void wl3501_release(unsigned long arg)
{ {
dev_link_t *link = (dev_link_t *) arg; dev_link_t *link = (dev_link_t *)arg;
struct net_device *dev = link->priv; struct net_device *dev = link->priv;
/* If the device is currently in use, we won't release until it is /* If the device is currently in use, we won't release until it is
...@@ -2199,3 +2351,11 @@ static void __exit wl3501_exit_module(void) ...@@ -2199,3 +2351,11 @@ static void __exit wl3501_exit_module(void)
module_init(wl3501_init_module); module_init(wl3501_init_module);
module_exit(wl3501_exit_module); module_exit(wl3501_exit_module);
MODULE_PARM(wl3501_irq_mask, "i");
MODULE_PARM(wl3501_irq_list, "1-4i");
MODULE_AUTHOR("Fox Chen <mhchen@golf.ccl.itri.org.tw>, "
"Arnaldo Carvalho de Melo <acme@conectiva.com.br>,"
"Gustavo Niemeyer <niemeyer@conectiva.com>");
MODULE_DESCRIPTION("Planet wl3501 wireless driver");
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment