Search Linux Wireless

Re: [PATCH 1/1] New driver: rtl8723au (mac80211)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Johannes Berg <johannes@xxxxxxxxxxxxxxxx> 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux