Commit aca361c1 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6

* 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6: (45 commits)
  [PATCH] Restore channel setting after scan.
  [PATCH] hostap: Fix memory leak on PCI probe error path
  [PATCH] hostap: Remove dead code (duplicated idx != 0)
  [PATCH] hostap: Fix unlikely read overrun in CIS parsing
  [PATCH] hostap: Fix double free in prism2_config() error path
  [PATCH] hostap: Fix ap_add_sta() return value verification
  [PATCH] hostap: Fix hw reset after CMDCODE_ACCESS_WRITE timeout
  [PATCH] wireless/airo: cache wireless scans
  [PATCH] wireless/airo: define default MTU
  [PATCH] wireless/airo: clean up printk usage to print device name
  [PATCH] WE-20 for kernel 2.6.16
  [PATCH] softmac: remove function_enter()
  [PATCH] skge: version 1.5
  [PATCH] skge: compute available ring buffers
  [PATCH] skge: dont free skb until multi-part transmit complete
  [PATCH] skge: multicast statistics fix
  [PATCH] skge: rx_reuse called twice
  [PATCH] skge: dont use dev_alloc_skb for rx buffs
  [PATCH] skge: align receive buffers
  [PATCH] sky2: dont need to use dev_kfree_skb_any
  ...
parents cec60620 9b7c8489
......@@ -44,7 +44,7 @@
#include "skge.h"
#define DRV_NAME "skge"
#define DRV_VERSION "1.4"
#define DRV_VERSION "1.5"
#define PFX DRV_NAME " "
#define DEFAULT_TX_RING_SIZE 128
......@@ -357,7 +357,7 @@ static struct net_device_stats *skge_get_stats(struct net_device *dev)
skge->net_stats.rx_bytes = data[1];
skge->net_stats.tx_packets = data[2] + data[4] + data[6];
skge->net_stats.rx_packets = data[3] + data[5] + data[7];
skge->net_stats.multicast = data[5] + data[7];
skge->net_stats.multicast = data[3] + data[5];
skge->net_stats.collisions = data[10];
skge->net_stats.tx_aborted_errors = data[12];
......@@ -781,7 +781,7 @@ static void skge_rx_setup(struct skge_port *skge, struct skge_element *e,
* Note: DMA address is not changed by chip.
* MTU not changed while receiver active.
*/
static void skge_rx_reuse(struct skge_element *e, unsigned int size)
static inline void skge_rx_reuse(struct skge_element *e, unsigned int size)
{
struct skge_rx_desc *rd = e->desc;
......@@ -829,7 +829,7 @@ static int skge_rx_fill(struct skge_port *skge)
do {
struct sk_buff *skb;
skb = dev_alloc_skb(skge->rx_buf_size + NET_IP_ALIGN);
skb = alloc_skb(skge->rx_buf_size + NET_IP_ALIGN, GFP_KERNEL);
if (!skb)
return -ENOMEM;
......@@ -847,8 +847,7 @@ static void skge_link_up(struct skge_port *skge)
LED_BLK_OFF|LED_SYNC_OFF|LED_ON);
netif_carrier_on(skge->netdev);
if (skge->tx_avail > MAX_SKB_FRAGS + 1)
netif_wake_queue(skge->netdev);
netif_wake_queue(skge->netdev);
if (netif_msg_link(skge))
printk(KERN_INFO PFX
......@@ -2155,7 +2154,7 @@ static int skge_up(struct net_device *dev)
printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
if (dev->mtu > RX_BUF_SIZE)
skge->rx_buf_size = dev->mtu + ETH_HLEN + NET_IP_ALIGN;
skge->rx_buf_size = dev->mtu + ETH_HLEN;
else
skge->rx_buf_size = RX_BUF_SIZE;
......@@ -2190,8 +2189,6 @@ static int skge_up(struct net_device *dev)
if (err)
goto free_rx_ring;
skge->tx_avail = skge->tx_ring.count - 1;
/* Initialize MAC */
spin_lock_bh(&hw->phy_lock);
if (hw->chip_id == CHIP_ID_GENESIS)
......@@ -2294,6 +2291,12 @@ static int skge_down(struct net_device *dev)
return 0;
}
static inline int skge_avail(const struct skge_ring *ring)
{
return ((ring->to_clean > ring->to_use) ? 0 : ring->count)
+ (ring->to_clean - ring->to_use) - 1;
}
static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
{
struct skge_port *skge = netdev_priv(dev);
......@@ -2314,7 +2317,7 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_LOCKED;
}
if (unlikely(skge->tx_avail < skb_shinfo(skb)->nr_frags +1)) {
if (unlikely(skge_avail(&skge->tx_ring) < skb_shinfo(skb)->nr_frags + 1)) {
if (!netif_queue_stopped(dev)) {
netif_stop_queue(dev);
......@@ -2390,8 +2393,7 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
dev->name, e - ring->start, skb->len);
ring->to_use = e->next;
skge->tx_avail -= skb_shinfo(skb)->nr_frags + 1;
if (skge->tx_avail <= MAX_SKB_FRAGS + 1) {
if (skge_avail(&skge->tx_ring) <= MAX_SKB_FRAGS + 1) {
pr_debug("%s: transmit queue full\n", dev->name);
netif_stop_queue(dev);
}
......@@ -2404,35 +2406,37 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
static inline void skge_tx_free(struct skge_hw *hw, struct skge_element *e)
static void skge_tx_complete(struct skge_port *skge, struct skge_element *last)
{
/* This ring element can be skb or fragment */
if (e->skb) {
pci_unmap_single(hw->pdev,
pci_unmap_addr(e, mapaddr),
pci_unmap_len(e, maplen),
PCI_DMA_TODEVICE);
dev_kfree_skb(e->skb);
struct pci_dev *pdev = skge->hw->pdev;
struct skge_element *e;
for (e = skge->tx_ring.to_clean; e != last; e = e->next) {
struct sk_buff *skb = e->skb;
int i;
e->skb = NULL;
} else {
pci_unmap_page(hw->pdev,
pci_unmap_addr(e, mapaddr),
pci_unmap_len(e, maplen),
PCI_DMA_TODEVICE);
pci_unmap_single(pdev, pci_unmap_addr(e, mapaddr),
skb_headlen(skb), PCI_DMA_TODEVICE);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
e = e->next;
pci_unmap_page(pdev, pci_unmap_addr(e, mapaddr),
skb_shinfo(skb)->frags[i].size,
PCI_DMA_TODEVICE);
}
dev_kfree_skb(skb);
}
skge->tx_ring.to_clean = e;
}
static void skge_tx_clean(struct skge_port *skge)
{
struct skge_ring *ring = &skge->tx_ring;
struct skge_element *e;
spin_lock_bh(&skge->tx_lock);
for (e = ring->to_clean; e != ring->to_use; e = e->next) {
++skge->tx_avail;
skge_tx_free(skge->hw, e);
}
ring->to_clean = e;
skge_tx_complete(skge, skge->tx_ring.to_use);
netif_wake_queue(skge->netdev);
spin_unlock_bh(&skge->tx_lock);
}
......@@ -2592,7 +2596,7 @@ static inline struct sk_buff *skge_rx_get(struct skge_port *skge,
goto error;
if (len < RX_COPY_THRESHOLD) {
skb = dev_alloc_skb(len + 2);
skb = alloc_skb(len + 2, GFP_ATOMIC);
if (!skb)
goto resubmit;
......@@ -2607,10 +2611,11 @@ static inline struct sk_buff *skge_rx_get(struct skge_port *skge,
skge_rx_reuse(e, skge->rx_buf_size);
} else {
struct sk_buff *nskb;
nskb = dev_alloc_skb(skge->rx_buf_size + NET_IP_ALIGN);
nskb = alloc_skb(skge->rx_buf_size + NET_IP_ALIGN, GFP_ATOMIC);
if (!nskb)
goto resubmit;
skb_reserve(nskb, NET_IP_ALIGN);
pci_unmap_single(skge->hw->pdev,
pci_unmap_addr(e, mapaddr),
pci_unmap_len(e, maplen),
......@@ -2661,30 +2666,29 @@ static inline struct sk_buff *skge_rx_get(struct skge_port *skge,
static void skge_tx_done(struct skge_port *skge)
{
struct skge_ring *ring = &skge->tx_ring;
struct skge_element *e;
struct skge_element *e, *last;
spin_lock(&skge->tx_lock);
for (e = ring->to_clean; prefetch(e->next), e != ring->to_use; e = e->next) {
last = ring->to_clean;
for (e = ring->to_clean; e != ring->to_use; e = e->next) {
struct skge_tx_desc *td = e->desc;
u32 control;
rmb();
control = td->control;
if (control & BMU_OWN)
if (td->control & BMU_OWN)
break;
if (unlikely(netif_msg_tx_done(skge)))
printk(KERN_DEBUG PFX "%s: tx done slot %td status 0x%x\n",
skge->netdev->name, e - ring->start, td->status);
skge_tx_free(skge->hw, e);
e->skb = NULL;
++skge->tx_avail;
if (td->control & BMU_EOF) {
last = e->next;
if (unlikely(netif_msg_tx_done(skge)))
printk(KERN_DEBUG PFX "%s: tx done slot %td\n",
skge->netdev->name, e - ring->start);
}
}
ring->to_clean = e;
skge_tx_complete(skge, last);
skge_write8(skge->hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F);
if (skge->tx_avail > MAX_SKB_FRAGS + 1)
if (skge_avail(&skge->tx_ring) > MAX_SKB_FRAGS + 1)
netif_wake_queue(skge->netdev);
spin_unlock(&skge->tx_lock);
......@@ -2718,8 +2722,7 @@ static int skge_poll(struct net_device *dev, int *budget)
netif_receive_skb(skb);
++work_done;
} else
skge_rx_reuse(e, skge->rx_buf_size);
}
}
ring->to_clean = e;
......
......@@ -2418,7 +2418,6 @@ struct skge_port {
int port;
spinlock_t tx_lock;
u32 tx_avail;
struct skge_ring tx_ring;
struct skge_ring rx_ring;
......
......@@ -1175,7 +1175,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
/* just drop the packet if non-linear expansion fails */
if (skb_header_cloned(skb) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
dev_kfree_skb_any(skb);
dev_kfree_skb(skb);
goto out_unlock;
}
......@@ -1324,7 +1324,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
PCI_DMA_TODEVICE);
}
dev_kfree_skb_any(skb);
dev_kfree_skb(skb);
}
sky2->tx_cons = put;
......@@ -2484,7 +2484,7 @@ static const struct sky2_stat {
{ "single_collisions", GM_TXF_SNG_COL },
{ "multi_collisions", GM_TXF_MUL_COL },
{ "rx_short", GM_RXE_SHT },
{ "rx_short", GM_RXF_SHT },
{ "rx_runt", GM_RXE_FRAG },
{ "rx_64_byte_packets", GM_RXF_64B },
{ "rx_65_to_127_byte_packets", GM_RXF_127B },
......@@ -2607,7 +2607,7 @@ static struct net_device_stats *sky2_get_stats(struct net_device *dev)
sky2->net_stats.rx_bytes = data[1];
sky2->net_stats.tx_packets = data[2] + data[4] + data[6];
sky2->net_stats.rx_packets = data[3] + data[5] + data[7];
sky2->net_stats.multicast = data[5] + data[7];
sky2->net_stats.multicast = data[3] + data[5];
sky2->net_stats.collisions = data[10];
sky2->net_stats.tx_aborted_errors = data[12];
......
......@@ -25,6 +25,15 @@ config NET_RADIO
the tools from
<http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
config NET_WIRELESS_RTNETLINK
bool "Wireless Extension API over RtNetlink"
---help---
Support the Wireless Extension API over the RtNetlink socket
in addition to the traditional ioctl interface (selected above).
For now, few tools use this facility, but it might grow in the
future. The only downside is that it adds 4.5 kB to your kernel.
# Note : the cards are obsolete (can't buy them anymore), but the drivers
# are not, as people are still using them...
comment "Obsolete Wireless cards support (pre-802.11)"
......
......@@ -769,6 +769,11 @@ typedef struct {
u16 atimWindow;
} BSSListRid;
typedef struct {
BSSListRid bss;
struct list_head list;
} BSSListElement;
typedef struct {
u8 rssipct;
u8 rssidBm;
......@@ -902,6 +907,7 @@ static char swversion[] = "2.1";
#define NUM_MODULES 2
#define MIC_MSGLEN_MAX 2400
#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
#define AIRO_DEF_MTU 2312
typedef struct {
u32 size; // size
......@@ -1119,6 +1125,8 @@ static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket,
static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
static void airo_networks_free(struct airo_info *ai);
struct airo_info {
struct net_device_stats stats;
struct net_device *dev;
......@@ -1150,7 +1158,7 @@ struct airo_info {
#define FLAG_COMMIT 13
#define FLAG_RESET 14
#define FLAG_FLASHING 15
#define JOB_MASK 0x1ff0000
#define JOB_MASK 0x2ff0000
#define JOB_DIE 16
#define JOB_XMIT 17
#define JOB_XMIT11 18
......@@ -1160,6 +1168,7 @@ struct airo_info {
#define JOB_EVENT 22
#define JOB_AUTOWEP 23
#define JOB_WSTATS 24
#define JOB_SCAN_RESULTS 25
int (*bap_read)(struct airo_info*, u16 *pu16Dst, int bytelen,
int whichbap);
unsigned short *flash;
......@@ -1176,7 +1185,7 @@ struct airo_info {
} xmit, xmit11;
struct net_device *wifidev;
struct iw_statistics wstats; // wireless stats
unsigned long scan_timestamp; /* Time started to scan */
unsigned long scan_timeout; /* Time scan should be read */
struct iw_spy_data spy_data;
struct iw_public_data wireless_data;
/* MIC stuff */
......@@ -1198,6 +1207,10 @@ struct airo_info {
APListRid *APList;
#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
char proc_name[IFNAMSIZ];
struct list_head network_list;
struct list_head network_free_list;
BSSListElement *networks;
};
static inline int bap_read(struct airo_info *ai, u16 *pu16Dst, int bytelen,
......@@ -1216,6 +1229,22 @@ static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
static int flashputbuf(struct airo_info *ai);
static int flashrestart(struct airo_info *ai,struct net_device *dev);
#define airo_print(type, name, fmt, args...) \
{ printk(type "airo(%s): " fmt "\n", name, ##args); }
#define airo_print_info(name, fmt, args...) \
airo_print(KERN_INFO, name, fmt, ##args)
#define airo_print_dbg(name, fmt, args...) \
airo_print(KERN_DEBUG, name, fmt, ##args)
#define airo_print_warn(name, fmt, args...) \
airo_print(KERN_WARNING, name, fmt, ##args)
#define airo_print_err(name, fmt, args...) \
airo_print(KERN_ERR, name, fmt, ##args)
/***********************************************************************
* MIC ROUTINES *
***********************************************************************
......@@ -1294,7 +1323,7 @@ static int micsetup(struct airo_info *ai) {
ai->tfm = crypto_alloc_tfm("aes", CRYPTO_TFM_REQ_MAY_SLEEP);
if (ai->tfm == NULL) {
printk(KERN_ERR "airo: failed to load transform for AES\n");
airo_print_err(ai->dev->name, "failed to load transform for AES");
return ERROR;
}
......@@ -1726,11 +1755,11 @@ static int writeWepKeyRid(struct airo_info*ai, WepKeyRid *pwkr, int perm, int lo
wkr.kindex = cpu_to_le16(wkr.kindex);
wkr.klen = cpu_to_le16(wkr.klen);
rc = PC4500_writerid(ai, RID_WEP_TEMP, &wkr, sizeof(wkr), lock);
if (rc!=SUCCESS) printk(KERN_ERR "airo: WEP_TEMP set %x\n", rc);
if (rc!=SUCCESS) airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc);
if (perm) {
rc = PC4500_writerid(ai, RID_WEP_PERM, &wkr, sizeof(wkr), lock);
if (rc!=SUCCESS) {
printk(KERN_ERR "airo: WEP_PERM set %x\n", rc);
airo_print_err(ai->dev->name, "WEP_PERM set %x", rc);
}
}
return rc;
......@@ -1909,7 +1938,7 @@ static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) {
struct airo_info *ai = dev->priv;
if (!skb) {
printk(KERN_ERR "airo: %s: skb==NULL\n",__FUNCTION__);
airo_print_err(dev->name, "%s: skb == NULL!",__FUNCTION__);
return 0;
}
npacks = skb_queue_len (&ai->txq);
......@@ -1955,8 +1984,8 @@ static int mpi_send_packet (struct net_device *dev)
/* get a packet to send */
if ((skb = skb_dequeue(&ai->txq)) == 0) {
printk (KERN_ERR
"airo: %s: Dequeue'd zero in send_packet()\n",
airo_print_err(dev->name,
"%s: Dequeue'd zero in send_packet()",
__FUNCTION__);
return 0;
}
......@@ -2108,7 +2137,7 @@ static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) {
u32 *fids = priv->fids;
if ( skb == NULL ) {
printk( KERN_ERR "airo: skb == NULL!!!\n" );
airo_print_err(dev->name, "%s: skb == NULL!", __FUNCTION__);
return 0;
}
......@@ -2179,7 +2208,7 @@ static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) {
}
if ( skb == NULL ) {
printk( KERN_ERR "airo: skb == NULL!!!\n" );
airo_print_err(dev->name, "%s: skb == NULL!", __FUNCTION__);
return 0;
}
......@@ -2364,6 +2393,8 @@ void stop_airo_card( struct net_device *dev, int freeres )
dev_kfree_skb(skb);
}
airo_networks_free (ai);
kfree(ai->flash);
kfree(ai->rssi);
kfree(ai->APList);
......@@ -2434,7 +2465,7 @@ static int mpi_init_descriptors (struct airo_info *ai)
cmd.parm2 = MPI_MAX_FIDS;
rc=issuecommand(ai, &cmd, &rsp);
if (rc != SUCCESS) {
printk(KERN_ERR "airo: Couldn't allocate RX FID\n");
airo_print_err(ai->dev->name, "Couldn't allocate RX FID");
return rc;
}
......@@ -2462,7 +2493,7 @@ static int mpi_init_descriptors (struct airo_info *ai)
rc=issuecommand(ai, &cmd, &rsp);
if (rc != SUCCESS) {
printk(KERN_ERR "airo: Couldn't allocate TX FID\n");
airo_print_err(ai->dev->name, "Couldn't allocate TX FID");
return rc;
}
......@@ -2476,7 +2507,7 @@ static int mpi_init_descriptors (struct airo_info *ai)
cmd.parm2 = 1; /* Magic number... */
rc=issuecommand(ai, &cmd, &rsp);
if (rc != SUCCESS) {
printk(KERN_ERR "airo: Couldn't allocate RID\n");
airo_print_err(ai->dev->name, "Couldn't allocate RID");
return rc;
}
......@@ -2508,25 +2539,25 @@ static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci,
aux_len = AUXMEMSIZE;
if (!request_mem_region(mem_start, mem_len, name)) {
printk(KERN_ERR "airo: Couldn't get region %x[%x] for %s\n",
airo_print_err(ai->dev->name, "Couldn't get region %x[%x] for %s",
(int)mem_start, (int)mem_len, name);
goto out;
}
if (!request_mem_region(aux_start, aux_len, name)) {
printk(KERN_ERR "airo: Couldn't get region %x[%x] for %s\n",
airo_print_err(ai->dev->name, "Couldn't get region %x[%x] for %s",
(int)aux_start, (int)aux_len, name);
goto free_region1;
}
ai->pcimem = ioremap(mem_start, mem_len);
if (!ai->pcimem) {
printk(KERN_ERR "airo: Couldn't map region %x[%x] for %s\n",
airo_print_err(ai->dev->name, "Couldn't map region %x[%x] for %s",
(int)mem_start, (int)mem_len, name);
goto free_region2;
}
ai->pciaux = ioremap(aux_start, aux_len);
if (!ai->pciaux) {
printk(KERN_ERR "airo: Couldn't map region %x[%x] for %s\n",
airo_print_err(ai->dev->name, "Couldn't map region %x[%x] for %s",
(int)aux_start, (int)aux_len, name);
goto free_memmap;
}
......@@ -2534,7 +2565,7 @@ static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci,
/* Reserve PKTSIZE for each fid and 2K for the Rids */
ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma);
if (!ai->shared) {
printk(KERN_ERR "airo: Couldn't alloc_consistent %d\n",
airo_print_err(ai->dev->name, "Couldn't alloc_consistent %d",
PCI_SHARED_LEN);
goto free_auxmap;
}
......@@ -2626,7 +2657,7 @@ static void wifi_setup(struct net_device *dev)
dev->type = ARPHRD_IEEE80211;
dev->hard_header_len = ETH_HLEN;
dev->mtu = 2312;
dev->mtu = AIRO_DEF_MTU;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 100;
......@@ -2670,6 +2701,42 @@ static int reset_card( struct net_device *dev , int lock) {
return 0;
}
#define MAX_NETWORK_COUNT 64
static int airo_networks_allocate(struct airo_info *ai)
{
if (ai->networks)
return 0;
ai->networks =
kzalloc(MAX_NETWORK_COUNT * sizeof(BSSListElement),
GFP_KERNEL);
if (!ai->networks) {
airo_print_warn(ai->dev->name, "Out of memory allocating beacons");
return -ENOMEM;
}
return 0;
}
static void airo_networks_free(struct airo_info *ai)
{
if (!ai->networks)
return;
kfree(ai->networks);
ai->networks = NULL;
}
static void airo_networks_initialize(struct airo_info *ai)
{
int i;
INIT_LIST_HEAD(&ai->network_free_list);
INIT_LIST_HEAD(&ai->network_list);
for (i = 0; i < MAX_NETWORK_COUNT; i++)
list_add_tail(&ai->networks[i].list,
&ai->network_free_list);
}
static struct net_device *_init_airo_card( unsigned short irq, int port,
int is_pcmcia, struct pci_dev *pci,
struct device *dmdev )
......@@ -2681,22 +2748,22 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
/* Create the network device object. */
dev = alloc_etherdev(sizeof(*ai));
if (!dev) {
printk(KERN_ERR "airo: Couldn't alloc_etherdev\n");
airo_print_err("", "Couldn't alloc_etherdev");
return NULL;
}
if (dev_alloc_name(dev, dev->name) < 0) {
printk(KERN_ERR "airo: Couldn't get name!\n");
airo_print_err("", "Couldn't get name!");
goto err_out_free;
}
ai = dev->priv;
ai->wifidev = NULL;
ai->flags = 0;
ai->dev = dev;
if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) {
printk(KERN_DEBUG "airo: Found an MPI350 card\n");
airo_print_dbg(dev->name, "Found an MPI350 card");
set_bit(FLAG_MPI, &ai->flags);
}
ai->dev = dev;
spin_lock_init(&ai->aux_lock);
sema_init(&ai->sem, 1);
ai->config.len = 0;
......@@ -2711,6 +2778,10 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
if (rc)
goto err_out_thr;
if (airo_networks_allocate (ai))
goto err_out_unlink;
airo_networks_initialize (ai);
/* The Airo-specific entries in the device structure. */
if (test_bit(FLAG_MPI,&ai->flags)) {
skb_queue_head_init (&ai->txq);
......@@ -2732,33 +2803,33 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
SET_NETDEV_DEV(dev, dmdev);
reset_card (dev, 1);
msleep(400);
rc = request_irq( dev->irq, airo_interrupt, SA_SHIRQ, dev->name, dev );
if (rc) {
printk(KERN_ERR "airo: register interrupt %d failed, rc %d\n", irq, rc );
airo_print_err(dev->name, "register interrupt %d failed, rc %d",
irq, rc);
goto err_out_unlink;
}
if (!is_pcmcia) {
if (!request_region( dev->base_addr, 64, dev->name )) {
rc = -EBUSY;
printk(KERN_ERR "airo: Couldn't request region\n");
airo_print_err(dev->name, "Couldn't request region");
goto err_out_irq;
}
}
if (test_bit(FLAG_MPI,&ai->flags)) {
if (mpi_map_card(ai, pci, dev->name)) {
printk(KERN_ERR "airo: Could not map memory\n");
airo_print_err(dev->name, "Could not map memory");
goto err_out_res;
}
}
if (probe) {
if ( setup_card( ai, dev->dev_addr, 1 ) != SUCCESS ) {
printk( KERN_ERR "airo: MAC could not be enabled\n" );
airo_print_err(dev->name, "MAC could not be enabled" );
rc = -EIO;
goto err_out_map;
}
......@@ -2769,21 +2840,20 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
rc = register_netdev(dev);
if (rc) {
printk(KERN_ERR "airo: Couldn't register_netdev\n");
airo_print_err(dev->name, "Couldn't register_netdev");
goto err_out_map;
}
ai->wifidev = init_wifidev(ai, dev);
set_bit(FLAG_REGISTERED,&ai->flags);
printk( KERN_INFO "airo: MAC enabled %s %x:%x:%x:%x:%x:%x\n",
dev->name,
airo_print_info(dev->name, "MAC enabled %x:%x:%x:%x:%x:%x",
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5] );
/* Allocate the transmit buffers */
if (probe && !test_bit(FLAG_MPI,&ai->flags))
for( i = 0; i < MAX_FIDS; i++ )
ai->fids[i] = transmit_allocate(ai,2312,i>=MAX_FIDS/2);
ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
setup_proc_entry( dev, dev->priv ); /* XXX check for failure */
netif_start_queue(dev);
......@@ -2840,16 +2910,16 @@ int reset_airo_card( struct net_device *dev )
return -1;
if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) {
printk( KERN_ERR "airo: MAC could not be enabled\n" );
airo_print_err(dev->name, "MAC could not be enabled");
return -1;
}
printk( KERN_INFO "airo: MAC enabled %s %x:%x:%x:%x:%x:%x\n", dev->name,
airo_print_info(dev->name, "MAC enabled %x:%x:%x:%x:%x:%x",
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
/* Allocate the transmit buffers if needed */
if (!test_bit(FLAG_MPI,&ai->flags))
for( i = 0; i < MAX_FIDS; i++ )
ai->fids[i] = transmit_allocate (ai,2312,i>=MAX_FIDS/2);
ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
enable_interrupts( ai );
netif_wake_queue(dev);
......@@ -2875,6 +2945,65 @@ static void airo_send_event(struct net_device *dev) {
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
}
static void airo_process_scan_results (struct airo_info *ai) {
union iwreq_data wrqu;
BSSListRid BSSList;
int rc;
BSSListElement * loop_net;
BSSListElement * tmp_net;
/* Blow away current list of scan results */
list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) {
list_move_tail (&loop_net->list, &ai->network_free_list);
/* Don't blow away ->list, just BSS data */
memset (loop_net, 0, sizeof (loop_net->bss));
}
/* Try to read the first entry of the scan result */
rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList), 0);
if((rc) || (BSSList.index == 0xffff)) {
/* No scan results */
goto out;
}
/* Read and parse all entries */
tmp_net = NULL;
while((!rc) && (BSSList.index != 0xffff)) {
/* Grab a network off the free list */
if (!list_empty(&ai->network_free_list)) {
tmp_net = list_entry(ai->network_free_list.next,
BSSListElement, list);
list_del(ai->network_free_list.next);
}
if (tmp_net != NULL) {
memcpy(tmp_net, &BSSList, sizeof(tmp_net->bss));
list_add_tail(&tmp_net->list, &ai->network_list);
tmp_net = NULL;
}
/* Read next entry */
rc = PC4500_readrid(ai, RID_BSSLISTNEXT,
&BSSList, sizeof(BSSList), 0);
}
out:
ai->scan_timeout = 0;
clear_bit(JOB_SCAN_RESULTS, &ai->flags);
up(&ai->sem);
/* Send an empty event to user space.
* We don't send the received data on
* the event because it would require
* us to do complex transcoding, and
* we want to minimise the work done in
* the irq handler. Use a request to
* extract the data - Jean II */
wrqu.data.length = 0;
wrqu.data.flags = 0;
wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL);
}
static int airo_thread(void *data) {
struct net_device *dev = data;
struct airo_info *ai = dev->priv;
......@@ -2904,13 +3033,26 @@ static int airo_thread(void *data) {
set_current_state(TASK_INTERRUPTIBLE);
if (ai->flags & JOB_MASK)
break;
if (ai->expires) {
if (time_after_eq(jiffies,ai->expires)){
if (ai->expires || ai->scan_timeout) {
if (ai->scan_timeout &&
time_after_eq(jiffies,ai->scan_timeout)){
set_bit(JOB_SCAN_RESULTS,&ai->flags);
break;
} else if (ai->expires &&
time_after_eq(jiffies,ai->expires)){
set_bit(JOB_AUTOWEP,&ai->flags);
break;
}
if (!signal_pending(current)) {
schedule_timeout(ai->expires - jiffies);
unsigned long wake_at;
if (!ai->expires || !ai->scan_timeout) {
wake_at = max(ai->expires,
ai->scan_timeout);
} else {
wake_at = min(ai->expires,
ai->scan_timeout);
}
schedule_timeout(wake_at - jiffies);
continue;
}
} else if (!signal_pending(current)) {
......@@ -2953,6 +3095,10 @@ static int airo_thread(void *data) {
airo_send_event(dev);
else if (test_bit(JOB_AUTOWEP, &ai->flags))
timer_func(dev);
else if (test_bit(JOB_SCAN_RESULTS, &ai->flags))
airo_process_scan_results(ai);
else /* Shouldn't get here, but we make sure to unlock */
up(&ai->sem);
}
complete_and_exit (&ai->thr_exited, 0);
}
......@@ -3047,19 +3193,15 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
* and reassociations as valid status
* Jean II */
if(newStatus == ASSOCIATED) {
if (apriv->scan_timestamp) {
/* Send an empty event to user space.
* We don't send the received data on
* the event because it would require
* us to do complex transcoding, and
* we want to minimise the work done in
* the irq handler. Use a request to
* extract the data - Jean II */
wrqu.data.length = 0;
wrqu.data.flags = 0;
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
apriv->scan_timestamp = 0;
#if 0
/* FIXME: Grabbing scan results here
* seems to be too early??? Just wait for
* timeout instead. */
if (apriv->scan_timeout > 0) {
set_bit(JOB_SCAN_RESULTS, &apriv->flags);
wake_up_interruptible(&apriv->thr_wait);
}
#endif
if (down_trylock(&apriv->sem) != 0) {
set_bit(JOB_EVENT, &apriv->flags);
wake_up_interruptible(&apriv->thr_wait);
......@@ -3117,8 +3259,8 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
}
len = le16_to_cpu(hdr.len);
if (len > 2312) {
printk( KERN_ERR "airo: Bad size %d\n", len );
if (len > AIRO_DEF_MTU) {
airo_print_err(apriv->dev->name, "Bad size %d", len);
goto badrx;
}
if (len == 0)
......@@ -3161,10 +3303,12 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
bap_read (apriv, &gap, sizeof(gap), BAP0);
gap = le16_to_cpu(gap);
if (gap) {
if (gap <= 8)
if (gap <= 8) {
bap_read (apriv, tmpbuf, gap, BAP0);
else
printk(KERN_ERR "airo: gaplen too big. Problems will follow...\n");
} else {
airo_print_err(apriv->dev->name, "gaplen too "
"big. Problems will follow...");
}
}
bap_read (apriv, buffer + hdrlen/2, len, BAP0);
} else {
......@@ -3281,12 +3425,13 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
}
} else {
OUT4500( apriv, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC));
printk( KERN_ERR "airo: Unallocated FID was used to xmit\n" );
airo_print_err(apriv->dev->name, "Unallocated FID was "
"used to xmit" );
}
}
exittx:
if ( status & ~STATUS_INTS & ~IGNORE_INTS )
printk( KERN_WARNING "airo: Got weird status %x\n",
airo_print_warn(apriv->dev->name, "Got weird status %x",
status & ~STATUS_INTS & ~IGNORE_INTS );
}
......@@ -3359,8 +3504,8 @@ static int enable_MAC( struct airo_info *ai, Resp *rsp, int lock ) {
up(&ai->sem);
if (rc)
printk(KERN_ERR "%s: Cannot enable MAC, err=%d\n",
__FUNCTION__,rc);
airo_print_err(ai->dev->name, "%s: Cannot enable MAC, err=%d",
__FUNCTION__, rc);
return rc;
}
......@@ -3489,8 +3634,8 @@ void mpi_receive_802_11 (struct airo_info *ai)
if (ai->wifidev == NULL)
hdr.len = 0;
len = le16_to_cpu(hdr.len);
if (len > 2312) {
printk( KERN_ERR "airo: Bad size %d\n", len );
if (len > AIRO_DEF_MTU) {
airo_print_err(ai->dev->name, "Bad size %d", len);
goto badrx;
}
if (len == 0)
......@@ -3531,8 +3676,8 @@ void mpi_receive_802_11 (struct airo_info *ai)
if (gap <= 8)
ptr += gap;
else
printk(KERN_ERR
"airo: gaplen too big. Problems will follow...\n");
airo_print_err(ai->dev->name,
"gaplen too big. Problems will follow...");
}
memcpy ((char *)buffer + hdrlen, ptr, len);
ptr += len;
......@@ -3604,15 +3749,15 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
if (lock)
up(&ai->sem);
printk(KERN_ERR "airo: Error checking for AUX port\n");
airo_print_err(ai->dev->name, "Error checking for AUX port");
return ERROR;
}
if (!aux_bap || rsp.status & 0xff00) {
ai->bap_read = fast_bap_read;
printk(KERN_DEBUG "airo: Doing fast bap_reads\n");
airo_print_dbg(ai->dev->name, "Doing fast bap_reads");
} else {
ai->bap_read = aux_bap_read;
printk(KERN_DEBUG "airo: Doing AUX bap_reads\n");
airo_print_dbg(ai->dev->name, "Doing AUX bap_reads");
}
}
if (lock)
......@@ -3643,7 +3788,8 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
if (cap_rid.softCap & 8)
ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
else
printk(KERN_WARNING "airo: unknown received signal level scale\n");
airo_print_warn(ai->dev->name, "unknown received signal "
"level scale");
}
ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
ai->config.authType = AUTH_OPEN;
......@@ -3706,7 +3852,8 @@ static u16 setup_card(struct airo_info *ai, u8 *mac, int lock)
status = enable_MAC(ai, &rsp, lock);
if ( status != SUCCESS || (rsp.status & 0xFF00) != 0) {
printk( KERN_ERR "airo: Bad MAC enable reason = %x, rid = %x, offset = %d\n", rsp.rsp0, rsp.rsp1, rsp.rsp2 );
airo_print_err(ai->dev->name, "Bad MAC enable reason = %x, rid = %x,"
" offset = %d", rsp.rsp0, rsp.rsp1, rsp.rsp2 );
return ERROR;
}
......@@ -3749,8 +3896,8 @@ static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
}
if ( max_tries == -1 ) {
printk( KERN_ERR
"airo: Max tries exceeded when issueing command\n" );
airo_print_err(ai->dev->name,
"Max tries exceeded when issueing command");
if (IN4500(ai, COMMAND) & COMMAND_BUSY)
OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY);
return ERROR;
......@@ -3762,11 +3909,11 @@ static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) {
pRsp->rsp1 = IN4500(ai, RESP1);
pRsp->rsp2 = IN4500(ai, RESP2);
if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET) {
printk (KERN_ERR "airo: cmd= %x\n", pCmd->cmd);
printk (KERN_ERR "airo: status= %x\n", pRsp->status);
printk (KERN_ERR "airo: Rsp0= %x\n", pRsp->rsp0);
printk (KERN_ERR "airo: Rsp1= %x\n", pRsp->rsp1);
printk (KERN_ERR "airo: Rsp2= %x\n", pRsp->rsp2);
airo_print_err(ai->dev->name, "cmd= %x\n", pCmd->cmd);
airo_print_err(ai->dev->name, "status= %x\n", pRsp->status);
airo_print_err(ai->dev->name, "Rsp0= %x\n", pRsp->rsp0);
airo_print_err(ai->dev->name, "Rsp1= %x\n", pRsp->rsp1);
airo_print_err(ai->dev->name, "Rsp2= %x\n", pRsp->rsp2);
}
// clear stuck command busy if necessary
......@@ -3799,15 +3946,15 @@ static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap )
}
} else if ( status & BAP_ERR ) {
/* invalid rid or offset */
printk( KERN_ERR "airo: BAP error %x %d\n",
airo_print_err(ai->dev->name, "BAP error %x %d",
status, whichbap );
return ERROR;
} else if (status & BAP_DONE) { // success
return SUCCESS;
}
if ( !(max_tries--) ) {
printk( KERN_ERR
"airo: BAP setup error too many retries\n" );
airo_print_err(ai->dev->name,
"airo: BAP setup error too many retries\n");
return ERROR;
}
// -- PC4500 missed it, try again
......@@ -3962,8 +4109,8 @@ static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, in
len = min(len, (int)le16_to_cpu(*(u16*)pBuf)) - 2;
if ( len <= 2 ) {
printk( KERN_ERR
"airo: Rid %x has a length of %d which is too short\n",
airo_print_err(ai->dev->name,
"Rid %x has a length of %d which is too short",
(int)rid, (int)len );
rc = ERROR;
goto done;
......@@ -3996,8 +4143,8 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid,
Resp rsp;
if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid))
printk(KERN_ERR
"%s: MAC should be disabled (rid=%04x)\n",
airo_print_err(ai->dev->name,
"%s: MAC should be disabled (rid=%04x)",
__FUNCTION__, rid);
memset(&cmd, 0, sizeof(cmd));
memset(&rsp, 0, sizeof(rsp));
......@@ -4013,7 +4160,7 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid,
&ai->config_desc.rid_desc, sizeof(Rid));
if (len < 4 || len > 2047) {
printk(KERN_ERR "%s: len=%d\n",__FUNCTION__,len);
airo_print_err(ai->dev->name, "%s: len=%d", __FUNCTION__, len);
rc = -1;
} else {
memcpy((char *)ai->config_desc.virtual_host_addr,
......@@ -4021,10 +4168,10 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid,
rc = issuecommand(ai, &cmd, &rsp);
if ((rc & 0xff00) != 0) {
printk(KERN_ERR "%s: Write rid Error %d\n",
__FUNCTION__,rc);
printk(KERN_ERR "%s: Cmd=%04x\n",
__FUNCTION__,cmd.cmd);
airo_print_err(ai->dev->name, "%s: Write rid Error %d",
__FUNCTION__, rc);
airo_print_err(ai->dev->name, "%s: Cmd=%04x",
__FUNCTION__, cmd.cmd);
}
if ((rsp.status & 0x7f00))
......@@ -4123,7 +4270,7 @@ static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
len >>= 16;
if (len <= ETH_ALEN * 2) {
printk( KERN_WARNING "Short packet %d\n", len );
airo_print_warn(ai->dev->name, "Short packet %d", len);
return ERROR;
}
len -= ETH_ALEN * 2;
......@@ -4187,7 +4334,7 @@ static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket)
}
if (len < hdrlen) {
printk( KERN_WARNING "Short packet %d\n", len );
airo_print_warn(ai->dev->name, "Short packet %d", len);
return ERROR;
}
......@@ -4584,15 +4731,14 @@ static int proc_stats_rid_open( struct inode *inode,
i*4<stats.len; i++){
if (!statsLabels[i]) continue;
if (j+strlen(statsLabels[i])+16>4096) {
printk(KERN_WARNING
"airo: Potentially disasterous buffer overflow averted!\n");
airo_print_warn(apriv->dev->name,
"Potentially disasterous buffer overflow averted!");
break;
}
j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i], vals[i]);
}
if (i*4>=stats.len){
printk(KERN_WARNING
"airo: Got a short rid\n");
airo_print_warn(apriv->dev->name, "Got a short rid");
}
data->readlen = j;
return 0;
......@@ -4754,7 +4900,7 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) {
line += 14;
v = get_dec_u16(line, &i, 4);
v = (v<0) ? 0 : ((v>2312) ? 2312 : v);
v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
ai->config.rtsThres = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) {
......@@ -4788,7 +4934,7 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) {
line += 15;
v = get_dec_u16(line, &i, 4);
v = (v<256) ? 256 : ((v>2312) ? 2312 : v);
v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v);
v = v & 0xfffe; /* Make sure its even */
ai->config.fragThresh = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
......@@ -4798,8 +4944,7 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) {
case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
default:
printk( KERN_WARNING "airo: Unknown modulation\n" );
default: airo_print_warn(ai->dev->name, "Unknown modulation");
}
} else if (!strncmp(line, "Preamble: ", 10)) {
line += 10;
......@@ -4807,10 +4952,10 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) {
case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
default: printk(KERN_WARNING "airo: Unknown preamble\n");
default: airo_print_warn(ai->dev->name, "Unknown preamble");
}
} else {
printk( KERN_WARNING "Couldn't figure out %s\n", line );
airo_print_warn(ai->dev->name, "Couldn't figure out %s", line);
}
while( line[0] && line[0] != '\n' ) line++;
if ( line[0] ) line++;
......@@ -5076,7 +5221,7 @@ static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
}
j = 2;
} else {
printk(KERN_ERR "airo: WepKey passed invalid key index\n");
airo_print_err(ai->dev->name, "WepKey passed invalid key index");
return;
}
......@@ -5489,17 +5634,16 @@ static int __init airo_init_module( void )
airo_entry->gid = proc_gid;
for( i = 0; i < 4 && io[i] && irq[i]; i++ ) {
printk( KERN_INFO
"airo: Trying to configure ISA adapter at irq=%d io=0x%x\n",
irq[i], io[i] );
airo_print_info("", "Trying to configure ISA adapter at irq=%d "
"io=0x%x", irq[i], io[i] );
if (init_airo_card( irq[i], io[i], 0, NULL ))
have_isa_dev = 1;
}
#ifdef CONFIG_PCI
printk( KERN_INFO "airo: Probing for PCI adapters\n" );
airo_print_info("", "Probing for PCI adapters");
pci_register_driver(&airo_driver);
printk( KERN_INFO "airo: Finished probing for PCI adapters\n" );
airo_print_info("", "Finished probing for PCI adapters");
#endif
/* Always exit with success, as we are a library module
......@@ -5511,7 +5655,7 @@ static int __init airo_init_module( void )
static void __exit airo_cleanup_module( void )
{
while( airo_devices ) {
printk( KERN_INFO "airo: Unregistering %s\n", airo_devices->dev->name );
airo_print_info(airo_devices->dev->name, "Unregistering...\n");
stop_airo_card( airo_devices->dev, 1 );
}
#ifdef CONFIG_PCI
......@@ -5622,7 +5766,8 @@ static int airo_set_freq(struct net_device *dev,
/* We should do a better check than that,
* based on the card capability !!! */
if((channel < 1) || (channel > 14)) {
printk(KERN_DEBUG "%s: New channel value of %d is invalid!\n", dev->name, fwrq->m);
airo_print_dbg(dev->name, "New channel value of %d is invalid!",
fwrq->m);
rc = -EINVAL;
} else {
readConfigRid(local, 1);
......@@ -5946,8 +6091,8 @@ static int airo_set_rts(struct net_device *dev,
int rthr = vwrq->value;
if(vwrq->disabled)
rthr = 2312;
if((rthr < 0) || (rthr > 2312)) {
rthr = AIRO_DEF_MTU;
if((rthr < 0) || (rthr > AIRO_DEF_MTU)) {
return -EINVAL;
}
readConfigRid(local, 1);
......@@ -5970,7 +6115,7 @@ static int airo_get_rts(struct net_device *dev,
readConfigRid(local, 1);
vwrq->value = local->config.rtsThres;
vwrq->disabled = (vwrq->value >= 2312);
vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
vwrq->fixed = 1;
return 0;
......@@ -5989,8 +6134,8 @@ static int airo_set_frag(struct net_device *dev,
int fthr = vwrq->value;
if(vwrq->disabled)
fthr = 2312;
if((fthr < 256) || (fthr > 2312)) {
fthr = AIRO_DEF_MTU;
if((fthr < 256) || (fthr > AIRO_DEF_MTU)) {
return -EINVAL;
}
fthr &= ~0x1; /* Get an even value - is it really needed ??? */
......@@ -6014,7 +6159,7 @@ static int airo_get_frag(struct net_device *dev,
readConfigRid(local, 1);
vwrq->value = local->config.fragThresh;
vwrq->disabled = (vwrq->value >= 2312);
vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU);
vwrq->fixed = 1;
return 0;
......@@ -6709,9 +6854,9 @@ static int airo_get_range(struct net_device *dev,
range->throughput = 1500 * 1000;
range->min_rts = 0;
range->max_rts = 2312;
range->max_rts = AIRO_DEF_MTU;
range->min_frag = 256;
range->max_frag = 2312;
range->max_frag = AIRO_DEF_MTU;
if(cap_rid.softCap & 2) {
// WEP: RC4 40 bits
......@@ -6972,6 +7117,7 @@ static int airo_set_scan(struct net_device *dev,
struct airo_info *ai = dev->priv;
Cmd cmd;
Resp rsp;
int wake = 0;
/* Note : you may have realised that, as this is a SET operation,
* this is privileged and therefore a normal user can't
......@@ -6981,17 +7127,25 @@ static int airo_set_scan(struct net_device *dev,
* Jean II */
if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
if (down_interruptible(&ai->sem))
return -ERESTARTSYS;
/* If there's already a scan in progress, don't
* trigger another one. */
if (ai->scan_timeout > 0)
goto out;
/* Initiate a scan command */
memset(&cmd, 0, sizeof(cmd));
cmd.cmd=CMD_LISTBSS;
if (down_interruptible(&ai->sem))
return -ERESTARTSYS;
issuecommand(ai, &cmd, &rsp);
ai->scan_timestamp = jiffies;
up(&ai->sem);
/* At this point, just return to the user. */
ai->scan_timeout = RUN_AT(3*HZ);
wake = 1;
out:
up(&ai->sem);
if (wake)
wake_up_interruptible(&ai->thr_wait);
return 0;
}
......@@ -7111,59 +7265,38 @@ static int airo_get_scan(struct net_device *dev,
char *extra)
{
struct airo_info *ai = dev->priv;
BSSListRid BSSList;
int rc;
BSSListElement *net;
int err = 0;
char *current_ev = extra;
/* When we are associated again, the scan has surely finished.
* Just in case, let's make sure enough time has elapsed since
* we started the scan. - Javier */
if(ai->scan_timestamp && time_before(jiffies,ai->scan_timestamp+3*HZ)) {
/* Important note : we don't want to block the caller
* until results are ready for various reasons.
* First, managing wait queues is complex and racy
* (there may be multiple simultaneous callers).
* Second, we grab some rtnetlink lock before comming
* here (in dev_ioctl()).
* Third, the caller can wait on the Wireless Event
* - Jean II */
/* If a scan is in-progress, return -EAGAIN */
if (ai->scan_timeout > 0)
return -EAGAIN;
}
ai->scan_timestamp = 0;
/* There's only a race with proc_BSSList_open(), but its
* consequences are begnign. So I don't bother fixing it - Javier */
/* Try to read the first entry of the scan result */
rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList), 1);
if((rc) || (BSSList.index == 0xffff)) {
/* Client error, no scan results...
* The caller need to restart the scan. */
return -ENODATA;
}
if (down_interruptible(&ai->sem))
return -EAGAIN;
/* Read and parse all entries */
while((!rc) && (BSSList.index != 0xffff)) {
list_for_each_entry (net, &ai->network_list, list) {
/* Translate to WE format this entry */
current_ev = airo_translate_scan(dev, current_ev,
extra + dwrq->length,
&BSSList);
&net->bss);
/* Check if there is space for one more entry */
if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
/* Ask user space to try again with a bigger buffer */
return -E2BIG;
err = -E2BIG;
goto out;
}
/* Read next entry */
rc = PC4500_readrid(ai, RID_BSSLISTNEXT,
&BSSList, sizeof(BSSList), 1);
}
/* Length of data */
dwrq->length = (current_ev - extra);
dwrq->flags = 0; /* todo */
return 0;
out:
up(&ai->sem);
return err;
}
/*------------------------------------------------------------------*/
......@@ -7711,7 +7844,7 @@ static int cmdreset(struct airo_info *ai) {
disable_MAC(ai, 1);
if(!waitbusy (ai)){
printk(KERN_INFO "Waitbusy hang before RESET\n");
airo_print_info(ai->dev->name, "Waitbusy hang before RESET");
return -EBUSY;
}
......@@ -7720,7 +7853,7 @@ static int cmdreset(struct airo_info *ai) {
ssleep(1); /* WAS 600 12/7/00 */
if(!waitbusy (ai)){
printk(KERN_INFO "Waitbusy hang AFTER RESET\n");
airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET");
return -EBUSY;
}
return 0;
......@@ -7748,7 +7881,7 @@ static int setflashmode (struct airo_info *ai) {
if(!waitbusy(ai)) {
clear_bit (FLAG_FLASHING, &ai->flags);
printk(KERN_INFO "Waitbusy hang after setflash mode\n");
airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode");
return -EIO;
}
return 0;
......@@ -7777,7 +7910,7 @@ static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
/* timeout for busy clear wait */
if(waittime <= 0 ){
printk(KERN_INFO "flash putchar busywait timeout! \n");
airo_print_info(ai->dev->name, "flash putchar busywait timeout!");
return -EBUSY;
}
......@@ -7866,7 +7999,7 @@ static int flashrestart(struct airo_info *ai,struct net_device *dev){
if (!test_bit(FLAG_MPI,&ai->flags))
for( i = 0; i < MAX_FIDS; i++ ) {
ai->fids[i] = transmit_allocate
( ai, 2312, i >= MAX_FIDS / 2 );
( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 );
}
ssleep(1); /* Added 12/7/00 */
......
......@@ -3141,7 +3141,7 @@ int hostap_add_sta(struct ap_data *ap, u8 *sta_addr)
if (ret == 1) {
sta = ap_add_sta(ap, sta_addr);
if (!sta)
ret = -1;
return -1;
sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
sta->ap = 1;
memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
......
......@@ -585,8 +585,6 @@ static int prism2_config(dev_link_t *link)
parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
if (parse == NULL || hw_priv == NULL) {
kfree(parse);
kfree(hw_priv);
ret = -ENOMEM;
goto failed;
}
......
......@@ -928,15 +928,15 @@ static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len)
res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL);
up(&local->rid_bap_sem);
if (res) {
printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE "
"failed (res=%d, rid=%04x, len=%d)\n",
dev->name, res, rid, len);
return res;
}
if (res == -ETIMEDOUT)
prism2_hw_reset(dev);
if (res == -ETIMEDOUT)
prism2_hw_reset(dev);
}
return res;
}
......
......@@ -3358,10 +3358,6 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
if (!sta_ptr)
local->tx_keyidx = i;
else if (i) {
ret = -EINVAL;
goto done;
}
}
......
......@@ -307,7 +307,7 @@ static int prism2_pci_probe(struct pci_dev *pdev,
memset(hw_priv, 0, sizeof(*hw_priv));
if (pci_enable_device(pdev))
return -EIO;
goto err_out_free;
phymem = pci_resource_start(pdev, 0);
......@@ -368,6 +368,8 @@ static int prism2_pci_probe(struct pci_dev *pdev,
err_out_disable:
pci_disable_device(pdev);
prism2_free_local_data(dev);
err_out_free:
kfree(hw_priv);
return -ENODEV;
......
......@@ -368,7 +368,7 @@ static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
switch (cis[pos]) {
case CISTPL_CONFIG:
if (cis[pos + 1] < 1)
if (cis[pos + 1] < 2)
goto cis_error;
rmsz = (cis[pos + 2] & 0x3c) >> 2;
rasz = cis[pos + 2] & 0x03;
......@@ -390,7 +390,7 @@ static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
break;
case CISTPL_MANFID:
if (cis[pos + 1] < 4)
if (cis[pos + 1] < 5)
goto cis_error;
manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
......@@ -452,7 +452,7 @@ static int prism2_plx_probe(struct pci_dev *pdev,
memset(hw_priv, 0, sizeof(*hw_priv));
if (pci_enable_device(pdev))
return -EIO;
goto err_out_free;
/* National Datacomm NCP130 based on TMD7160, not PLX9052. */
tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
......@@ -567,9 +567,6 @@ static int prism2_plx_probe(struct pci_dev *pdev,
return hostap_hw_ready(dev);
fail:
prism2_free_local_data(dev);
kfree(hw_priv);
if (irq_registered && dev)
free_irq(dev->irq, dev);
......@@ -577,6 +574,10 @@ static int prism2_plx_probe(struct pci_dev *pdev,
iounmap(attr_mem);
pci_disable_device(pdev);
prism2_free_local_data(dev);
err_out_free:
kfree(hw_priv);
return -ENODEV;
}
......
/*
* This file define a set of standard wireless extensions
*
* Version : 19 18.3.05
* Version : 20 17.2.06
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
* Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
*/
#ifndef _LINUX_WIRELESS_H
......@@ -80,7 +80,7 @@
* (there is some stuff that will be added in the future...)
* I just plan to increment with each new version.
*/
#define WIRELESS_EXT 19
#define WIRELESS_EXT 20
/*
* Changes :
......@@ -204,6 +204,10 @@
* - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros
* - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM
* - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros
*
* V19 to V20
* ----------
* - RtNetlink requests support (SET/GET)
*/
/**************************** CONSTANTS ****************************/
......
/*
* ieee80211softmac.h - public interface to the softmac
*
* Copyright (c) 2005 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#ifndef IEEE80211SOFTMAC_H_
#define IEEE80211SOFTMAC_H_
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/list.h>
#include <net/ieee80211.h>
/* Once the API is considered more or less stable,
* this should be incremented on API incompatible changes.
*/
#define IEEE80211SOFTMAC_API 0
#define IEEE80211SOFTMAC_MAX_RATES_LEN 8
#define IEEE80211SOFTMAC_MAX_EX_RATES_LEN 255
struct ieee80211softmac_ratesinfo {
u8 count;
u8 rates[IEEE80211SOFTMAC_MAX_RATES_LEN + IEEE80211SOFTMAC_MAX_EX_RATES_LEN];
};
/* internal structures */
struct ieee80211softmac_network;
struct ieee80211softmac_scaninfo;
struct ieee80211softmac_essid {
u8 len;
char data[IW_ESSID_MAX_SIZE+1];
};
struct ieee80211softmac_wpa {
char *IE;
int IElen;
int IEbuflen;
};
/*
* Information about association
*
* Do we need a lock for this?
* We only ever use this structure inlined
* into our global struct. I've used its lock,
* but maybe we need a local one here?
*/
struct ieee80211softmac_assoc_info {
/*
* This is the requested ESSID. It is written
* only by the WX handlers.
*
*/
struct ieee80211softmac_essid req_essid;
/*
* the ESSID of the network we're currently
* associated (or trying) to. This is
* updated to the network's actual ESSID
* even if the requested ESSID was 'ANY'
*/
struct ieee80211softmac_essid associate_essid;
/* BSSID we're trying to associate to */
char bssid[ETH_ALEN];
/* some flags.
* static_essid is valid if the essid is constant,
* this is for use by the wx handlers only.
*
* associating is true, if the network has been
* auth'ed on and we are in the process of associating.
*
* bssvalid is true if we found a matching network
* and saved it's BSSID into the bssid above.
*/
u8 static_essid:1,
associating:1,
bssvalid:1;
/* Scan retries remaining */
int scan_retry;
struct work_struct work;
struct work_struct timeout;
};
enum {
IEEE80211SOFTMAC_AUTH_OPEN_REQUEST = 1,
IEEE80211SOFTMAC_AUTH_OPEN_RESPONSE = 2,
};
enum {
IEEE80211SOFTMAC_AUTH_SHARED_REQUEST = 1,
IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE = 2,
IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE = 3,
IEEE80211SOFTMAC_AUTH_SHARED_PASS = 4,
};
/* We should make these tunable
* AUTH_TIMEOUT seems really long, but that's what it is in BSD */
#define IEEE80211SOFTMAC_AUTH_TIMEOUT (12 * HZ)
#define IEEE80211SOFTMAC_AUTH_RETRY_LIMIT 5
#define IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT 3
struct ieee80211softmac_txrates {
/* The Bit-Rate to be used for multicast frames. */
u8 mcast_rate;
/* The Bit-Rate to be used for multicast fallback
* (If the device supports fallback and hardware-retry)
*/
u8 mcast_fallback;
/* The Bit-Rate to be used for any other (normal) data packet. */
u8 default_rate;
/* The Bit-Rate to be used for default fallback
* (If the device supports fallback and hardware-retry)
*/
u8 default_fallback;
};
/* Bits for txrates_change callback. */
#define IEEE80211SOFTMAC_TXRATECHG_DEFAULT (1 << 0) /* default_rate */
#define IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK (1 << 1) /* default_fallback */
#define IEEE80211SOFTMAC_TXRATECHG_MCAST (1 << 2) /* mcast_rate */
#define IEEE80211SOFTMAC_TXRATECHG_MCAST_FBACK (1 << 3) /* mcast_fallback */
struct ieee80211softmac_device {
/* 802.11 structure for data stuff */
struct ieee80211_device *ieee;
struct net_device *dev;
/* only valid if associated, then holds the Association ID */
u16 association_id;
/* the following methods are callbacks that the driver
* using this framework has to assign
*/
/* always assign these */
void (*set_bssid_filter)(struct net_device *dev, const u8 *bssid);
void (*set_channel)(struct net_device *dev, u8 channel);
/* assign if you need it, informational only */
void (*link_change)(struct net_device *dev);
/* If the hardware can do scanning, assign _all_ three of these callbacks.
* When the scan finishes, call ieee80211softmac_scan_finished().
*/
/* when called, start_scan is guaranteed to not be called again
* until you call ieee80211softmac_scan_finished.
* Return 0 if scanning could start, error otherwise.
* SOFTMAC AUTHORS: don't call this, use ieee80211softmac_start_scan */
int (*start_scan)(struct net_device *dev);
/* this should block until after ieee80211softmac_scan_finished was called
* SOFTMAC AUTHORS: don't call this, use ieee80211softmac_wait_for_scan */
void (*wait_for_scan)(struct net_device *dev);
/* stop_scan aborts a scan, but is asynchronous.
* if you want to wait for it too, use wait_for_scan
* SOFTMAC AUTHORS: don't call this, use ieee80211softmac_stop_scan */
void (*stop_scan)(struct net_device *dev);
/* we'll need something about beacons here too, for AP or ad-hoc modes */
/* Transmission rates to be used by the driver.
* The SoftMAC figures out the best possible rates.
* The driver just needs to read them.
*/
struct ieee80211softmac_txrates txrates;
/* If the driver needs to do stuff on TX rate changes, assign this callback. */
void (*txrates_change)(struct net_device *dev,
u32 changes, /* see IEEE80211SOFTMAC_TXRATECHG flags */
const struct ieee80211softmac_txrates *rates_before_change);
/* private stuff follows */
/* this lock protects this structure */
spinlock_t lock;
/* couple of flags */
u8 scanning:1, /* protects scanning from being done multiple times at once */
associated:1;
struct ieee80211softmac_scaninfo *scaninfo;
struct ieee80211softmac_assoc_info associnfo;
struct list_head auth_queue;
struct list_head events;
struct ieee80211softmac_ratesinfo ratesinfo;
int txrate_badness;
/* WPA stuff */
struct ieee80211softmac_wpa wpa;
/* we need to keep a list of network structs we copied */
struct list_head network_list;
/* This must be the last item so that it points to the data
* allocated beyond this structure by alloc_ieee80211 */
u8 priv[0];
};
extern void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm);
static inline void * ieee80211softmac_priv(struct net_device *dev)
{
return ((struct ieee80211softmac_device *)ieee80211_priv(dev))->priv;
}
extern struct net_device * alloc_ieee80211softmac(int sizeof_priv);
extern void free_ieee80211softmac(struct net_device *dev);
/* Call this function if you detect a lost TX fragment.
* (If the device indicates failure of ACK RX, for example.)
* It is wise to call this function if you are able to detect lost packets,
* because it contributes to the TX Rates auto adjustment.
*/
extern void ieee80211softmac_fragment_lost(struct net_device *dev,
u16 wireless_sequence_number);
/* Call this function before _start to tell the softmac what rates
* the hw supports. The rates parameter is copied, so you can
* free it right after calling this function.
* Note that the rates need to be sorted. */
extern void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates);
/* Start the SoftMAC. Call this after you initialized the device
* and it is ready to run.
*/
extern void ieee80211softmac_start(struct net_device *dev);
/* Stop the SoftMAC. Call this before you shutdown the device. */
extern void ieee80211softmac_stop(struct net_device *dev);
/*
* Event system
*/
/* valid event types */
#define IEEE80211SOFTMAC_EVENT_ANY -1 /*private use only*/
#define IEEE80211SOFTMAC_EVENT_SCAN_FINISHED 0
#define IEEE80211SOFTMAC_EVENT_ASSOCIATED 1
#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED 2
#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT 3
#define IEEE80211SOFTMAC_EVENT_AUTHENTICATED 4
#define IEEE80211SOFTMAC_EVENT_AUTH_FAILED 5
#define IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT 6
#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND 7
/* keep this updated! */
#define IEEE80211SOFTMAC_EVENT_LAST 7
/*
* If you want to be notified of certain events, you can call
* ieee80211softmac_notify[_atomic] with
* - event set to one of the constants below
* - fun set to a function pointer of the appropriate type
* - context set to the context data you want passed
* The return value is 0, or an error.
*/
typedef void (*notify_function_ptr)(struct net_device *dev, void *context);
#define ieee80211softmac_notify(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_KERNEL);
#define ieee80211softmac_notify_atomic(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_ATOMIC);
extern int ieee80211softmac_notify_gfp(struct net_device *dev,
int event, notify_function_ptr fun, void *context, gfp_t gfp_mask);
/* To clear pending work (for ifconfig down, etc.) */
extern void
ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm);
#endif /* IEEE80211SOFTMAC_H_ */
/*
* This file contains the prototypes for the wireless extension
* handlers that the softmac API provides. Include this file to
* use the wx handlers, you can assign these directly.
*
* Copyright (c) 2005 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#ifndef _IEEE80211SOFTMAC_WX_H
#define _IEEE80211SOFTMAC_WX_H
#include <net/ieee80211softmac.h>
#include <net/iw_handler.h>
extern int
ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra);
extern int
ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra);
extern int
ieee80211softmac_wx_set_essid(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra);
extern int
ieee80211softmac_wx_get_essid(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra);
extern int
ieee80211softmac_wx_set_rate(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra);
extern int
ieee80211softmac_wx_get_rate(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra);
extern int
ieee80211softmac_wx_get_wap(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra);
extern int
ieee80211softmac_wx_set_wap(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra);
extern int
ieee80211softmac_wx_set_genie(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra);
extern int
ieee80211softmac_wx_get_genie(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra);
#endif /* _IEEE80211SOFTMAC_WX */
......@@ -4,7 +4,7 @@
* Version : 7 18.3.05
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 2001-2005 Jean Tourrilhes, All Rights Reserved.
* Copyright (c) 2001-2006 Jean Tourrilhes, All Rights Reserved.
*/
#ifndef _IW_HANDLER_H
......@@ -436,6 +436,16 @@ extern int dev_get_wireless_info(char * buffer, char **start, off_t offset,
/* Handle IOCTLs, called in net/core/dev.c */
extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd);
/* Handle RtNetlink requests, called in net/core/rtnetlink.c */
extern int wireless_rtnetlink_set(struct net_device * dev,
char * data,
int len);
extern int wireless_rtnetlink_get(struct net_device * dev,
char * data,
int len,
char ** p_buf,
int * p_len);
/* Second : functions that may be called by driver modules */
/* Send a single event to user space */
......
......@@ -51,6 +51,10 @@
#include <net/sock.h>
#include <net/pkt_sched.h>
#include <net/netlink.h>
#ifdef CONFIG_NET_WIRELESS_RTNETLINK
#include <linux/wireless.h>
#include <net/iw_handler.h>
#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
static DEFINE_MUTEX(rtnl_mutex);
......@@ -467,6 +471,17 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
goto out;
}
#ifdef CONFIG_NET_WIRELESS_RTNETLINK
if (ida[IFLA_WIRELESS - 1]) {
/* Call Wireless Extensions.
* Various stuff checked in there... */
err = wireless_rtnetlink_set(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len);
if (err)
goto out;
}
#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
err = 0;
out:
......@@ -477,6 +492,83 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
return err;
}
#ifdef CONFIG_NET_WIRELESS_RTNETLINK
static int do_getlink(struct sk_buff *in_skb, struct nlmsghdr* in_nlh, void *arg)
{
struct ifinfomsg *ifm = NLMSG_DATA(in_nlh);
struct rtattr **ida = arg;
struct net_device *dev;
struct ifinfomsg *r;
struct nlmsghdr *nlh;
int err = -ENOBUFS;
struct sk_buff *skb;
unsigned char *b;
char *iw_buf = NULL;
int iw_buf_len = 0;
if (ifm->ifi_index >= 0)
dev = dev_get_by_index(ifm->ifi_index);
else
return -EINVAL;
if (!dev)
return -ENODEV;
#ifdef CONFIG_NET_WIRELESS_RTNETLINK
if (ida[IFLA_WIRELESS - 1]) {
/* Call Wireless Extensions. We need to know the size before
* we can alloc. Various stuff checked in there... */
err = wireless_rtnetlink_get(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len, &iw_buf, &iw_buf_len);
if (err)
goto out;
}
#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
/* Create a skb big enough to include all the data.
* Some requests are way bigger than 4k... Jean II */
skb = alloc_skb((NLMSG_LENGTH(sizeof(*r))) + (RTA_SPACE(iw_buf_len)),
GFP_KERNEL);
if (!skb)
goto out;
b = skb->tail;
/* Put in the message the usual good stuff */
nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, in_nlh->nlmsg_seq,
RTM_NEWLINK, sizeof(*r));
r = NLMSG_DATA(nlh);
r->ifi_family = AF_UNSPEC;
r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_index = dev->ifindex;
r->ifi_flags = dev->flags;
r->ifi_change = 0;
/* Put the wireless payload if it exist */
if(iw_buf != NULL)
RTA_PUT(skb, IFLA_WIRELESS, iw_buf_len,
iw_buf + IW_EV_POINT_OFF);
nlh->nlmsg_len = skb->tail - b;
/* Needed ? */
NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
if (err > 0)
err = 0;
out:
if(iw_buf != NULL)
kfree(iw_buf);
dev_put(dev);
return err;
rtattr_failure:
nlmsg_failure:
kfree_skb(skb);
goto out;
}
#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
static int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
{
int idx;
......@@ -642,7 +734,11 @@ static void rtnetlink_rcv(struct sock *sk, int len)
static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] =
{
[RTM_GETLINK - RTM_BASE] = { .dumpit = rtnetlink_dump_ifinfo },
[RTM_GETLINK - RTM_BASE] = {
#ifdef CONFIG_NET_WIRELESS_RTNETLINK
.doit = do_getlink,
#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
.dumpit = rtnetlink_dump_ifinfo },
[RTM_SETLINK - RTM_BASE] = { .doit = do_setlink },
[RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all },
[RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all },
......
......@@ -2,7 +2,7 @@
* This file implement the Wireless Extensions APIs.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
* Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
*
* (As all part of the Linux kernel, this file is GPL)
*/
......@@ -65,6 +65,9 @@
* o Start deprecating dev->get_wireless_stats, output a warning
* o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
* o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
*
* v8 - 17.02.06 - Jean II
* o RtNetlink requests support (SET/GET)
*/
/***************************** INCLUDES *****************************/
......@@ -89,11 +92,13 @@
/* Debugging stuff */
#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */
#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
#undef WE_SPY_DEBUG /* Debug enhanced spy support */
/* Options */
#define WE_EVENT_NETLINK /* Propagate events using rtnetlink */
//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */
#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */
#define WE_SET_EVENT /* Generate an event on some set commands */
/************************* GLOBAL VARIABLES *************************/
......@@ -156,13 +161,18 @@ static const struct iw_ioctl_description standard_ioctl[] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */
.header_type = IW_HEADER_TYPE_NULL,
.header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct iw_priv_args),
.max_tokens = 16,
.flags = IW_DESCR_FLAG_NOMAX,
},
[SIOCSIWSTATS - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */
.header_type = IW_HEADER_TYPE_NULL,
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = sizeof(struct iw_statistics),
.flags = IW_DESCR_FLAG_DUMP,
},
[SIOCSIWSPY - SIOCIWFIRST] = {
......@@ -529,6 +539,70 @@ static inline int adjust_priv_size(__u16 args,
return num * iw_priv_type_size[type];
}
/* ---------------------------------------------------------------- */
/*
* Standard Wireless Handler : get wireless stats
* Allow programatic access to /proc/net/wireless even if /proc
* doesn't exist... Also more efficient...
*/
static int iw_handler_get_iwstats(struct net_device * dev,
struct iw_request_info * info,
union iwreq_data * wrqu,
char * extra)
{
/* Get stats from the driver */
struct iw_statistics *stats;
stats = get_wireless_stats(dev);
if (stats != (struct iw_statistics *) NULL) {
/* Copy statistics to extra */
memcpy(extra, stats, sizeof(struct iw_statistics));
wrqu->data.length = sizeof(struct iw_statistics);
/* Check if we need to clear the updated flag */
if(wrqu->data.flags != 0)
stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
return 0;
} else
return -EOPNOTSUPP;
}
/* ---------------------------------------------------------------- */
/*
* Standard Wireless Handler : get iwpriv definitions
* Export the driver private handler definition
* They will be picked up by tools like iwpriv...
*/
static int iw_handler_get_private(struct net_device * dev,
struct iw_request_info * info,
union iwreq_data * wrqu,
char * extra)
{
/* Check if the driver has something to export */
if((dev->wireless_handlers->num_private_args == 0) ||
(dev->wireless_handlers->private_args == NULL))
return -EOPNOTSUPP;
/* Check if there is enough buffer up there */
if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
/* User space can't know in advance how large the buffer
* needs to be. Give it a hint, so that we can support
* any size buffer we want somewhat efficiently... */
wrqu->data.length = dev->wireless_handlers->num_private_args;
return -E2BIG;
}
/* Set the number of available ioctls. */
wrqu->data.length = dev->wireless_handlers->num_private_args;
/* Copy structure to the user buffer. */
memcpy(extra, dev->wireless_handlers->private_args,
sizeof(struct iw_priv_args) * wrqu->data.length);
return 0;
}
/******************** /proc/net/wireless SUPPORT ********************/
/*
......@@ -628,83 +702,16 @@ int __init wireless_proc_init(void)
* or just call the driver ioctl handler.
*/
/* ---------------------------------------------------------------- */
/*
* Allow programatic access to /proc/net/wireless even if /proc
* doesn't exist... Also more efficient...
*/
static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
{
/* Get stats from the driver */
struct iw_statistics *stats;
stats = get_wireless_stats(dev);
if (stats != (struct iw_statistics *) NULL) {
struct iwreq * wrq = (struct iwreq *)ifr;
/* Copy statistics to the user buffer */
if(copy_to_user(wrq->u.data.pointer, stats,
sizeof(struct iw_statistics)))
return -EFAULT;
/* Check if we need to clear the updated flag */
if(wrq->u.data.flags != 0)
stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
return 0;
} else
return -EOPNOTSUPP;
}
/* ---------------------------------------------------------------- */
/*
* Export the driver private handler definition
* They will be picked up by tools like iwpriv...
*/
static inline int ioctl_export_private(struct net_device * dev,
struct ifreq * ifr)
{
struct iwreq * iwr = (struct iwreq *) ifr;
/* Check if the driver has something to export */
if((dev->wireless_handlers->num_private_args == 0) ||
(dev->wireless_handlers->private_args == NULL))
return -EOPNOTSUPP;
/* Check NULL pointer */
if(iwr->u.data.pointer == NULL)
return -EFAULT;
/* Check if there is enough buffer up there */
if(iwr->u.data.length < dev->wireless_handlers->num_private_args) {
/* User space can't know in advance how large the buffer
* needs to be. Give it a hint, so that we can support
* any size buffer we want somewhat efficiently... */
iwr->u.data.length = dev->wireless_handlers->num_private_args;
return -E2BIG;
}
/* Set the number of available ioctls. */
iwr->u.data.length = dev->wireless_handlers->num_private_args;
/* Copy structure to the user buffer. */
if (copy_to_user(iwr->u.data.pointer,
dev->wireless_handlers->private_args,
sizeof(struct iw_priv_args) * iwr->u.data.length))
return -EFAULT;
return 0;
}
/* ---------------------------------------------------------------- */
/*
* Wrapper to call a standard Wireless Extension handler.
* We do various checks and also take care of moving data between
* user space and kernel space.
*/
static inline int ioctl_standard_call(struct net_device * dev,
struct ifreq * ifr,
unsigned int cmd,
iw_handler handler)
static int ioctl_standard_call(struct net_device * dev,
struct ifreq * ifr,
unsigned int cmd,
iw_handler handler)
{
struct iwreq * iwr = (struct iwreq *) ifr;
const struct iw_ioctl_description * descr;
......@@ -1048,14 +1055,20 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
{
case SIOCGIWSTATS:
/* Get Wireless Stats */
return dev_iwstats(dev, ifr);
return ioctl_standard_call(dev,
ifr,
cmd,
&iw_handler_get_iwstats);
case SIOCGIWPRIV:
/* Check if we have some wireless handlers defined */
if(dev->wireless_handlers != NULL) {
/* We export to user space the definition of
* the private handler ourselves */
return ioctl_export_private(dev, ifr);
return ioctl_standard_call(dev,
ifr,
cmd,
&iw_handler_get_private);
}
// ## Fall-through for old API ##
default:
......@@ -1088,16 +1101,739 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
return -EINVAL;
}
/********************** RTNETLINK REQUEST API **********************/
/*
* The alternate user space API to configure all those Wireless Extensions
* is through RtNetlink.
* This API support only the new driver API (iw_handler).
*
* This RtNetlink API use the same query/reply model as the ioctl API.
* Maximum effort has been done to fit in the RtNetlink model, and
* we support both RtNetlink Set and RtNelink Get operations.
* On the other hand, we don't offer Dump operations because of the
* following reasons :
* o Large number of parameters, most optional
* o Large size of some parameters (> 100 bytes)
* o Each parameters need to be extracted from hardware
* o Scan requests can take seconds and disable network activity.
* Because of this high cost/overhead, we want to return only the
* parameters the user application is really interested in.
* We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
*
* The API uses the standard RtNetlink socket. When the RtNetlink code
* find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
* it calls here.
*/
#ifdef CONFIG_NET_WIRELESS_RTNETLINK
/* ---------------------------------------------------------------- */
/*
* Wrapper to call a standard Wireless Extension GET handler.
* We do various checks and call the handler with the proper args.
*/
static int rtnetlink_standard_get(struct net_device * dev,
struct iw_event * request,
int request_len,
iw_handler handler,
char ** p_buf,
int * p_len)
{
const struct iw_ioctl_description * descr = NULL;
unsigned int cmd;
union iwreq_data * wrqu;
int hdr_len;
struct iw_request_info info;
char * buffer = NULL;
int buffer_size = 0;
int ret = -EINVAL;
/* Get the description of the Request */
cmd = request->cmd;
if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
return -EOPNOTSUPP;
descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
dev->name, cmd);
printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
#endif /* WE_RTNETLINK_DEBUG */
/* Check if wrqu is complete */
hdr_len = event_type_size[descr->header_type];
if(request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
"%s (WE.r) : Wireless request too short (%d)\n",
dev->name, request_len);
#endif /* WE_RTNETLINK_DEBUG */
return -EINVAL;
}
/* Prepare the call */
info.cmd = cmd;
info.flags = 0;
/* Check if we have extra data in the reply or not */
if(descr->header_type != IW_HEADER_TYPE_POINT) {
/* Create the kernel buffer that we will return.
* It's at an offset to match the TYPE_POINT case... */
buffer_size = request_len + IW_EV_POINT_OFF;
buffer = kmalloc(buffer_size, GFP_KERNEL);
if (buffer == NULL) {
return -ENOMEM;
}
/* Copy event data */
memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
/* Use our own copy of wrqu */
wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+ IW_EV_LCP_LEN);
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, NULL);
} else {
union iwreq_data wrqu_point;
char * extra = NULL;
int extra_size = 0;
/* Get a temp copy of wrqu (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
((char *) request) + IW_EV_LCP_LEN,
IW_EV_POINT_LEN - IW_EV_LCP_LEN);
/* Calculate space needed by arguments. Always allocate
* for max space. Easier, and won't last long... */
extra_size = descr->max_tokens * descr->token_size;
/* Support for very large requests */
if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
(wrqu_point.data.length > descr->max_tokens))
extra_size = (wrqu_point.data.length
* descr->token_size);
buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
dev->name, extra_size, buffer_size);
#endif /* WE_RTNETLINK_DEBUG */
/* Create the kernel buffer that we will return */
buffer = kmalloc(buffer_size, GFP_KERNEL);
if (buffer == NULL) {
return -ENOMEM;
}
/* Put wrqu in the right place (just before extra).
* Leave space for IWE header and dummy pointer...
* Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
*/
memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
((char *) &wrqu_point) + IW_EV_POINT_OFF,
IW_EV_POINT_LEN - IW_EV_LCP_LEN);
wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
/* Extra comes logically after that. Offset +12 bytes. */
extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
/* Call the handler */
ret = handler(dev, &info, wrqu, extra);
/* Calculate real returned length */
extra_size = (wrqu->data.length * descr->token_size);
/* Re-adjust reply size */
request->len = extra_size + IW_EV_POINT_LEN;
/* Put the iwe header where it should, i.e. scrap the
* dummy pointer. */
memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
#endif /* WE_RTNETLINK_DEBUG */
/* Check if there is enough buffer up there */
if(wrqu_point.data.length < wrqu->data.length)
ret = -E2BIG;
}
/* Return the buffer to the caller */
if (!ret) {
*p_buf = buffer;
*p_len = request->len;
} else {
/* Cleanup */
if(buffer)
kfree(buffer);
}
return ret;
}
/* ---------------------------------------------------------------- */
/*
* Wrapper to call a standard Wireless Extension SET handler.
* We do various checks and call the handler with the proper args.
*/
static inline int rtnetlink_standard_set(struct net_device * dev,
struct iw_event * request,
int request_len,
iw_handler handler)
{
const struct iw_ioctl_description * descr = NULL;
unsigned int cmd;
union iwreq_data * wrqu;
union iwreq_data wrqu_point;
int hdr_len;
char * extra = NULL;
int extra_size = 0;
struct iw_request_info info;
int ret = -EINVAL;
/* Get the description of the Request */
cmd = request->cmd;
if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
return -EOPNOTSUPP;
descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
dev->name, cmd);
printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
#endif /* WE_RTNETLINK_DEBUG */
/* Extract fixed header from request. This is properly aligned. */
wrqu = &request->u;
/* Check if wrqu is complete */
hdr_len = event_type_size[descr->header_type];
if(request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
"%s (WE.r) : Wireless request too short (%d)\n",
dev->name, request_len);
#endif /* WE_RTNETLINK_DEBUG */
return -EINVAL;
}
/* Prepare the call */
info.cmd = cmd;
info.flags = 0;
/* Check if we have extra data in the request or not */
if(descr->header_type != IW_HEADER_TYPE_POINT) {
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, NULL);
} else {
int extra_len;
/* Put wrqu in the right place (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
/* Don't forget about the event code... */
wrqu = &wrqu_point;
/* Check if number of token fits within bounds */
if(wrqu_point.data.length > descr->max_tokens)
return -E2BIG;
if(wrqu_point.data.length < descr->min_tokens)
return -EINVAL;
/* Real length of payload */
extra_len = wrqu_point.data.length * descr->token_size;
/* Check if request is self consistent */
if((request_len - hdr_len) < extra_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
dev->name, extra_size);
#endif /* WE_RTNETLINK_DEBUG */
return -EINVAL;
}
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
dev->name, extra_size);
#endif /* WE_RTNETLINK_DEBUG */
/* Always allocate for max space. Easier, and won't last
* long... */
extra_size = descr->max_tokens * descr->token_size;
extra = kmalloc(extra_size, GFP_KERNEL);
if (extra == NULL)
return -ENOMEM;
/* Copy extra in aligned buffer */
memcpy(extra, ((char *) request) + hdr_len, extra_len);
/* Call the handler */
ret = handler(dev, &info, &wrqu_point, extra);
}
#ifdef WE_SET_EVENT
/* Generate an event to notify listeners of the change */
if((descr->flags & IW_DESCR_FLAG_EVENT) &&
((ret == 0) || (ret == -EIWCOMMIT))) {
if(descr->flags & IW_DESCR_FLAG_RESTRICT)
/* If the event is restricted, don't
* export the payload */
wireless_send_event(dev, cmd, wrqu, NULL);
else
wireless_send_event(dev, cmd, wrqu, extra);
}
#endif /* WE_SET_EVENT */
/* Cleanup - I told you it wasn't that long ;-) */
if(extra)
kfree(extra);
/* Call commit handler if needed and defined */
if(ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
return ret;
}
/* ---------------------------------------------------------------- */
/*
* Wrapper to call a private Wireless Extension GET handler.
* Same as above...
* It's not as nice and slimline as the standard wrapper. The cause
* is struct iw_priv_args, which was not really designed for the
* job we are going here.
*
* IMPORTANT : This function prevent to set and get data on the same
* IOCTL and enforce the SET/GET convention. Not doing it would be
* far too hairy...
* If you need to set and get data at the same time, please don't use
* a iw_handler but process it in your ioctl handler (i.e. use the
* old driver API).
*/
static inline int rtnetlink_private_get(struct net_device * dev,
struct iw_event * request,
int request_len,
iw_handler handler,
char ** p_buf,
int * p_len)
{
const struct iw_priv_args * descr = NULL;
unsigned int cmd;
union iwreq_data * wrqu;
int hdr_len;
struct iw_request_info info;
int extra_size = 0;
int i;
char * buffer = NULL;
int buffer_size = 0;
int ret = -EINVAL;
/* Get the description of the Request */
cmd = request->cmd;
for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
if(cmd == dev->wireless_handlers->private_args[i].cmd) {
descr = &(dev->wireless_handlers->private_args[i]);
break;
}
if(descr == NULL)
return -EOPNOTSUPP;
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
dev->name, cmd);
printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
dev->name, descr->name, descr->set_args, descr->get_args);
#endif /* WE_RTNETLINK_DEBUG */
/* Compute the max size of the get arguments */
extra_size = get_priv_size(descr->get_args);
/* Does it fits in wrqu ? */
if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
(extra_size <= IFNAMSIZ)) {
hdr_len = extra_size;
extra_size = 0;
} else {
hdr_len = IW_EV_POINT_LEN;
}
/* Check if wrqu is complete */
if(request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
"%s (WE.r) : Wireless request too short (%d)\n",
dev->name, request_len);
#endif /* WE_RTNETLINK_DEBUG */
return -EINVAL;
}
/* Prepare the call */
info.cmd = cmd;
info.flags = 0;
/* Check if we have a pointer to user space data or not. */
if(extra_size == 0) {
/* Create the kernel buffer that we will return.
* It's at an offset to match the TYPE_POINT case... */
buffer_size = request_len + IW_EV_POINT_OFF;
buffer = kmalloc(buffer_size, GFP_KERNEL);
if (buffer == NULL) {
return -ENOMEM;
}
/* Copy event data */
memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
/* Use our own copy of wrqu */
wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+ IW_EV_LCP_LEN);
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, (char *) wrqu);
} else {
char * extra;
/* Buffer for full reply */
buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
dev->name, extra_size, buffer_size);
#endif /* WE_RTNETLINK_DEBUG */
/* Create the kernel buffer that we will return */
buffer = kmalloc(buffer_size, GFP_KERNEL);
if (buffer == NULL) {
return -ENOMEM;
}
/* Put wrqu in the right place (just before extra).
* Leave space for IWE header and dummy pointer...
* Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
*/
memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
((char *) request) + IW_EV_LCP_LEN,
IW_EV_POINT_LEN - IW_EV_LCP_LEN);
wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
/* Extra comes logically after that. Offset +12 bytes. */
extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
/* Call the handler */
ret = handler(dev, &info, wrqu, extra);
/* Adjust for the actual length if it's variable,
* avoid leaking kernel bits outside. */
if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
extra_size = adjust_priv_size(descr->get_args, wrqu);
/* Re-adjust reply size */
request->len = extra_size + IW_EV_POINT_LEN;
/* Put the iwe header where it should, i.e. scrap the
* dummy pointer. */
memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
#endif /* WE_RTNETLINK_DEBUG */
}
/* Return the buffer to the caller */
if (!ret) {
*p_buf = buffer;
*p_len = request->len;
} else {
/* Cleanup */
if(buffer)
kfree(buffer);
}
return ret;
}
/* ---------------------------------------------------------------- */
/*
* Wrapper to call a private Wireless Extension SET handler.
* Same as above...
* It's not as nice and slimline as the standard wrapper. The cause
* is struct iw_priv_args, which was not really designed for the
* job we are going here.
*
* IMPORTANT : This function prevent to set and get data on the same
* IOCTL and enforce the SET/GET convention. Not doing it would be
* far too hairy...
* If you need to set and get data at the same time, please don't use
* a iw_handler but process it in your ioctl handler (i.e. use the
* old driver API).
*/
static inline int rtnetlink_private_set(struct net_device * dev,
struct iw_event * request,
int request_len,
iw_handler handler)
{
const struct iw_priv_args * descr = NULL;
unsigned int cmd;
union iwreq_data * wrqu;
union iwreq_data wrqu_point;
int hdr_len;
char * extra = NULL;
int extra_size = 0;
int offset = 0; /* For sub-ioctls */
struct iw_request_info info;
int i;
int ret = -EINVAL;
/* Get the description of the Request */
cmd = request->cmd;
for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
if(cmd == dev->wireless_handlers->private_args[i].cmd) {
descr = &(dev->wireless_handlers->private_args[i]);
break;
}
if(descr == NULL)
return -EOPNOTSUPP;
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
ifr->ifr_name, cmd);
printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
dev->name, descr->name, descr->set_args, descr->get_args);
#endif /* WE_RTNETLINK_DEBUG */
/* Compute the size of the set arguments */
/* Check for sub-ioctl handler */
if(descr->name[0] == '\0')
/* Reserve one int for sub-ioctl index */
offset = sizeof(__u32);
/* Size of set arguments */
extra_size = get_priv_size(descr->set_args);
/* Does it fits in wrqu ? */
if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
(extra_size <= IFNAMSIZ)) {
hdr_len = IW_EV_LCP_LEN + extra_size;
extra_size = 0;
} else {
hdr_len = IW_EV_POINT_LEN;
}
/* Extract fixed header from request. This is properly aligned. */
wrqu = &request->u;
/* Check if wrqu is complete */
if(request_len < hdr_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG
"%s (WE.r) : Wireless request too short (%d)\n",
dev->name, request_len);
#endif /* WE_RTNETLINK_DEBUG */
return -EINVAL;
}
/* Prepare the call */
info.cmd = cmd;
info.flags = 0;
/* Check if we have a pointer to user space data or not. */
if(extra_size == 0) {
/* No extra arguments. Trivial to handle */
ret = handler(dev, &info, wrqu, (char *) wrqu);
} else {
int extra_len;
/* Put wrqu in the right place (skip pointer) */
memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
/* Does it fits within bounds ? */
if(wrqu_point.data.length > (descr->set_args &
IW_PRIV_SIZE_MASK))
return -E2BIG;
/* Real length of payload */
extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
/* Check if request is self consistent */
if((request_len - hdr_len) < extra_len) {
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
dev->name, extra_size);
#endif /* WE_RTNETLINK_DEBUG */
return -EINVAL;
}
#ifdef WE_RTNETLINK_DEBUG
printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
dev->name, extra_size);
#endif /* WE_RTNETLINK_DEBUG */
/* Always allocate for max space. Easier, and won't last
* long... */
extra = kmalloc(extra_size, GFP_KERNEL);
if (extra == NULL)
return -ENOMEM;
/* Copy extra in aligned buffer */
memcpy(extra, ((char *) request) + hdr_len, extra_len);
/* Call the handler */
ret = handler(dev, &info, &wrqu_point, extra);
/* Cleanup - I told you it wasn't that long ;-) */
kfree(extra);
}
/* Call commit handler if needed and defined */
if(ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
return ret;
}
/* ---------------------------------------------------------------- */
/*
* Main RtNetlink dispatcher. Called from the main networking code
* (do_getlink() in net/core/rtnetlink.c).
* Check the type of Request and call the appropriate wrapper...
*/
int wireless_rtnetlink_get(struct net_device * dev,
char * data,
int len,
char ** p_buf,
int * p_len)
{
struct iw_event * request = (struct iw_event *) data;
iw_handler handler;
/* Check length */
if(len < IW_EV_LCP_LEN) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
dev->name, len);
return -EINVAL;
}
/* ReCheck length (len may have padding) */
if(request->len > len) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
dev->name, request->len, len);
return -EINVAL;
}
/* Only accept GET requests in here */
if(!IW_IS_GET(request->cmd))
return -EOPNOTSUPP;
/* Special cases */
if(request->cmd == SIOCGIWSTATS)
/* Get Wireless Stats */
return rtnetlink_standard_get(dev,
request,
request->len,
&iw_handler_get_iwstats,
p_buf, p_len);
if(request->cmd == SIOCGIWPRIV) {
/* Check if we have some wireless handlers defined */
if(dev->wireless_handlers == NULL)
return -EOPNOTSUPP;
/* Get Wireless Stats */
return rtnetlink_standard_get(dev,
request,
request->len,
&iw_handler_get_private,
p_buf, p_len);
}
/* Basic check */
if (!netif_device_present(dev))
return -ENODEV;
/* Try to find the handler */
handler = get_handler(dev, request->cmd);
if(handler != NULL) {
/* Standard and private are not the same */
if(request->cmd < SIOCIWFIRSTPRIV)
return rtnetlink_standard_get(dev,
request,
request->len,
handler,
p_buf, p_len);
else
return rtnetlink_private_get(dev,
request,
request->len,
handler,
p_buf, p_len);
}
return -EOPNOTSUPP;
}
/* ---------------------------------------------------------------- */
/*
* Main RtNetlink dispatcher. Called from the main networking code
* (do_setlink() in net/core/rtnetlink.c).
* Check the type of Request and call the appropriate wrapper...
*/
int wireless_rtnetlink_set(struct net_device * dev,
char * data,
int len)
{
struct iw_event * request = (struct iw_event *) data;
iw_handler handler;
/* Check length */
if(len < IW_EV_LCP_LEN) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
dev->name, len);
return -EINVAL;
}
/* ReCheck length (len may have padding) */
if(request->len > len) {
printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
dev->name, request->len, len);
return -EINVAL;
}
/* Only accept SET requests in here */
if(!IW_IS_SET(request->cmd))
return -EOPNOTSUPP;
/* Basic check */
if (!netif_device_present(dev))
return -ENODEV;
/* New driver API : try to find the handler */
handler = get_handler(dev, request->cmd);
if(handler != NULL) {
/* Standard and private are not the same */
if(request->cmd < SIOCIWFIRSTPRIV)
return rtnetlink_standard_set(dev,
request,
request->len,
handler);
else
return rtnetlink_private_set(dev,
request,
request->len,
handler);
}
return -EOPNOTSUPP;
}
#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
/************************* EVENT PROCESSING *************************/
/*
* Process events generated by the wireless layer or the driver.
* Most often, the event will be propagated through rtnetlink
*/
#ifdef WE_EVENT_NETLINK
/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here.
* It is declared in <linux/rtnetlink.h> */
#ifdef WE_EVENT_RTNETLINK
/* ---------------------------------------------------------------- */
/*
* Fill a rtnetlink message with our event data.
......@@ -1121,12 +1857,11 @@ static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb,
r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_index = dev->ifindex;
r->ifi_flags = dev->flags;
r->ifi_flags = dev_get_flags(dev);
r->ifi_change = 0; /* Wireless changes don't affect those flags */
/* Add the wireless events in the netlink packet */
RTA_PUT(skb, IFLA_WIRELESS,
event_len, event);
RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
nlh->nlmsg_len = skb->tail - b;
return skb->len;
......@@ -1163,7 +1898,7 @@ static inline void rtmsg_iwinfo(struct net_device * dev,
NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC);
}
#endif /* WE_EVENT_NETLINK */
#endif /* WE_EVENT_RTNETLINK */
/* ---------------------------------------------------------------- */
/*
......@@ -1255,10 +1990,10 @@ void wireless_send_event(struct net_device * dev,
if(extra != NULL)
memcpy(((char *) event) + hdr_len, extra, extra_len);
#ifdef WE_EVENT_NETLINK
/* rtnetlink event channel */
#ifdef WE_EVENT_RTNETLINK
/* Send via the RtNetlink event channel */
rtmsg_iwinfo(dev, (char *) event, event_len);
#endif /* WE_EVENT_NETLINK */
#endif /* WE_EVENT_RTNETLINK */
/* Cleanup */
kfree(event);
......
......@@ -66,3 +66,4 @@ config IEEE80211_CRYPT_TKIP
This can be compiled as a modules and it will be called
"ieee80211_crypt_tkip".
source "net/ieee80211/softmac/Kconfig"
......@@ -10,3 +10,4 @@ ieee80211-objs := \
ieee80211_wx.o \
ieee80211_geo.o
obj-$(CONFIG_IEEE80211_SOFTMAC) += softmac/
......@@ -780,6 +780,80 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
return 0;
}
/* Filter out unrelated packets, call ieee80211_rx[_mgt] */
int ieee80211_rx_any(struct ieee80211_device *ieee,
struct sk_buff *skb, struct ieee80211_rx_stats *stats)
{
struct ieee80211_hdr_4addr *hdr;
int is_packet_for_us;
u16 fc;
if (ieee->iw_mode == IW_MODE_MONITOR)
return ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL;
hdr = (struct ieee80211_hdr_4addr *)skb->data;
fc = le16_to_cpu(hdr->frame_ctl);
if ((fc & IEEE80211_FCTL_VERS) != 0)
return -EINVAL;
switch (fc & IEEE80211_FCTL_FTYPE) {
case IEEE80211_FTYPE_MGMT:
ieee80211_rx_mgt(ieee, hdr, stats);
return 0;
case IEEE80211_FTYPE_DATA:
break;
case IEEE80211_FTYPE_CTL:
return 0;
default:
return -EINVAL;
}
is_packet_for_us = 0;
switch (ieee->iw_mode) {
case IW_MODE_ADHOC:
/* our BSS and not from/to DS */
if (memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) == 0)
if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) {
/* promisc: get all */
if (ieee->dev->flags & IFF_PROMISC)
is_packet_for_us = 1;
/* to us */
else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
is_packet_for_us = 1;
/* mcast */
else if (is_multicast_ether_addr(hdr->addr1))
is_packet_for_us = 1;
}
break;
case IW_MODE_INFRA:
/* our BSS (== from our AP) and from DS */
if (memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) == 0)
if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) {
/* promisc: get all */
if (ieee->dev->flags & IFF_PROMISC)
is_packet_for_us = 1;
/* to us */
else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
is_packet_for_us = 1;
/* mcast */
else if (is_multicast_ether_addr(hdr->addr1)) {
/* not our own packet bcasted from AP */
if (memcmp(hdr->addr3, ieee->dev->dev_addr, ETH_ALEN))
is_packet_for_us = 1;
}
}
break;
default:
/* ? */
break;
}
if (is_packet_for_us)
return (ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL);
return 0;
}
#define MGMT_FRAME_FIXED_PART_LENGTH 0x24
static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
......
config IEEE80211_SOFTMAC
tristate "Software MAC add-on to the IEEE 802.11 networking stack"
depends on IEEE80211 && EXPERIMENTAL
---help---
This option enables the hardware independent software MAC addon
for the IEEE 802.11 networking stack.
config IEEE80211_SOFTMAC_DEBUG
bool "Enable full debugging output"
depends on IEEE80211_SOFTMAC
obj-$(CONFIG_IEEE80211_SOFTMAC) += ieee80211softmac.o
ieee80211softmac-objs := \
ieee80211softmac_io.o \
ieee80211softmac_auth.o \
ieee80211softmac_module.o \
ieee80211softmac_scan.o \
ieee80211softmac_wx.o \
ieee80211softmac_assoc.o \
ieee80211softmac_event.o
/*
* This file contains the softmac's association logic.
*
* Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#include "ieee80211softmac_priv.h"
/*
* Overview
*
* Before you can associate, you have to authenticate.
*
*/
/* Sends out an association request to the desired AP */
static void
ieee80211softmac_assoc(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
{
unsigned long flags;
/* Switch to correct channel for this network */
mac->set_channel(mac->dev, net->channel);
/* Send association request */
ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_ASSOC_REQ, 0);
dprintk(KERN_INFO PFX "sent association request!\n");
/* Change the state to associating */
spin_lock_irqsave(&mac->lock, flags);
mac->associnfo.associating = 1;
mac->associated = 0; /* just to make sure */
spin_unlock_irqrestore(&mac->lock, flags);
/* Set a timer for timeout */
/* FIXME: make timeout configurable */
schedule_delayed_work(&mac->associnfo.timeout, 5 * HZ);
}
void
ieee80211softmac_assoc_timeout(void *d)
{
struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d;
unsigned long flags;
spin_lock_irqsave(&mac->lock, flags);
/* we might race against ieee80211softmac_handle_assoc_response,
* so make sure only one of us does something */
if (!mac->associnfo.associating) {
spin_unlock_irqrestore(&mac->lock, flags);
return;
}
mac->associnfo.associating = 0;
mac->associnfo.bssvalid = 0;
mac->associated = 0;
spin_unlock_irqrestore(&mac->lock, flags);
dprintk(KERN_INFO PFX "assoc request timed out!\n");
/* FIXME: we need to know the network here. that requires a bit of restructuring */
ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, NULL);
}
/* Sends out a disassociation request to the desired AP */
static void
ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason)
{
unsigned long flags;
struct ieee80211softmac_network *found;
if (mac->associnfo.bssvalid && mac->associated) {
found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);
if (found)
ieee80211softmac_send_mgt_frame(mac, found, IEEE80211_STYPE_DISASSOC, reason);
} else if (mac->associnfo.associating) {
cancel_delayed_work(&mac->associnfo.timeout);
}
/* Change our state */
spin_lock_irqsave(&mac->lock, flags);
/* Do NOT clear bssvalid as that will break ieee80211softmac_assoc_work! */
mac->associated = 0;
mac->associnfo.associating = 0;
spin_unlock_irqrestore(&mac->lock, flags);
}
static inline int
we_support_all_basic_rates(struct ieee80211softmac_device *mac, u8 *from, u8 from_len)
{
int idx, search, found;
u8 rate, search_rate;
for (idx = 0; idx < (from_len); idx++) {
rate = (from)[idx];
if (!(rate & IEEE80211_BASIC_RATE_MASK))
continue;
found = 0;
rate &= ~IEEE80211_BASIC_RATE_MASK;
for (search = 0; search < mac->ratesinfo.count; search++) {
search_rate = mac->ratesinfo.rates[search];
search_rate &= ~IEEE80211_BASIC_RATE_MASK;
if (rate == search_rate) {
found = 1;
break;
}
}
if (!found)
return 0;
}
return 1;
}
static int
network_matches_request(struct ieee80211softmac_device *mac, struct ieee80211_network *net)
{
/* we cannot associate to networks whose name we don't know */
if (ieee80211_is_empty_essid(net->ssid, net->ssid_len))
return 0;
/* do not associate to a network whose BSSBasicRateSet we cannot support */
if (!we_support_all_basic_rates(mac, net->rates, net->rates_len))
return 0;
/* do we really need to check the ex rates? */
if (!we_support_all_basic_rates(mac, net->rates_ex, net->rates_ex_len))
return 0;
/* if 'ANY' network requested, take any that doesn't have privacy enabled */
if (mac->associnfo.req_essid.len == 0
&& !(net->capability & WLAN_CAPABILITY_PRIVACY))
return 1;
if (net->ssid_len != mac->associnfo.req_essid.len)
return 0;
if (!memcmp(net->ssid, mac->associnfo.req_essid.data, mac->associnfo.req_essid.len))
return 1;
return 0;
}
static void
ieee80211softmac_assoc_notify(struct net_device *dev, void *context)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
ieee80211softmac_assoc_work((void*)mac);
}
/* This function is called to handle userspace requests (asynchronously) */
void
ieee80211softmac_assoc_work(void *d)
{
struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d;
struct ieee80211softmac_network *found = NULL;
struct ieee80211_network *net = NULL, *best = NULL;
unsigned long flags;
/* meh */
if (mac->associated)
ieee80211softmac_disassoc(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT);
/* try to find the requested network in our list, if we found one already */
if (mac->associnfo.bssvalid)
found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);
/* Search the ieee80211 networks for this network if we didn't find it by bssid,
* but only if we've scanned at least once (to get a better list of networks to
* select from). If we have not scanned before, the !found logic below will be
* invoked and will scan. */
if (!found && (mac->associnfo.scan_retry < IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT))
{
s8 rssi = -128; /* if I don't initialise, gcc emits an invalid warning
because it cannot follow the best pointer logic. */
spin_lock_irqsave(&mac->ieee->lock, flags);
list_for_each_entry(net, &mac->ieee->network_list, list) {
/* we're supposed to find the network with
* the best signal here, as we're asked to join
* any network with a specific ESSID, and many
* different ones could have that.
*
* I'll for now just go with the reported rssi.
*
* We also should take into account the rateset
* here to find the best BSSID to try.
*/
if (network_matches_request(mac, net)) {
if (!best) {
best = net;
rssi = best->stats.rssi;
continue;
}
/* we already had a matching network, so
* compare their properties to get the
* better of the two ... (see above)
*/
if (rssi < net->stats.rssi) {
best = net;
rssi = best->stats.rssi;
}
}
}
/* if we unlock here, we might get interrupted and the `best'
* pointer could go stale */
if (best) {
found = ieee80211softmac_create_network(mac, best);
/* if found is still NULL, then we got -ENOMEM somewhere */
if (found)
ieee80211softmac_add_network(mac, found);
}
spin_unlock_irqrestore(&mac->ieee->lock, flags);
}
if (!found) {
if (mac->associnfo.scan_retry > 0) {
spin_lock_irqsave(&mac->lock, flags);
mac->associnfo.scan_retry--;
spin_unlock_irqrestore(&mac->lock, flags);
/* We know of no such network. Let's scan.
* NB: this also happens if we had no memory to copy the network info...
* Maybe we can hope to have more memory after scanning finishes ;)
*/
dprintk(KERN_INFO PFX "Associate: Scanning for networks first.\n");
ieee80211softmac_notify(mac->dev, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, ieee80211softmac_assoc_notify, NULL);
if (ieee80211softmac_start_scan(mac))
dprintk(KERN_INFO PFX "Associate: failed to initiate scan. Is device up?\n");
return;
}
else {
spin_lock_irqsave(&mac->lock, flags);
mac->associnfo.associating = 0;
mac->associated = 0;
spin_unlock_irqrestore(&mac->lock, flags);
dprintk(KERN_INFO PFX "Unable to find matching network after scan!\n");
ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND, NULL);
return;
}
}
mac->associnfo.bssvalid = 1;
memcpy(mac->associnfo.bssid, found->bssid, ETH_ALEN);
/* copy the ESSID for displaying it */
mac->associnfo.associate_essid.len = found->essid.len;
memcpy(mac->associnfo.associate_essid.data, found->essid.data, IW_ESSID_MAX_SIZE + 1);
/* we found a network! authenticate (if necessary) and associate to it. */
if (!found->authenticated) {
/* This relies on the fact that _auth_req only queues the work,
* otherwise adding the notification would be racy. */
if (!ieee80211softmac_auth_req(mac, found)) {
dprintk(KERN_INFO PFX "cannot associate without being authenticated, requested authentication\n");
ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL);
} else {
printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n");
ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found);
}
return;
}
/* finally! now we can start associating */
ieee80211softmac_assoc(mac, found);
}
/* call this to do whatever is necessary when we're associated */
static void
ieee80211softmac_associated(struct ieee80211softmac_device *mac,
struct ieee80211_assoc_response * resp,
struct ieee80211softmac_network *net)
{
mac->associnfo.associating = 0;
mac->associated = 1;
if (mac->set_bssid_filter)
mac->set_bssid_filter(mac->dev, net->bssid);
memcpy(mac->ieee->bssid, net->bssid, ETH_ALEN);
netif_carrier_on(mac->dev);
mac->association_id = le16_to_cpup(&resp->aid);
}
/* received frame handling functions */
int
ieee80211softmac_handle_assoc_response(struct net_device * dev,
struct ieee80211_assoc_response * resp,
struct ieee80211_network * _ieee80211_network_do_not_use)
{
/* NOTE: the network parameter has to be ignored by
* this code because it is the ieee80211's pointer
* to the struct, not ours (we made a copy)
*/
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
u16 status = le16_to_cpup(&resp->status);
struct ieee80211softmac_network *network = NULL;
unsigned long flags;
spin_lock_irqsave(&mac->lock, flags);
if (!mac->associnfo.associating) {
/* we race against the timeout function, so make sure
* only one of us can do work */
spin_unlock_irqrestore(&mac->lock, flags);
return 0;
}
network = ieee80211softmac_get_network_by_bssid_locked(mac, resp->header.addr3);
/* someone sending us things without us knowing him? Ignore. */
if (!network) {
dprintk(KERN_INFO PFX "Received unrequested assocation response from " MAC_FMT "\n", MAC_ARG(resp->header.addr3));
spin_unlock_irqrestore(&mac->lock, flags);
return 0;
}
/* now that we know it was for us, we can cancel the timeout */
cancel_delayed_work(&mac->associnfo.timeout);
switch (status) {
case 0:
dprintk(KERN_INFO PFX "associated!\n");
ieee80211softmac_associated(mac, resp, network);
ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, network);
break;
case WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH:
if (!network->auth_desynced_once) {
/* there seem to be a few rare cases where our view of
* the world is obscured, or buggy APs that don't DEAUTH
* us properly. So we handle that, but allow it only once.
*/
printkl(KERN_INFO PFX "We were not authenticated during association, retrying...\n");
network->authenticated = 0;
/* we don't want to do this more than once ... */
network->auth_desynced_once = 1;
schedule_work(&mac->associnfo.work);
break;
}
default:
dprintk(KERN_INFO PFX "associating failed (reason: 0x%x)!\n", status);
mac->associnfo.associating = 0;
mac->associnfo.bssvalid = 0;
mac->associated = 0;
ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, network);
}
spin_unlock_irqrestore(&mac->lock, flags);
return 0;
}
int
ieee80211softmac_handle_disassoc(struct net_device * dev,
struct ieee80211_disassoc *disassoc)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
unsigned long flags;
if (memcmp(disassoc->header.addr2, mac->associnfo.bssid, ETH_ALEN))
return 0;
if (memcmp(disassoc->header.addr1, mac->dev->dev_addr, ETH_ALEN))
return 0;
dprintk(KERN_INFO PFX "got disassoc frame\n");
netif_carrier_off(dev);
spin_lock_irqsave(&mac->lock, flags);
mac->associnfo.bssvalid = 0;
mac->associated = 0;
schedule_work(&mac->associnfo.work);
spin_unlock_irqrestore(&mac->lock, flags);
return 0;
}
int
ieee80211softmac_handle_reassoc_req(struct net_device * dev,
struct ieee80211_reassoc_request * resp)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
struct ieee80211softmac_network *network;
network = ieee80211softmac_get_network_by_bssid(mac, resp->header.addr3);
if (!network) {
dprintkl(KERN_INFO PFX "reassoc request from unknown network\n");
return 0;
}
ieee80211softmac_assoc(mac, network);
return 0;
}
/*
* This file contains the softmac's authentication logic.
*
* Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#include "ieee80211softmac_priv.h"
static void ieee80211softmac_auth_queue(void *data);
/* Queues an auth request to the desired AP */
int
ieee80211softmac_auth_req(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *net)
{
struct ieee80211softmac_auth_queue_item *auth;
unsigned long flags;
if (net->authenticating)
return 0;
/* Add the network if it's not already added */
ieee80211softmac_add_network(mac, net);
dprintk(KERN_NOTICE PFX "Queueing Authentication Request to "MAC_FMT"\n", MAC_ARG(net->bssid));
/* Queue the auth request */
auth = (struct ieee80211softmac_auth_queue_item *)
kmalloc(sizeof(struct ieee80211softmac_auth_queue_item), GFP_KERNEL);
if(auth == NULL)
return -ENOMEM;
auth->net = net;
auth->mac = mac;
auth->retry = IEEE80211SOFTMAC_AUTH_RETRY_LIMIT;
auth->state = IEEE80211SOFTMAC_AUTH_OPEN_REQUEST;
INIT_WORK(&auth->work, &ieee80211softmac_auth_queue, (void *)auth);
/* Lock (for list) */
spin_lock_irqsave(&mac->lock, flags);
/* add to list */
list_add_tail(&auth->list, &mac->auth_queue);
schedule_work(&auth->work);
spin_unlock_irqrestore(&mac->lock, flags);
return 0;
}
/* Sends an auth request to the desired AP and handles timeouts */
static void
ieee80211softmac_auth_queue(void *data)
{
struct ieee80211softmac_device *mac;
struct ieee80211softmac_auth_queue_item *auth;
struct ieee80211softmac_network *net;
unsigned long flags;
auth = (struct ieee80211softmac_auth_queue_item *)data;
net = auth->net;
mac = auth->mac;
if(auth->retry > 0) {
/* Switch to correct channel for this network */
mac->set_channel(mac->dev, net->channel);
/* Lock and set flags */
spin_lock_irqsave(&mac->lock, flags);
net->authenticated = 0;
net->authenticating = 1;
/* add a timeout call so we eventually give up waiting for an auth reply */
schedule_delayed_work(&auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT);
auth->retry--;
spin_unlock_irqrestore(&mac->lock, flags);
if (ieee80211softmac_send_mgt_frame(mac, auth->net, IEEE80211_STYPE_AUTH, auth->state))
dprintk(KERN_NOTICE PFX "Sending Authentication Request to "MAC_FMT" failed (this shouldn't happen, wait for the timeout).\n", MAC_ARG(net->bssid));
else
dprintk(KERN_NOTICE PFX "Sent Authentication Request to "MAC_FMT".\n", MAC_ARG(net->bssid));
return;
}
printkl(KERN_WARNING PFX "Authentication timed out with "MAC_FMT"\n", MAC_ARG(net->bssid));
/* Remove this item from the queue */
spin_lock_irqsave(&mac->lock, flags);
ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT, net);
cancel_delayed_work(&auth->work); /* just to make sure... */
list_del(&auth->list);
spin_unlock_irqrestore(&mac->lock, flags);
/* Free it */
kfree(auth);
}
/* Handle the auth response from the AP
* This should be registered with ieee80211 as handle_auth
*/
int
ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth)
{
struct list_head *list_ptr;
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
struct ieee80211softmac_auth_queue_item *aq = NULL;
struct ieee80211softmac_network *net = NULL;
unsigned long flags;
u8 * data;
/* Find correct auth queue item */
spin_lock_irqsave(&mac->lock, flags);
list_for_each(list_ptr, &mac->auth_queue) {
aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
net = aq->net;
if (!memcmp(net->bssid, auth->header.addr2, ETH_ALEN))
break;
else
aq = NULL;
}
spin_unlock_irqrestore(&mac->lock, flags);
/* Make sure that we've got an auth queue item for this request */
if(aq == NULL)
{
printkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but no queue item exists.\n", MAC_ARG(auth->header.addr2));
/* Error #? */
return -1;
}
/* Check for out of order authentication */
if(!net->authenticating)
{
printkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but did not request authentication.\n",MAC_ARG(auth->header.addr2));
return -1;
}
/* Parse the auth packet */
switch(auth->algorithm) {
case WLAN_AUTH_OPEN:
/* Check the status code of the response */
switch(auth->status) {
case WLAN_STATUS_SUCCESS:
/* Update the status to Authenticated */
spin_lock_irqsave(&mac->lock, flags);
net->authenticating = 0;
net->authenticated = 1;
spin_unlock_irqrestore(&mac->lock, flags);
/* Send event */
printkl(KERN_NOTICE PFX "Open Authentication completed with "MAC_FMT"\n", MAC_ARG(net->bssid));
ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net);
break;
default:
/* Lock and reset flags */
spin_lock_irqsave(&mac->lock, flags);
net->authenticated = 0;
net->authenticating = 0;
spin_unlock_irqrestore(&mac->lock, flags);
printkl(KERN_NOTICE PFX "Open Authentication with "MAC_FMT" failed, error code: %i\n",
MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
/* Count the error? */
break;
}
goto free_aq;
break;
case WLAN_AUTH_SHARED_KEY:
/* Figure out where we are in the process */
switch(auth->transaction) {
case IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE:
/* Check to make sure we have a challenge IE */
data = (u8 *)auth->info_element;
if(*data++ != MFIE_TYPE_CHALLENGE){
printkl(KERN_NOTICE PFX "Shared Key Authentication failed due to a missing challenge.\n");
break;
}
/* Save the challenge */
spin_lock_irqsave(&mac->lock, flags);
net->challenge_len = *data++;
if(net->challenge_len > WLAN_AUTH_CHALLENGE_LEN)
net->challenge_len = WLAN_AUTH_CHALLENGE_LEN;
if(net->challenge != NULL)
kfree(net->challenge);
net->challenge = kmalloc(net->challenge_len, GFP_ATOMIC);
memcpy(net->challenge, data, net->challenge_len);
aq->state = IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE;
spin_unlock_irqrestore(&mac->lock, flags);
/* Switch to correct channel for this network */
mac->set_channel(mac->dev, net->channel);
/* Send our response (How to encrypt?) */
ieee80211softmac_send_mgt_frame(mac, aq->net, IEEE80211_STYPE_AUTH, aq->state);
break;
case IEEE80211SOFTMAC_AUTH_SHARED_PASS:
/* Check the status code of the response */
switch(auth->status) {
case WLAN_STATUS_SUCCESS:
/* Update the status to Authenticated */
spin_lock_irqsave(&mac->lock, flags);
net->authenticating = 0;
net->authenticated = 1;
spin_unlock_irqrestore(&mac->lock, flags);
printkl(KERN_NOTICE PFX "Shared Key Authentication completed with "MAC_FMT"\n",
MAC_ARG(net->bssid));
break;
default:
printkl(KERN_NOTICE PFX "Shared Key Authentication with "MAC_FMT" failed, error code: %i\n",
MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
/* Lock and reset flags */
spin_lock_irqsave(&mac->lock, flags);
net->authenticating = 0;
net->authenticated = 0;
spin_unlock_irqrestore(&mac->lock, flags);
/* Count the error? */
break;
}
goto free_aq;
break;
default:
printkl(KERN_WARNING PFX "Unhandled Authentication Step: %i\n", auth->transaction);
break;
}
goto free_aq;
break;
default:
/* ERROR */
goto free_aq;
break;
}
return 0;
free_aq:
/* Cancel the timeout */
spin_lock_irqsave(&mac->lock, flags);
cancel_delayed_work(&aq->work);
/* Remove this item from the queue */
list_del(&aq->list);
spin_unlock_irqrestore(&mac->lock, flags);
/* Free it */
kfree(aq);
return 0;
}
/*
* Handle deauthorization
*/
static void
ieee80211softmac_deauth_from_net(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *net)
{
struct ieee80211softmac_auth_queue_item *aq = NULL;
struct list_head *list_ptr;
unsigned long flags;
/* Lock and reset status flags */
spin_lock_irqsave(&mac->lock, flags);
net->authenticating = 0;
net->authenticated = 0;
/* Find correct auth queue item, if it exists */
list_for_each(list_ptr, &mac->auth_queue) {
aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
if (!memcmp(net->bssid, aq->net->bssid, ETH_ALEN))
break;
else
aq = NULL;
}
/* Cancel pending work */
if(aq != NULL)
/* Not entirely safe? What about running work? */
cancel_delayed_work(&aq->work);
/* Free our network ref */
ieee80211softmac_del_network_locked(mac, net);
if(net->challenge != NULL)
kfree(net->challenge);
kfree(net);
/* can't transmit data right now... */
netif_carrier_off(mac->dev);
/* let's try to re-associate */
schedule_work(&mac->associnfo.work);
spin_unlock_irqrestore(&mac->lock, flags);
}
/*
* Sends a deauth request to the desired AP
*/
int
ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *net, int reason)
{
int ret;
/* Make sure the network is authenticated */
if (!net->authenticated)
{
printkl(KERN_DEBUG PFX "Can't send deauthentication packet, network is not authenticated.\n");
/* Error okay? */
return -EPERM;
}
/* Send the de-auth packet */
if((ret = ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_DEAUTH, reason)))
return ret;
ieee80211softmac_deauth_from_net(mac, net);
return 0;
}
/*
* This should be registered with ieee80211 as handle_deauth
*/
int
ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *deauth)
{
struct ieee80211softmac_network *net = NULL;
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
if (!deauth) {
dprintk("deauth without deauth packet. eek!\n");
return 0;
}
net = ieee80211softmac_get_network_by_bssid(mac, deauth->header.addr2);
if (net == NULL) {
printkl(KERN_DEBUG PFX "Received deauthentication packet from "MAC_FMT", but that network is unknown.\n",
MAC_ARG(deauth->header.addr2));
return 0;
}
/* Make sure the network is authenticated */
if(!net->authenticated)
{
printkl(KERN_DEBUG PFX "Can't perform deauthentication, network is not authenticated.\n");
/* Error okay? */
return -EPERM;
}
ieee80211softmac_deauth_from_net(mac, net);
return 0;
}
/*
* Event system
* Also see comments in public header file and longer explanation below.
*
* Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#include "ieee80211softmac_priv.h"
/*
* Each event has associated to it
* - an event type (see constants in public header)
* - an event context (see below)
* - the function to be called
* - a context (extra parameter to call the function with)
* - and the softmac struct
*
* The event context is private and can only be used from
* within this module. Its meaning varies with the event
* type:
* SCAN_FINISHED: no special meaning
* ASSOCIATED,
* ASSOCIATE_FAILED,
* ASSOCIATE_TIMEOUT,
* AUTHENTICATED,
* AUTH_FAILED,
* AUTH_TIMEOUT: a pointer to the network struct
* ...
* Code within this module can use the event context to be only
* called when the event is true for that specific context
* as per above table.
* If the event context is NULL, then the notification is always called,
* regardless of the event context. The event context is not passed to
* the callback, it is assumed that the context suffices.
*
* You can also use the event context only by setting the event type
* to -1 (private use only), in which case you'll be notified
* whenever the event context matches.
*/
static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = {
"scan finished",
"associated",
"associating failed",
"associating timed out",
"authenticated",
"authenticating failed",
"authenticating timed out",
"associating failed because no suitable network was found",
};
static void
ieee80211softmac_notify_callback(void *d)
{
struct ieee80211softmac_event event = *(struct ieee80211softmac_event*) d;
kfree(d);
event.fun(event.mac->dev, event.context);
}
int
ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask)
{
struct ieee80211softmac_event *eventptr;
unsigned long flags;
if (event < -1 || event > IEEE80211SOFTMAC_EVENT_LAST)
return -ENOSYS;
if (!fun)
return -EINVAL;
eventptr = kmalloc(sizeof(struct ieee80211softmac_event), gfp_mask);
if (!eventptr)
return -ENOMEM;
eventptr->event_type = event;
INIT_WORK(&eventptr->work, ieee80211softmac_notify_callback, eventptr);
eventptr->fun = fun;
eventptr->context = context;
eventptr->mac = mac;
eventptr->event_context = event_context;
spin_lock_irqsave(&mac->lock, flags);
list_add(&eventptr->list, &mac->events);
spin_unlock_irqrestore(&mac->lock, flags);
return 0;
}
int
ieee80211softmac_notify_gfp(struct net_device *dev,
int event, notify_function_ptr fun, void *context, gfp_t gfp_mask)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
if (event < 0 || event > IEEE80211SOFTMAC_EVENT_LAST)
return -ENOSYS;
return ieee80211softmac_notify_internal(mac, event, NULL, fun, context, gfp_mask);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_notify_gfp);
/* private -- calling all callbacks that were specified */
void
ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_ctx)
{
struct ieee80211softmac_event *eventptr, *tmp;
union iwreq_data wrqu;
char *msg;
if (event >= 0) {
msg = event_descriptions[event];
wrqu.data.length = strlen(msg);
wireless_send_event(mac->dev, IWEVCUSTOM, &wrqu, msg);
}
if (!list_empty(&mac->events))
list_for_each_entry_safe(eventptr, tmp, &mac->events, list) {
if ((eventptr->event_type == event || eventptr->event_type == -1)
&& (eventptr->event_context == NULL || eventptr->event_context == event_ctx)) {
list_del(&eventptr->list);
schedule_work(&eventptr->work);
}
}
}
void
ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_ctx)
{
unsigned long flags;
spin_lock_irqsave(&mac->lock, flags);
ieee80211softmac_call_events_locked(mac, event, event_ctx);
spin_unlock_irqrestore(&mac->lock, flags);
}
/*
* Some parts based on code from net80211
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "ieee80211softmac_priv.h"
/* Helper functions for inserting data into the frames */
/*
* Adds an ESSID element to the frame
*
*/
static u8 *
ieee80211softmac_add_essid(u8 *dst, struct ieee80211softmac_essid *essid)
{
if (essid) {
*dst++ = MFIE_TYPE_SSID;
*dst++ = essid->len;
memcpy(dst, essid->data, essid->len);
return dst+essid->len;
} else {
*dst++ = MFIE_TYPE_SSID;
*dst++ = 0;
return dst;
}
}
/* Adds Supported Rates and if required Extended Rates Information Element
* to the frame, ASSUMES WE HAVE A SORTED LIST OF RATES */
static u8 *
ieee80211softmac_frame_add_rates(u8 *dst, const struct ieee80211softmac_ratesinfo *r)
{
int cck_len, ofdm_len;
*dst++ = MFIE_TYPE_RATES;
for(cck_len=0; ieee80211_is_cck_rate(r->rates[cck_len]) && (cck_len < r->count);cck_len++);
if(cck_len > IEEE80211SOFTMAC_MAX_RATES_LEN)
cck_len = IEEE80211SOFTMAC_MAX_RATES_LEN;
*dst++ = cck_len;
memcpy(dst, r->rates, cck_len);
dst += cck_len;
if(cck_len < r->count){
for (ofdm_len=0; ieee80211_is_ofdm_rate(r->rates[ofdm_len + cck_len]) && (ofdm_len + cck_len < r->count); ofdm_len++);
if (ofdm_len > 0) {
if (ofdm_len > IEEE80211SOFTMAC_MAX_EX_RATES_LEN)
ofdm_len = IEEE80211SOFTMAC_MAX_EX_RATES_LEN;
*dst++ = MFIE_TYPE_RATES_EX;
*dst++ = ofdm_len;
memcpy(dst, r->rates + cck_len, ofdm_len);
dst += ofdm_len;
}
}
return dst;
}
/* Allocate a management frame */
static u8 *
ieee80211softmac_alloc_mgt(u32 size)
{
u8 * data;
/* Add the header and FCS to the size */
size = size + IEEE80211_3ADDR_LEN;
if(size > IEEE80211_DATA_LEN)
return NULL;
/* Allocate the frame */
data = kmalloc(size, GFP_ATOMIC);
memset(data, 0, size);
return data;
}
/*
* Add a 2 Address Header
*/
static void
ieee80211softmac_hdr_2addr(struct ieee80211softmac_device *mac,
struct ieee80211_hdr_2addr *header, u32 type, u8 *dest)
{
/* Fill in the frame control flags */
header->frame_ctl = cpu_to_le16(type);
/* Control packets always have WEP turned off */
if(type > IEEE80211_STYPE_CFENDACK && type < IEEE80211_STYPE_PSPOLL)
header->frame_ctl |= mac->ieee->sec.level ? cpu_to_le16(IEEE80211_FCTL_PROTECTED) : 0;
/* Fill in the duration */
header->duration_id = 0;
/* FIXME: How do I find this?
* calculate. But most drivers just fill in 0 (except if it's a station id of course) */
/* Fill in the Destination Address */
if(dest == NULL)
memset(header->addr1, 0xFF, ETH_ALEN);
else
memcpy(header->addr1, dest, ETH_ALEN);
/* Fill in the Source Address */
memcpy(header->addr2, mac->ieee->dev->dev_addr, ETH_ALEN);
}
/* Add a 3 Address Header */
static void
ieee80211softmac_hdr_3addr(struct ieee80211softmac_device *mac,
struct ieee80211_hdr_3addr *header, u32 type, u8 *dest, u8 *bssid)
{
/* This is common with 2addr, so use that instead */
ieee80211softmac_hdr_2addr(mac, (struct ieee80211_hdr_2addr *)header, type, dest);
/* Fill in the BSS ID */
if(bssid == NULL)
memset(header->addr3, 0xFF, ETH_ALEN);
else
memcpy(header->addr3, bssid, ETH_ALEN);
/* Fill in the sequence # */
/* FIXME: I need to add this to the softmac struct
* shouldn't the sequence number be in ieee80211? */
}
/*****************************************************************************
* Create Management packets
*****************************************************************************/
/* Creates an association request packet */
static u32
ieee80211softmac_assoc_req(struct ieee80211_assoc_request **pkt,
struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
{
u8 *data;
(*pkt) = (struct ieee80211_assoc_request *)ieee80211softmac_alloc_mgt(
2 + /* Capability Info */
2 + /* Listen Interval */
/* SSID IE */
1 + 1 + IW_ESSID_MAX_SIZE +
/* Rates IE */
1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
/* Extended Rates IE */
1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN +
/* WPA IE if present */
mac->wpa.IElen
/* Other IE's? Optional?
* Yeah, probably need an extra IE parameter -- lots of vendors like to
* fill in their own IEs */
);
if (unlikely((*pkt) == NULL))
return 0;
ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_ASSOC_REQ, net->bssid, net->bssid);
/* Fill in capability Info */
(*pkt)->capability = (mac->ieee->iw_mode == IW_MODE_MASTER) || (mac->ieee->iw_mode == IW_MODE_INFRA) ?
cpu_to_le16(WLAN_CAPABILITY_ESS) :
cpu_to_le16(WLAN_CAPABILITY_IBSS);
/* Need to add this
(*pkt)->capability |= mac->ieee->short_slot ?
cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0;
*/
(*pkt)->capability |= mac->ieee->sec.level ? cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0;
/* Fill in Listen Interval (?) */
(*pkt)->listen_interval = cpu_to_le16(10);
data = (u8 *)(*pkt)->info_element;
/* Add SSID */
data = ieee80211softmac_add_essid(data, &net->essid);
/* Add Rates */
data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
/* Add WPA IE */
if (mac->wpa.IElen && mac->wpa.IE) {
memcpy(data, mac->wpa.IE, mac->wpa.IElen);
data += mac->wpa.IElen;
}
/* Return the number of used bytes */
return (data - (u8*)(*pkt));
}
/* Create a reassociation request packet */
static u32
ieee80211softmac_reassoc_req(struct ieee80211_reassoc_request **pkt,
struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
{
u8 *data;
(*pkt) = (struct ieee80211_reassoc_request *)ieee80211softmac_alloc_mgt(
2 + /* Capability Info */
2 + /* Listen Interval */
ETH_ALEN + /* AP MAC */
/* SSID IE */
1 + 1 + IW_ESSID_MAX_SIZE +
/* Rates IE */
1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
/* Extended Rates IE */
1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN
/* Other IE's? */
);
if (unlikely((*pkt) == NULL))
return 0;
ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_REASSOC_REQ, net->bssid, net->bssid);
/* Fill in capability Info */
(*pkt)->capability = mac->ieee->iw_mode == IW_MODE_MASTER ?
cpu_to_le16(WLAN_CAPABILITY_ESS) :
cpu_to_le16(WLAN_CAPABILITY_IBSS);
/*
(*pkt)->capability |= mac->ieee->short_slot ?
cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0;
*/
(*pkt)->capability |= mac->ieee->sec.level ?
cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0;
/* Fill in Listen Interval (?) */
(*pkt)->listen_interval = cpu_to_le16(10);
/* Fill in the current AP MAC */
memcpy((*pkt)->current_ap, mac->ieee->bssid, ETH_ALEN);
data = (u8 *)(*pkt)->info_element;
/* Add SSID */
data = ieee80211softmac_add_essid(data, &net->essid);
/* Add Rates */
data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
/* Return packet size */
return (data - (u8 *)(*pkt));
}
/* Create an authentication packet */
static u32
ieee80211softmac_auth(struct ieee80211_auth **pkt,
struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
u16 transaction, u16 status)
{
u8 *data;
/* Allocate Packet */
(*pkt) = (struct ieee80211_auth *)ieee80211softmac_alloc_mgt(
2 + /* Auth Algorithm */
2 + /* Auth Transaction Seq */
2 + /* Status Code */
/* Challenge Text IE */
mac->ieee->open_wep ? 0 :
1 + 1 + WLAN_AUTH_CHALLENGE_LEN
);
if (unlikely((*pkt) == NULL))
return 0;
ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_AUTH, net->bssid, net->bssid);
/* Algorithm */
(*pkt)->algorithm = mac->ieee->open_wep ?
cpu_to_le16(WLAN_AUTH_OPEN) :
cpu_to_le16(WLAN_AUTH_SHARED_KEY);
/* Transaction */
(*pkt)->transaction = cpu_to_le16(transaction);
/* Status */
(*pkt)->status = cpu_to_le16(status);
data = (u8 *)(*pkt)->info_element;
/* Challenge Text */
if(!mac->ieee->open_wep){
*data = MFIE_TYPE_CHALLENGE;
data++;
/* Copy the challenge in */
// *data = challenge length
// data += sizeof(u16);
// memcpy(data, challenge, challenge length);
// data += challenge length;
/* Add the full size to the packet length */
}
/* Return the packet size */
return (data - (u8 *)(*pkt));
}
/* Create a disassocation or deauthentication packet */
static u32
ieee80211softmac_disassoc_deauth(struct ieee80211_disassoc **pkt,
struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
u16 type, u16 reason)
{
/* Allocate Packet */
(*pkt) = (struct ieee80211_disassoc *)ieee80211softmac_alloc_mgt(2);
if (unlikely((*pkt) == NULL))
return 0;
ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), type, net->bssid, net->bssid);
/* Reason */
(*pkt)->reason = cpu_to_le16(reason);
/* Return the packet size */
return (2 + IEEE80211_3ADDR_LEN);
}
/* Create a probe request packet */
static u32
ieee80211softmac_probe_req(struct ieee80211_probe_request **pkt,
struct ieee80211softmac_device *mac, struct ieee80211softmac_essid *essid)
{
u8 *data;
/* Allocate Packet */
(*pkt) = (struct ieee80211_probe_request *)ieee80211softmac_alloc_mgt(
/* SSID of requested network */
1 + 1 + IW_ESSID_MAX_SIZE +
/* Rates IE */
1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
/* Extended Rates IE */
1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN
);
if (unlikely((*pkt) == NULL))
return 0;
ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_REQ, NULL, NULL);
data = (u8 *)(*pkt)->info_element;
/* Add ESSID (can be NULL) */
data = ieee80211softmac_add_essid(data, essid);
/* Add Rates */
data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
/* Return packet size */
return (data - (u8 *)(*pkt));
}
/* Create a probe response packet */
/* FIXME: Not complete */
static u32
ieee80211softmac_probe_resp(struct ieee80211_probe_response **pkt,
struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
{
u8 *data;
/* Allocate Packet */
(*pkt) = (struct ieee80211_probe_response *)ieee80211softmac_alloc_mgt(
8 + /* Timestamp */
2 + /* Beacon Interval */
2 + /* Capability Info */
/* SSID IE */
1 + 1 + IW_ESSID_MAX_SIZE +
7 + /* FH Parameter Set */
2 + /* DS Parameter Set */
8 + /* CF Parameter Set */
4 /* IBSS Parameter Set */
);
if (unlikely((*pkt) == NULL))
return 0;
ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_RESP, net->bssid, net->bssid);
data = (u8 *)(*pkt)->info_element;
/* Return the packet size */
return (data - (u8 *)(*pkt));
}
/* Sends a manangement packet
* FIXME: document the use of the arg parameter
* for _AUTH: (transaction #) | (status << 16)
*/
int
ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac,
void *ptrarg, u32 type, u32 arg)
{
void *pkt = NULL;
u32 pkt_size = 0;
switch(type) {
case IEEE80211_STYPE_ASSOC_REQ:
pkt_size = ieee80211softmac_assoc_req((struct ieee80211_assoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
break;
case IEEE80211_STYPE_REASSOC_REQ:
pkt_size = ieee80211softmac_reassoc_req((struct ieee80211_reassoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
break;
case IEEE80211_STYPE_AUTH:
pkt_size = ieee80211softmac_auth((struct ieee80211_auth **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, (u16)(arg & 0xFFFF), (u16) (arg >> 16));
break;
case IEEE80211_STYPE_DISASSOC:
case IEEE80211_STYPE_DEAUTH:
pkt_size = ieee80211softmac_disassoc_deauth((struct ieee80211_disassoc **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, type, (u16)(arg & 0xFFFF));
break;
case IEEE80211_STYPE_PROBE_REQ:
pkt_size = ieee80211softmac_probe_req((struct ieee80211_probe_request **)(&pkt), mac, (struct ieee80211softmac_essid *)ptrarg);
break;
case IEEE80211_STYPE_PROBE_RESP:
pkt_size = ieee80211softmac_probe_resp((struct ieee80211_probe_response **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
break;
default:
printkl(KERN_DEBUG PFX "Unsupported Management Frame type: %i\n", type);
return -EINVAL;
};
if(pkt_size == 0 || pkt == NULL) {
printkl(KERN_DEBUG PFX "Error, packet is nonexistant or 0 length\n");
return -ENOMEM;
}
/* Send the packet to the ieee80211 layer for tx */
/* we defined softmac->mgmt_xmit for this. Should we keep it
* as it is (that means we'd need to wrap this into a txb),
* modify the prototype (so it matches this function),
* or get rid of it alltogether?
* Does this work for you now?
*/
ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *)pkt, pkt_size);
kfree(pkt);
return 0;
}
/* Create an rts/cts frame */
static u32
ieee80211softmac_rts_cts(struct ieee80211_hdr_2addr **pkt,
struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
u32 type)
{
/* Allocate Packet */
(*pkt) = kmalloc(IEEE80211_2ADDR_LEN, GFP_ATOMIC);
memset(*pkt, 0, IEEE80211_2ADDR_LEN);
if((*pkt) == NULL)
return 0;
ieee80211softmac_hdr_2addr(mac, (*pkt), type, net->bssid);
return IEEE80211_2ADDR_LEN;
}
/* Sends a control packet */
static int
ieee80211softmac_send_ctl_frame(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *net, u32 type, u32 arg)
{
void *pkt = NULL;
u32 pkt_size = 0;
switch(type) {
case IEEE80211_STYPE_RTS:
case IEEE80211_STYPE_CTS:
pkt_size = ieee80211softmac_rts_cts((struct ieee80211_hdr_2addr **)(&pkt), mac, net, type);
break;
default:
printkl(KERN_DEBUG PFX "Unsupported Control Frame type: %i\n", type);
return -EINVAL;
}
if(pkt_size == 0)
return -ENOMEM;
/* Send the packet to the ieee80211 layer for tx */
ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *) pkt, pkt_size);
kfree(pkt);
return 0;
}
/*
* Contains some basic softmac functions along with module registration code etc.
*
* Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#include "ieee80211softmac_priv.h"
#include <linux/sort.h>
struct net_device *alloc_ieee80211softmac(int sizeof_priv)
{
struct ieee80211softmac_device *softmac;
struct net_device *dev;
dev = alloc_ieee80211(sizeof(struct ieee80211softmac_device) + sizeof_priv);
softmac = ieee80211_priv(dev);
softmac->dev = dev;
softmac->ieee = netdev_priv(dev);
spin_lock_init(&softmac->lock);
softmac->ieee->handle_auth = ieee80211softmac_auth_resp;
softmac->ieee->handle_deauth = ieee80211softmac_deauth_resp;
softmac->ieee->handle_assoc_response = ieee80211softmac_handle_assoc_response;
softmac->ieee->handle_reassoc_request = ieee80211softmac_handle_reassoc_req;
softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc;
softmac->scaninfo = NULL;
/* TODO: initialise all the other callbacks in the ieee struct
* (once they're written)
*/
INIT_LIST_HEAD(&softmac->auth_queue);
INIT_LIST_HEAD(&softmac->network_list);
INIT_LIST_HEAD(&softmac->events);
INIT_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work, softmac);
INIT_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout, softmac);
softmac->start_scan = ieee80211softmac_start_scan_implementation;
softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation;
softmac->stop_scan = ieee80211softmac_stop_scan_implementation;
//TODO: The mcast rate has to be assigned dynamically somewhere (in scanning, association. Not sure...)
// It has to be set to the highest rate all stations in the current network can handle.
softmac->txrates.mcast_rate = IEEE80211_CCK_RATE_1MB;
softmac->txrates.mcast_fallback = IEEE80211_CCK_RATE_1MB;
/* This is reassigned in ieee80211softmac_start to sane values. */
softmac->txrates.default_rate = IEEE80211_CCK_RATE_1MB;
softmac->txrates.default_fallback = IEEE80211_CCK_RATE_1MB;
/* to start with, we can't send anything ... */
netif_carrier_off(dev);
return dev;
}
EXPORT_SYMBOL_GPL(alloc_ieee80211softmac);
/* Clears the pending work queue items, stops all scans, etc. */
void
ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm)
{
unsigned long flags;
struct ieee80211softmac_event *eventptr, *eventtmp;
struct ieee80211softmac_auth_queue_item *authptr, *authtmp;
struct ieee80211softmac_network *netptr, *nettmp;
ieee80211softmac_stop_scan(sm);
ieee80211softmac_wait_for_scan(sm);
spin_lock_irqsave(&sm->lock, flags);
/* Free all pending assoc work items */
cancel_delayed_work(&sm->associnfo.work);
/* Free all pending scan work items */
if(sm->scaninfo != NULL)
cancel_delayed_work(&sm->scaninfo->softmac_scan);
/* Free all pending auth work items */
list_for_each_entry(authptr, &sm->auth_queue, list)
cancel_delayed_work(&authptr->work);
/* delete all pending event calls and work items */
list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list)
cancel_delayed_work(&eventptr->work);
spin_unlock_irqrestore(&sm->lock, flags);
flush_scheduled_work();
/* now we should be save and no longer need locking... */
spin_lock_irqsave(&sm->lock, flags);
/* Free all pending auth work items */
list_for_each_entry_safe(authptr, authtmp, &sm->auth_queue, list) {
list_del(&authptr->list);
kfree(authptr);
}
/* delete all pending event calls and work items */
list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) {
list_del(&eventptr->list);
kfree(eventptr);
}
/* Free all networks */
list_for_each_entry_safe(netptr, nettmp, &sm->network_list, list) {
ieee80211softmac_del_network_locked(sm, netptr);
if(netptr->challenge != NULL)
kfree(netptr->challenge);
kfree(netptr);
}
spin_unlock_irqrestore(&sm->lock, flags);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_clear_pending_work);
void free_ieee80211softmac(struct net_device *dev)
{
struct ieee80211softmac_device *sm = ieee80211_priv(dev);
ieee80211softmac_clear_pending_work(sm);
kfree(sm->scaninfo);
kfree(sm->wpa.IE);
free_ieee80211(dev);
}
EXPORT_SYMBOL_GPL(free_ieee80211softmac);
static void ieee80211softmac_start_check_rates(struct ieee80211softmac_device *mac)
{
struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
/* I took out the sorting check, we're seperating by modulation now. */
if (ri->count)
return;
/* otherwise assume we hav'em all! */
if (mac->ieee->modulation & IEEE80211_CCK_MODULATION) {
ri->rates[ri->count++] = IEEE80211_CCK_RATE_1MB;
ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB;
ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB;
ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB;
}
if (mac->ieee->modulation & IEEE80211_OFDM_MODULATION) {
ri->rates[ri->count++] = IEEE80211_OFDM_RATE_6MB;
ri->rates[ri->count++] = IEEE80211_OFDM_RATE_9MB;
ri->rates[ri->count++] = IEEE80211_OFDM_RATE_12MB;
ri->rates[ri->count++] = IEEE80211_OFDM_RATE_18MB;
ri->rates[ri->count++] = IEEE80211_OFDM_RATE_24MB;
ri->rates[ri->count++] = IEEE80211_OFDM_RATE_36MB;
ri->rates[ri->count++] = IEEE80211_OFDM_RATE_48MB;
ri->rates[ri->count++] = IEEE80211_OFDM_RATE_54MB;
}
}
void ieee80211softmac_start(struct net_device *dev)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
struct ieee80211_device *ieee = mac->ieee;
u32 change = 0;
struct ieee80211softmac_txrates oldrates;
ieee80211softmac_start_check_rates(mac);
/* TODO: We need some kind of state machine to lower the default rates
* if we loose too many packets.
*/
/* Change the default txrate to the highest possible value.
* The txrate machine will lower it, if it is too high.
*/
if (mac->txrates_change)
oldrates = mac->txrates;
if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
mac->txrates.default_rate = IEEE80211_OFDM_RATE_54MB;
change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
mac->txrates.default_fallback = IEEE80211_OFDM_RATE_24MB;
change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
} else if (ieee->modulation & IEEE80211_CCK_MODULATION) {
mac->txrates.default_rate = IEEE80211_CCK_RATE_11MB;
change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
mac->txrates.default_fallback = IEEE80211_CCK_RATE_5MB;
change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
} else
assert(0);
if (mac->txrates_change)
mac->txrates_change(dev, change, &oldrates);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_start);
void ieee80211softmac_stop(struct net_device *dev)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
ieee80211softmac_clear_pending_work(mac);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_stop);
void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
unsigned long flags;
spin_lock_irqsave(&mac->lock, flags);
memcpy(mac->ratesinfo.rates, rates, count);
mac->ratesinfo.count = count;
spin_unlock_irqrestore(&mac->lock, flags);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_set_rates);
static u8 raise_rate(struct ieee80211softmac_device *mac, u8 rate)
{
int i;
struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
for (i=0; i<ri->count-1; i++) {
if (ri->rates[i] == rate)
return ri->rates[i+1];
}
/* I guess we can't go any higher... */
return ri->rates[ri->count];
}
u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta)
{
int i;
struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
for (i=delta; i<ri->count; i++) {
if (ri->rates[i] == rate)
return ri->rates[i-delta];
}
/* I guess we can't go any lower... */
return ri->rates[0];
}
static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac,
int amount)
{
struct ieee80211softmac_txrates oldrates;
u8 default_rate = mac->txrates.default_rate;
u8 default_fallback = mac->txrates.default_fallback;
u32 changes = 0;
//TODO: This is highly experimental code.
// Maybe the dynamic rate selection does not work
// and it has to be removed again.
printk("badness %d\n", mac->txrate_badness);
mac->txrate_badness += amount;
if (mac->txrate_badness <= -1000) {
/* Very small badness. Try a faster bitrate. */
if (mac->txrates_change)
memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
default_rate = raise_rate(mac, default_rate);
changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
default_fallback = get_fallback_rate(mac, default_rate);
changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
mac->txrate_badness = 0;
printk("Bitrate raised to %u\n", default_rate);
} else if (mac->txrate_badness >= 10000) {
/* Very high badness. Try a slower bitrate. */
if (mac->txrates_change)
memcpy(&oldrates, &mac->txrates, sizeof(oldrates));
default_rate = lower_rate(mac, default_rate);
changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
default_fallback = get_fallback_rate(mac, default_rate);
changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
mac->txrate_badness = 0;
printk("Bitrate lowered to %u\n", default_rate);
}
mac->txrates.default_rate = default_rate;
mac->txrates.default_fallback = default_fallback;
if (changes && mac->txrates_change)
mac->txrates_change(mac->dev, changes, &oldrates);
}
void ieee80211softmac_fragment_lost(struct net_device *dev,
u16 wl_seq)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
unsigned long flags;
spin_lock_irqsave(&mac->lock, flags);
ieee80211softmac_add_txrates_badness(mac, 1000);
//TODO
spin_unlock_irqrestore(&mac->lock, flags);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_fragment_lost);
static int rate_cmp(const void *a_, const void *b_) {
u8 *a, *b;
a = (u8*)a_;
b = (u8*)b_;
return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK));
}
/* Allocate a softmac network struct and fill it from a network */
struct ieee80211softmac_network *
ieee80211softmac_create_network(struct ieee80211softmac_device *mac,
struct ieee80211_network *net)
{
struct ieee80211softmac_network *softnet;
softnet = kzalloc(sizeof(struct ieee80211softmac_network), GFP_ATOMIC);
if(softnet == NULL)
return NULL;
memcpy(softnet->bssid, net->bssid, ETH_ALEN);
softnet->channel = net->channel;
softnet->essid.len = net->ssid_len;
memcpy(softnet->essid.data, net->ssid, softnet->essid.len);
/* copy rates over */
softnet->supported_rates.count = net->rates_len;
memcpy(&softnet->supported_rates.rates[0], net->rates, net->rates_len);
memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len);
softnet->supported_rates.count += net->rates_ex_len;
sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL);
softnet->capabilities = net->capability;
return softnet;
}
/* Add a network to the list, while locked */
void
ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *add_net)
{
struct list_head *list_ptr;
struct ieee80211softmac_network *softmac_net = NULL;
list_for_each(list_ptr, &mac->network_list) {
softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
if(!memcmp(softmac_net->bssid, add_net->bssid, ETH_ALEN))
break;
else
softmac_net = NULL;
}
if(softmac_net == NULL)
list_add(&(add_net->list), &mac->network_list);
}
/* Add a network to the list, with locking */
void
ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *add_net)
{
unsigned long flags;
spin_lock_irqsave(&mac->lock, flags);
ieee80211softmac_add_network_locked(mac, add_net);
spin_unlock_irqrestore(&mac->lock, flags);
}
/* Delete a network from the list, while locked*/
void
ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *del_net)
{
list_del(&(del_net->list));
}
/* Delete a network from the list with locking */
void
ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *del_net)
{
unsigned long flags;
spin_lock_irqsave(&mac->lock, flags);
ieee80211softmac_del_network_locked(mac, del_net);
spin_unlock_irqrestore(&mac->lock, flags);
}
/* Get a network from the list by MAC while locked */
struct ieee80211softmac_network *
ieee80211softmac_get_network_by_bssid_locked(struct ieee80211softmac_device *mac,
u8 *bssid)
{
struct list_head *list_ptr;
struct ieee80211softmac_network *softmac_net = NULL;
list_for_each(list_ptr, &mac->network_list) {
softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN))
break;
else
softmac_net = NULL;
}
return softmac_net;
}
/* Get a network from the list by BSSID with locking */
struct ieee80211softmac_network *
ieee80211softmac_get_network_by_bssid(struct ieee80211softmac_device *mac,
u8 *bssid)
{
unsigned long flags;
struct ieee80211softmac_network *softmac_net;
spin_lock_irqsave(&mac->lock, flags);
softmac_net = ieee80211softmac_get_network_by_bssid_locked(mac, bssid);
spin_unlock_irqrestore(&mac->lock, flags);
return softmac_net;
}
/* Get a network from the list by ESSID while locked */
struct ieee80211softmac_network *
ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
struct ieee80211softmac_essid *essid)
{
struct list_head *list_ptr;
struct ieee80211softmac_network *softmac_net = NULL;
list_for_each(list_ptr, &mac->network_list) {
softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
if (softmac_net->essid.len == essid->len &&
!memcmp(softmac_net->essid.data, essid->data, essid->len))
return softmac_net;
}
return NULL;
}
/* Get a network from the list by ESSID with locking */
struct ieee80211softmac_network *
ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
struct ieee80211softmac_essid *essid)
{
unsigned long flags;
struct ieee80211softmac_network *softmac_net = NULL;
spin_lock_irqsave(&mac->lock, flags);
softmac_net = ieee80211softmac_get_network_by_essid_locked(mac, essid);
spin_unlock_irqrestore(&mac->lock, flags);
return softmac_net;
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johannes Berg");
MODULE_AUTHOR("Joseph Jezak");
MODULE_AUTHOR("Larry Finger");
MODULE_AUTHOR("Danny van Dyk");
MODULE_AUTHOR("Michael Buesch");
MODULE_DESCRIPTION("802.11 software MAC");
/*
* Internal softmac API definitions.
*
* Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#ifndef IEEE80211SOFTMAC_PRIV_H_
#define IEEE80211SOFTMAC_PRIV_H_
#include <net/ieee80211softmac.h>
#include <net/ieee80211softmac_wx.h>
#include <linux/kernel.h>
#include <linux/stringify.h>
#define PFX "SoftMAC: "
#ifdef assert
# undef assert
#endif
#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
#define assert(expr) \
do { \
if (unlikely(!(expr))) { \
printkl(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", #expr, \
__FILE__, __LINE__, __FUNCTION__); \
} \
} while (0)
#else
#define assert(expr) do {} while (0)
#endif
/* rate limited printk(). */
#ifdef printkl
# undef printkl
#endif
#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0)
/* rate limited printk() for debugging */
#ifdef dprintkl
# undef dprintkl
#endif
#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
# define dprintkl printkl
#else
# define dprintkl(f, x...) do { /* nothing */ } while (0)
#endif
/* debugging printk() */
#ifdef dprintk
# undef dprintk
#endif
#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
# define dprintk(f, x...) do { printk(f ,##x); } while (0)
#else
# define dprintk(f, x...) do { /* nothing */ } while (0)
#endif
/* private definitions and prototypes */
/*** prototypes from _scan.c */
void ieee80211softmac_scan(void *sm);
/* for internal use if scanning is needed */
int ieee80211softmac_start_scan(struct ieee80211softmac_device *mac);
void ieee80211softmac_stop_scan(struct ieee80211softmac_device *mac);
void ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *mac);
/* for use by _module.c to assign to the callbacks */
int ieee80211softmac_start_scan_implementation(struct net_device *dev);
void ieee80211softmac_stop_scan_implementation(struct net_device *dev);
void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev);
/*** Network prototypes from _module.c */
struct ieee80211softmac_network * ieee80211softmac_create_network(
struct ieee80211softmac_device *mac, struct ieee80211_network *net);
void ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *net);
void ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *net);
void ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *net);
void ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
struct ieee80211softmac_network *net);
struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid_locked(
struct ieee80211softmac_device *mac, u8 *ea);
struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid(
struct ieee80211softmac_device *mac, u8 *ea);
struct ieee80211softmac_network * ieee80211softmac_get_network_by_ssid_locked(
struct ieee80211softmac_device *mac, u8 *ssid, u8 ssid_len);
struct ieee80211softmac_network * ieee80211softmac_get_network_by_ssid(
struct ieee80211softmac_device *mac, u8 *ssid, u8 ssid_len);
struct ieee80211softmac_network *
ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
struct ieee80211softmac_essid *essid);
struct ieee80211softmac_network *
ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
struct ieee80211softmac_essid *essid);
/* Rates related */
u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta);
static inline u8 lower_rate(struct ieee80211softmac_device *mac, u8 rate) {
return ieee80211softmac_lower_rate_delta(mac, rate, 1);
}
static inline u8 get_fallback_rate(struct ieee80211softmac_device *mac, u8 rate)
{
return ieee80211softmac_lower_rate_delta(mac, rate, 2);
}
/*** prototypes from _io.c */
int ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac,
void* ptrarg, u32 type, u32 arg);
/*** prototypes from _auth.c */
/* do these have to go into the public header? */
int ieee80211softmac_auth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net);
int ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, int reason);
/* for use by _module.c to assign to the callbacks */
int ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth);
int ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *deauth);
/*** prototypes from _assoc.c */
void ieee80211softmac_assoc_work(void *d);
int ieee80211softmac_handle_assoc_response(struct net_device * dev,
struct ieee80211_assoc_response * resp,
struct ieee80211_network * network);
int ieee80211softmac_handle_disassoc(struct net_device * dev,
struct ieee80211_disassoc * disassoc);
int ieee80211softmac_handle_reassoc_req(struct net_device * dev,
struct ieee80211_reassoc_request * reassoc);
void ieee80211softmac_assoc_timeout(void *d);
/* some helper functions */
static inline int ieee80211softmac_scan_handlers_check_self(struct ieee80211softmac_device *sm)
{
return (sm->start_scan == ieee80211softmac_start_scan_implementation) &&
(sm->stop_scan == ieee80211softmac_stop_scan_implementation) &&
(sm->wait_for_scan == ieee80211softmac_wait_for_scan_implementation);
}
static inline int ieee80211softmac_scan_sanity_check(struct ieee80211softmac_device *sm)
{
return ((sm->start_scan != ieee80211softmac_start_scan_implementation) &&
(sm->stop_scan != ieee80211softmac_stop_scan_implementation) &&
(sm->wait_for_scan != ieee80211softmac_wait_for_scan_implementation)
) || ieee80211softmac_scan_handlers_check_self(sm);
}
#define IEEE80211SOFTMAC_PROBE_DELAY HZ/2
#define IEEE80211SOFTMAC_WORKQUEUE_NAME_LEN (17 + IFNAMSIZ)
struct ieee80211softmac_network {
struct list_head list; /* List */
/* Network information copied from ieee80211_network */
u8 bssid[ETH_ALEN];
u8 channel;
struct ieee80211softmac_essid essid;
struct ieee80211softmac_ratesinfo supported_rates;
/* SoftMAC specific */
u16 authenticating:1, /* Status Flags */
authenticated:1,
auth_desynced_once:1;
u16 capabilities; /* Capabilities bitfield */
u8 challenge_len; /* Auth Challenge length */
char *challenge; /* Challenge Text */
};
/* structure used to keep track of networks we're auth'ing to */
struct ieee80211softmac_auth_queue_item {
struct list_head list; /* List head */
struct ieee80211softmac_network *net; /* Network to auth */
struct ieee80211softmac_device *mac; /* SoftMAC device */
u8 retry; /* Retry limit */
u8 state; /* Auth State */
struct work_struct work; /* Work queue */
};
/* scanning information */
struct ieee80211softmac_scaninfo {
u8 current_channel_idx,
number_channels;
struct ieee80211_channel *channels;
u8 started:1,
stop:1;
u8 skip_flags;
struct completion finished;
struct work_struct softmac_scan;
};
/* private event struct */
struct ieee80211softmac_event {
struct list_head list;
int event_type;
void *event_context;
struct work_struct work;
notify_function_ptr fun;
void *context;
struct ieee80211softmac_device *mac;
};
void ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_context);
void ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_context);
int ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask);
#endif /* IEEE80211SOFTMAC_PRIV_H_ */
/*
* Scanning routines.
*
* These are not exported because they're assigned to the function pointers.
*
* Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#include <linux/completion.h>
#include "ieee80211softmac_priv.h"
/* internal, use to trigger scanning if needed.
* Returns -EBUSY if already scanning,
* result of start_scan otherwise */
int
ieee80211softmac_start_scan(struct ieee80211softmac_device *sm)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&sm->lock, flags);
if (sm->scanning)
{
spin_unlock_irqrestore(&sm->lock, flags);
return -EINPROGRESS;
}
sm->scanning = 1;
spin_unlock_irqrestore(&sm->lock, flags);
ret = sm->start_scan(sm->dev);
if (ret) {
spin_lock_irqsave(&sm->lock, flags);
sm->scanning = 0;
spin_unlock_irqrestore(&sm->lock, flags);
}
return ret;
}
void
ieee80211softmac_stop_scan(struct ieee80211softmac_device *sm)
{
unsigned long flags;
spin_lock_irqsave(&sm->lock, flags);
if (!sm->scanning) {
spin_unlock_irqrestore(&sm->lock, flags);
return;
}
spin_unlock_irqrestore(&sm->lock, flags);
sm->stop_scan(sm->dev);
}
void
ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *sm)
{
unsigned long flags;
spin_lock_irqsave(&sm->lock, flags);
if (!sm->scanning) {
spin_unlock_irqrestore(&sm->lock, flags);
return;
}
spin_unlock_irqrestore(&sm->lock, flags);
sm->wait_for_scan(sm->dev);
}
/* internal scanning implementation follows */
void ieee80211softmac_scan(void *d)
{
int invalid_channel;
u8 current_channel_idx;
struct ieee80211softmac_device *sm = (struct ieee80211softmac_device *)d;
struct ieee80211softmac_scaninfo *si = sm->scaninfo;
unsigned long flags;
while (!(si->stop) && (si->current_channel_idx < si->number_channels)) {
current_channel_idx = si->current_channel_idx;
si->current_channel_idx++; /* go to the next channel */
invalid_channel = (si->skip_flags & si->channels[current_channel_idx].flags);
if (!invalid_channel) {
sm->set_channel(sm->dev, si->channels[current_channel_idx].channel);
// FIXME make this user configurable (active/passive)
if(ieee80211softmac_send_mgt_frame(sm, NULL, IEEE80211_STYPE_PROBE_REQ, 0))
printkl(KERN_DEBUG PFX "Sending Probe Request Failed\n");
/* also send directed management frame for the network we're looking for */
// TODO: is this if correct, or should we do this only if scanning from assoc request?
if (sm->associnfo.req_essid.len)
ieee80211softmac_send_mgt_frame(sm, &sm->associnfo.req_essid, IEEE80211_STYPE_PROBE_REQ, 0);
schedule_delayed_work(&si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
return;
} else {
dprintk(PFX "Not probing Channel %d (not allowed here)\n", si->channels[current_channel_idx].channel);
}
}
spin_lock_irqsave(&sm->lock, flags);
cancel_delayed_work(&si->softmac_scan);
si->started = 0;
spin_unlock_irqrestore(&sm->lock, flags);
dprintk(PFX "Scanning finished\n");
ieee80211softmac_scan_finished(sm);
complete_all(&sm->scaninfo->finished);
}
static inline struct ieee80211softmac_scaninfo *allocate_scaninfo(struct ieee80211softmac_device *mac)
{
/* ugh. can we call this without having the spinlock held? */
struct ieee80211softmac_scaninfo *info = kmalloc(sizeof(struct ieee80211softmac_scaninfo), GFP_ATOMIC);
if (unlikely(!info))
return NULL;
INIT_WORK(&info->softmac_scan, ieee80211softmac_scan, mac);
init_completion(&info->finished);
return info;
}
int ieee80211softmac_start_scan_implementation(struct net_device *dev)
{
struct ieee80211softmac_device *sm = ieee80211_priv(dev);
unsigned long flags;
if (!(dev->flags & IFF_UP))
return -ENODEV;
assert(ieee80211softmac_scan_handlers_check_self(sm));
if (!ieee80211softmac_scan_handlers_check_self(sm))
return -EINVAL;
spin_lock_irqsave(&sm->lock, flags);
/* it looks like we need to hold the lock here
* to make sure we don't allocate two of these... */
if (unlikely(!sm->scaninfo))
sm->scaninfo = allocate_scaninfo(sm);
if (unlikely(!sm->scaninfo)) {
spin_unlock_irqrestore(&sm->lock, flags);
return -ENOMEM;
}
sm->scaninfo->skip_flags = IEEE80211_CH_INVALID;
if (0 /* not scanning in IEEE802.11b */)//TODO
sm->scaninfo->skip_flags |= IEEE80211_CH_B_ONLY;
if (0 /* IEEE802.11a */) {//TODO
sm->scaninfo->channels = sm->ieee->geo.a;
sm->scaninfo->number_channels = sm->ieee->geo.a_channels;
} else {
sm->scaninfo->channels = sm->ieee->geo.bg;
sm->scaninfo->number_channels = sm->ieee->geo.bg_channels;
}
dprintk(PFX "Start scanning with channel: %d\n", sm->scaninfo->channels[0].channel);
dprintk(PFX "Scanning %d channels\n", sm->scaninfo->number_channels);
sm->scaninfo->current_channel_idx = 0;
sm->scaninfo->started = 1;
sm->scaninfo->stop = 0;
INIT_COMPLETION(sm->scaninfo->finished);
schedule_work(&sm->scaninfo->softmac_scan);
spin_unlock_irqrestore(&sm->lock, flags);
return 0;
}
void ieee80211softmac_stop_scan_implementation(struct net_device *dev)
{
struct ieee80211softmac_device *sm = ieee80211_priv(dev);
unsigned long flags;
assert(ieee80211softmac_scan_handlers_check_self(sm));
if (!ieee80211softmac_scan_handlers_check_self(sm))
return;
spin_lock_irqsave(&sm->lock, flags);
assert(sm->scaninfo != NULL);
if (sm->scaninfo) {
if (sm->scaninfo->started)
sm->scaninfo->stop = 1;
else
complete_all(&sm->scaninfo->finished);
}
spin_unlock_irqrestore(&sm->lock, flags);
}
void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev)
{
struct ieee80211softmac_device *sm = ieee80211_priv(dev);
unsigned long flags;
assert(ieee80211softmac_scan_handlers_check_self(sm));
if (!ieee80211softmac_scan_handlers_check_self(sm))
return;
spin_lock_irqsave(&sm->lock, flags);
if (!sm->scaninfo->started) {
spin_unlock_irqrestore(&sm->lock, flags);
return;
}
spin_unlock_irqrestore(&sm->lock, flags);
wait_for_completion(&sm->scaninfo->finished);
}
/* this is what drivers (that do scanning) call when they're done */
void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm)
{
unsigned long flags;
spin_lock_irqsave(&sm->lock, flags);
sm->scanning = 0;
spin_unlock_irqrestore(&sm->lock, flags);
if (sm->associnfo.bssvalid) {
struct ieee80211softmac_network *net;
net = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid);
if (net)
sm->set_channel(sm->dev, net->channel);
}
ieee80211softmac_call_events(sm, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, NULL);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_scan_finished);
/*
* This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them
*
* Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
* Joseph Jezak <josejx@gentoo.org>
* Larry Finger <Larry.Finger@lwfinger.net>
* Danny van Dyk <kugelfang@gentoo.org>
* Michael Buesch <mbuesch@freenet.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
#include "ieee80211softmac_priv.h"
#include <net/iw_handler.h>
int
ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
return ieee80211softmac_start_scan(sm);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan);
int
ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
return ieee80211_wx_get_scan(sm->ieee, info, data, extra);
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results);
int
ieee80211softmac_wx_set_essid(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
int length = 0;
unsigned long flags;
spin_lock_irqsave(&sm->lock, flags);
sm->associnfo.static_essid = 0;
if (data->essid.flags && data->essid.length && extra /*required?*/) {
length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE);
if (length) {
memcpy(sm->associnfo.req_essid.data, extra, length);
sm->associnfo.static_essid = 1;
}
}
sm->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
/* set our requested ESSID length.
* If applicable, we have already copied the data in */
sm->associnfo.req_essid.len = length;
/* queue lower level code to do work (if necessary) */
schedule_work(&sm->associnfo.work);
spin_unlock_irqrestore(&sm->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
int
ieee80211softmac_wx_get_essid(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
unsigned long flags;
/* avoid getting inconsistent information */
spin_lock_irqsave(&sm->lock, flags);
/* If all fails, return ANY (empty) */
data->essid.length = 0;
data->essid.flags = 0; /* active */
/* If we have a statically configured ESSID then return it */
if (sm->associnfo.static_essid) {
data->essid.length = sm->associnfo.req_essid.len;
data->essid.flags = 1; /* active */
memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
}
/* If we're associating/associated, return that */
if (sm->associated || sm->associnfo.associating) {
data->essid.length = sm->associnfo.associate_essid.len;
data->essid.flags = 1; /* active */
memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
}
spin_unlock_irqrestore(&sm->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
int
ieee80211softmac_wx_set_rate(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
struct ieee80211_device *ieee = mac->ieee;
unsigned long flags;
s32 in_rate = data->bitrate.value;
u8 rate;
int is_ofdm = 0;
int err = -EINVAL;
if (in_rate == -1) {
/* automatic detect */
if (ieee->modulation & IEEE80211_OFDM_MODULATION)
in_rate = 54000000;
else
in_rate = 11000000;
}
switch (in_rate) {
case 1000000:
rate = IEEE80211_CCK_RATE_1MB;
break;
case 2000000:
rate = IEEE80211_CCK_RATE_2MB;
break;
case 5500000:
rate = IEEE80211_CCK_RATE_5MB;
break;
case 11000000:
rate = IEEE80211_CCK_RATE_11MB;
break;
case 6000000:
rate = IEEE80211_OFDM_RATE_6MB;
is_ofdm = 1;
break;
case 9000000:
rate = IEEE80211_OFDM_RATE_9MB;
is_ofdm = 1;
break;
case 12000000:
rate = IEEE80211_OFDM_RATE_12MB;
is_ofdm = 1;
break;
case 18000000:
rate = IEEE80211_OFDM_RATE_18MB;
is_ofdm = 1;
break;
case 24000000:
rate = IEEE80211_OFDM_RATE_24MB;
is_ofdm = 1;
break;
case 36000000:
rate = IEEE80211_OFDM_RATE_36MB;
is_ofdm = 1;
break;
case 48000000:
rate = IEEE80211_OFDM_RATE_48MB;
is_ofdm = 1;
break;
case 54000000:
rate = IEEE80211_OFDM_RATE_54MB;
is_ofdm = 1;
break;
default:
goto out;
}
spin_lock_irqsave(&mac->lock, flags);
/* Check if correct modulation for this PHY. */
if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
goto out_unlock;
mac->txrates.default_rate = rate;
mac->txrates.default_fallback = lower_rate(mac, rate);
err = 0;
out_unlock:
spin_unlock_irqrestore(&mac->lock, flags);
out:
return err;
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
int
ieee80211softmac_wx_get_rate(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
unsigned long flags;
int err = -EINVAL;
spin_lock_irqsave(&mac->lock, flags);
switch (mac->txrates.default_rate) {
case IEEE80211_CCK_RATE_1MB:
data->bitrate.value = 1000000;
break;
case IEEE80211_CCK_RATE_2MB:
data->bitrate.value = 2000000;
break;
case IEEE80211_CCK_RATE_5MB:
data->bitrate.value = 5500000;
break;
case IEEE80211_CCK_RATE_11MB:
data->bitrate.value = 11000000;
break;
case IEEE80211_OFDM_RATE_6MB:
data->bitrate.value = 6000000;
break;
case IEEE80211_OFDM_RATE_9MB:
data->bitrate.value = 9000000;
break;
case IEEE80211_OFDM_RATE_12MB:
data->bitrate.value = 12000000;
break;
case IEEE80211_OFDM_RATE_18MB:
data->bitrate.value = 18000000;
break;
case IEEE80211_OFDM_RATE_24MB:
data->bitrate.value = 24000000;
break;
case IEEE80211_OFDM_RATE_36MB:
data->bitrate.value = 36000000;
break;
case IEEE80211_OFDM_RATE_48MB:
data->bitrate.value = 48000000;
break;
case IEEE80211_OFDM_RATE_54MB:
data->bitrate.value = 54000000;
break;
default:
assert(0);
goto out_unlock;
}
err = 0;
out_unlock:
spin_unlock_irqrestore(&mac->lock, flags);
return err;
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
int
ieee80211softmac_wx_get_wap(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
int err = 0;
unsigned long flags;
spin_lock_irqsave(&mac->lock, flags);
if (mac->associnfo.bssvalid)
memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
else
memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
data->ap_addr.sa_family = ARPHRD_ETHER;
spin_unlock_irqrestore(&mac->lock, flags);
return err;
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
int
ieee80211softmac_wx_set_wap(struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
static const unsigned char any[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static const unsigned char off[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned long flags;
/* sanity check */
if (data->ap_addr.sa_family != ARPHRD_ETHER) {
return -EINVAL;
}
spin_lock_irqsave(&mac->lock, flags);
if (!memcmp(any, data->ap_addr.sa_data, ETH_ALEN) ||
!memcmp(off, data->ap_addr.sa_data, ETH_ALEN)) {
schedule_work(&mac->associnfo.work);
goto out;
} else {
if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
if (mac->associnfo.associating || mac->associated) {
/* bssid unchanged and associated or associating - just return */
goto out;
}
} else {
/* copy new value in data->ap_addr.sa_data to bssid */
memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
}
/* queue associate if new bssid or (old one again and not associated) */
schedule_work(&mac->associnfo.work);
}
out:
spin_unlock_irqrestore(&mac->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
int
ieee80211softmac_wx_set_genie(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
unsigned long flags;
int err = 0;
char *buf;
int i;
spin_lock_irqsave(&mac->lock, flags);
/* bleh. shouldn't be locked for that kmalloc... */
if (wrqu->data.length) {
if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
/* this is an IE, so the length must be
* correct. Is it possible though that
* more than one IE is passed in?
*/
err = -EINVAL;
goto out;
}
if (mac->wpa.IEbuflen <= wrqu->data.length) {
buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
if (!buf) {
err = -ENOMEM;
goto out;
}
kfree(mac->wpa.IE);
mac->wpa.IE = buf;
mac->wpa.IEbuflen = wrqu->data.length;
}
memcpy(mac->wpa.IE, extra, wrqu->data.length);
dprintk(KERN_INFO PFX "generic IE set to ");
for (i=0;i<wrqu->data.length;i++)
dprintk("%.2x", mac->wpa.IE[i]);
dprintk("\n");
mac->wpa.IElen = wrqu->data.length;
} else {
kfree(mac->wpa.IE);
mac->wpa.IE = NULL;
mac->wpa.IElen = 0;
mac->wpa.IEbuflen = 0;
}
out:
spin_unlock_irqrestore(&mac->lock, flags);
return err;
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
int
ieee80211softmac_wx_get_genie(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct ieee80211softmac_device *mac = ieee80211_priv(dev);
unsigned long flags;
int err = 0;
int space = wrqu->data.length;
spin_lock_irqsave(&mac->lock, flags);
wrqu->data.length = 0;
if (mac->wpa.IE && mac->wpa.IElen) {
wrqu->data.length = mac->wpa.IElen;
if (mac->wpa.IElen <= space)
memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
else
err = -E2BIG;
}
spin_unlock_irqrestore(&mac->lock, flags);
return err;
}
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
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