Johannes Berg <[email protected]> writes:
> Hi Jes,
>
>> Many thanks to Johannes Berg for 802.11 insights and help and Larry
>> Finger for help with the vendor driver.
>
> :-)
>
> Just a few comments since this is really the first time I'm looking
> closely at the code.
Thanks!
>> + h2c.ramask.cmd = H2C_SET_RATE_MASK;
>> + put_unaligned_le16(ramask & 0xffff, &h2c.ramask.mask_lo);
>> + put_unaligned_le16(ramask >> 16, &h2c.ramask.mask_hi);
>
> you marked that __packed, so the put_unaligned_le16() isn't really
> needed, you can use regular assignments (= cpu_to_le16(...))
I see, I wasn't aware of this, so figured better safe than sorry.
>> +static void
>> +rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif
>> *vif,
>> + struct ieee80211_bss_conf *bss_conf, u32 changed)
>> +{
>> + struct rtl8xxxu_priv *priv = hw->priv;
>> + struct device *dev = &priv->udev->dev;
>> + struct ieee80211_sta *sta;
>> + u32 val32;
>> +#if 0
>> + u16 val16;
>> +#endif
>> + u8 val8;
>> +
>> + if (changed & BSS_CHANGED_ASSOC) {
>> + struct h2c_cmd h2c;
>> +
>> + memset(&h2c, 0, sizeof(struct h2c_cmd));
>> + rtl8xxxu_set_linktype(priv, vif->type);
>> +
>> + if (bss_conf->assoc) {
>> + rcu_read_lock();
>> + sta = ieee80211_find_sta(vif, bss_conf->bssid);
>> + if (!sta) {
>> + dev_info(dev, "%s: ASSOC no sta found\n",
>> + __func__);
>> + rcu_read_unlock();
>> + goto error;
>> + }
>> +
>> + if (sta->ht_cap.ht_supported)
>> + dev_info(dev, "%s: HT supported\n", __func__);
>> + if (sta->vht_cap.vht_supported)
>> + dev_info(dev, "%s: VHT supported\n", __func__);
>> + rtl8xxxu_update_rate_table(priv, sta);
>> + rcu_read_unlock();
>
> You could perhaps consider doing this in the sta_state() callback, when
> the station becomes associated, which is just before (or just after?)
> this gets called. Then you don't need to do the find_sta() here. Not
> that it's really a problem, but might look a bit nicer/cleaner.
I'll have a look. I wasn't sure which cases were only valid as a
sub-case of others, as I didn't find any documentation on it (but
admittedly I didn't look too hard).
>> + if (changed & BSS_CHANGED_HT) {
>> + u8 ampdu_factor, ampdu_density;
>> + u8 sifs;
>> +
>> + rcu_read_lock();
>> + sta = ieee80211_find_sta(vif, bss_conf->bssid);
>> + if (!sta) {
>> + dev_info(dev, "BSS_CHANGED_HT: No HT sta found!\n");
>> + rcu_read_unlock();
>> + goto error;
>> + }
>> + if (sta->ht_cap.ht_supported) {
>> + ampdu_factor = sta->ht_cap.ampdu_factor;
>> + ampdu_density = sta->ht_cap.ampdu_density;
>> + sifs = 0x0e;
>> + } else {
>> + ampdu_factor = 0;
>> + ampdu_density = 0;
>> + sifs = 0x0a;
>> + }
>> + rcu_read_unlock();
>
> This doesn't really make much sense - BSS_CHANGED_HT only says we
> changed bss_conf.ht_operation_mode which you don't use here.
In this case, is there another flag that would be more appropriate to
handle this under?
>> + if (changed & BSS_CHANGED_BSSID) {
>> + dev_info(dev, "Changed BSSID!\n");
>> + rtl8xxxu_set_bssid(priv, bss_conf->bssid);
>> +
>> + rcu_read_lock();
>> + sta = ieee80211_find_sta(vif, bss_conf->bssid);
>> + if (!sta) {
>> + dev_info(dev, "No bssid sta found!\n");
>> + rcu_read_unlock();
>> + goto error;
>> + }
>> + rcu_read_unlock();
>> + }
>
> You probably want to roll this up into CHANGED_ASSOC, since
> CHANGED_BSSID always really comes together with that for station mode.
> For IBSS things would be different but you don't seem to do that.
OK, sounds reasonable.
>> + if (changed & BSS_CHANGED_BASIC_RATES) {
>> + dev_info(dev, "Changed BASIC_RATES!\n");
>> + rcu_read_lock();
>> + sta = ieee80211_find_sta(vif, bss_conf->bssid);
>> + if (sta)
>> + rtl8xxxu_set_basic_rates(priv, sta);
>> + else
>> + dev_info(dev,
>> + "BSS_CHANGED_BASIC_RATES: No sta found!\n");
>> +
>> + rcu_read_unlock();
>> + }
>
> Ditto, this ever change during the lifetime of a BSS either, i.e. it
> cannot change while you're associated, and you don't care while you're
> not. Again, IBSS could be different, not quite sure off the top of my
> head.
Ditto
>> +static u32 rtl8xxxu_queue_select(struct ieee80211_hw *hw, struct
>> sk_buff *skb)
>> +{
>> + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
>> + u32 queue;
>> +
>> + if (unlikely(ieee80211_is_beacon(hdr->frame_control)))
>> + queue = TXDESC_QUEUE_BEACON;
>> + if (ieee80211_is_mgmt(hdr->frame_control))
>
> I think you mean else if?
>
> Anyway beacons cannot happen here except perhaps for injection, in which
> case you probably wouldn't want to use the beacon queue anyway. I'd
> remove the beacon case here entirely.
This is probably suffer from being derived from vendor driver. They have
a special beacon queue in the hardware which doesn't seem to map
directly to anything the Linux stack does.
Until I or someone else eventually implements AP support in this driver,
it probably doesn't matter.
>> + queue = TXDESC_QUEUE_MGNT;
>> + else
>> + queue = rtl8xxxu_80211_to_rtl_queue(skb_get_queue_mapping(skb));
>> +
>> + return queue;
>> +}
>> +
>> +static void rtl8xxxu_calc_tx_desc_csum(struct rtl8xxxu_tx_desc *tx_desc)
>> +{
>> + u16 *ptr = (u16 *)tx_desc;
>> + u16 csum = 0;
>> + int i;
>> +
>> + tx_desc->csum = cpu_to_le32(0);
>> +
>> + for (i = 0; i < (sizeof(struct rtl8xxxu_tx_desc) / sizeof(u16)); i++)
>> + csum = csum ^ le16_to_cpu(ptr[i]);
>> +
>> + tx_desc->csum |= cpu_to_le32(csum);
>
> This seems kinda pointless - you can remove the = 0 above and just use =
> below?
Good point!
>> +static void rtl8xxxu_tx_complete(struct urb *urb)
>> +{
>> + struct sk_buff *skb = (struct sk_buff *)urb->context;
>> + struct ieee80211_tx_info *tx_info;
>> + struct ieee80211_hw *hw;
>> +
>> + tx_info = IEEE80211_SKB_CB(skb);
>> + hw = tx_info->rate_driver_data[0];
>
> Seems a bit odd to do one initialisation in the declarations, and the
> other two not, but hey, that really doesn't matter :)
>
>> + skb_pull(skb, sizeof(struct rtl8xxxu_tx_desc));
>> +
>> + ieee80211_tx_info_clear_status(tx_info);
>> + tx_info->status.rates[0].idx = -1;
>> + tx_info->status.rates[0].count = 0;
>
> This is ... suboptimal. If you have any idea what the rates were it'd be
> good to report at least the one that went through.
I have been trying to find a way to get the actual rate used from the
hardware/firmware, but so far I haven't figured out how to get it. This
is the fun about having no specs.
>> + tx_info->flags |= IEEE80211_TX_STAT_ACK;
>
> This shouldn't be done I think, if you can't report ACK status then I
> think you shouldn't do it at all, you aren't setting the
> IEEE80211_HW_REPORTS_TX_ACK_STATUS flag (though if you could have the
> ack status it would certainly be better to report it properly)
I think I was inspired by rtlwifi on this one. I can remove it.
> [snip code]
>
> Something about the TX is a bit fishy, you're using some software rate
> control data but aren't reporting any data back for rate control which
> seems to imply the device does it. You're also setting
> IEEE80211_HW_HAS_RATE_CONTROL which means you can't be using rates etc.
> here? I'll probably have to look into this more, it's an area that I'm
> not intimately familiar with.
The reason for this is that I can do either way and I have been playing
around with both. The vendor driver explicitly uses sw rate control for
management packets, whether this is necessary or legacy I cannot tell,
but I was trying to map to what they were doing.
>> + for (i = 0; i < (sizeof(struct rtl8xxxu_rx_desc) / sizeof(u32)); i++)
>> + _rx_desc[i] = le32_to_cpu(_rx_desc_le[i]);
>
> This is ... interesting. Wouldn't it make more sense to declare with
> little endian and without bitfields instead? bitfields are a bit of a
> minefield anyway as far as the C standard is concerned, so since you
> need to rely here on exact memory layout to satisfy the firmware API,
> I'd really avoid them.
Why declare things if you can blow your foot off with a machine gun?
It's something I was planning to look at eventually.
>> + ieee80211_rx_irqsafe(hw, skb);
>
> Here you give up ownership of the skb
>
>> + skb_size = sizeof(struct rtl8xxxu_rx_desc) +
>> + RTL_RX_BUFFER_SIZE;
>> + skb = dev_alloc_skb(skb_size);
>
> And then you allocate a new one into the same variable. From a
> maintenance POV that seems a bit ugly, perhaps you should avoid that.
I have always done this, kinda like it, but I am not really objecting to
it.
>> + if (!skb) {
>> + dev_warn(dev, "%s: Unable to allocate skb\n", __func__);
>> + goto cleanup;
>> + }
>> +
>> + memset(skb->data, 0, sizeof(struct rtl8xxxu_rx_desc));
>> + usb_fill_bulk_urb(&rx_urb->urb, priv->udev, priv->pipe_in,
>> + skb->data, skb_size, rtl8xxxu_rx_complete,
>
> This is also very strange, you're sending essentially random data, from
> an skb pointer that's not even a proper skb len? I think you just want
> to use kmalloc() here instead of having an skb? Or at least use
> skb_put() to reserve that memory area - it doesn't make a big difference
> to you but using the skb API in this way is likely to confuse people in
> the future.
I wanted to avoid having to allocate and then copy on arrival. It's been
quite common for drivers to do this, but I guess I ought to set the skb
len as well at this point to keep it clean.
> In fact you could even allocate a page instead, and then add it to the
> skb as a frag like iwlwifi does for example, and potentially not have to
> copy the data around later when it goes somewhere. Not sure that's worth
> it for this device though :)
I think the device can handle fragments, problem is I have zero
documentation on how it does so.
> Or perhaps just refactor the resubmission code into a small new
> function ... would also help with the error path here :)
>
> Also, what happens if you cannot allocate an skb here a few times? do
> your outstanding URBs get fewer every time? Or do you just have one?
Most likely I roll over and die, I'll make sure to sign the life
insurance over to you first though :)
>> +static int rtl8xxxu_submit_rx_urb(struct ieee80211_hw *hw)
>> +{
>> + struct rtl8xxxu_priv *priv = hw->priv;
>> + struct sk_buff *skb;
>> + struct rtl8xxxu_rx_urb *rx_urb;
>> + int skb_size;
>> + int ret;
>> +
>> + skb_size = sizeof(struct rtl8xxxu_rx_desc) + RTL_RX_BUFFER_SIZE;
>> + skb = dev_alloc_skb(skb_size);
>> + if (!skb)
>> + return -ENOMEM;
>> +
>> + memset(skb->data, 0, sizeof(struct rtl8xxxu_rx_desc));
>> +
>> + rx_urb = kmalloc(sizeof(struct rtl8xxxu_rx_urb), GFP_ATOMIC);
>> + if (!rx_urb) {
>> + dev_kfree_skb(skb);
>> + return -ENOMEM;
>> + }
>> + usb_init_urb(&rx_urb->urb);
>> + rx_urb->hw = hw;
>> +
>> + usb_fill_bulk_urb(&rx_urb->urb, priv->udev, priv->pipe_in, skb->data,
>> + skb_size, rtl8xxxu_rx_complete, skb);
>> + usb_anchor_urb(&rx_urb->urb, &priv->rx_anchor);
>> + ret = usb_submit_urb(&rx_urb->urb, GFP_ATOMIC);
>> + if (ret)
>> + usb_unanchor_urb(&rx_urb->urb);
>> + return ret;
>> +}
>
> Perhaps some part of this could also be refactored with the above
> resubmission? It looks essentially the same, perhaps the above code
> could even just call this.
I'll have to look into this.
>
>> +static int rtl8xxxu_set_rts_threshold(struct ieee80211_hw *hw, u32 rts)
>> +{
>> + if (rts > 2347)
>> + return -EINVAL;
>> +
>> + return 0;
>> +}
>
> You should either use the value here, or not implement this callback if
> you want to rely on mac80211 setting the flags, IIRC.
I'll just remove it for now then - another one of these cases where I
looked at other code to see what it was doing, but didn't get to
figuring out what to do with it in this case.
>> + switch (cmd) {
>> + case SET_KEY:
>> + /*
>> + * This is a bit of a hack - the lower bits of the cipher
>> + * suite selector happens to match the cipher index in the
>> + * CAM
>> + */
>
> It's probably not really a hack - the cipher suite selectors are defined
> in the spec, and we prefix them with the OUI to get the 32-bit
> identifier. If you assume standard-only ciphers then the lower 8 bits
> are just the number given to the suite in the spec.
Well yes and no, the hardware happens to match to those values too,
which is a bit of good luck. I think when it uses some of the strange
encryptions it no longer matches up.
>> + if (priv->rf_paths > 1) {
>> + sband->ht_cap.mcs.rx_mask[1] = 0xff;
>> + sband->ht_cap.mcs.rx_highest = cpu_to_le16(300);
>> + sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
>> + } else {
>> + sband->ht_cap.mcs.rx_mask[1] = 0x00;
>> + sband->ht_cap.mcs.rx_highest = cpu_to_le16(150);
>> + }
>
> You don't really need to set rx_highest, and you already cleared
> rx_mask[1], so you don't need the else branch and can remove the
> rx_highest from the if branch.
I wanted to prepare the code for adding support for 2r2t devices, but
I'll remove the rx_highest bits.
>> + sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
>
> That doesn't seem true?
I may misunderstand the meaning of this parameter.
>> + hw->wiphy->max_remain_on_channel_duration = 65535; /* ms */
>
> That's a bit excessive, but you don't implement ROC in hw anyway? so I'd
> remove this part I think.
OK
>> + hw->wiphy->cipher_suites = rtl8xxxu_cipher_suites;
>> + hw->wiphy->n_cipher_suites = ARRAY_SIZE(rtl8xxxu_cipher_suites);
>
> If you can deal with software crypto (which I believe you can) then you
> should just remove this and get all the ciphers, just making the ones
> you can do in software faster. You'll get all the new GCM ones, and
> CCM-256 too, in SW.
I'll try to remove it - this came in with some of the earliest pieces
of code I looked at when I started writing the driver.
Thanks for the feedback, it's very much appreciated!
Jes
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html