int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain) { struct ieee80211_geo geo; const struct channel_range *range; int i; u8 channel; dev_dbg(zd_mac_dev(zd_netdev_mac(ieee->dev)), "regdomain %#04x\n", regdomain); range = zd_channel_range(regdomain); if (range->start == 0) { dev_err(zd_mac_dev(zd_netdev_mac(ieee->dev)), "zd1211 regdomain %#04x not supported\n", regdomain); return -EINVAL; } memset(&geo, 0, sizeof(geo)); for (i = 0, channel = range->start; channel < range->end; channel++) { struct ieee80211_channel *chan = &geo.bg[i++]; chan->freq = channel_frequencies[channel - 1]; chan->channel = channel; } geo.bg_channels = i; memcpy(geo.name, "XX ", 4); ieee80211_set_geo(ieee, &geo); return 0; }
static void set_rx_filter_handler(struct work_struct *work) { struct zd_mac *mac = container_of(work, struct zd_mac, set_rx_filter_work); int r; dev_dbg_f(zd_mac_dev(mac), "\n"); r = set_rx_filter(mac); if (r) dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r); }
void zd_process_intr(struct work_struct *work) { u16 int_status; struct zd_mac *mac = container_of(work, struct zd_mac, process_intr); int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer+4)); if (int_status & INT_CFG_NEXT_BCN) { if (net_ratelimit()) dev_dbg_f(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n"); } else dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n"); zd_chip_enable_hwint(&mac->chip); }
int zd_mac_set_mac_address(struct net_device *netdev, void *p) { int r; unsigned long flags; struct sockaddr *addr = p; struct zd_mac *mac = zd_netdev_mac(netdev); struct zd_chip *chip = &mac->chip; DECLARE_MAC_BUF(mac2); if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; dev_dbg_f(zd_mac_dev(mac), "Setting MAC to %s\n", print_mac(mac2, addr->sa_data)); r = zd_write_mac_addr(chip, addr->sa_data); if (r) return r; spin_lock_irqsave(&mac->lock, flags); memcpy(netdev->dev_addr, addr->sa_data, ETH_ALEN); spin_unlock_irqrestore(&mac->lock, flags); return 0; }
u8 zd_mac_get_channel(struct zd_mac *mac) { u8 channel = zd_chip_get_channel(&mac->chip); dev_dbg_f(zd_mac_dev(mac), "channel %u\n", channel); return channel; }
int zd_mac_set_mode(struct zd_mac *mac, u32 mode) { struct ieee80211_device *ieee; switch (mode) { case IW_MODE_AUTO: case IW_MODE_ADHOC: case IW_MODE_INFRA: mac->netdev->type = ARPHRD_ETHER; break; case IW_MODE_MONITOR: mac->netdev->type = ARPHRD_IEEE80211_RADIOTAP; break; default: dev_dbg_f(zd_mac_dev(mac), "wrong mode %u\n", mode); return -EINVAL; } ieee = zd_mac_to_ieee80211(mac); ZD_ASSERT(!irqs_disabled()); spin_lock_irq(&ieee->lock); ieee->iw_mode = mode; spin_unlock_irq(&ieee->lock); if (netif_running(mac->netdev)) return reset_mode(mac); return 0; }
static void housekeeping_disable(struct zd_mac *mac) { dev_dbg_f(zd_mac_dev(mac), "\n"); cancel_rearming_delayed_workqueue(zd_workqueue, &mac->housekeeping.link_led_work); zd_chip_control_leds(&mac->chip, LED_OFF); }
static void set_channel(struct net_device *netdev, u8 channel) { struct zd_mac *mac = zd_netdev_mac(netdev); dev_dbg_f(zd_mac_dev(mac), "channel %d\n", channel); zd_chip_set_channel(&mac->chip, channel); }
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; 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, skb->data, skb->len); if (r) { /* Only packets with rx errors are included here. */ goto free_skb; } __skb_pull(skb, ZD_PLCP_HEADER_SIZE); __skb_trim(skb, skb->len - (IEEE80211_FCS_LEN + sizeof(struct rx_status))); update_qual_rssi(mac, skb->data, skb->len, stats.signal, status->signal_strength); 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; } if (ieee->iw_mode == IW_MODE_MONITOR) fill_rt_header(skb_push(skb, sizeof(struct zd_rt_hdr)), mac, &stats, status); r = ieee80211_rx(ieee, skb, &stats); if (r) return; free_skb: /* We are always in a soft irq. */ dev_kfree_skb(skb); }
int zd_mac_init_hw(struct zd_mac *mac, u8 device_type) { int r; struct zd_chip *chip = &mac->chip; u8 addr[ETH_ALEN]; u8 default_regdomain; r = zd_chip_enable_int(chip); if (r) goto out; r = zd_chip_init_hw(chip, device_type); if (r) goto disable_int; zd_get_e2p_mac_addr(chip, addr); r = zd_write_mac_addr(chip, addr); if (r) goto disable_int; ZD_ASSERT(!irqs_disabled()); spin_lock_irq(&mac->lock); memcpy(mac->netdev->dev_addr, addr, ETH_ALEN); spin_unlock_irq(&mac->lock); r = zd_read_regdomain(chip, &default_regdomain); if (r) goto disable_int; if (!zd_regdomain_supported(default_regdomain)) { dev_dbg_f(zd_mac_dev(mac), "Regulatory Domain %#04x is not supported.\n", default_regdomain); r = -EINVAL; goto disable_int; } spin_lock_irq(&mac->lock); mac->regdomain = mac->default_regdomain = default_regdomain; spin_unlock_irq(&mac->lock); r = reset_channel(mac); if (r) goto disable_int; /* We must inform the device that we are doing encryption/decryption in * software at the moment. */ r = zd_set_encryption_type(chip, ENC_SNIFFER); if (r) goto disable_int; r = zd_geo_init(zd_mac_to_ieee80211(mac), mac->regdomain); if (r) goto disable_int; r = 0; disable_int: zd_chip_disable_int(chip); out: return r; }
static int iw_get_range(struct net_device *netdev, struct iw_request_info *info, union iwreq_data *req, char *extra) { struct iw_range *range = (struct iw_range *)extra; dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n"); req->data.length = sizeof(*range); return zd_mac_get_range(zd_netdev_mac(netdev), range); }
static void zd_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, int mc_count, struct dev_mc_list *mclist) { struct zd_mc_hash hash; struct zd_mac *mac = zd_hw_mac(hw); unsigned long flags; int i; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; *new_flags &= SUPPORTED_FIF_FLAGS; /* changed_flags is always populated but this driver * doesn't support all FIF flags so its possible we don't * need to do anything */ if (!changed_flags) return; if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) { zd_mc_add_all(&hash); } else { DECLARE_MAC_BUF(macbuf); zd_mc_clear(&hash); for (i = 0; i < mc_count; i++) { if (!mclist) break; dev_dbg_f(zd_mac_dev(mac), "mc addr %s\n", print_mac(macbuf, mclist->dmi_addr)); zd_mc_add_addr(&hash, mclist->dmi_addr); mclist = mclist->next; } } spin_lock_irqsave(&mac->lock, flags); mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL); mac->pass_ctrl = !!(*new_flags & FIF_CONTROL); mac->multicast_hash = hash; spin_unlock_irqrestore(&mac->lock, flags); queue_work(zd_workqueue, &mac->set_multicast_hash_work); if (changed_flags & FIF_CONTROL) queue_work(zd_workqueue, &mac->set_rx_filter_work); /* no handling required for FIF_OTHER_BSS as we don't currently * do BSSID filtering */ /* FIXME: in future it would be nice to enable the probe response * filter (so that the driver doesn't see them) until * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd * have to schedule work to enable prbresp reception, which might * happen too late. For now we'll just listen and forward them all the * time. */ }
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; }
static void set_security(struct net_device *netdev, struct ieee80211_security *sec) { struct ieee80211_device *ieee = zd_netdev_ieee80211(netdev); struct ieee80211_security *secinfo = &ieee->sec; int keyidx; dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n"); for (keyidx = 0; keyidx<WEP_KEYS; keyidx++) if (sec->flags & (1<<keyidx)) { secinfo->encode_alg[keyidx] = sec->encode_alg[keyidx]; secinfo->key_sizes[keyidx] = sec->key_sizes[keyidx]; memcpy(secinfo->keys[keyidx], sec->keys[keyidx], SCM_KEY_LEN); } if (sec->flags & SEC_ACTIVE_KEY) { secinfo->active_key = sec->active_key; dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), " .active_key = %d\n", sec->active_key); } if (sec->flags & SEC_UNICAST_GROUP) { secinfo->unicast_uses_group = sec->unicast_uses_group; dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), " .unicast_uses_group = %d\n", sec->unicast_uses_group); } if (sec->flags & SEC_LEVEL) { secinfo->level = sec->level; dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), " .level = %d\n", sec->level); } if (sec->flags & SEC_ENABLED) { secinfo->enabled = sec->enabled; dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), " .enabled = %d\n", sec->enabled); } if (sec->flags & SEC_ENCRYPT) { secinfo->encrypt = sec->encrypt; dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), " .encrypt = %d\n", sec->encrypt); } if (sec->flags & SEC_AUTH_MODE) { secinfo->auth_mode = sec->auth_mode; dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), " .auth_mode = %d\n", sec->auth_mode); } }
static void link_led_handler(struct work_struct *work) { struct zd_mac *mac = container_of(work, struct zd_mac, housekeeping.link_led_work.work); struct zd_chip *chip = &mac->chip; int is_associated; int r; spin_lock_irq(&mac->lock); is_associated = mac->associated; spin_unlock_irq(&mac->lock); r = zd_chip_control_leds(chip, is_associated ? LED_ASSOCIATED : LED_SCANNING); if (r) dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r); queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, LINK_LED_WORK_DELAY); }
static int iw_get_freq(struct net_device *netdev, struct iw_request_info *info, union iwreq_data *req, char *extra) { int r; struct zd_mac *mac = zd_netdev_mac(netdev); struct iw_freq *freq = &req->freq; u8 channel; u8 flags; r = zd_mac_get_channel(mac, &channel, &flags); if (r) return r; freq->flags = (flags & MAC_FIXED_CHANNEL) ? IW_FREQ_FIXED : IW_FREQ_AUTO; dev_dbg_f(zd_mac_dev(mac), "channel %s\n", (flags & MAC_FIXED_CHANNEL) ? "fixed" : "auto"); return zd_channel_to_freq(freq, channel); }
static void zd_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct zd_mac *mac = zd_hw_mac(hw); unsigned long flags; dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes); if (changes & BSS_CHANGED_ERP_PREAMBLE) { spin_lock_irqsave(&mac->lock, flags); mac->short_preamble = bss_conf->use_short_preamble; if (!mac->updating_rts_rate) { mac->updating_rts_rate = 1; /* FIXME: should disable TX here, until work has * completed and RTS_CTS reg is updated */ queue_work(zd_workqueue, &mac->set_rts_cts_work); } spin_unlock_irqrestore(&mac->lock, flags); } }
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); }
static void bssinfo_change(struct net_device *netdev, u32 changes) { struct zd_mac *mac = zd_netdev_mac(netdev); struct ieee80211softmac_device *softmac = ieee80211_priv(netdev); struct ieee80211softmac_bss_info *bssinfo = &softmac->bssinfo; int need_set_rts_cts = 0; int need_set_rates = 0; u16 basic_rates; unsigned long flags; dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes); if (changes & IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE) { spin_lock_irqsave(&mac->lock, flags); mac->short_preamble = bssinfo->short_preamble; spin_unlock_irqrestore(&mac->lock, flags); need_set_rts_cts = 1; } if (changes & IEEE80211SOFTMAC_BSSINFOCHG_RATES) { /* Set RTS rate to highest available basic rate */ u8 hi_rate = ieee80211softmac_highest_supported_rate(softmac, &bssinfo->supported_rates, 1); hi_rate = rate_to_zd_rate(hi_rate); spin_lock_irqsave(&mac->lock, flags); if (hi_rate != mac->rts_rate) { mac->rts_rate = hi_rate; need_set_rts_cts = 1; } spin_unlock_irqrestore(&mac->lock, flags); /* Set basic rates */ need_set_rates = 1; if (bssinfo->supported_rates.count == 0) { /* Allow the device to be flexible */ basic_rates = CR_RATES_80211B | CR_RATES_80211G; } else { int i = 0; basic_rates = 0; for (i = 0; i < bssinfo->supported_rates.count; i++) { u16 rate = bssinfo->supported_rates.rates[i]; if ((rate & IEEE80211_BASIC_RATE_MASK) == 0) continue; rate &= ~IEEE80211_BASIC_RATE_MASK; basic_rates |= rate_to_cr_rate(rate); } } spin_lock_irqsave(&mac->lock, flags); mac->basic_rates = basic_rates; spin_unlock_irqrestore(&mac->lock, flags); } /* Schedule any changes we made above */ spin_lock_irqsave(&mac->lock, flags); if (need_set_rts_cts && !mac->updating_rts_rate) { mac->updating_rts_rate = 1; netif_stop_queue(mac->netdev); queue_delayed_work(zd_workqueue, &mac->set_rts_cts_work, 0); } if (need_set_rates && !mac->updating_basic_rates) { mac->updating_basic_rates = 1; netif_stop_queue(mac->netdev); queue_delayed_work(zd_workqueue, &mac->set_basic_rates_work, 0); } spin_unlock_irqrestore(&mac->lock, flags); }
static void housekeeping_enable(struct zd_mac *mac) { dev_dbg_f(zd_mac_dev(mac), "\n"); queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, 0); }