diff options
Diffstat (limited to 'drivers/media/dvb/firewire/avc.c')
-rw-r--r-- | drivers/media/dvb/firewire/avc.c | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/drivers/media/dvb/firewire/avc.c b/drivers/media/dvb/firewire/avc.c new file mode 100644 index 000000000000..847a537b1f58 --- /dev/null +++ b/drivers/media/dvb/firewire/avc.c | |||
@@ -0,0 +1,1051 @@ | |||
1 | /* | ||
2 | * FireDTV driver (formerly known as FireSAT) | ||
3 | * | ||
4 | * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> | ||
5 | * Copyright (C) 2008 Ben Backx <ben@bbackx.com> | ||
6 | * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation; either version 2 of | ||
11 | * the License, or (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/bug.h> | ||
15 | #include <linux/crc32.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/moduleparam.h> | ||
20 | #include <linux/mutex.h> | ||
21 | #include <linux/string.h> | ||
22 | #include <linux/wait.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | |||
25 | #include <ieee1394_transactions.h> | ||
26 | #include <nodemgr.h> | ||
27 | |||
28 | #include "avc.h" | ||
29 | #include "firedtv.h" | ||
30 | #include "firedtv-rc.h" | ||
31 | |||
32 | #define FCP_COMMAND_REGISTER 0xfffff0000b00ULL | ||
33 | |||
34 | static int __avc_write(struct firedtv *fdtv, | ||
35 | const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) | ||
36 | { | ||
37 | int err, retry; | ||
38 | |||
39 | if (RspFrm) | ||
40 | fdtv->avc_reply_received = false; | ||
41 | |||
42 | for (retry = 0; retry < 6; retry++) { | ||
43 | err = hpsb_node_write(fdtv->ud->ne, FCP_COMMAND_REGISTER, | ||
44 | (quadlet_t *)CmdFrm, CmdFrm->length); | ||
45 | if (err) { | ||
46 | fdtv->avc_reply_received = true; | ||
47 | dev_err(&fdtv->ud->device, | ||
48 | "FCP command write failed\n"); | ||
49 | return err; | ||
50 | } | ||
51 | |||
52 | if (!RspFrm) | ||
53 | return 0; | ||
54 | |||
55 | /* | ||
56 | * AV/C specs say that answers should be sent within 150 ms. | ||
57 | * Time out after 200 ms. | ||
58 | */ | ||
59 | if (wait_event_timeout(fdtv->avc_wait, | ||
60 | fdtv->avc_reply_received, | ||
61 | HZ / 5) != 0) { | ||
62 | memcpy(RspFrm, fdtv->respfrm, fdtv->resp_length); | ||
63 | RspFrm->length = fdtv->resp_length; | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | } | ||
68 | dev_err(&fdtv->ud->device, "FCP response timed out\n"); | ||
69 | return -ETIMEDOUT; | ||
70 | } | ||
71 | |||
72 | static int avc_write(struct firedtv *fdtv, | ||
73 | const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) | ||
74 | { | ||
75 | int ret; | ||
76 | |||
77 | if (mutex_lock_interruptible(&fdtv->avc_mutex)) | ||
78 | return -EINTR; | ||
79 | |||
80 | ret = __avc_write(fdtv, CmdFrm, RspFrm); | ||
81 | |||
82 | mutex_unlock(&fdtv->avc_mutex); | ||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | int avc_recv(struct firedtv *fdtv, u8 *data, size_t length) | ||
87 | { | ||
88 | AVCRspFrm *RspFrm = (AVCRspFrm *)data; | ||
89 | |||
90 | if (length >= 8 && | ||
91 | RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && | ||
92 | RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && | ||
93 | RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && | ||
94 | RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { | ||
95 | if (RspFrm->resp == CHANGED) { | ||
96 | fdtv_handle_rc(fdtv, | ||
97 | RspFrm->operand[4] << 8 | RspFrm->operand[5]); | ||
98 | schedule_work(&fdtv->remote_ctrl_work); | ||
99 | } else if (RspFrm->resp != INTERIM) { | ||
100 | dev_info(&fdtv->ud->device, | ||
101 | "remote control result = %d\n", RspFrm->resp); | ||
102 | } | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | if (fdtv->avc_reply_received) { | ||
107 | dev_err(&fdtv->ud->device, | ||
108 | "received out-of-order AVC response, ignored\n"); | ||
109 | return -EIO; | ||
110 | } | ||
111 | |||
112 | memcpy(fdtv->respfrm, data, length); | ||
113 | fdtv->resp_length = length; | ||
114 | |||
115 | fdtv->avc_reply_received = true; | ||
116 | wake_up(&fdtv->avc_wait); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * tuning command for setting the relative LNB frequency | ||
123 | * (not supported by the AVC standard) | ||
124 | */ | ||
125 | static void avc_tuner_tuneqpsk(struct firedtv *fdtv, | ||
126 | struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) | ||
127 | { | ||
128 | CmdFrm->opcode = VENDOR; | ||
129 | |||
130 | CmdFrm->operand[0] = SFE_VENDOR_DE_COMPANYID_0; | ||
131 | CmdFrm->operand[1] = SFE_VENDOR_DE_COMPANYID_1; | ||
132 | CmdFrm->operand[2] = SFE_VENDOR_DE_COMPANYID_2; | ||
133 | CmdFrm->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; | ||
134 | |||
135 | CmdFrm->operand[4] = (params->frequency >> 24) & 0xff; | ||
136 | CmdFrm->operand[5] = (params->frequency >> 16) & 0xff; | ||
137 | CmdFrm->operand[6] = (params->frequency >> 8) & 0xff; | ||
138 | CmdFrm->operand[7] = params->frequency & 0xff; | ||
139 | |||
140 | CmdFrm->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; | ||
141 | CmdFrm->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; | ||
142 | |||
143 | switch(params->u.qpsk.fec_inner) { | ||
144 | case FEC_1_2: | ||
145 | CmdFrm->operand[10] = 0x1; break; | ||
146 | case FEC_2_3: | ||
147 | CmdFrm->operand[10] = 0x2; break; | ||
148 | case FEC_3_4: | ||
149 | CmdFrm->operand[10] = 0x3; break; | ||
150 | case FEC_5_6: | ||
151 | CmdFrm->operand[10] = 0x4; break; | ||
152 | case FEC_7_8: | ||
153 | CmdFrm->operand[10] = 0x5; break; | ||
154 | case FEC_4_5: | ||
155 | case FEC_8_9: | ||
156 | case FEC_AUTO: | ||
157 | default: | ||
158 | CmdFrm->operand[10] = 0x0; | ||
159 | } | ||
160 | |||
161 | if (fdtv->voltage == 0xff) | ||
162 | CmdFrm->operand[11] = 0xff; | ||
163 | else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */ | ||
164 | CmdFrm->operand[11] = 0; | ||
165 | else | ||
166 | CmdFrm->operand[11] = 1; | ||
167 | |||
168 | if (fdtv->tone == 0xff) | ||
169 | CmdFrm->operand[12] = 0xff; | ||
170 | else if (fdtv->tone == SEC_TONE_ON) /* band */ | ||
171 | CmdFrm->operand[12] = 1; | ||
172 | else | ||
173 | CmdFrm->operand[12] = 0; | ||
174 | |||
175 | if (fdtv->type == FIREDTV_DVB_S2) { | ||
176 | CmdFrm->operand[13] = 0x1; | ||
177 | CmdFrm->operand[14] = 0xff; | ||
178 | CmdFrm->operand[15] = 0xff; | ||
179 | CmdFrm->length = 20; | ||
180 | } else { | ||
181 | CmdFrm->length = 16; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, | ||
186 | AVCCmdFrm *CmdFrm) | ||
187 | { | ||
188 | M_VALID_FLAGS flags; | ||
189 | |||
190 | flags.Bits.Modulation = params->u.qam.modulation != QAM_AUTO; | ||
191 | flags.Bits.FEC_inner = params->u.qam.fec_inner != FEC_AUTO; | ||
192 | flags.Bits.FEC_outer = 0; | ||
193 | flags.Bits.Symbol_Rate = 1; | ||
194 | flags.Bits.Frequency = 1; | ||
195 | flags.Bits.Orbital_Pos = 0; | ||
196 | flags.Bits.Polarisation = 0; | ||
197 | flags.Bits.reserved_fields = 0; | ||
198 | flags.Bits.reserved1 = 0; | ||
199 | flags.Bits.Network_ID = 0; | ||
200 | |||
201 | CmdFrm->opcode = DSD; | ||
202 | |||
203 | CmdFrm->operand[0] = 0; /* source plug */ | ||
204 | CmdFrm->operand[1] = 0xd2; /* subfunction replace */ | ||
205 | CmdFrm->operand[2] = 0x20; /* system id = DVB */ | ||
206 | CmdFrm->operand[3] = 0x00; /* antenna number */ | ||
207 | /* system_specific_multiplex selection_length */ | ||
208 | CmdFrm->operand[4] = 0x11; | ||
209 | CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ | ||
210 | CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ | ||
211 | CmdFrm->operand[7] = 0x00; | ||
212 | CmdFrm->operand[8] = 0x00; | ||
213 | CmdFrm->operand[9] = 0x00; | ||
214 | CmdFrm->operand[10] = 0x00; | ||
215 | |||
216 | CmdFrm->operand[11] = | ||
217 | (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6); | ||
218 | CmdFrm->operand[12] = | ||
219 | ((params->frequency / 4000) >> 8) & 0xff; | ||
220 | CmdFrm->operand[13] = (params->frequency / 4000) & 0xff; | ||
221 | CmdFrm->operand[14] = | ||
222 | ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff; | ||
223 | CmdFrm->operand[15] = | ||
224 | ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff; | ||
225 | CmdFrm->operand[16] = | ||
226 | ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0; | ||
227 | CmdFrm->operand[17] = 0x00; | ||
228 | |||
229 | switch (params->u.qpsk.fec_inner) { | ||
230 | case FEC_1_2: | ||
231 | CmdFrm->operand[18] = 0x1; break; | ||
232 | case FEC_2_3: | ||
233 | CmdFrm->operand[18] = 0x2; break; | ||
234 | case FEC_3_4: | ||
235 | CmdFrm->operand[18] = 0x3; break; | ||
236 | case FEC_5_6: | ||
237 | CmdFrm->operand[18] = 0x4; break; | ||
238 | case FEC_7_8: | ||
239 | CmdFrm->operand[18] = 0x5; break; | ||
240 | case FEC_8_9: | ||
241 | CmdFrm->operand[18] = 0x6; break; | ||
242 | case FEC_4_5: | ||
243 | CmdFrm->operand[18] = 0x8; break; | ||
244 | case FEC_AUTO: | ||
245 | default: | ||
246 | CmdFrm->operand[18] = 0x0; | ||
247 | } | ||
248 | switch (params->u.qam.modulation) { | ||
249 | case QAM_16: | ||
250 | CmdFrm->operand[19] = 0x08; break; | ||
251 | case QAM_32: | ||
252 | CmdFrm->operand[19] = 0x10; break; | ||
253 | case QAM_64: | ||
254 | CmdFrm->operand[19] = 0x18; break; | ||
255 | case QAM_128: | ||
256 | CmdFrm->operand[19] = 0x20; break; | ||
257 | case QAM_256: | ||
258 | CmdFrm->operand[19] = 0x28; break; | ||
259 | case QAM_AUTO: | ||
260 | default: | ||
261 | CmdFrm->operand[19] = 0x00; | ||
262 | } | ||
263 | CmdFrm->operand[20] = 0x00; | ||
264 | CmdFrm->operand[21] = 0x00; | ||
265 | /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ | ||
266 | CmdFrm->operand[22] = 0x00; | ||
267 | |||
268 | CmdFrm->length = 28; | ||
269 | } | ||
270 | |||
271 | static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, | ||
272 | AVCCmdFrm *CmdFrm) | ||
273 | { | ||
274 | M_VALID_FLAGS flags; | ||
275 | |||
276 | flags.Bits_T.GuardInterval = | ||
277 | params->u.ofdm.guard_interval != GUARD_INTERVAL_AUTO; | ||
278 | flags.Bits_T.CodeRateLPStream = | ||
279 | params->u.ofdm.code_rate_LP != FEC_AUTO; | ||
280 | flags.Bits_T.CodeRateHPStream = | ||
281 | params->u.ofdm.code_rate_HP != FEC_AUTO; | ||
282 | flags.Bits_T.HierarchyInfo = | ||
283 | params->u.ofdm.hierarchy_information != HIERARCHY_AUTO; | ||
284 | flags.Bits_T.Constellation = | ||
285 | params->u.ofdm.constellation != QAM_AUTO; | ||
286 | flags.Bits_T.Bandwidth = | ||
287 | params->u.ofdm.bandwidth != BANDWIDTH_AUTO; | ||
288 | flags.Bits_T.CenterFrequency = 1; | ||
289 | flags.Bits_T.reserved1 = 0; | ||
290 | flags.Bits_T.reserved2 = 0; | ||
291 | flags.Bits_T.OtherFrequencyFlag = 0; | ||
292 | flags.Bits_T.TransmissionMode = | ||
293 | params->u.ofdm.transmission_mode != TRANSMISSION_MODE_AUTO; | ||
294 | flags.Bits_T.NetworkId = 0; | ||
295 | |||
296 | CmdFrm->opcode = DSD; | ||
297 | |||
298 | CmdFrm->operand[0] = 0; /* source plug */ | ||
299 | CmdFrm->operand[1] = 0xd2; /* subfunction replace */ | ||
300 | CmdFrm->operand[2] = 0x20; /* system id = DVB */ | ||
301 | CmdFrm->operand[3] = 0x00; /* antenna number */ | ||
302 | /* system_specific_multiplex selection_length */ | ||
303 | CmdFrm->operand[4] = 0x0c; | ||
304 | CmdFrm->operand[5] = flags.Valid_Word.ByteHi; /* valid_flags [0] */ | ||
305 | CmdFrm->operand[6] = flags.Valid_Word.ByteLo; /* valid_flags [1] */ | ||
306 | CmdFrm->operand[7] = 0x0; | ||
307 | CmdFrm->operand[8] = (params->frequency / 10) >> 24; | ||
308 | CmdFrm->operand[9] = ((params->frequency / 10) >> 16) & 0xff; | ||
309 | CmdFrm->operand[10] = ((params->frequency / 10) >> 8) & 0xff; | ||
310 | CmdFrm->operand[11] = (params->frequency / 10) & 0xff; | ||
311 | |||
312 | switch (params->u.ofdm.bandwidth) { | ||
313 | case BANDWIDTH_7_MHZ: | ||
314 | CmdFrm->operand[12] = 0x20; break; | ||
315 | case BANDWIDTH_8_MHZ: | ||
316 | case BANDWIDTH_6_MHZ: /* not defined by AVC spec */ | ||
317 | case BANDWIDTH_AUTO: | ||
318 | default: | ||
319 | CmdFrm->operand[12] = 0x00; | ||
320 | } | ||
321 | switch (params->u.ofdm.constellation) { | ||
322 | case QAM_16: | ||
323 | CmdFrm->operand[13] = 1 << 6; break; | ||
324 | case QAM_64: | ||
325 | CmdFrm->operand[13] = 2 << 6; break; | ||
326 | case QPSK: | ||
327 | default: | ||
328 | CmdFrm->operand[13] = 0x00; | ||
329 | } | ||
330 | switch (params->u.ofdm.hierarchy_information) { | ||
331 | case HIERARCHY_1: | ||
332 | CmdFrm->operand[13] |= 1 << 3; break; | ||
333 | case HIERARCHY_2: | ||
334 | CmdFrm->operand[13] |= 2 << 3; break; | ||
335 | case HIERARCHY_4: | ||
336 | CmdFrm->operand[13] |= 3 << 3; break; | ||
337 | case HIERARCHY_AUTO: | ||
338 | case HIERARCHY_NONE: | ||
339 | default: | ||
340 | break; | ||
341 | } | ||
342 | switch (params->u.ofdm.code_rate_HP) { | ||
343 | case FEC_2_3: | ||
344 | CmdFrm->operand[13] |= 1; break; | ||
345 | case FEC_3_4: | ||
346 | CmdFrm->operand[13] |= 2; break; | ||
347 | case FEC_5_6: | ||
348 | CmdFrm->operand[13] |= 3; break; | ||
349 | case FEC_7_8: | ||
350 | CmdFrm->operand[13] |= 4; break; | ||
351 | case FEC_1_2: | ||
352 | default: | ||
353 | break; | ||
354 | } | ||
355 | switch (params->u.ofdm.code_rate_LP) { | ||
356 | case FEC_2_3: | ||
357 | CmdFrm->operand[14] = 1 << 5; break; | ||
358 | case FEC_3_4: | ||
359 | CmdFrm->operand[14] = 2 << 5; break; | ||
360 | case FEC_5_6: | ||
361 | CmdFrm->operand[14] = 3 << 5; break; | ||
362 | case FEC_7_8: | ||
363 | CmdFrm->operand[14] = 4 << 5; break; | ||
364 | case FEC_1_2: | ||
365 | default: | ||
366 | CmdFrm->operand[14] = 0x00; break; | ||
367 | } | ||
368 | switch (params->u.ofdm.guard_interval) { | ||
369 | case GUARD_INTERVAL_1_16: | ||
370 | CmdFrm->operand[14] |= 1 << 3; break; | ||
371 | case GUARD_INTERVAL_1_8: | ||
372 | CmdFrm->operand[14] |= 2 << 3; break; | ||
373 | case GUARD_INTERVAL_1_4: | ||
374 | CmdFrm->operand[14] |= 3 << 3; break; | ||
375 | case GUARD_INTERVAL_1_32: | ||
376 | case GUARD_INTERVAL_AUTO: | ||
377 | default: | ||
378 | break; | ||
379 | } | ||
380 | switch (params->u.ofdm.transmission_mode) { | ||
381 | case TRANSMISSION_MODE_8K: | ||
382 | CmdFrm->operand[14] |= 1 << 1; break; | ||
383 | case TRANSMISSION_MODE_2K: | ||
384 | case TRANSMISSION_MODE_AUTO: | ||
385 | default: | ||
386 | break; | ||
387 | } | ||
388 | |||
389 | CmdFrm->operand[15] = 0x00; /* network_ID[0] */ | ||
390 | CmdFrm->operand[16] = 0x00; /* network_ID[1] */ | ||
391 | /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ | ||
392 | CmdFrm->operand[17] = 0x00; | ||
393 | |||
394 | CmdFrm->length = 24; | ||
395 | } | ||
396 | |||
397 | int avc_tuner_dsd(struct firedtv *fdtv, | ||
398 | struct dvb_frontend_parameters *params) | ||
399 | { | ||
400 | AVCCmdFrm CmdFrm; | ||
401 | AVCRspFrm RspFrm; | ||
402 | |||
403 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
404 | |||
405 | CmdFrm.cts = AVC; | ||
406 | CmdFrm.ctype = CONTROL; | ||
407 | CmdFrm.sutyp = 0x5; | ||
408 | CmdFrm.suid = fdtv->subunit; | ||
409 | |||
410 | switch (fdtv->type) { | ||
411 | case FIREDTV_DVB_S: | ||
412 | case FIREDTV_DVB_S2: | ||
413 | avc_tuner_tuneqpsk(fdtv, params, &CmdFrm); break; | ||
414 | case FIREDTV_DVB_C: | ||
415 | avc_tuner_dsd_dvb_c(params, &CmdFrm); break; | ||
416 | case FIREDTV_DVB_T: | ||
417 | avc_tuner_dsd_dvb_t(params, &CmdFrm); break; | ||
418 | default: | ||
419 | BUG(); | ||
420 | } | ||
421 | |||
422 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
423 | return -EIO; | ||
424 | |||
425 | msleep(500); | ||
426 | #if 0 | ||
427 | /* FIXME: */ | ||
428 | /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ | ||
429 | if(status) | ||
430 | *status=RspFrm.operand[2]; | ||
431 | #endif | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) | ||
436 | { | ||
437 | AVCCmdFrm CmdFrm; | ||
438 | AVCRspFrm RspFrm; | ||
439 | int pos, k; | ||
440 | |||
441 | if (pidc > 16 && pidc != 0xff) | ||
442 | return -EINVAL; | ||
443 | |||
444 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
445 | |||
446 | CmdFrm.cts = AVC; | ||
447 | CmdFrm.ctype = CONTROL; | ||
448 | CmdFrm.sutyp = 0x5; | ||
449 | CmdFrm.suid = fdtv->subunit; | ||
450 | CmdFrm.opcode = DSD; | ||
451 | |||
452 | CmdFrm.operand[0] = 0; // source plug | ||
453 | CmdFrm.operand[1] = 0xD2; // subfunction replace | ||
454 | CmdFrm.operand[2] = 0x20; // system id = DVB | ||
455 | CmdFrm.operand[3] = 0x00; // antenna number | ||
456 | CmdFrm.operand[4] = 0x00; // system_specific_multiplex selection_length | ||
457 | CmdFrm.operand[5] = pidc; // Nr_of_dsd_sel_specs | ||
458 | |||
459 | pos = 6; | ||
460 | if (pidc != 0xff) | ||
461 | for (k = 0; k < pidc; k++) { | ||
462 | CmdFrm.operand[pos++] = 0x13; // flowfunction relay | ||
463 | CmdFrm.operand[pos++] = 0x80; // dsd_sel_spec_valid_flags -> PID | ||
464 | CmdFrm.operand[pos++] = (pid[k] >> 8) & 0x1F; | ||
465 | CmdFrm.operand[pos++] = pid[k] & 0xFF; | ||
466 | CmdFrm.operand[pos++] = 0x00; // tableID | ||
467 | CmdFrm.operand[pos++] = 0x00; // filter_length | ||
468 | } | ||
469 | |||
470 | CmdFrm.length = ALIGN(3 + pos, 4); | ||
471 | |||
472 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
473 | return -EIO; | ||
474 | |||
475 | msleep(50); | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | int avc_tuner_get_ts(struct firedtv *fdtv) | ||
480 | { | ||
481 | AVCCmdFrm CmdFrm; | ||
482 | AVCRspFrm RspFrm; | ||
483 | |||
484 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
485 | |||
486 | CmdFrm.cts = AVC; | ||
487 | CmdFrm.ctype = CONTROL; | ||
488 | CmdFrm.sutyp = 0x5; | ||
489 | CmdFrm.suid = fdtv->subunit; | ||
490 | CmdFrm.opcode = DSIT; | ||
491 | |||
492 | CmdFrm.operand[0] = 0; // source plug | ||
493 | CmdFrm.operand[1] = 0xD2; // subfunction replace | ||
494 | CmdFrm.operand[2] = 0xFF; //status | ||
495 | CmdFrm.operand[3] = 0x20; // system id = DVB | ||
496 | CmdFrm.operand[4] = 0x00; // antenna number | ||
497 | CmdFrm.operand[5] = 0x0; // system_specific_search_flags | ||
498 | CmdFrm.operand[6] = (fdtv->type == FIREDTV_DVB_T)?0x0c:0x11; // system_specific_multiplex selection_length | ||
499 | CmdFrm.operand[7] = 0x00; // valid_flags [0] | ||
500 | CmdFrm.operand[8] = 0x00; // valid_flags [1] | ||
501 | CmdFrm.operand[7 + (fdtv->type == FIREDTV_DVB_T)?0x0c:0x11] = 0x00; // nr_of_dsit_sel_specs (always 0) | ||
502 | |||
503 | CmdFrm.length = (fdtv->type == FIREDTV_DVB_T)?24:28; | ||
504 | |||
505 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
506 | return -EIO; | ||
507 | |||
508 | msleep(250); | ||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | int avc_identify_subunit(struct firedtv *fdtv) | ||
513 | { | ||
514 | AVCCmdFrm CmdFrm; | ||
515 | AVCRspFrm RspFrm; | ||
516 | |||
517 | memset(&CmdFrm,0,sizeof(AVCCmdFrm)); | ||
518 | |||
519 | CmdFrm.cts = AVC; | ||
520 | CmdFrm.ctype = CONTROL; | ||
521 | CmdFrm.sutyp = 0x5; // tuner | ||
522 | CmdFrm.suid = fdtv->subunit; | ||
523 | CmdFrm.opcode = READ_DESCRIPTOR; | ||
524 | |||
525 | CmdFrm.operand[0]=DESCRIPTOR_SUBUNIT_IDENTIFIER; | ||
526 | CmdFrm.operand[1]=0xff; | ||
527 | CmdFrm.operand[2]=0x00; | ||
528 | CmdFrm.operand[3]=0x00; // length highbyte | ||
529 | CmdFrm.operand[4]=0x08; // length lowbyte | ||
530 | CmdFrm.operand[5]=0x00; // offset highbyte | ||
531 | CmdFrm.operand[6]=0x0d; // offset lowbyte | ||
532 | |||
533 | CmdFrm.length=12; | ||
534 | |||
535 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
536 | return -EIO; | ||
537 | |||
538 | if ((RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) || | ||
539 | (RspFrm.operand[3] << 8) + RspFrm.operand[4] != 8) { | ||
540 | dev_err(&fdtv->ud->device, | ||
541 | "cannot read subunit identifier\n"); | ||
542 | return -EINVAL; | ||
543 | } | ||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | int avc_tuner_status(struct firedtv *fdtv, | ||
548 | ANTENNA_INPUT_INFO *antenna_input_info) | ||
549 | { | ||
550 | AVCCmdFrm CmdFrm; | ||
551 | AVCRspFrm RspFrm; | ||
552 | int length; | ||
553 | |||
554 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
555 | |||
556 | CmdFrm.cts=AVC; | ||
557 | CmdFrm.ctype=CONTROL; | ||
558 | CmdFrm.sutyp=0x05; // tuner | ||
559 | CmdFrm.suid=fdtv->subunit; | ||
560 | CmdFrm.opcode=READ_DESCRIPTOR; | ||
561 | |||
562 | CmdFrm.operand[0]=DESCRIPTOR_TUNER_STATUS; | ||
563 | CmdFrm.operand[1]=0xff; //read_result_status | ||
564 | CmdFrm.operand[2]=0x00; // reserver | ||
565 | CmdFrm.operand[3]=0;//sizeof(ANTENNA_INPUT_INFO) >> 8; | ||
566 | CmdFrm.operand[4]=0;//sizeof(ANTENNA_INPUT_INFO) & 0xFF; | ||
567 | CmdFrm.operand[5]=0x00; | ||
568 | CmdFrm.operand[6]=0x00; | ||
569 | CmdFrm.length=12; | ||
570 | |||
571 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
572 | return -EIO; | ||
573 | |||
574 | if (RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { | ||
575 | dev_err(&fdtv->ud->device, "cannot read tuner status\n"); | ||
576 | return -EINVAL; | ||
577 | } | ||
578 | |||
579 | length = RspFrm.operand[9]; | ||
580 | if (RspFrm.operand[1] != 0x10 || length != sizeof(ANTENNA_INPUT_INFO)) { | ||
581 | dev_err(&fdtv->ud->device, "got invalid tuner status\n"); | ||
582 | return -EINVAL; | ||
583 | } | ||
584 | |||
585 | memcpy(antenna_input_info, &RspFrm.operand[10], length); | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, | ||
590 | char conttone, char nrdiseq, | ||
591 | struct dvb_diseqc_master_cmd *diseqcmd) | ||
592 | { | ||
593 | AVCCmdFrm CmdFrm; | ||
594 | AVCRspFrm RspFrm; | ||
595 | int i, j, k; | ||
596 | |||
597 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
598 | |||
599 | CmdFrm.cts=AVC; | ||
600 | CmdFrm.ctype=CONTROL; | ||
601 | CmdFrm.sutyp=0x05; | ||
602 | CmdFrm.suid=fdtv->subunit; | ||
603 | CmdFrm.opcode=VENDOR; | ||
604 | |||
605 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
606 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
607 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
608 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_LNB_CONTROL; | ||
609 | |||
610 | CmdFrm.operand[4]=voltage; | ||
611 | CmdFrm.operand[5]=nrdiseq; | ||
612 | |||
613 | i=6; | ||
614 | |||
615 | for (j = 0; j < nrdiseq; j++) { | ||
616 | CmdFrm.operand[i++] = diseqcmd[j].msg_len; | ||
617 | |||
618 | for (k = 0; k < diseqcmd[j].msg_len; k++) | ||
619 | CmdFrm.operand[i++] = diseqcmd[j].msg[k]; | ||
620 | } | ||
621 | |||
622 | CmdFrm.operand[i++]=burst; | ||
623 | CmdFrm.operand[i++]=conttone; | ||
624 | |||
625 | CmdFrm.length = ALIGN(3 + i, 4); | ||
626 | |||
627 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
628 | return -EIO; | ||
629 | |||
630 | if (RspFrm.resp != ACCEPTED) { | ||
631 | dev_err(&fdtv->ud->device, "LNB control failed\n"); | ||
632 | return -EINVAL; | ||
633 | } | ||
634 | |||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | int avc_register_remote_control(struct firedtv *fdtv) | ||
639 | { | ||
640 | AVCCmdFrm CmdFrm; | ||
641 | |||
642 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
643 | |||
644 | CmdFrm.cts = AVC; | ||
645 | CmdFrm.ctype = NOTIFY; | ||
646 | CmdFrm.sutyp = 0x1f; | ||
647 | CmdFrm.suid = 0x7; | ||
648 | CmdFrm.opcode = VENDOR; | ||
649 | |||
650 | CmdFrm.operand[0] = SFE_VENDOR_DE_COMPANYID_0; | ||
651 | CmdFrm.operand[1] = SFE_VENDOR_DE_COMPANYID_1; | ||
652 | CmdFrm.operand[2] = SFE_VENDOR_DE_COMPANYID_2; | ||
653 | CmdFrm.operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; | ||
654 | |||
655 | CmdFrm.length = 8; | ||
656 | |||
657 | return avc_write(fdtv, &CmdFrm, NULL); | ||
658 | } | ||
659 | |||
660 | void avc_remote_ctrl_work(struct work_struct *work) | ||
661 | { | ||
662 | struct firedtv *fdtv = | ||
663 | container_of(work, struct firedtv, remote_ctrl_work); | ||
664 | |||
665 | /* Should it be rescheduled in failure cases? */ | ||
666 | avc_register_remote_control(fdtv); | ||
667 | } | ||
668 | |||
669 | #if 0 /* FIXME: unused */ | ||
670 | int avc_tuner_host2ca(struct firedtv *fdtv) | ||
671 | { | ||
672 | AVCCmdFrm CmdFrm; | ||
673 | AVCRspFrm RspFrm; | ||
674 | |||
675 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
676 | CmdFrm.cts = AVC; | ||
677 | CmdFrm.ctype = CONTROL; | ||
678 | CmdFrm.sutyp = 0x5; | ||
679 | CmdFrm.suid = fdtv->subunit; | ||
680 | CmdFrm.opcode = VENDOR; | ||
681 | |||
682 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
683 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
684 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
685 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; | ||
686 | CmdFrm.operand[4] = 0; // slot | ||
687 | CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag | ||
688 | CmdFrm.operand[6] = 0; // more/last | ||
689 | CmdFrm.operand[7] = 0; // length | ||
690 | CmdFrm.length = 12; | ||
691 | |||
692 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
693 | return -EIO; | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | #endif | ||
698 | |||
699 | static int get_ca_object_pos(AVCRspFrm *RspFrm) | ||
700 | { | ||
701 | int length = 1; | ||
702 | |||
703 | /* Check length of length field */ | ||
704 | if (RspFrm->operand[7] & 0x80) | ||
705 | length = (RspFrm->operand[7] & 0x7f) + 1; | ||
706 | return length + 7; | ||
707 | } | ||
708 | |||
709 | static int get_ca_object_length(AVCRspFrm *RspFrm) | ||
710 | { | ||
711 | #if 0 /* FIXME: unused */ | ||
712 | int size = 0; | ||
713 | int i; | ||
714 | |||
715 | if (RspFrm->operand[7] & 0x80) | ||
716 | for (i = 0; i < (RspFrm->operand[7] & 0x7f); i++) { | ||
717 | size <<= 8; | ||
718 | size += RspFrm->operand[8 + i]; | ||
719 | } | ||
720 | #endif | ||
721 | return RspFrm->operand[7]; | ||
722 | } | ||
723 | |||
724 | int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) | ||
725 | { | ||
726 | AVCCmdFrm CmdFrm; | ||
727 | AVCRspFrm RspFrm; | ||
728 | int pos; | ||
729 | |||
730 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
731 | CmdFrm.cts = AVC; | ||
732 | CmdFrm.ctype = STATUS; | ||
733 | CmdFrm.sutyp = 0x5; | ||
734 | CmdFrm.suid = fdtv->subunit; | ||
735 | CmdFrm.opcode = VENDOR; | ||
736 | |||
737 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
738 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
739 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
740 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; | ||
741 | CmdFrm.operand[4] = 0; // slot | ||
742 | CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag | ||
743 | CmdFrm.length = 12; | ||
744 | |||
745 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
746 | return -EIO; | ||
747 | |||
748 | /* FIXME: check response code and validate response data */ | ||
749 | |||
750 | pos = get_ca_object_pos(&RspFrm); | ||
751 | app_info[0] = (TAG_APP_INFO >> 16) & 0xFF; | ||
752 | app_info[1] = (TAG_APP_INFO >> 8) & 0xFF; | ||
753 | app_info[2] = (TAG_APP_INFO >> 0) & 0xFF; | ||
754 | app_info[3] = 6 + RspFrm.operand[pos + 4]; | ||
755 | app_info[4] = 0x01; | ||
756 | memcpy(&app_info[5], &RspFrm.operand[pos], 5 + RspFrm.operand[pos + 4]); | ||
757 | *len = app_info[3] + 4; | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) | ||
763 | { | ||
764 | AVCCmdFrm CmdFrm; | ||
765 | AVCRspFrm RspFrm; | ||
766 | int pos; | ||
767 | |||
768 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
769 | CmdFrm.cts = AVC; | ||
770 | CmdFrm.ctype = STATUS; | ||
771 | CmdFrm.sutyp = 0x5; | ||
772 | CmdFrm.suid = fdtv->subunit; | ||
773 | CmdFrm.opcode = VENDOR; | ||
774 | |||
775 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
776 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
777 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
778 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; | ||
779 | CmdFrm.operand[4] = 0; // slot | ||
780 | CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; // ca tag | ||
781 | CmdFrm.length = 12; | ||
782 | |||
783 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
784 | return -EIO; | ||
785 | |||
786 | pos = get_ca_object_pos(&RspFrm); | ||
787 | app_info[0] = (TAG_CA_INFO >> 16) & 0xFF; | ||
788 | app_info[1] = (TAG_CA_INFO >> 8) & 0xFF; | ||
789 | app_info[2] = (TAG_CA_INFO >> 0) & 0xFF; | ||
790 | app_info[3] = 2; | ||
791 | app_info[4] = RspFrm.operand[pos + 0]; | ||
792 | app_info[5] = RspFrm.operand[pos + 1]; | ||
793 | *len = app_info[3] + 4; | ||
794 | |||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | int avc_ca_reset(struct firedtv *fdtv) | ||
799 | { | ||
800 | AVCCmdFrm CmdFrm; | ||
801 | AVCRspFrm RspFrm; | ||
802 | |||
803 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
804 | CmdFrm.cts = AVC; | ||
805 | CmdFrm.ctype = CONTROL; | ||
806 | CmdFrm.sutyp = 0x5; | ||
807 | CmdFrm.suid = fdtv->subunit; | ||
808 | CmdFrm.opcode = VENDOR; | ||
809 | |||
810 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
811 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
812 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
813 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; | ||
814 | CmdFrm.operand[4] = 0; // slot | ||
815 | CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_RESET; // ca tag | ||
816 | CmdFrm.operand[6] = 0; // more/last | ||
817 | CmdFrm.operand[7] = 1; // length | ||
818 | CmdFrm.operand[8] = 0; // force hardware reset | ||
819 | CmdFrm.length = 12; | ||
820 | |||
821 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
822 | return -EIO; | ||
823 | |||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) | ||
828 | { | ||
829 | AVCCmdFrm CmdFrm; | ||
830 | AVCRspFrm RspFrm; | ||
831 | int list_management; | ||
832 | int program_info_length; | ||
833 | int pmt_cmd_id; | ||
834 | int read_pos; | ||
835 | int write_pos; | ||
836 | int es_info_length; | ||
837 | int crc32_csum; | ||
838 | |||
839 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
840 | CmdFrm.cts = AVC; | ||
841 | CmdFrm.ctype = CONTROL; | ||
842 | CmdFrm.sutyp = 0x5; | ||
843 | CmdFrm.suid = fdtv->subunit; | ||
844 | CmdFrm.opcode = VENDOR; | ||
845 | |||
846 | if (msg[0] != LIST_MANAGEMENT_ONLY) { | ||
847 | dev_info(&fdtv->ud->device, | ||
848 | "forcing list_management to ONLY\n"); | ||
849 | msg[0] = LIST_MANAGEMENT_ONLY; | ||
850 | } | ||
851 | // We take the cmd_id from the programme level only! | ||
852 | list_management = msg[0]; | ||
853 | program_info_length = ((msg[4] & 0x0F) << 8) + msg[5]; | ||
854 | if (program_info_length > 0) | ||
855 | program_info_length--; // Remove pmt_cmd_id | ||
856 | pmt_cmd_id = msg[6]; | ||
857 | |||
858 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
859 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
860 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
861 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; | ||
862 | CmdFrm.operand[4] = 0; // slot | ||
863 | CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_PMT; // ca tag | ||
864 | CmdFrm.operand[6] = 0; // more/last | ||
865 | //CmdFrm.operand[7] = XXXprogram_info_length + 17; // length | ||
866 | CmdFrm.operand[8] = list_management; | ||
867 | CmdFrm.operand[9] = 0x01; // pmt_cmd=OK_descramble | ||
868 | |||
869 | // TS program map table | ||
870 | |||
871 | // Table id=2 | ||
872 | CmdFrm.operand[10] = 0x02; | ||
873 | // Section syntax + length | ||
874 | CmdFrm.operand[11] = 0x80; | ||
875 | //CmdFrm.operand[12] = XXXprogram_info_length + 12; | ||
876 | // Program number | ||
877 | CmdFrm.operand[13] = msg[1]; | ||
878 | CmdFrm.operand[14] = msg[2]; | ||
879 | // Version number=0 + current/next=1 | ||
880 | CmdFrm.operand[15] = 0x01; | ||
881 | // Section number=0 | ||
882 | CmdFrm.operand[16] = 0x00; | ||
883 | // Last section number=0 | ||
884 | CmdFrm.operand[17] = 0x00; | ||
885 | // PCR_PID=1FFF | ||
886 | CmdFrm.operand[18] = 0x1F; | ||
887 | CmdFrm.operand[19] = 0xFF; | ||
888 | // Program info length | ||
889 | CmdFrm.operand[20] = (program_info_length >> 8); | ||
890 | CmdFrm.operand[21] = (program_info_length & 0xFF); | ||
891 | // CA descriptors at programme level | ||
892 | read_pos = 6; | ||
893 | write_pos = 22; | ||
894 | if (program_info_length > 0) { | ||
895 | pmt_cmd_id = msg[read_pos++]; | ||
896 | if (pmt_cmd_id != 1 && pmt_cmd_id != 4) | ||
897 | dev_err(&fdtv->ud->device, | ||
898 | "invalid pmt_cmd_id %d\n", pmt_cmd_id); | ||
899 | |||
900 | memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], | ||
901 | program_info_length); | ||
902 | read_pos += program_info_length; | ||
903 | write_pos += program_info_length; | ||
904 | } | ||
905 | while (read_pos < length) { | ||
906 | CmdFrm.operand[write_pos++] = msg[read_pos++]; | ||
907 | CmdFrm.operand[write_pos++] = msg[read_pos++]; | ||
908 | CmdFrm.operand[write_pos++] = msg[read_pos++]; | ||
909 | es_info_length = | ||
910 | ((msg[read_pos] & 0x0F) << 8) + msg[read_pos + 1]; | ||
911 | read_pos += 2; | ||
912 | if (es_info_length > 0) | ||
913 | es_info_length--; // Remove pmt_cmd_id | ||
914 | CmdFrm.operand[write_pos++] = es_info_length >> 8; | ||
915 | CmdFrm.operand[write_pos++] = es_info_length & 0xFF; | ||
916 | if (es_info_length > 0) { | ||
917 | pmt_cmd_id = msg[read_pos++]; | ||
918 | if (pmt_cmd_id != 1 && pmt_cmd_id != 4) | ||
919 | dev_err(&fdtv->ud->device, | ||
920 | "invalid pmt_cmd_id %d " | ||
921 | "at stream level\n", pmt_cmd_id); | ||
922 | |||
923 | memcpy(&CmdFrm.operand[write_pos], &msg[read_pos], | ||
924 | es_info_length); | ||
925 | read_pos += es_info_length; | ||
926 | write_pos += es_info_length; | ||
927 | } | ||
928 | } | ||
929 | |||
930 | // CRC | ||
931 | CmdFrm.operand[write_pos++] = 0x00; | ||
932 | CmdFrm.operand[write_pos++] = 0x00; | ||
933 | CmdFrm.operand[write_pos++] = 0x00; | ||
934 | CmdFrm.operand[write_pos++] = 0x00; | ||
935 | |||
936 | CmdFrm.operand[7] = write_pos - 8; | ||
937 | CmdFrm.operand[12] = write_pos - 13; | ||
938 | |||
939 | crc32_csum = crc32_be(0, &CmdFrm.operand[10], | ||
940 | CmdFrm.operand[12] - 1); | ||
941 | CmdFrm.operand[write_pos - 4] = (crc32_csum >> 24) & 0xFF; | ||
942 | CmdFrm.operand[write_pos - 3] = (crc32_csum >> 16) & 0xFF; | ||
943 | CmdFrm.operand[write_pos - 2] = (crc32_csum >> 8) & 0xFF; | ||
944 | CmdFrm.operand[write_pos - 1] = (crc32_csum >> 0) & 0xFF; | ||
945 | |||
946 | CmdFrm.length = ALIGN(3 + write_pos, 4); | ||
947 | |||
948 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
949 | return -EIO; | ||
950 | |||
951 | if (RspFrm.resp != ACCEPTED) { | ||
952 | dev_err(&fdtv->ud->device, | ||
953 | "CA PMT failed with response 0x%x\n", RspFrm.resp); | ||
954 | return -EFAULT; | ||
955 | } | ||
956 | |||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) | ||
961 | { | ||
962 | AVCCmdFrm CmdFrm; | ||
963 | AVCRspFrm RspFrm; | ||
964 | |||
965 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
966 | CmdFrm.cts = AVC; | ||
967 | CmdFrm.ctype = STATUS; | ||
968 | CmdFrm.sutyp = 0x5; | ||
969 | CmdFrm.suid = fdtv->subunit; | ||
970 | CmdFrm.opcode = VENDOR; | ||
971 | |||
972 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
973 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
974 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
975 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; | ||
976 | CmdFrm.operand[4] = 0; // slot | ||
977 | CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; // ca tag | ||
978 | CmdFrm.operand[6] = 0; // more/last | ||
979 | CmdFrm.operand[7] = 0; // length | ||
980 | CmdFrm.length = 12; | ||
981 | |||
982 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
983 | return -EIO; | ||
984 | |||
985 | /* FIXME: check response code and validate response data */ | ||
986 | |||
987 | *interval = RspFrm.operand[get_ca_object_pos(&RspFrm)]; | ||
988 | |||
989 | return 0; | ||
990 | } | ||
991 | |||
992 | int avc_ca_enter_menu(struct firedtv *fdtv) | ||
993 | { | ||
994 | AVCCmdFrm CmdFrm; | ||
995 | AVCRspFrm RspFrm; | ||
996 | |||
997 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
998 | CmdFrm.cts = AVC; | ||
999 | CmdFrm.ctype = STATUS; | ||
1000 | CmdFrm.sutyp = 0x5; | ||
1001 | CmdFrm.suid = fdtv->subunit; | ||
1002 | CmdFrm.opcode = VENDOR; | ||
1003 | |||
1004 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
1005 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
1006 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
1007 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_HOST2CA; | ||
1008 | CmdFrm.operand[4] = 0; // slot | ||
1009 | CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; | ||
1010 | CmdFrm.operand[6] = 0; // more/last | ||
1011 | CmdFrm.operand[7] = 0; // length | ||
1012 | CmdFrm.length = 12; | ||
1013 | |||
1014 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
1015 | return -EIO; | ||
1016 | |||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
1020 | int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) | ||
1021 | { | ||
1022 | AVCCmdFrm CmdFrm; | ||
1023 | AVCRspFrm RspFrm; | ||
1024 | |||
1025 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | ||
1026 | CmdFrm.cts = AVC; | ||
1027 | CmdFrm.ctype = STATUS; | ||
1028 | CmdFrm.sutyp = 0x5; | ||
1029 | CmdFrm.suid = fdtv->subunit; | ||
1030 | CmdFrm.opcode = VENDOR; | ||
1031 | |||
1032 | CmdFrm.operand[0]=SFE_VENDOR_DE_COMPANYID_0; | ||
1033 | CmdFrm.operand[1]=SFE_VENDOR_DE_COMPANYID_1; | ||
1034 | CmdFrm.operand[2]=SFE_VENDOR_DE_COMPANYID_2; | ||
1035 | CmdFrm.operand[3]=SFE_VENDOR_OPCODE_CA2HOST; | ||
1036 | CmdFrm.operand[4] = 0; // slot | ||
1037 | CmdFrm.operand[5] = SFE_VENDOR_TAG_CA_MMI; | ||
1038 | CmdFrm.operand[6] = 0; // more/last | ||
1039 | CmdFrm.operand[7] = 0; // length | ||
1040 | CmdFrm.length = 12; | ||
1041 | |||
1042 | if (avc_write(fdtv, &CmdFrm, &RspFrm) < 0) | ||
1043 | return -EIO; | ||
1044 | |||
1045 | /* FIXME: check response code and validate response data */ | ||
1046 | |||
1047 | *len = get_ca_object_length(&RspFrm); | ||
1048 | memcpy(mmi_object, &RspFrm.operand[get_ca_object_pos(&RspFrm)], *len); | ||
1049 | |||
1050 | return 0; | ||
1051 | } | ||