From 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Sat, 27 Apr 2024 12:05:51 +0200
Subject: Adding upstream version 5.10.209.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 drivers/media/usb/as102/as102_fw.c | 226 +++++++++++++++++++++++++++++++++++++
 1 file changed, 226 insertions(+)
 create mode 100644 drivers/media/usb/as102/as102_fw.c

(limited to 'drivers/media/usb/as102/as102_fw.c')

diff --git a/drivers/media/usb/as102/as102_fw.c b/drivers/media/usb/as102/as102_fw.c
new file mode 100644
index 000000000..514764247
--- /dev/null
+++ b/drivers/media/usb/as102/as102_fw.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Abilis Systems Single DVB-T Receiver
+ * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+
+#include "as102_drv.h"
+#include "as102_fw.h"
+
+static const char as102_st_fw1[] = "as102_data1_st.hex";
+static const char as102_st_fw2[] = "as102_data2_st.hex";
+static const char as102_dt_fw1[] = "as102_data1_dt.hex";
+static const char as102_dt_fw2[] = "as102_data2_dt.hex";
+
+static unsigned char atohx(unsigned char *dst, char *src)
+{
+	unsigned char value = 0;
+
+	char msb = tolower(*src) - '0';
+	char lsb = tolower(*(src + 1)) - '0';
+
+	if (msb > 9)
+		msb -= 7;
+	if (lsb > 9)
+		lsb -= 7;
+
+	*dst = value = ((msb & 0xF) << 4) | (lsb & 0xF);
+	return value;
+}
+
+/*
+ * Parse INTEL HEX firmware file to extract address and data.
+ */
+static int parse_hex_line(unsigned char *fw_data, unsigned char *addr,
+			  unsigned char *data, int *dataLength,
+			  unsigned char *addr_has_changed) {
+
+	int count = 0;
+	unsigned char *src, dst;
+
+	if (*fw_data++ != ':') {
+		pr_err("invalid firmware file\n");
+		return -EFAULT;
+	}
+
+	/* locate end of line */
+	for (src = fw_data; *src != '\n'; src += 2) {
+		atohx(&dst, src);
+		/* parse line to split addr / data */
+		switch (count) {
+		case 0:
+			*dataLength = dst;
+			break;
+		case 1:
+			addr[2] = dst;
+			break;
+		case 2:
+			addr[3] = dst;
+			break;
+		case 3:
+			/* check if data is an address */
+			if (dst == 0x04)
+				*addr_has_changed = 1;
+			else
+				*addr_has_changed = 0;
+			break;
+		case  4:
+		case  5:
+			if (*addr_has_changed)
+				addr[(count - 4)] = dst;
+			else
+				data[(count - 4)] = dst;
+			break;
+		default:
+			data[(count - 4)] = dst;
+			break;
+		}
+		count++;
+	}
+
+	/* return read value + ':' + '\n' */
+	return (count * 2) + 2;
+}
+
+static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap,
+				 unsigned char *cmd,
+				 const struct firmware *firmware) {
+
+	struct as10x_fw_pkt_t *fw_pkt;
+	int total_read_bytes = 0, errno = 0;
+	unsigned char addr_has_changed = 0;
+
+	fw_pkt = kmalloc(sizeof(*fw_pkt), GFP_KERNEL);
+	if (!fw_pkt)
+		return -ENOMEM;
+
+
+	for (total_read_bytes = 0; total_read_bytes < firmware->size; ) {
+		int read_bytes = 0, data_len = 0;
+
+		/* parse intel hex line */
+		read_bytes = parse_hex_line(
+				(u8 *) (firmware->data + total_read_bytes),
+				fw_pkt->raw.address,
+				fw_pkt->raw.data,
+				&data_len,
+				&addr_has_changed);
+
+		if (read_bytes <= 0)
+			goto error;
+
+		/* detect the end of file */
+		total_read_bytes += read_bytes;
+		if (total_read_bytes == firmware->size) {
+			fw_pkt->u.request[0] = 0x00;
+			fw_pkt->u.request[1] = 0x03;
+
+			/* send EOF command */
+			errno = bus_adap->ops->upload_fw_pkt(bus_adap,
+							     (uint8_t *)
+							     fw_pkt, 2, 0);
+			if (errno < 0)
+				goto error;
+		} else {
+			if (!addr_has_changed) {
+				/* prepare command to send */
+				fw_pkt->u.request[0] = 0x00;
+				fw_pkt->u.request[1] = 0x01;
+
+				data_len += sizeof(fw_pkt->u.request);
+				data_len += sizeof(fw_pkt->raw.address);
+
+				/* send cmd to device */
+				errno = bus_adap->ops->upload_fw_pkt(bus_adap,
+								     (uint8_t *)
+								     fw_pkt,
+								     data_len,
+								     0);
+				if (errno < 0)
+					goto error;
+			}
+		}
+	}
+error:
+	kfree(fw_pkt);
+	return (errno == 0) ? total_read_bytes : errno;
+}
+
+int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap)
+{
+	int errno = -EFAULT;
+	const struct firmware *firmware = NULL;
+	unsigned char *cmd_buf = NULL;
+	const char *fw1, *fw2;
+	struct usb_device *dev = bus_adap->usb_dev;
+
+	/* select fw file to upload */
+	if (dual_tuner) {
+		fw1 = as102_dt_fw1;
+		fw2 = as102_dt_fw2;
+	} else {
+		fw1 = as102_st_fw1;
+		fw2 = as102_st_fw2;
+	}
+
+	/* allocate buffer to store firmware upload command and data */
+	cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
+	if (cmd_buf == NULL) {
+		errno = -ENOMEM;
+		goto error;
+	}
+
+	/* request kernel to locate firmware file: part1 */
+	errno = request_firmware(&firmware, fw1, &dev->dev);
+	if (errno < 0) {
+		pr_err("%s: unable to locate firmware file: %s\n",
+		       DRIVER_NAME, fw1);
+		goto error;
+	}
+
+	/* initiate firmware upload */
+	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
+	if (errno < 0) {
+		pr_err("%s: error during firmware upload part1\n",
+		       DRIVER_NAME);
+		goto error;
+	}
+
+	pr_info("%s: firmware: %s loaded with success\n",
+		DRIVER_NAME, fw1);
+	release_firmware(firmware);
+	firmware = NULL;
+
+	/* wait for boot to complete */
+	mdelay(100);
+
+	/* request kernel to locate firmware file: part2 */
+	errno = request_firmware(&firmware, fw2, &dev->dev);
+	if (errno < 0) {
+		pr_err("%s: unable to locate firmware file: %s\n",
+		       DRIVER_NAME, fw2);
+		goto error;
+	}
+
+	/* initiate firmware upload */
+	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
+	if (errno < 0) {
+		pr_err("%s: error during firmware upload part2\n",
+		       DRIVER_NAME);
+		goto error;
+	}
+
+	pr_info("%s: firmware: %s loaded with success\n",
+		DRIVER_NAME, fw2);
+error:
+	kfree(cmd_buf);
+	release_firmware(firmware);
+
+	return errno;
+}
-- 
cgit v1.2.3