RE: [PATCH v3 07/11] rtw88: Add common USB chip support

From: Ping-Ke Shih
Date: Sun Nov 27 2022 - 21:02:03 EST




> -----Original Message-----
> From: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>
> Sent: Tuesday, November 22, 2022 10:52 PM
> To: linux-wireless@xxxxxxxxxxxxxxx
> Cc: Neo Jou <neojou@xxxxxxxxx>; Hans Ulli Kroll <linux@xxxxxxxxxxxxx>; Ping-Ke Shih <pkshih@xxxxxxxxxxx>;
> Yan-Hsuan Chuang <tony0620emma@xxxxxxxxx>; Kalle Valo <kvalo@xxxxxxxxxx>; netdev@xxxxxxxxxxxxxxx;
> linux-kernel@xxxxxxxxxxxxxxx; Martin Blumenstingl <martin.blumenstingl@xxxxxxxxxxxxxx>;
> kernel@xxxxxxxxxxxxxx; Johannes Berg <johannes@xxxxxxxxxxxxxxxx>; Alexander Hochbaum <alex@xxxxxxxxxx>;
> Da Xue <da@libre.computer>; Bernie Huang <phhuang@xxxxxxxxxxx>; Viktor Petrenko <g0000ga@xxxxxxxxx>;
> Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>; neo_jou <neo_jou@xxxxxxxxxxx>
> Subject: [PATCH v3 07/11] rtw88: Add common USB chip support
>
> Add the common bits and pieces to add USB support to the RTW88 driver.
> This is based on https://github.com/ulli-kroll/rtw88-usb.git which
> itself is first written by Neo Jou.
>
> Signed-off-by: neo_jou <neo_jou@xxxxxxxxxxx>
> Signed-off-by: Hans Ulli Kroll <linux@xxxxxxxxxxxxx>
> Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>
> ---
>
> Notes:
> Changes since v2:
> - Fix buffer length for aggregated tx packets
> - Increase maximum transmit buffer size to 20KiB as found in downstream drivers
> - Change register write functions to synchronous accesses instead of just firing
> a URB without waiting for its completion
> - requeue rx URBs directly in completion handler rather than having a workqueue
> for it.
>
> Changes since v1:
> - Make checkpatch.pl clean
> - Drop WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL flag
> - Use 'ret' as variable name for return values
> - Sort variable declarations in reverse Xmas tree order
> - Change potentially endless loop to a limited loop
> - Change locking to be more obviously correct
> - drop unnecessary check for !rtwdev
> - make sure the refill workqueue is not restarted again after we have
> cancelled it
>
> drivers/net/wireless/realtek/rtw88/Kconfig | 3 +
> drivers/net/wireless/realtek/rtw88/Makefile | 2 +
> drivers/net/wireless/realtek/rtw88/mac.c | 3 +
> drivers/net/wireless/realtek/rtw88/main.c | 4 +
> drivers/net/wireless/realtek/rtw88/main.h | 4 +
> drivers/net/wireless/realtek/rtw88/reg.h | 1 +
> drivers/net/wireless/realtek/rtw88/tx.h | 31 +
> drivers/net/wireless/realtek/rtw88/usb.c | 918 ++++++++++++++++++++
> drivers/net/wireless/realtek/rtw88/usb.h | 107 +++
> 9 files changed, 1073 insertions(+)
> create mode 100644 drivers/net/wireless/realtek/rtw88/usb.c
> create mode 100644 drivers/net/wireless/realtek/rtw88/usb.h
>
> diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig
> index e3d7cb6c12902..1624c5db69bac 100644
> --- a/drivers/net/wireless/realtek/rtw88/Kconfig
> +++ b/drivers/net/wireless/realtek/rtw88/Kconfig
> @@ -16,6 +16,9 @@ config RTW88_CORE
> config RTW88_PCI
> tristate
>
> +config RTW88_USB
> + tristate
> +
> config RTW88_8822B
> tristate
>
> diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile
> index 834c66ec0af9e..9e095f8181483 100644
> --- a/drivers/net/wireless/realtek/rtw88/Makefile
> +++ b/drivers/net/wireless/realtek/rtw88/Makefile
> @@ -45,4 +45,6 @@ obj-$(CONFIG_RTW88_8821CE) += rtw88_8821ce.o
> rtw88_8821ce-objs := rtw8821ce.o
>
> obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o
> +obj-$(CONFIG_RTW88_USB) += rtw88_usb.o
> rtw88_pci-objs := pci.o
> +rtw88_usb-objs := usb.o

nit: I prefer not interleaving with PCI.


[...]

> diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
> new file mode 100644
> index 0000000000000..4a12934d20712
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/usb.c

[...]

> +static u32 rtw_usb_read(struct rtw_dev *rtwdev, u32 addr, u16 len)
> +{
> + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
> + struct usb_device *udev = rtwusb->udev;
> + __le32 *data;
> + unsigned long flags;
> + int ret;
> + static int count;
> +
> + spin_lock_irqsave(&rtwusb->usb_lock, flags);
> +
> + rtwusb->usb_data_index++;
> + rtwusb->usb_data_index &= (RTW_USB_MAX_RXTX_COUNT - 1);
> +
> + spin_unlock_irqrestore(&rtwusb->usb_lock, flags);
> +
> + data = &rtwusb->usb_data[rtwusb->usb_data_index];

Don't you need to hold &rtwusb->usb_lock to access rtwusb->usb_data_index?
rtw_usb_write() has similar code.

> +
> + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
> + RTW_USB_CMD_REQ, RTW_USB_CMD_READ, addr,
> + RTW_USB_VENQT_CMD_IDX, data, len, 1000);
> + if (ret < 0 && ret != -ENODEV && count++ < 4)
> + rtw_err(rtwdev, "read register 0x%x failed with %d\n",
> + addr, ret);
> +
> + return le32_to_cpu(*data);
> +}
> +

[...]

> +
> +static void rtw_usb_write_port_tx_complete(struct urb *urb)
> +{
> + struct rtw_usb_txcb *txcb = urb->context;
> + struct rtw_dev *rtwdev = txcb->rtwdev;
> + struct ieee80211_hw *hw = rtwdev->hw;
> +
> + while (true) {

Is it possible to have a hard limit to prevent unexpected infinite loop?

> + struct sk_buff *skb = skb_dequeue(&txcb->tx_ack_queue);
> + struct ieee80211_tx_info *info;
> + struct rtw_usb_tx_data *tx_data;
> +
> + if (!skb)
> + break;
> +
> + info = IEEE80211_SKB_CB(skb);
> + tx_data = rtw_usb_get_tx_data(skb);
> +
> + /* enqueue to wait for tx report */
> + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
> + rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn);
> + continue;
> + }
> +
> + /* always ACK for others, then they won't be marked as drop */
> + ieee80211_tx_info_clear_status(info);
> + if (info->flags & IEEE80211_TX_CTL_NO_ACK)
> + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
> + else
> + info->flags |= IEEE80211_TX_STAT_ACK;
> +
> + ieee80211_tx_status_irqsafe(hw, skb);
> + }
> +
> + kfree(txcb);
> +}
> +

[...]

> +
> +static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list)
> +{
> + struct rtw_dev *rtwdev = rtwusb->rtwdev;
> + struct rtw_usb_txcb *txcb;
> + struct sk_buff *skb_head;
> + struct sk_buff *skb_iter;
> + u8 *data_ptr;
> + int agg_num = 0;
> + unsigned int align_next = 0;
> +
> + if (skb_queue_empty(list))
> + return false;
> +
> + txcb = kmalloc(sizeof(*txcb), GFP_ATOMIC);
> + if (!txcb)
> + return false;
> +
> + txcb->rtwdev = rtwdev;
> + skb_queue_head_init(&txcb->tx_ack_queue);
> +
> + skb_iter = skb_dequeue(list);
> +
> + if (skb_queue_empty(list)) {
> + skb_head = skb_iter;
> + goto queue;
> + }
> +
> + skb_head = dev_alloc_skb(RTW_USB_MAX_XMITBUF_SZ);
> + if (!skb_head) {
> + skb_head = skb_iter;
> + goto queue;
> + }
> +
> + data_ptr = skb_head->data;
> +
> + while (skb_iter) {
> + unsigned long flags;
> +
> + memcpy(data_ptr, skb_iter->data, skb_iter->len);
> + skb_put(skb_head, skb_iter->len + align_next);

skb_put(skb_head, align_next);
skb_put_data(skb_head, skb_iter->data, skb_iter->len);

Then, don't need to maintain 'data_ptr'.

> +
> + align_next = ALIGN(skb_iter->len, 8) - skb_iter->len;
> + data_ptr += skb_iter->len + align_next;
> +
> + agg_num++;
> +
> + skb_queue_tail(&txcb->tx_ack_queue, skb_iter);
> +
> + spin_lock_irqsave(&list->lock, flags);
> +
> + skb_iter = skb_peek(list);
> +
> + if (skb_iter && skb_iter->len + skb_head->len <= RTW_USB_MAX_XMITBUF_SZ)
> + __skb_unlink(skb_iter, list);
> + else
> + skb_iter = NULL;
> + spin_unlock_irqrestore(&list->lock, flags);
> + }
> +
> + if (agg_num > 1)
> + rtw_usb_fill_tx_checksum(rtwusb, skb_head, agg_num);
> +
> +queue:
> + skb_queue_tail(&txcb->tx_ack_queue, skb_head);
> +
> + rtw_usb_write_port(rtwdev, GET_TX_DESC_QSEL(skb_head->data), skb_head,
> + rtw_usb_write_port_tx_complete, txcb);
> +
> + return true;
> +}
> +

[...]

> +
> +static void rtw_usb_rx_resubmit(struct rtw_usb *rtwusb, struct rx_usb_ctrl_block *rxcb)
> +{
> + struct rtw_dev *rtwdev = rtwusb->rtwdev;
> + int error;
> +
> + rxcb->rx_skb = alloc_skb(RTW_USB_MAX_RECVBUF_SZ, GFP_ATOMIC);
> + if (!rxcb->rx_skb)
> + return;
> +
> + usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev,
> + usb_rcvbulkpipe(rtwusb->udev, rtwusb->pipe_in),
> + rxcb->rx_skb->data, RTW_USB_MAX_RECVBUF_SZ,
> + rtw_usb_read_port_complete, rxcb);
> +
> + error = usb_submit_urb(rxcb->rx_urb, GFP_ATOMIC);
> + if (error) {
> + kfree_skb(rxcb->rx_skb);
> + if (error != -ENODEV)
> + rtw_err(rtwdev, "Err sending rx data urb %d\n",
> + error);

nit: straighten rtw_err()

> + }
> +}
> +

[...]

> diff --git a/drivers/net/wireless/realtek/rtw88/usb.h b/drivers/net/wireless/realtek/rtw88/usb.h
> new file mode 100644
> index 0000000000000..e26f8afb09f29
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/usb.h

[...]

> +
> +static inline struct rtw_usb_tx_data *rtw_usb_get_tx_data(struct sk_buff *skb)
> +{
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +
> + BUILD_BUG_ON(sizeof(struct rtw_usb_tx_data) >
> + sizeof(info->status.status_driver_data));

coding style:

align the open parenthesis

BUILD_BUG_ON(sizeof(struct rtw_usb_tx_data) >
sizeof(info->status.status_driver_data));

> +
> + return (struct rtw_usb_tx_data *)info->status.status_driver_data;
> +}
> +
> +int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id);
> +void rtw_usb_disconnect(struct usb_interface *intf);
> +
> +#endif
> --
> 2.30.2