diff options
Diffstat (limited to 'drivers/net/wireless/p54')
-rw-r--r-- | drivers/net/wireless/p54/p54common.c | 59 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54pci.c | 7 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54usb.c | 151 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54usb.h | 1 |
4 files changed, 143 insertions, 75 deletions
diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 89968a5bff8..a4e99b02af0 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -540,6 +540,14 @@ static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb) size_t header_len = sizeof(*hdr); u32 tsf32; + /* + * If the device is in a unspecified state we have to + * ignore all data frames. Else we could end up with a + * nasty crash. + */ + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return 0; + if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD))) { if (priv->filter_flags & FIF_FCSFAIL) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; @@ -608,6 +616,12 @@ void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) if (unlikely(!skb || !dev || !skb_queue_len(&priv->tx_queue))) return; + /* + * don't try to free an already unlinked skb + */ + if (unlikely((!skb->next) || (!skb->prev))) + return; + spin_lock_irqsave(&priv->tx_queue.lock, flags); info = IEEE80211_SKB_CB(skb); range = (void *)info->rate_driver_data; @@ -874,7 +888,27 @@ static int p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb, return -EINVAL; spin_lock_irqsave(&priv->tx_queue.lock, flags); + left = skb_queue_len(&priv->tx_queue); + if (unlikely(left >= 28)) { + /* + * The tx_queue is nearly full! + * We have throttle normal data traffic, because we must + * have a few spare slots for control frames left. + */ + ieee80211_stop_queues(dev); + + if (unlikely(left == 32)) { + /* + * The tx_queue is now really full. + * + * TODO: check if the device has crashed and reset it. + */ + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return -ENOSPC; + } + } + while (left--) { u32 hole_size; info = IEEE80211_SKB_CB(entry); @@ -903,7 +937,7 @@ static int p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb, if (!target_skb) { spin_unlock_irqrestore(&priv->tx_queue.lock, flags); ieee80211_stop_queues(dev); - return -ENOMEM; + return -ENOSPC; } info = IEEE80211_SKB_CB(skb); @@ -1051,19 +1085,6 @@ static int p54_sta_unlock(struct ieee80211_hw *dev, u8 *addr) return 0; } -static void p54_sta_notify_ps(struct ieee80211_hw *dev, - enum sta_notify_ps_cmd notify_cmd, - struct ieee80211_sta *sta) -{ - switch (notify_cmd) { - case STA_NOTIFY_AWAKE: - p54_sta_unlock(dev, sta->addr); - break; - default: - break; - } -} - static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, enum sta_notify_cmd notify_cmd, struct ieee80211_sta *sta) @@ -1078,6 +1099,10 @@ static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, p54_sta_unlock(dev, sta->addr); break; + case STA_NOTIFY_AWAKE: + /* update the firmware's filter table */ + p54_sta_unlock(dev, sta->addr); + break; default: break; } @@ -1684,19 +1709,18 @@ static void p54_stop(struct ieee80211_hw *dev) struct sk_buff *skb; mutex_lock(&priv->conf_mutex); + priv->mode = NL80211_IFTYPE_UNSPECIFIED; del_timer(&priv->stats_timer); p54_free_skb(dev, priv->cached_stats); priv->cached_stats = NULL; if (priv->cached_beacon) p54_tx_cancel(dev, priv->cached_beacon); + priv->stop(dev); while ((skb = skb_dequeue(&priv->tx_queue))) kfree_skb(skb); - priv->cached_beacon = NULL; - priv->stop(dev); priv->tsf_high32 = priv->tsf_low32 = 0; - priv->mode = NL80211_IFTYPE_UNSPECIFIED; mutex_unlock(&priv->conf_mutex); } @@ -2027,7 +2051,6 @@ static const struct ieee80211_ops p54_ops = { .add_interface = p54_add_interface, .remove_interface = p54_remove_interface, .set_tim = p54_set_tim, - .sta_notify_ps = p54_sta_notify_ps, .sta_notify = p54_sta_notify, .set_key = p54_set_key, .config = p54_config, diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index d21c509325f..c28220e401b 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -332,13 +332,6 @@ static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb, P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); P54P_READ(dev_int); - - /* FIXME: unlikely to happen because the device usually runs out of - memory before we fill the ring up, but we can make it impossible */ - if (idx - device_idx > ARRAY_SIZE(ring_control->tx_data) - 2) { - p54_free_skb(dev, skb); - printk(KERN_INFO "%s: tx overflow.\n", wiphy_name(dev->wiphy)); - } } static void p54p_stop(struct ieee80211_hw *dev) diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 2dd3cd41d0f..c2789e53b98 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -86,13 +86,13 @@ static void p54u_rx_cb(struct urb *urb) struct ieee80211_hw *dev = info->dev; struct p54u_priv *priv = dev->priv; + skb_unlink(skb, &priv->rx_queue); + if (unlikely(urb->status)) { - info->urb = NULL; - usb_free_urb(urb); + dev_kfree_skb_irq(skb); return; } - skb_unlink(skb, &priv->rx_queue); skb_put(skb, urb->actual_length); if (priv->hw_type == P54U_NET2280) @@ -105,7 +105,6 @@ static void p54u_rx_cb(struct urb *urb) if (p54_rx(dev, skb)) { skb = dev_alloc_skb(priv->common.rx_mtu + 32); if (unlikely(!skb)) { - usb_free_urb(urb); /* TODO check rx queue length and refill *somewhere* */ return; } @@ -115,7 +114,6 @@ static void p54u_rx_cb(struct urb *urb) info->dev = dev; urb->transfer_buffer = skb_tail_pointer(skb); urb->context = skb; - skb_queue_tail(&priv->rx_queue, skb); } else { if (priv->hw_type == P54U_NET2280) skb_push(skb, priv->common.tx_hdr_len); @@ -130,11 +128,14 @@ static void p54u_rx_cb(struct urb *urb) WARN_ON(1); urb->transfer_buffer = skb_tail_pointer(skb); } - - skb_queue_tail(&priv->rx_queue, skb); } - - usb_submit_urb(urb, GFP_ATOMIC); + skb_queue_tail(&priv->rx_queue, skb); + usb_anchor_urb(urb, &priv->submitted); + if (usb_submit_urb(urb, GFP_ATOMIC)) { + skb_unlink(skb, &priv->rx_queue); + usb_unanchor_urb(urb); + dev_kfree_skb_irq(skb); + } } static void p54u_tx_reuse_skb_cb(struct urb *urb) @@ -144,18 +145,6 @@ static void p54u_tx_reuse_skb_cb(struct urb *urb) usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)))->priv; skb_pull(skb, priv->common.tx_hdr_len); - usb_free_urb(urb); -} - -static void p54u_tx_cb(struct urb *urb) -{ - usb_free_urb(urb); -} - -static void p54u_tx_free_cb(struct urb *urb) -{ - kfree(urb->transfer_buffer); - usb_free_urb(urb); } static void p54u_tx_free_skb_cb(struct urb *urb) @@ -165,25 +154,36 @@ static void p54u_tx_free_skb_cb(struct urb *urb) usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); p54_free_skb(dev, skb); - usb_free_urb(urb); +} + +static void p54u_tx_dummy_cb(struct urb *urb) { } + +static void p54u_free_urbs(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + usb_kill_anchored_urbs(&priv->submitted); } static int p54u_init_urbs(struct ieee80211_hw *dev) { struct p54u_priv *priv = dev->priv; - struct urb *entry; + struct urb *entry = NULL; struct sk_buff *skb; struct p54u_rx_info *info; + int ret = 0; while (skb_queue_len(&priv->rx_queue) < 32) { skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL); - if (!skb) - break; + if (!skb) { + ret = -ENOMEM; + goto err; + } entry = usb_alloc_urb(0, GFP_KERNEL); if (!entry) { - kfree_skb(skb); - break; + ret = -ENOMEM; + goto err; } + usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), skb_tail_pointer(skb), @@ -192,26 +192,25 @@ static int p54u_init_urbs(struct ieee80211_hw *dev) info->urb = entry; info->dev = dev; skb_queue_tail(&priv->rx_queue, skb); - usb_submit_urb(entry, GFP_KERNEL); + + usb_anchor_urb(entry, &priv->submitted); + ret = usb_submit_urb(entry, GFP_KERNEL); + if (ret) { + skb_unlink(skb, &priv->rx_queue); + usb_unanchor_urb(entry); + goto err; + } + usb_free_urb(entry); + entry = NULL; } return 0; -} -static void p54u_free_urbs(struct ieee80211_hw *dev) -{ - struct p54u_priv *priv = dev->priv; - struct p54u_rx_info *info; - struct sk_buff *skb; - - while ((skb = skb_dequeue(&priv->rx_queue))) { - info = (struct p54u_rx_info *) skb->cb; - if (!info->urb) - continue; - - usb_kill_urb(info->urb); - kfree_skb(skb); - } + err: + usb_free_urb(entry); + kfree_skb(skb); + p54u_free_urbs(dev); + return ret; } static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb, @@ -219,6 +218,7 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb, { struct p54u_priv *priv = dev->priv; struct urb *addr_urb, *data_urb; + int err = 0; addr_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!addr_urb) @@ -233,15 +233,31 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb, usb_fill_bulk_urb(addr_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), &((struct p54_hdr *)skb->data)->req_id, 4, - p54u_tx_cb, dev); + p54u_tx_dummy_cb, dev); usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), skb->data, skb->len, free_on_tx ? p54u_tx_free_skb_cb : p54u_tx_reuse_skb_cb, skb); - usb_submit_urb(addr_urb, GFP_ATOMIC); - usb_submit_urb(data_urb, GFP_ATOMIC); + usb_anchor_urb(addr_urb, &priv->submitted); + err = usb_submit_urb(addr_urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(addr_urb); + goto out; + } + + usb_anchor_urb(addr_urb, &priv->submitted); + err = usb_submit_urb(data_urb, GFP_ATOMIC); + if (err) + usb_unanchor_urb(data_urb); + + out: + usb_free_urb(addr_urb); + usb_free_urb(data_urb); + + if (err) + p54_free_skb(dev, skb); } static __le32 p54u_lm87_chksum(const __le32 *data, size_t length) @@ -281,7 +297,13 @@ static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb, free_on_tx ? p54u_tx_free_skb_cb : p54u_tx_reuse_skb_cb, skb); - usb_submit_urb(data_urb, GFP_ATOMIC); + usb_anchor_urb(data_urb, &priv->submitted); + if (usb_submit_urb(data_urb, GFP_ATOMIC)) { + usb_unanchor_urb(data_urb); + skb_pull(skb, sizeof(*hdr)); + p54_free_skb(dev, skb); + } + usb_free_urb(data_urb); } static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb, @@ -291,6 +313,7 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb, struct urb *int_urb, *data_urb; struct net2280_tx_hdr *hdr; struct net2280_reg_write *reg; + int err = 0; reg = kmalloc(sizeof(*reg), GFP_ATOMIC); if (!reg) @@ -320,15 +343,42 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb, usb_fill_bulk_urb(int_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), - p54u_tx_free_cb, dev); - usb_submit_urb(int_urb, GFP_ATOMIC); + p54u_tx_dummy_cb, dev); + + /* + * This flag triggers a code path in the USB subsystem that will + * free what's inside the transfer_buffer after the callback routine + * has completed. + */ + int_urb->transfer_flags |= URB_FREE_BUFFER; usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), skb->data, skb->len, free_on_tx ? p54u_tx_free_skb_cb : p54u_tx_reuse_skb_cb, skb); - usb_submit_urb(data_urb, GFP_ATOMIC); + + usb_anchor_urb(int_urb, &priv->submitted); + err = usb_submit_urb(int_urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(int_urb); + goto out; + } + + usb_anchor_urb(data_urb, &priv->submitted); + err = usb_submit_urb(data_urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(data_urb); + goto out; + } + out: + usb_free_urb(int_urb); + usb_free_urb(data_urb); + + if (err) { + skb_pull(skb, sizeof(*hdr)); + p54_free_skb(dev, skb); + } } static int p54u_write(struct p54u_priv *priv, @@ -885,6 +935,7 @@ static int __devinit p54u_probe(struct usb_interface *intf, goto err_free_dev; skb_queue_head_init(&priv->rx_queue); + init_usb_anchor(&priv->submitted); p54u_open(dev); err = p54_read_eeprom(dev); diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h index 5b8fe91379c..54ee738bf2a 100644 --- a/drivers/net/wireless/p54/p54usb.h +++ b/drivers/net/wireless/p54/p54usb.h @@ -133,6 +133,7 @@ struct p54u_priv { spinlock_t lock; struct sk_buff_head rx_queue; + struct usb_anchor submitted; }; #endif /* P54USB_H */ |