Thread (27 messages) 27 messages, 3 authors, 2022-11-28

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

From: Ping-Ke Shih <pkshih@realtek.com>
Date: 2022-11-28 02:02:02
Also in: linux-wireless, lkml

quoted hunk ↗ jump to hunk
-----Original Message-----
From: Sascha Hauer <s.hauer@pengutronix.de>
Sent: Tuesday, November 22, 2022 10:52 PM
To: linux-wireless@vger.kernel.org
Cc: Neo Jou <redacted>; Hans Ulli Kroll <redacted>; Ping-Ke Shih <pkshih@realtek.com>;
Yan-Hsuan Chuang [off-list ref]; Kalle Valo [off-list ref]; netdev@vger.kernel.org;
linux-kernel@vger.kernel.org; Martin Blumenstingl [off-list ref];
kernel@pengutronix.de; Johannes Berg [off-list ref]; Alexander Hochbaum [off-list ref];
Da Xue [off-list ref]; Bernie Huang [off-list ref]; Viktor Petrenko [off-list ref];
Sascha Hauer [off-list ref]; neo_jou [off-list ref]
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 <redacted>
Signed-off-by: Hans Ulli Kroll <redacted>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---

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.


[...]
quoted hunk ↗ jump to hunk
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()
+	}
+}
+
[...]
quoted hunk ↗ jump to hunk
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
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help