struct net_device_stats *stats = &priv->stats;
        struct iw_statistics *wstats = &priv->wstats;
        struct sk_buff *skb = NULL;
-       u16 rxfid, status, fc;
+       u16 rxfid, status;
        int length;
-       struct hermes_rx_descriptor desc;
-       struct ethhdr *hdr;
+       struct hermes_rx_descriptor *desc;
+       struct orinoco_rx_data *rx_data;
        int err;
 
+       desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
+       if (!desc) {
+               printk(KERN_WARNING
+                      "%s: Can't allocate space for RX descriptor\n",
+                      dev->name);
+               goto update_stats;
+       }
+
        rxfid = hermes_read_regn(hw, RXFID);
 
-       err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc),
+       err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
                               rxfid, 0);
        if (err) {
                printk(KERN_ERR "%s: error %d reading Rx descriptor. "
                goto update_stats;
        }
 
-       status = le16_to_cpu(desc.status);
+       status = le16_to_cpu(desc->status);
 
        if (status & HERMES_RXSTAT_BADCRC) {
                DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
 
        /* Handle frames in monitor mode */
        if (priv->iw_mode == IW_MODE_MONITOR) {
-               orinoco_rx_monitor(dev, rxfid, &desc);
-               return;
+               orinoco_rx_monitor(dev, rxfid, desc);
+               goto out;
        }
 
        if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
                goto update_stats;
        }
 
-       length = le16_to_cpu(desc.data_len);
-       fc = le16_to_cpu(desc.frame_ctl);
+       length = le16_to_cpu(desc->data_len);
 
        /* Sanity checks */
        if (length < 3) { /* No for even an 802.2 LLC header */
                /* At least on Symbol firmware with PCF we get quite a
                    lot of these legitimately - Poll frames with no
                    data. */
-               return;
+               goto out;
        }
        if (length > IEEE80211_DATA_LEN) {
                printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
                goto drop;
        }
 
+       /* Add desc and skb to rx queue */
+       rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC);
+       if (!rx_data) {
+               printk(KERN_WARNING "%s: Can't allocate RX packet\n",
+                       dev->name);
+               goto drop;
+       }
+       rx_data->desc = desc;
+       rx_data->skb = skb;
+       list_add_tail(&rx_data->list, &priv->rx_list);
+       tasklet_schedule(&priv->rx_tasklet);
+
+       return;
+
+drop:
+       dev_kfree_skb_irq(skb);
+update_stats:
+       stats->rx_errors++;
+       stats->rx_dropped++;
+out:
+       kfree(desc);
+}
+
+static void orinoco_rx(struct net_device *dev,
+                      struct hermes_rx_descriptor *desc,
+                      struct sk_buff *skb)
+{
+       struct orinoco_private *priv = netdev_priv(dev);
+       struct net_device_stats *stats = &priv->stats;
+       u16 status, fc;
+       int length;
+       struct ethhdr *hdr;
+
+       status = le16_to_cpu(desc->status);
+       length = le16_to_cpu(desc->data_len);
+       fc = le16_to_cpu(desc->frame_ctl);
+
        /* Handle decapsulation
         * In most cases, the firmware tell us about SNAP frames.
         * For some reason, the SNAP frames sent by LinkSys APs
                hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
                hdr->h_proto = htons(length);
        }
-       memcpy(hdr->h_dest, desc.addr1, ETH_ALEN);
+       memcpy(hdr->h_dest, desc->addr1, ETH_ALEN);
        if (fc & IEEE80211_FCTL_FROMDS)
-               memcpy(hdr->h_source, desc.addr3, ETH_ALEN);
+               memcpy(hdr->h_source, desc->addr3, ETH_ALEN);
        else
-               memcpy(hdr->h_source, desc.addr2, ETH_ALEN);
+               memcpy(hdr->h_source, desc->addr2, ETH_ALEN);
 
        dev->last_rx = jiffies;
        skb->protocol = eth_type_trans(skb, dev);
                skb->pkt_type = PACKET_OTHERHOST;
        
        /* Process the wireless stats if needed */
-       orinoco_stat_gather(dev, skb, &desc);
+       orinoco_stat_gather(dev, skb, desc);
 
        /* Pass the packet to the networking stack */
        netif_rx(skb);
        stats->rx_bytes += length;
 
        return;
+}
 
- drop: 
-       dev_kfree_skb_irq(skb);
- update_stats:
-       stats->rx_errors++;
-       stats->rx_dropped++;
+static void orinoco_rx_isr_tasklet(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *) data;
+       struct orinoco_private *priv = netdev_priv(dev);
+       struct orinoco_rx_data *rx_data, *temp;
+       struct hermes_rx_descriptor *desc;
+       struct sk_buff *skb;
+
+       /* extract desc and skb from queue */
+       list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
+               desc = rx_data->desc;
+               skb = rx_data->skb;
+               list_del(&rx_data->list);
+               kfree(rx_data);
+
+               orinoco_rx(dev, desc, skb);
+
+               kfree(desc);
+       }
 }
 
 /********************************************************************/
        INIT_WORK(&priv->join_work, orinoco_join_ap);
        INIT_WORK(&priv->wevent_work, orinoco_send_wevents);
 
+       INIT_LIST_HEAD(&priv->rx_list);
+       tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet,
+                    (unsigned long) dev);
+
        netif_carrier_off(dev);
        priv->last_linkstatus = 0xffff;
 
 {
        struct orinoco_private *priv = netdev_priv(dev);
 
+       /* No need to empty priv->rx_list: if the tasklet is scheduled
+        * when we call tasklet_kill it will run one final time,
+        * emptying the list */
+       tasklet_kill(&priv->rx_tasklet);
        priv->wpa_ie_len = 0;
        kfree(priv->wpa_ie);
        orinoco_bss_data_free(priv);