|
@@ -41,6 +41,8 @@ static void housekeeping_disable(struct zd_mac *mac);
|
|
|
|
|
|
static void set_multicast_hash_handler(struct work_struct *work);
|
|
|
|
|
|
+static void do_rx(unsigned long mac_ptr);
|
|
|
+
|
|
|
int zd_mac_init(struct zd_mac *mac,
|
|
|
struct net_device *netdev,
|
|
|
struct usb_interface *intf)
|
|
@@ -53,6 +55,10 @@ int zd_mac_init(struct zd_mac *mac,
|
|
|
INIT_DELAYED_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
|
|
|
INIT_DELAYED_WORK(&mac->set_basic_rates_work, set_basic_rates_work);
|
|
|
|
|
|
+ skb_queue_head_init(&mac->rx_queue);
|
|
|
+ tasklet_init(&mac->rx_tasklet, do_rx, (unsigned long)mac);
|
|
|
+ tasklet_disable(&mac->rx_tasklet);
|
|
|
+
|
|
|
ieee_init(ieee);
|
|
|
softmac_init(ieee80211_priv(netdev));
|
|
|
zd_chip_init(&mac->chip, netdev, intf);
|
|
@@ -140,6 +146,8 @@ out:
|
|
|
void zd_mac_clear(struct zd_mac *mac)
|
|
|
{
|
|
|
flush_workqueue(zd_workqueue);
|
|
|
+ skb_queue_purge(&mac->rx_queue);
|
|
|
+ tasklet_kill(&mac->rx_tasklet);
|
|
|
zd_chip_clear(&mac->chip);
|
|
|
ZD_ASSERT(!spin_is_locked(&mac->lock));
|
|
|
ZD_MEMCLEAR(mac, sizeof(struct zd_mac));
|
|
@@ -168,6 +176,8 @@ int zd_mac_open(struct net_device *netdev)
|
|
|
struct zd_chip *chip = &mac->chip;
|
|
|
int r;
|
|
|
|
|
|
+ tasklet_enable(&mac->rx_tasklet);
|
|
|
+
|
|
|
r = zd_chip_enable_int(chip);
|
|
|
if (r < 0)
|
|
|
goto out;
|
|
@@ -218,6 +228,8 @@ int zd_mac_stop(struct net_device *netdev)
|
|
|
*/
|
|
|
|
|
|
zd_chip_disable_rx(chip);
|
|
|
+ skb_queue_purge(&mac->rx_queue);
|
|
|
+ tasklet_disable(&mac->rx_tasklet);
|
|
|
housekeeping_disable(mac);
|
|
|
ieee80211softmac_stop(netdev);
|
|
|
|
|
@@ -470,13 +482,13 @@ static void bssinfo_change(struct net_device *netdev, u32 changes)
|
|
|
|
|
|
if (changes & IEEE80211SOFTMAC_BSSINFOCHG_RATES) {
|
|
|
/* Set RTS rate to highest available basic rate */
|
|
|
- u8 rate = ieee80211softmac_highest_supported_rate(softmac,
|
|
|
+ u8 hi_rate = ieee80211softmac_highest_supported_rate(softmac,
|
|
|
&bssinfo->supported_rates, 1);
|
|
|
- rate = rate_to_zd_rate(rate);
|
|
|
+ hi_rate = rate_to_zd_rate(hi_rate);
|
|
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
|
- if (rate != mac->rts_rate) {
|
|
|
- mac->rts_rate = rate;
|
|
|
+ if (hi_rate != mac->rts_rate) {
|
|
|
+ mac->rts_rate = hi_rate;
|
|
|
need_set_rts_cts = 1;
|
|
|
}
|
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
@@ -1072,43 +1084,75 @@ static int fill_rx_stats(struct ieee80211_rx_stats *stats,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length)
|
|
|
+static void zd_mac_rx(struct zd_mac *mac, struct sk_buff *skb)
|
|
|
{
|
|
|
int r;
|
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
|
struct ieee80211_rx_stats stats;
|
|
|
const struct rx_status *status;
|
|
|
- struct sk_buff *skb;
|
|
|
|
|
|
- if (length < ZD_PLCP_HEADER_SIZE + IEEE80211_1ADDR_LEN +
|
|
|
- IEEE80211_FCS_LEN + sizeof(struct rx_status))
|
|
|
- return -EINVAL;
|
|
|
+ if (skb->len < ZD_PLCP_HEADER_SIZE + IEEE80211_1ADDR_LEN +
|
|
|
+ IEEE80211_FCS_LEN + sizeof(struct rx_status))
|
|
|
+ {
|
|
|
+ dev_dbg_f(zd_mac_dev(mac), "Packet with length %u to small.\n",
|
|
|
+ skb->len);
|
|
|
+ goto free_skb;
|
|
|
+ }
|
|
|
|
|
|
- r = fill_rx_stats(&stats, &status, mac, buffer, length);
|
|
|
- if (r)
|
|
|
- return r;
|
|
|
+ r = fill_rx_stats(&stats, &status, mac, skb->data, skb->len);
|
|
|
+ if (r) {
|
|
|
+ /* Only packets with rx errors are included here. */
|
|
|
+ goto free_skb;
|
|
|
+ }
|
|
|
|
|
|
- length -= ZD_PLCP_HEADER_SIZE+IEEE80211_FCS_LEN+
|
|
|
- sizeof(struct rx_status);
|
|
|
- buffer += ZD_PLCP_HEADER_SIZE;
|
|
|
+ __skb_pull(skb, ZD_PLCP_HEADER_SIZE);
|
|
|
+ __skb_trim(skb, skb->len -
|
|
|
+ (IEEE80211_FCS_LEN + sizeof(struct rx_status)));
|
|
|
|
|
|
- update_qual_rssi(mac, buffer, length, stats.signal, stats.rssi);
|
|
|
+ update_qual_rssi(mac, skb->data, skb->len, stats.signal,
|
|
|
+ status->signal_strength);
|
|
|
|
|
|
- r = filter_rx(ieee, buffer, length, &stats);
|
|
|
- if (r <= 0)
|
|
|
- return r;
|
|
|
+ r = filter_rx(ieee, skb->data, skb->len, &stats);
|
|
|
+ if (r <= 0) {
|
|
|
+ if (r < 0)
|
|
|
+ dev_dbg_f(zd_mac_dev(mac), "Error in packet.\n");
|
|
|
+ goto free_skb;
|
|
|
+ }
|
|
|
|
|
|
- skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length);
|
|
|
- if (!skb)
|
|
|
- return -ENOMEM;
|
|
|
if (ieee->iw_mode == IW_MODE_MONITOR)
|
|
|
- fill_rt_header(skb_put(skb, sizeof(struct zd_rt_hdr)), mac,
|
|
|
+ fill_rt_header(skb_push(skb, sizeof(struct zd_rt_hdr)), mac,
|
|
|
&stats, status);
|
|
|
- memcpy(skb_put(skb, length), buffer, length);
|
|
|
|
|
|
r = ieee80211_rx(ieee, skb, &stats);
|
|
|
- if (!r)
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
+ if (r)
|
|
|
+ return;
|
|
|
+free_skb:
|
|
|
+ /* We are always in a soft irq. */
|
|
|
+ dev_kfree_skb(skb);
|
|
|
+}
|
|
|
+
|
|
|
+static void do_rx(unsigned long mac_ptr)
|
|
|
+{
|
|
|
+ struct zd_mac *mac = (struct zd_mac *)mac_ptr;
|
|
|
+ struct sk_buff *skb;
|
|
|
+
|
|
|
+ while ((skb = skb_dequeue(&mac->rx_queue)) != NULL)
|
|
|
+ zd_mac_rx(mac, skb);
|
|
|
+}
|
|
|
+
|
|
|
+int zd_mac_rx_irq(struct zd_mac *mac, const u8 *buffer, unsigned int length)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+
|
|
|
+ skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length);
|
|
|
+ if (!skb) {
|
|
|
+ dev_warn(zd_mac_dev(mac), "Could not allocate skb.\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ skb_reserve(skb, sizeof(struct zd_rt_hdr));
|
|
|
+ memcpy(__skb_put(skb, length), buffer, length);
|
|
|
+ skb_queue_tail(&mac->rx_queue, skb);
|
|
|
+ tasklet_schedule(&mac->rx_tasklet);
|
|
|
return 0;
|
|
|
}
|
|
|
|