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

Reply via email to