Package: linux / 4.19.37-3

features/all/drivers-media-dvb-usb-af9005-request_firmware.patch Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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(-)

--- a/drivers/media/usb/dvb-usb/Kconfig
+++ b/drivers/media/usb/dvb-usb/Kconfig
@@ -246,10 +246,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)
--- a/drivers/media/usb/dvb-usb/af9005-fe.c
+++ b/drivers/media/usb/dvb-usb/af9005-fe.c
@@ -18,10 +18,26 @@
  * see Documentation/media/dvb-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;
@@ -813,6 +829,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];
@@ -965,37 +983,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");
@@ -1075,6 +1111,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)