From: Ben Hutchings <ben@decadent.org.uk>
Date: Mon, 24 Aug 2009 23:19:58 +0100
Subject: af9005: Use request_firmware() to load register init script
Forwarded: no

Read the register init script from the Windows driver.  This is sick
but should avoid the potential copyright infringement in distributing
a version of the script which is directly derived from the driver.
---
 drivers/media/dvb/dvb-usb/Kconfig     |    2 +-
 drivers/media/dvb/dvb-usb/af9005-fe.c |   66 ++++++++++++++++++++++++++------
 2 files changed, 54 insertions(+), 14 deletions(-)

Index: debian-kernel/drivers/media/usb/dvb-usb/Kconfig
===================================================================
--- debian-kernel.orig/drivers/media/usb/dvb-usb/Kconfig
+++ debian-kernel/drivers/media/usb/dvb-usb/Kconfig
@@ -260,10 +260,10 @@ config DVB_USB_OPERA1
 
 config DVB_USB_AF9005
 	tristate "Afatech AF9005 DVB-T USB1.1 support"
-	depends on BROKEN
 	depends on DVB_USB
 	select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
 	select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
+	select FW_LOADER
 	help
 	  Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
 	  and the TerraTec Cinergy T USB XE (Rev.1)
Index: debian-kernel/drivers/media/usb/dvb-usb/af9005-fe.c
===================================================================
--- debian-kernel.orig/drivers/media/usb/dvb-usb/af9005-fe.c
+++ debian-kernel/drivers/media/usb/dvb-usb/af9005-fe.c
@@ -9,10 +9,26 @@
  * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
  */
 #include "af9005.h"
-#include "af9005-script.h"
 #include "mt2060.h"
 #include "qt1010.h"
 #include <asm/div64.h>
+#include <linux/firmware.h>
+
+/* Register initialisation script to be extracted from the Windows driver */
+
+typedef struct {
+	__le16 reg;
+	u8 pos;
+	u8 len;
+	u8 val;
+	u8 pad;
+} __packed RegDesc;
+
+#define WIN_DRV_NAME		"AF05BDA.sys"
+#define WIN_DRV_VERSION		"6.3.2.1"
+#define WIN_DRV_SIZE		133504
+#define WIN_DRV_SCRIPT_OFFSET	88316
+#define WIN_DRV_SCRIPT_SIZE	1110
 
 struct af9005_fe_state {
 	struct dvb_usb_device *d;
@@ -804,6 +820,8 @@ static int af9005_fe_init(struct dvb_fro
 {
 	struct af9005_fe_state *state = fe->demodulator_priv;
 	struct dvb_usb_adapter *adap = fe->dvb->priv;
+	const struct firmware *fw;
+	const RegDesc *script;
 	int ret, i, scriptlen;
 	u8 temp, temp0 = 0, temp1 = 0, temp2 = 0;
 	u8 buf[2];
@@ -956,37 +974,55 @@ static int af9005_fe_init(struct dvb_fro
 	if ((ret = af9005_write_ofdm_register(state->d, 0xaefb, 0x01)))
 		return ret;
 
-	/* load init script */
-	deb_info("load init script\n");
-	scriptlen = sizeof(script) / sizeof(RegDesc);
+	/* load and validate init script */
+	deb_info("load init script from Windows driver\n");
+	ret = request_firmware(&fw, WIN_DRV_NAME, &state->d->udev->dev);
+	if (ret)
+		return ret;
+	BUILD_BUG_ON(sizeof(RegDesc) != 6);
+	if (fw->size != WIN_DRV_SIZE ||
+	    memcmp(fw->data + WIN_DRV_SCRIPT_OFFSET,
+		   "\x80\xa1\x00\x08\x0a\x00", 6) ||
+	    memcmp(fw->data + WIN_DRV_SCRIPT_OFFSET + WIN_DRV_SCRIPT_SIZE - 6,
+		   "\x49\xa3\x00\x06\x02\x00", 6)) {
+		err("%s is invalid - should be version %s, size %u bytes\n",
+		    WIN_DRV_NAME, WIN_DRV_VERSION, WIN_DRV_SIZE);
+		ret = -EINVAL;
+		goto fail_release;
+	}
+
+	script = (const RegDesc *)(fw->data + WIN_DRV_SCRIPT_OFFSET);
+	scriptlen = WIN_DRV_SCRIPT_SIZE / sizeof(RegDesc);
 	for (i = 0; i < scriptlen; i++) {
+		u16 reg = le16_to_cpu(script[i].reg);
 		if ((ret =
-		     af9005_write_register_bits(state->d, script[i].reg,
+		     af9005_write_register_bits(state->d, reg,
 						script[i].pos,
 						script[i].len, script[i].val)))
-			return ret;
+			goto fail_release;
 		/* save 3 bytes of original fcw */
-		if (script[i].reg == 0xae18)
+		if (reg == 0xae18)
 			temp2 = script[i].val;
-		if (script[i].reg == 0xae19)
+		if (reg == 0xae19)
 			temp1 = script[i].val;
-		if (script[i].reg == 0xae1a)
+		if (reg == 0xae1a)
 			temp0 = script[i].val;
 
 		/* save original unplug threshold */
-		if (script[i].reg == xd_p_reg_unplug_th)
+		if (reg == xd_p_reg_unplug_th)
 			state->original_if_unplug_th = script[i].val;
-		if (script[i].reg == xd_p_reg_unplug_rf_gain_th)
+		if (reg == xd_p_reg_unplug_rf_gain_th)
 			state->original_rf_unplug_th = script[i].val;
-		if (script[i].reg == xd_p_reg_unplug_dtop_if_gain_th)
+		if (reg == xd_p_reg_unplug_dtop_if_gain_th)
 			state->original_dtop_if_unplug_th = script[i].val;
-		if (script[i].reg == xd_p_reg_unplug_dtop_rf_gain_th)
+		if (reg == xd_p_reg_unplug_dtop_rf_gain_th)
 			state->original_dtop_rf_unplug_th = script[i].val;
 
 	}
 	state->original_fcw =
 	    ((u32) temp2 << 16) + ((u32) temp1 << 8) + (u32) temp0;
 
+	release_firmware(fw);
 
 	/* save original TOPs */
 	deb_info("save original TOPs\n");
@@ -1066,6 +1102,10 @@ static int af9005_fe_init(struct dvb_fro
 
 	deb_info("profit!\n");
 	return 0;
+
+fail_release:
+	release_firmware(fw);
+	return ret;
 }
 
 static int af9005_fe_sleep(struct dvb_frontend *fe)
