From 9d5d96f3b88287511a8db4b0899a0e85088d9842 Mon Sep 17 00:00:00 2001
From: Dean Ramsier <dean.ramsier@lairdconnect.com>
Date: Fri, 17 Mar 2023 16:59:04 -0400
Subject: [PATCH 284/296] brcmfmac: Add CLM support to USB interface

Upstream change to generic firmware blob implementation did not
include USB.

Signed-off-by: Dean Ramsier <dean.ramsier@lairdconnect.com>
---
 .../broadcom/brcm80211/brcmfmac/firmware.c    | 48 +++++++++-
 .../broadcom/brcm80211/brcmfmac/firmware.h    |  9 ++
 .../broadcom/brcm80211/brcmfmac/usb.c         | 91 ++++++++++++++++++-
 3 files changed, 143 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
index 1c13137f2d1a..f35eaf3ea166 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -773,6 +773,50 @@ static bool brcmf_fw_request_is_valid(struct brcmf_fw_request *req)
 	return true;
 }
 
+int brcmf_fw_get_firmware_sync(struct device *dev, struct brcmf_fw_request *req,
+			       void (*fw_cb)(struct device *dev, int err,
+					     struct brcmf_fw_request *req))
+{
+	struct brcmf_fw_item *first = &req->items[0];
+	struct brcmf_fw *fwctx;
+	char *alt_path = NULL;
+	const struct firmware *fw;
+	int ret = -ENOENT;
+
+	brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
+
+	if (!brcmf_fw_request_is_valid(req))
+		return -EINVAL;
+
+	if (req->n_items > 1)
+		return -EINVAL;
+
+	fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
+	if (!fwctx)
+		return -ENOMEM;
+
+	fwctx->dev = dev;
+	fwctx->req = req;
+	fwctx->done = fw_cb;
+
+	/* First try alternative board-specific path if any */
+	if (fwctx->req->board_types[0])
+		alt_path = brcm_alt_fw_path(first->path,
+					    fwctx->req->board_types[0]);
+	if (alt_path) {
+		/* Do not fallback to user-mode helper if file does not exist */
+		ret = request_firmware_direct(&fw, alt_path, fwctx->dev);
+		kfree(alt_path);
+	}
+
+	if (ret == -ENOENT)
+		ret = request_firmware(&fw, first->path, fwctx->dev);
+
+	brcmf_fw_request_done(fw, fwctx);
+
+	return ret;
+}
+
 int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
 			   void (*fw_cb)(struct device *dev, int err,
 					 struct brcmf_fw_request *req))
@@ -804,8 +848,8 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
 	if (alt_path) {
 		fwctx->board_index++;
 		ret = request_firmware_nowait(THIS_MODULE, true, alt_path,
-					      fwctx->dev, GFP_KERNEL, fwctx,
-					      brcmf_fw_request_done_alt_path);
+						fwctx->dev, GFP_KERNEL, fwctx,
+						brcmf_fw_request_done_alt_path);
 		kfree(alt_path);
 	} else {
 		ret = request_firmware_nowait(THIS_MODULE, true, first->path,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
index 2a74e4b00347..f1cc6296bea8 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
@@ -111,4 +111,13 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
 			   void (*fw_cb)(struct device *dev, int err,
 					 struct brcmf_fw_request *req));
 
+/**
+ * Request single firmware synchronously.
+ * Callback is called on a valid request
+ * whether it succeeds or not.
+ */
+int brcmf_fw_get_firmware_sync(struct device *dev, struct brcmf_fw_request *req,
+			       void (*fw_cb)(struct device *dev, int err,
+					     struct brcmf_fw_request *req));
+
 #endif /* BRCMFMAC_FIRMWARE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index bb2d4d902f57..8251f1c4fe0c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -20,6 +20,7 @@
 #include "common.h"
 #include "bcdc.h"
 #include "cfg80211.h"
+#include "fwil.h"
 
 
 #define IOCTL_RESP_TIMEOUT		msecs_to_jiffies(2000)
@@ -148,6 +149,9 @@ struct brcmf_usbdev_info {
 	const u8 *image;	/* buffer for combine fw and nvram */
 	int image_len;
 
+	char clm_name[BRCMF_FW_NAME_LEN];
+	const struct firmware *clm_fw;
+
 	struct usb_device *usbdev;
 	struct device *dev;
 	struct completion dev_init_done;
@@ -168,6 +172,8 @@ struct brcmf_usbdev_info {
 	struct brcmf_mp_device *settings;
 };
 
+static int brcmf_usb_get_clmblob(struct brcmf_usbdev_info *devinfo);
+
 static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
 				struct brcmf_usbreq  *req);
 
@@ -677,6 +683,7 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
 static int brcmf_usb_up(struct device *dev)
 {
 	struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+	int ret;
 
 	brcmf_dbg(USB, "Enter\n");
 	if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
@@ -704,7 +711,11 @@ static int brcmf_usb_up(struct device *dev)
 		devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum);
 	}
 	brcmf_usb_rx_fill_all(devinfo);
-	return 0;
+
+	/* CLM blob needed after firmware download and re-attach has occurred */
+	ret = brcmf_usb_get_clmblob(devinfo);
+
+	return ret;
 }
 
 static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo)
@@ -1162,8 +1173,21 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
 static int brcmf_usb_get_blob(struct device *dev, const struct firmware **fw,
 			      enum brcmf_blob_type type)
 {
-	/* No blobs for USB devices... */
-	return -ENOENT;
+	struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+	switch (type) {
+	case BRCMF_BLOB_CLM:
+		*fw = devinfo->clm_fw;
+		devinfo->clm_fw = NULL;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (!*fw)
+		return -ENOENT;
+
+	return 0;
 }
 
 static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
@@ -1177,6 +1201,9 @@ static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
 
 #define BRCMF_USB_FW_CODE	0
 
+/* Note - CLM blob file obtained separately from FW so also uses index 0 */
+#define BRCMF_USB_FW_CLM	0
+
 static void brcmf_usb_probe_phase2(struct device *dev, int ret,
 				   struct brcmf_fw_request *fwreq)
 {
@@ -1560,12 +1587,70 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
 	return ret;
 }
 
+
+static void brcmf_usb_get_clmblob_complete(struct device *dev, int ret,
+					   struct brcmf_fw_request *fwreq)
+{
+	struct brcmf_bus *bus = dev_get_drvdata(dev);
+	struct brcmf_usbdev_info *devinfo = bus->bus_priv.usb->devinfo;
+
+	brcmf_dbg(USB, "Enter\n");
+
+	if (fwreq) {
+		devinfo->clm_fw = fwreq->items[BRCMF_USB_FW_CLM].binary;
+		kfree(fwreq);
+	}
+}
+
+static int brcmf_usb_get_clmblob(struct brcmf_usbdev_info *devinfo)
+{
+	struct brcmf_fw_request *fwreq;
+	struct brcmf_fw_name fwnames[] = {
+		{ ".clm_blob", devinfo->clm_name },
+	};
+	struct brcmf_bus *bus_if;
+	struct brcmf_if *ifp;
+	struct brcmf_rev_info_le revinfo;
+	int ret = -1;
+
+	brcmf_dbg(USB, "Enter\n");
+
+	bus_if = dev_get_drvdata(devinfo->dev);
+	if (bus_if && bus_if->drvr) {
+		ifp = bus_if->drvr->iflist[0];
+		ret = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO,
+					     &revinfo, sizeof(revinfo));
+	}
+
+	if (ret) {
+		brcmf_err("Unable to get version for CLM blob request!");
+		goto error_return;
+	}
+
+	fwreq = brcmf_fw_alloc_request(le32_to_cpu(revinfo.chipnum),
+				       le32_to_cpu(revinfo.chiprev),
+				       brcmf_usb_fwnames,
+				       ARRAY_SIZE(brcmf_usb_fwnames),
+				       fwnames, ARRAY_SIZE(fwnames));
+	if (!fwreq)
+		return -ENOMEM;
+
+	fwreq->items[BRCMF_USB_FW_CLM].type = BRCMF_FW_TYPE_BINARY;
+	fwreq->items[BRCMF_USB_FW_CLM].flags = BRCMF_FW_REQF_OPTIONAL;
+
+	ret = brcmf_fw_get_firmware_sync(devinfo->dev, fwreq, brcmf_usb_get_clmblob_complete);
+
+error_return:
+	return ret;
+}
+
 #define BRCMF_USB_DEVICE(dev_id) \
 	{ \
 		USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id), \
 		.driver_info = BRCMF_FWVENDOR_WCC \
 	}
 
+
 #define LINKSYS_USB_DEVICE(dev_id) \
 	{ \
 		USB_DEVICE(BRCM_USB_VENDOR_ID_LINKSYS, dev_id), \
-- 
2.25.1

