X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Fzd1211rw%2Fzd_mac.c;h=4c5f78eac349a4f77d5d897a9936618c0dc562a3;hb=5fc77247f7db01b6377a5ea6ab18c8ac60021045;hp=f1573a9c23369e5051e8571189171b160e267d03;hpb=dd8856bda5f1308beb113281b248683992998a9e;p=powerpc.git diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index f1573a9c23..4c5f78eac3 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -39,6 +39,10 @@ static void housekeeping_init(struct zd_mac *mac); 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) @@ -51,10 +55,15 @@ 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); housekeeping_init(mac); + INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler); return 0; } @@ -136,6 +145,9 @@ 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)); @@ -144,7 +156,7 @@ void zd_mac_clear(struct zd_mac *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 }, }; @@ -152,10 +164,9 @@ static int reset_mode(struct zd_mac *mac) 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) @@ -164,6 +175,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; @@ -214,6 +227,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); @@ -256,6 +271,43 @@ int zd_mac_set_mac_address(struct net_device *netdev, void *p) 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; @@ -429,13 +481,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); @@ -618,6 +670,9 @@ int zd_mac_get_range(struct zd_mac *mac, struct iw_range *range) 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; @@ -848,16 +903,21 @@ static int fill_ctrlset(struct zd_mac *mac, 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? */ @@ -930,7 +990,8 @@ static int is_data_packet_for_us(struct ieee80211_device *ieee, } 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); } @@ -1006,9 +1067,23 @@ static int fill_rx_stats(struct ieee80211_rx_stats *stats, *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)); @@ -1027,45 +1102,81 @@ 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)) + { + 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; }