static void housekeeping_enable(struct zd_mac *mac);
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)
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);
housekeeping_init(mac);
+ INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
return 0;
}
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));
static int reset_mode(struct zd_mac *mac)
{
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
- struct zd_ioreq32 ioreqs[3] = {
+ struct zd_ioreq32 ioreqs[] = {
{ CR_RX_FILTER, STA_RX_FILTER },
{ CR_SNIFFER_ON, 0U },
};
if (ieee->iw_mode == IW_MODE_MONITOR) {
ioreqs[0].value = 0xffffffff;
ioreqs[1].value = 0x1;
- ioreqs[2].value = ENC_SNIFFER;
}
- return zd_iowrite32a(&mac->chip, ioreqs, 3);
+ return zd_iowrite32a(&mac->chip, ioreqs, ARRAY_SIZE(ioreqs));
}
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;
*/
zd_chip_disable_rx(chip);
+ skb_queue_purge(&mac->rx_queue);
+ tasklet_disable(&mac->rx_tasklet);
housekeeping_disable(mac);
ieee80211softmac_stop(netdev);
return 0;
}
+static void set_multicast_hash_handler(struct work_struct *work)
+{
+ struct zd_mac *mac = container_of(work, struct zd_mac,
+ set_multicast_hash_work);
+ struct zd_mc_hash hash;
+
+ spin_lock_irq(&mac->lock);
+ hash = mac->multicast_hash;
+ spin_unlock_irq(&mac->lock);
+
+ zd_chip_set_multicast_hash(&mac->chip, &hash);
+}
+
+void zd_mac_set_multicast_list(struct net_device *dev)
+{
+ struct zd_mc_hash hash;
+ struct zd_mac *mac = zd_netdev_mac(dev);
+ struct dev_mc_list *mc;
+ unsigned long flags;
+
+ if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
+ zd_mc_add_all(&hash);
+ } else {
+ zd_mc_clear(&hash);
+ for (mc = dev->mc_list; mc; mc = mc->next) {
+ dev_dbg_f(zd_mac_dev(mac), "mc addr " MAC_FMT "\n",
+ MAC_ARG(mc->dmi_addr));
+ zd_mc_add_addr(&hash, mc->dmi_addr);
+ }
+ }
+
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->multicast_hash = hash;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ queue_work(zd_workqueue, &mac->set_multicast_hash_work);
+}
+
int zd_mac_set_regdomain(struct zd_mac *mac, u8 regdomain)
{
int r;
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);
range->we_version_compiled = WIRELESS_EXT;
range->we_version_source = 20;
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
ZD_ASSERT(!irqs_disabled());
spin_lock_irq(&mac->lock);
regdomain = mac->regdomain;
static int zd_mac_tx(struct zd_mac *mac, struct ieee80211_txb *txb, int pri)
{
int i, r;
+ struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
for (i = 0; i < txb->nr_frags; i++) {
struct sk_buff *skb = txb->fragments[i];
r = fill_ctrlset(mac, txb, i);
- if (r)
+ if (r) {
+ ieee->stats.tx_dropped++;
return r;
+ }
r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len);
- if (r)
+ if (r) {
+ ieee->stats.tx_dropped++;
return r;
+ }
}
/* FIXME: shouldn't this be handled by the upper layers? */
}
return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 ||
- is_multicast_ether_addr(hdr->addr1) ||
+ (is_multicast_ether_addr(hdr->addr1) &&
+ memcmp(hdr->addr3, netdev->dev_addr, ETH_ALEN) != 0) ||
(netdev->flags & IFF_PROMISC);
}
*pstatus = status = zd_tail(buffer, length, sizeof(struct rx_status));
if (status->frame_status & ZD_RX_ERROR) {
- /* FIXME: update? */
+ struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
+ ieee->stats.rx_errors++;
+ if (status->frame_status & ZD_RX_TIMEOUT_ERROR)
+ ieee->stats.rx_missed_errors++;
+ else if (status->frame_status & ZD_RX_FIFO_OVERRUN_ERROR)
+ ieee->stats.rx_fifo_errors++;
+ else if (status->frame_status & ZD_RX_DECRYPTION_ERROR)
+ ieee->ieee_stats.rx_discards_undecryptable++;
+ else if (status->frame_status & ZD_RX_CRC32_ERROR) {
+ ieee->stats.rx_crc_errors++;
+ ieee->ieee_stats.rx_fcs_errors++;
+ }
+ else if (status->frame_status & ZD_RX_CRC16_ERROR)
+ ieee->stats.rx_crc_errors++;
return -EINVAL;
}
+
memset(stats, 0, sizeof(struct ieee80211_rx_stats));
stats->len = length - (ZD_PLCP_HEADER_SIZE + IEEE80211_FCS_LEN +
+ sizeof(struct rx_status));
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))
+ {
+ ieee->stats.rx_errors++;
+ ieee->stats.rx_length_errors++;
+ 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.
+ * The error stats have already been set in fill_rx_stats.
+ */
+ 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) {
+ ieee->stats.rx_errors++;
+ 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) {
- ZD_ASSERT(in_irq());
- dev_kfree_skb_irq(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) {
+ struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
+ dev_warn(zd_mac_dev(mac), "Could not allocate skb.\n");
+ ieee->stats.rx_dropped++;
+ 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;
}