提交 9e75af30 编写于 作者: D Dan Williams 提交者: John W. Linville

[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: NDan Williams <dcbw@redhat.com>
Signed-off-by: NJohn W. Linville <linville@tuxdriver.com>
上级 15db2763
...@@ -769,6 +769,11 @@ typedef struct { ...@@ -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, ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ) ...@@ -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) { ...@@ -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, ...@@ -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, ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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);
up(&ai->sem); wake = 1;
/* At this point, just return to the user. */
out:
up(&ai->sem);
if (wake)
wake_up_interruptible(&ai->thr_wait);
return 0; return 0;
} }
...@@ -7131,59 +7265,38 @@ static int airo_get_scan(struct net_device *dev, ...@@ -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
* consequences are begnign. So I don't bother fixing it - Javier */
/* Try to read the first entry of the scan result */ if (down_interruptible(&ai->sem))
rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList), 1); return -EAGAIN;
if((rc) || (BSSList.index == 0xffff)) {
/* Client error, no scan results...
* The caller need to restart the scan. */
return -ENODATA;
}
/* Read and parse all entries */ list_for_each_entry (net, &ai->network_list, list) {
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;
} }
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册