[PATCH] wireless/airo: cache wireless scans
Observed problems when multiple processes request scans and subsequently scan results. This causes a scan result request to hit card registers before the scan is complete, returning an incomplete scan list and possibly making the card very angry. Instead, cache the results of a wireless scan and serve result requests from the cache, rather than hitting the hardware for them. Signed-off-by: Dan Williams <dcbw@redhat.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
committed by
John W. Linville
parent
15db276320
commit
9e75af30d5
@@ -769,6 +769,11 @@ typedef struct {
|
|||||||
u16 atimWindow;
|
u16 atimWindow;
|
||||||
} BSSListRid;
|
} BSSListRid;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BSSListRid bss;
|
||||||
|
struct list_head list;
|
||||||
|
} BSSListElement;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u8 rssipct;
|
u8 rssipct;
|
||||||
u8 rssidBm;
|
u8 rssidBm;
|
||||||
@@ -1120,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_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
|
||||||
static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
|
static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
|
||||||
|
|
||||||
|
static void airo_networks_free(struct airo_info *ai);
|
||||||
|
|
||||||
struct airo_info {
|
struct airo_info {
|
||||||
struct net_device_stats stats;
|
struct net_device_stats stats;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
@@ -1151,7 +1158,7 @@ struct airo_info {
|
|||||||
#define FLAG_COMMIT 13
|
#define FLAG_COMMIT 13
|
||||||
#define FLAG_RESET 14
|
#define FLAG_RESET 14
|
||||||
#define FLAG_FLASHING 15
|
#define FLAG_FLASHING 15
|
||||||
#define JOB_MASK 0x1ff0000
|
#define JOB_MASK 0x2ff0000
|
||||||
#define JOB_DIE 16
|
#define JOB_DIE 16
|
||||||
#define JOB_XMIT 17
|
#define JOB_XMIT 17
|
||||||
#define JOB_XMIT11 18
|
#define JOB_XMIT11 18
|
||||||
@@ -1161,6 +1168,7 @@ struct airo_info {
|
|||||||
#define JOB_EVENT 22
|
#define JOB_EVENT 22
|
||||||
#define JOB_AUTOWEP 23
|
#define JOB_AUTOWEP 23
|
||||||
#define JOB_WSTATS 24
|
#define JOB_WSTATS 24
|
||||||
|
#define JOB_SCAN_RESULTS 25
|
||||||
int (*bap_read)(struct airo_info*, u16 *pu16Dst, int bytelen,
|
int (*bap_read)(struct airo_info*, u16 *pu16Dst, int bytelen,
|
||||||
int whichbap);
|
int whichbap);
|
||||||
unsigned short *flash;
|
unsigned short *flash;
|
||||||
@@ -1177,7 +1185,7 @@ struct airo_info {
|
|||||||
} xmit, xmit11;
|
} xmit, xmit11;
|
||||||
struct net_device *wifidev;
|
struct net_device *wifidev;
|
||||||
struct iw_statistics wstats; // wireless stats
|
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_spy_data spy_data;
|
||||||
struct iw_public_data wireless_data;
|
struct iw_public_data wireless_data;
|
||||||
/* MIC stuff */
|
/* MIC stuff */
|
||||||
@@ -1199,6 +1207,10 @@ struct airo_info {
|
|||||||
APListRid *APList;
|
APListRid *APList;
|
||||||
#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
|
#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE
|
||||||
char proc_name[IFNAMSIZ];
|
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,
|
static inline int bap_read(struct airo_info *ai, u16 *pu16Dst, int bytelen,
|
||||||
@@ -2381,6 +2393,8 @@ void stop_airo_card( struct net_device *dev, int freeres )
|
|||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
airo_networks_free (ai);
|
||||||
|
|
||||||
kfree(ai->flash);
|
kfree(ai->flash);
|
||||||
kfree(ai->rssi);
|
kfree(ai->rssi);
|
||||||
kfree(ai->APList);
|
kfree(ai->APList);
|
||||||
@@ -2687,6 +2701,42 @@ static int reset_card( struct net_device *dev , int lock) {
|
|||||||
return 0;
|
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,
|
static struct net_device *_init_airo_card( unsigned short irq, int port,
|
||||||
int is_pcmcia, struct pci_dev *pci,
|
int is_pcmcia, struct pci_dev *pci,
|
||||||
struct device *dmdev )
|
struct device *dmdev )
|
||||||
@@ -2728,6 +2778,10 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
|
|||||||
if (rc)
|
if (rc)
|
||||||
goto err_out_thr;
|
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. */
|
/* The Airo-specific entries in the device structure. */
|
||||||
if (test_bit(FLAG_MPI,&ai->flags)) {
|
if (test_bit(FLAG_MPI,&ai->flags)) {
|
||||||
skb_queue_head_init (&ai->txq);
|
skb_queue_head_init (&ai->txq);
|
||||||
@@ -2749,7 +2803,6 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
|
|||||||
|
|
||||||
SET_NETDEV_DEV(dev, dmdev);
|
SET_NETDEV_DEV(dev, dmdev);
|
||||||
|
|
||||||
|
|
||||||
reset_card (dev, 1);
|
reset_card (dev, 1);
|
||||||
msleep(400);
|
msleep(400);
|
||||||
|
|
||||||
@@ -2892,6 +2945,65 @@ static void airo_send_event(struct net_device *dev) {
|
|||||||
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
|
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) {
|
static int airo_thread(void *data) {
|
||||||
struct net_device *dev = data;
|
struct net_device *dev = data;
|
||||||
struct airo_info *ai = dev->priv;
|
struct airo_info *ai = dev->priv;
|
||||||
@@ -2921,13 +3033,26 @@ static int airo_thread(void *data) {
|
|||||||
set_current_state(TASK_INTERRUPTIBLE);
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
if (ai->flags & JOB_MASK)
|
if (ai->flags & JOB_MASK)
|
||||||
break;
|
break;
|
||||||
if (ai->expires) {
|
if (ai->expires || ai->scan_timeout) {
|
||||||
if (time_after_eq(jiffies,ai->expires)){
|
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);
|
set_bit(JOB_AUTOWEP,&ai->flags);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!signal_pending(current)) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (!signal_pending(current)) {
|
} else if (!signal_pending(current)) {
|
||||||
@@ -2970,6 +3095,10 @@ static int airo_thread(void *data) {
|
|||||||
airo_send_event(dev);
|
airo_send_event(dev);
|
||||||
else if (test_bit(JOB_AUTOWEP, &ai->flags))
|
else if (test_bit(JOB_AUTOWEP, &ai->flags))
|
||||||
timer_func(dev);
|
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);
|
complete_and_exit (&ai->thr_exited, 0);
|
||||||
}
|
}
|
||||||
@@ -3064,19 +3193,15 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
|
|||||||
* and reassociations as valid status
|
* and reassociations as valid status
|
||||||
* Jean II */
|
* Jean II */
|
||||||
if(newStatus == ASSOCIATED) {
|
if(newStatus == ASSOCIATED) {
|
||||||
if (apriv->scan_timestamp) {
|
#if 0
|
||||||
/* Send an empty event to user space.
|
/* FIXME: Grabbing scan results here
|
||||||
* We don't send the received data on
|
* seems to be too early??? Just wait for
|
||||||
* the event because it would require
|
* timeout instead. */
|
||||||
* us to do complex transcoding, and
|
if (apriv->scan_timeout > 0) {
|
||||||
* we want to minimise the work done in
|
set_bit(JOB_SCAN_RESULTS, &apriv->flags);
|
||||||
* the irq handler. Use a request to
|
wake_up_interruptible(&apriv->thr_wait);
|
||||||
* extract the data - Jean II */
|
|
||||||
wrqu.data.length = 0;
|
|
||||||
wrqu.data.flags = 0;
|
|
||||||
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
|
|
||||||
apriv->scan_timestamp = 0;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (down_trylock(&apriv->sem) != 0) {
|
if (down_trylock(&apriv->sem) != 0) {
|
||||||
set_bit(JOB_EVENT, &apriv->flags);
|
set_bit(JOB_EVENT, &apriv->flags);
|
||||||
wake_up_interruptible(&apriv->thr_wait);
|
wake_up_interruptible(&apriv->thr_wait);
|
||||||
@@ -6992,6 +7117,7 @@ static int airo_set_scan(struct net_device *dev,
|
|||||||
struct airo_info *ai = dev->priv;
|
struct airo_info *ai = dev->priv;
|
||||||
Cmd cmd;
|
Cmd cmd;
|
||||||
Resp rsp;
|
Resp rsp;
|
||||||
|
int wake = 0;
|
||||||
|
|
||||||
/* Note : you may have realised that, as this is a SET operation,
|
/* Note : you may have realised that, as this is a SET operation,
|
||||||
* this is privileged and therefore a normal user can't
|
* this is privileged and therefore a normal user can't
|
||||||
@@ -7001,17 +7127,25 @@ static int airo_set_scan(struct net_device *dev,
|
|||||||
* Jean II */
|
* Jean II */
|
||||||
if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN;
|
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 */
|
/* Initiate a scan command */
|
||||||
memset(&cmd, 0, sizeof(cmd));
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
cmd.cmd=CMD_LISTBSS;
|
cmd.cmd=CMD_LISTBSS;
|
||||||
if (down_interruptible(&ai->sem))
|
|
||||||
return -ERESTARTSYS;
|
|
||||||
issuecommand(ai, &cmd, &rsp);
|
issuecommand(ai, &cmd, &rsp);
|
||||||
ai->scan_timestamp = jiffies;
|
ai->scan_timeout = RUN_AT(3*HZ);
|
||||||
|
wake = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
up(&ai->sem);
|
up(&ai->sem);
|
||||||
|
if (wake)
|
||||||
/* At this point, just return to the user. */
|
wake_up_interruptible(&ai->thr_wait);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7131,59 +7265,38 @@ static int airo_get_scan(struct net_device *dev,
|
|||||||
char *extra)
|
char *extra)
|
||||||
{
|
{
|
||||||
struct airo_info *ai = dev->priv;
|
struct airo_info *ai = dev->priv;
|
||||||
BSSListRid BSSList;
|
BSSListElement *net;
|
||||||
int rc;
|
int err = 0;
|
||||||
char *current_ev = extra;
|
char *current_ev = extra;
|
||||||
|
|
||||||
/* When we are associated again, the scan has surely finished.
|
/* If a scan is in-progress, return -EAGAIN */
|
||||||
* Just in case, let's make sure enough time has elapsed since
|
if (ai->scan_timeout > 0)
|
||||||
* 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 */
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
|
||||||
ai->scan_timestamp = 0;
|
|
||||||
|
|
||||||
/* There's only a race with proc_BSSList_open(), but its
|
if (down_interruptible(&ai->sem))
|
||||||
* consequences are begnign. So I don't bother fixing it - Javier */
|
return -EAGAIN;
|
||||||
|
|
||||||
/* Try to read the first entry of the scan result */
|
list_for_each_entry (net, &ai->network_list, list) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read and parse all entries */
|
|
||||||
while((!rc) && (BSSList.index != 0xffff)) {
|
|
||||||
/* Translate to WE format this entry */
|
/* Translate to WE format this entry */
|
||||||
current_ev = airo_translate_scan(dev, current_ev,
|
current_ev = airo_translate_scan(dev, current_ev,
|
||||||
extra + dwrq->length,
|
extra + dwrq->length,
|
||||||
&BSSList);
|
&net->bss);
|
||||||
|
|
||||||
/* Check if there is space for one more entry */
|
/* Check if there is space for one more entry */
|
||||||
if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
|
if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) {
|
||||||
/* Ask user space to try again with a bigger buffer */
|
/* 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 */
|
/* Length of data */
|
||||||
dwrq->length = (current_ev - extra);
|
dwrq->length = (current_ev - extra);
|
||||||
dwrq->flags = 0; /* todo */
|
dwrq->flags = 0; /* todo */
|
||||||
|
|
||||||
return 0;
|
out:
|
||||||
|
up(&ai->sem);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------*/
|
/*------------------------------------------------------------------*/
|
||||||
|
Reference in New Issue
Block a user