Commit | Line | Data |
---|---|---|
c81c8b68 | 1 | /* |
612262a5 | 2 | * FireDTV driver (formerly known as FireSAT) |
c81c8b68 | 3 | * |
612262a5 SR |
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> | |
c81c8b68 GKH |
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 | ||
8ae83cdf | 14 | #include <linux/bug.h> |
612262a5 SR |
15 | #include <linux/crc32.h> |
16 | #include <linux/delay.h> | |
8ae83cdf | 17 | #include <linux/device.h> |
612262a5 SR |
18 | #include <linux/kernel.h> |
19 | #include <linux/moduleparam.h> | |
20 | #include <linux/mutex.h> | |
8ae83cdf | 21 | #include <linux/string.h> |
612262a5 SR |
22 | #include <linux/wait.h> |
23 | #include <linux/workqueue.h> | |
612262a5 | 24 | |
c81c8b68 GKH |
25 | #include <ieee1394_transactions.h> |
26 | #include <nodemgr.h> | |
612262a5 | 27 | |
c81c8b68 | 28 | #include "avc_api.h" |
612262a5 | 29 | #include "firesat.h" |
c81c8b68 GKH |
30 | #include "firesat-rc.h" |
31 | ||
8ae83cdf | 32 | #define FCP_COMMAND_REGISTER 0xfffff0000b00ULL |
c81c8b68 | 33 | |
8ae83cdf SR |
34 | static int __avc_write(struct firesat *firesat, |
35 | const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) | |
df4846c3 | 36 | { |
8ae83cdf SR |
37 | int err, retry; |
38 | ||
39 | if (RspFrm) | |
40 | firesat->avc_reply_received = false; | |
41 | ||
42 | for (retry = 0; retry < 6; retry++) { | |
43 | err = hpsb_node_write(firesat->ud->ne, FCP_COMMAND_REGISTER, | |
44 | (quadlet_t *)CmdFrm, CmdFrm->length); | |
45 | if (err) { | |
46 | firesat->avc_reply_received = true; | |
47 | dev_err(&firesat->ud->device, | |
48 | "FCP command write failed\n"); | |
49 | return err; | |
81c67b7f | 50 | } |
c81c8b68 | 51 | |
8ae83cdf SR |
52 | if (!RspFrm) |
53 | return 0; | |
c81c8b68 | 54 | |
8ae83cdf SR |
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(firesat->avc_wait, | |
60 | firesat->avc_reply_received, | |
61 | HZ / 5) != 0) { | |
62 | memcpy(RspFrm, firesat->respfrm, firesat->resp_length); | |
63 | RspFrm->length = firesat->resp_length; | |
81c67b7f | 64 | |
8ae83cdf | 65 | return 0; |
df4846c3 | 66 | } |
c81c8b68 | 67 | } |
8ae83cdf SR |
68 | dev_err(&firesat->ud->device, "FCP response timed out\n"); |
69 | return -ETIMEDOUT; | |
c81c8b68 GKH |
70 | } |
71 | ||
8ae83cdf SR |
72 | static int avc_write(struct firesat *firesat, |
73 | const AVCCmdFrm *CmdFrm, AVCRspFrm *RspFrm) | |
612262a5 | 74 | { |
c81c8b68 | 75 | int ret; |
612262a5 SR |
76 | |
77 | if (mutex_lock_interruptible(&firesat->avc_mutex)) | |
c81c8b68 GKH |
78 | return -EINTR; |
79 | ||
8ae83cdf | 80 | ret = __avc_write(firesat, CmdFrm, RspFrm); |
c81c8b68 | 81 | |
612262a5 | 82 | mutex_unlock(&firesat->avc_mutex); |
c81c8b68 GKH |
83 | return ret; |
84 | } | |
85 | ||
8ae83cdf | 86 | int avc_recv(struct firesat *firesat, u8 *data, size_t length) |
612262a5 SR |
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) { | |
8ae83cdf SR |
96 | firesat_handle_rc(firesat, |
97 | RspFrm->operand[4] << 8 | RspFrm->operand[5]); | |
612262a5 SR |
98 | schedule_work(&firesat->remote_ctrl_work); |
99 | } else if (RspFrm->resp != INTERIM) { | |
8ae83cdf SR |
100 | dev_info(&firesat->ud->device, |
101 | "remote control result = %d\n", RspFrm->resp); | |
c81c8b68 | 102 | } |
c81c8b68 GKH |
103 | return 0; |
104 | } | |
612262a5 | 105 | |
8ae83cdf SR |
106 | if (firesat->avc_reply_received) { |
107 | dev_err(&firesat->ud->device, | |
108 | "received out-of-order AVC response, ignored\n"); | |
109 | return -EIO; | |
c81c8b68 | 110 | } |
df4846c3 | 111 | |
8ae83cdf SR |
112 | memcpy(firesat->respfrm, data, length); |
113 | firesat->resp_length = length; | |
c81c8b68 | 114 | |
8ae83cdf | 115 | firesat->avc_reply_received = true; |
81c67b7f | 116 | wake_up(&firesat->avc_wait); |
c81c8b68 GKH |
117 | |
118 | return 0; | |
119 | } | |
120 | ||
8ae83cdf SR |
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 firesat *firesat, | |
126 | struct dvb_frontend_parameters *params, AVCCmdFrm *CmdFrm) | |
127 | { | |
c81c8b68 GKH |
128 | CmdFrm->opcode = VENDOR; |
129 | ||
8ae83cdf SR |
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; | |
c81c8b68 | 134 | |
8ae83cdf SR |
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; | |
c81c8b68 | 139 | |
8ae83cdf SR |
140 | CmdFrm->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; |
141 | CmdFrm->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; | |
c81c8b68 GKH |
142 | |
143 | switch(params->u.qpsk.fec_inner) { | |
144 | case FEC_1_2: | |
8ae83cdf | 145 | CmdFrm->operand[10] = 0x1; break; |
c81c8b68 | 146 | case FEC_2_3: |
8ae83cdf | 147 | CmdFrm->operand[10] = 0x2; break; |
c81c8b68 | 148 | case FEC_3_4: |
8ae83cdf | 149 | CmdFrm->operand[10] = 0x3; break; |
c81c8b68 | 150 | case FEC_5_6: |
8ae83cdf | 151 | CmdFrm->operand[10] = 0x4; break; |
c81c8b68 | 152 | case FEC_7_8: |
8ae83cdf | 153 | CmdFrm->operand[10] = 0x5; break; |
c81c8b68 GKH |
154 | case FEC_4_5: |
155 | case FEC_8_9: | |
156 | case FEC_AUTO: | |
157 | default: | |
158 | CmdFrm->operand[10] = 0x0; | |
159 | } | |
160 | ||
8ae83cdf | 161 | if (firesat->voltage == 0xff) |
c81c8b68 | 162 | CmdFrm->operand[11] = 0xff; |
8ae83cdf SR |
163 | else if (firesat->voltage == SEC_VOLTAGE_18) /* polarisation */ |
164 | CmdFrm->operand[11] = 0; | |
c81c8b68 | 165 | else |
8ae83cdf SR |
166 | CmdFrm->operand[11] = 1; |
167 | ||
168 | if (firesat->tone == 0xff) | |
c81c8b68 | 169 | CmdFrm->operand[12] = 0xff; |
8ae83cdf SR |
170 | else if (firesat->tone == SEC_TONE_ON) /* band */ |
171 | CmdFrm->operand[12] = 1; | |
c81c8b68 | 172 | else |
8ae83cdf | 173 | CmdFrm->operand[12] = 0; |
c81c8b68 | 174 | |
2c228614 BB |
175 | if (firesat->type == FireSAT_DVB_S2) { |
176 | CmdFrm->operand[13] = 0x1; | |
8ae83cdf SR |
177 | CmdFrm->operand[14] = 0xff; |
178 | CmdFrm->operand[15] = 0xff; | |
179 | CmdFrm->length = 20; | |
180 | } else { | |
181 | CmdFrm->length = 16; | |
2c228614 | 182 | } |
8ae83cdf SR |
183 | } |
184 | ||
185 | static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, | |
186 | AVCCmdFrm *CmdFrm) | |
187 | { | |
188 | M_VALID_FLAGS flags; | |
2c228614 | 189 | |
8ae83cdf SR |
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; | |
c81c8b68 GKH |
269 | } |
270 | ||
8ae83cdf SR |
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 firesat *firesat, | |
398 | struct dvb_frontend_parameters *params) | |
399 | { | |
c81c8b68 GKH |
400 | AVCCmdFrm CmdFrm; |
401 | AVCRspFrm RspFrm; | |
c81c8b68 | 402 | |
8ae83cdf | 403 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); |
c81c8b68 | 404 | |
8ae83cdf SR |
405 | CmdFrm.cts = AVC; |
406 | CmdFrm.ctype = CONTROL; | |
407 | CmdFrm.sutyp = 0x5; | |
408 | CmdFrm.suid = firesat->subunit; | |
c81c8b68 | 409 | |
8ae83cdf SR |
410 | switch (firesat->type) { |
411 | case FireSAT_DVB_S: | |
412 | case FireSAT_DVB_S2: | |
413 | avc_tuner_tuneqpsk(firesat, params, &CmdFrm); break; | |
414 | case FireSAT_DVB_C: | |
415 | avc_tuner_dsd_dvb_c(params, &CmdFrm); break; | |
416 | case FireSAT_DVB_T: | |
417 | avc_tuner_dsd_dvb_t(params, &CmdFrm); break; | |
418 | default: | |
419 | BUG(); | |
420 | } | |
c81c8b68 | 421 | |
8ae83cdf SR |
422 | if (avc_write(firesat, &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 */ | |
c81c8b68 GKH |
429 | if(status) |
430 | *status=RspFrm.operand[2]; | |
8ae83cdf | 431 | #endif |
c81c8b68 GKH |
432 | return 0; |
433 | } | |
434 | ||
8ae83cdf | 435 | int avc_tuner_set_pids(struct firesat *firesat, unsigned char pidc, u16 pid[]) |
df4846c3 | 436 | { |
c81c8b68 GKH |
437 | AVCCmdFrm CmdFrm; |
438 | AVCRspFrm RspFrm; | |
8ae83cdf | 439 | int pos, k; |
c81c8b68 | 440 | |
8ae83cdf | 441 | if (pidc > 16 && pidc != 0xff) |
c81c8b68 GKH |
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 = firesat->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 | |
df4846c3 HK |
456 | CmdFrm.operand[4] = 0x00; // system_specific_multiplex selection_length |
457 | CmdFrm.operand[5] = pidc; // Nr_of_dsd_sel_specs | |
458 | ||
8ae83cdf SR |
459 | pos = 6; |
460 | if (pidc != 0xff) | |
461 | for (k = 0; k < pidc; k++) { | |
c81c8b68 GKH |
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 | ||
8ae83cdf | 470 | CmdFrm.length = ALIGN(3 + pos, 4); |
c81c8b68 | 471 | |
8ae83cdf SR |
472 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
473 | return -EIO; | |
c81c8b68 | 474 | |
8ae83cdf | 475 | msleep(50); |
c81c8b68 GKH |
476 | return 0; |
477 | } | |
478 | ||
8ae83cdf SR |
479 | int avc_tuner_get_ts(struct firesat *firesat) |
480 | { | |
c81c8b68 GKH |
481 | AVCCmdFrm CmdFrm; |
482 | AVCRspFrm RspFrm; | |
c81c8b68 GKH |
483 | |
484 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | |
485 | ||
486 | CmdFrm.cts = AVC; | |
487 | CmdFrm.ctype = CONTROL; | |
488 | CmdFrm.sutyp = 0x5; | |
489 | CmdFrm.suid = firesat->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 | |
df4846c3 | 498 | CmdFrm.operand[6] = (firesat->type == FireSAT_DVB_T)?0x0c:0x11; // system_specific_multiplex selection_length |
c81c8b68 GKH |
499 | CmdFrm.operand[7] = 0x00; // valid_flags [0] |
500 | CmdFrm.operand[8] = 0x00; // valid_flags [1] | |
df4846c3 | 501 | CmdFrm.operand[7 + (firesat->type == FireSAT_DVB_T)?0x0c:0x11] = 0x00; // nr_of_dsit_sel_specs (always 0) |
c81c8b68 | 502 | |
df4846c3 | 503 | CmdFrm.length = (firesat->type == FireSAT_DVB_T)?24:28; |
c81c8b68 | 504 | |
8ae83cdf SR |
505 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
506 | return -EIO; | |
c81c8b68 | 507 | |
8ae83cdf | 508 | msleep(250); |
c81c8b68 GKH |
509 | return 0; |
510 | } | |
511 | ||
8ae83cdf | 512 | int avc_identify_subunit(struct firesat *firesat) |
612262a5 | 513 | { |
c81c8b68 GKH |
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 = firesat->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 | ||
8ae83cdf | 535 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
c81c8b68 GKH |
536 | return -EIO; |
537 | ||
8ae83cdf SR |
538 | if ((RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) || |
539 | (RspFrm.operand[3] << 8) + RspFrm.operand[4] != 8) { | |
540 | dev_err(&firesat->ud->device, | |
541 | "cannot read subunit identifier\n"); | |
c81c8b68 GKH |
542 | return -EINVAL; |
543 | } | |
c81c8b68 GKH |
544 | return 0; |
545 | } | |
546 | ||
8ae83cdf SR |
547 | int avc_tuner_status(struct firesat *firesat, |
548 | ANTENNA_INPUT_INFO *antenna_input_info) | |
549 | { | |
c81c8b68 GKH |
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=firesat->subunit; | |
560 | CmdFrm.opcode=READ_DESCRIPTOR; | |
561 | ||
562 | CmdFrm.operand[0]=DESCRIPTOR_TUNER_STATUS; | |
df4846c3 HK |
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; | |
c81c8b68 | 567 | CmdFrm.operand[5]=0x00; |
df4846c3 | 568 | CmdFrm.operand[6]=0x00; |
c81c8b68 | 569 | CmdFrm.length=12; |
8ae83cdf SR |
570 | |
571 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) | |
c81c8b68 GKH |
572 | return -EIO; |
573 | ||
8ae83cdf SR |
574 | if (RspFrm.resp != STABLE && RspFrm.resp != ACCEPTED) { |
575 | dev_err(&firesat->ud->device, "cannot read tuner status\n"); | |
c81c8b68 GKH |
576 | return -EINVAL; |
577 | } | |
578 | ||
df4846c3 | 579 | length = RspFrm.operand[9]; |
8ae83cdf SR |
580 | if (RspFrm.operand[1] != 0x10 || length != sizeof(ANTENNA_INPUT_INFO)) { |
581 | dev_err(&firesat->ud->device, "got invalid tuner status\n"); | |
582 | return -EINVAL; | |
c81c8b68 | 583 | } |
8ae83cdf SR |
584 | |
585 | memcpy(antenna_input_info, &RspFrm.operand[10], length); | |
586 | return 0; | |
c81c8b68 GKH |
587 | } |
588 | ||
8ae83cdf SR |
589 | int avc_lnb_control(struct firesat *firesat, char voltage, char burst, |
590 | char conttone, char nrdiseq, | |
591 | struct dvb_diseqc_master_cmd *diseqcmd) | |
c81c8b68 GKH |
592 | { |
593 | AVCCmdFrm CmdFrm; | |
594 | AVCRspFrm RspFrm; | |
8ae83cdf | 595 | int i, j, k; |
c81c8b68 GKH |
596 | |
597 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | |
598 | ||
599 | CmdFrm.cts=AVC; | |
600 | CmdFrm.ctype=CONTROL; | |
601 | CmdFrm.sutyp=0x05; | |
602 | CmdFrm.suid=firesat->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 | ||
8ae83cdf SR |
615 | for (j = 0; j < nrdiseq; j++) { |
616 | CmdFrm.operand[i++] = diseqcmd[j].msg_len; | |
c81c8b68 | 617 | |
8ae83cdf SR |
618 | for (k = 0; k < diseqcmd[j].msg_len; k++) |
619 | CmdFrm.operand[i++] = diseqcmd[j].msg[k]; | |
c81c8b68 GKH |
620 | } |
621 | ||
622 | CmdFrm.operand[i++]=burst; | |
623 | CmdFrm.operand[i++]=conttone; | |
624 | ||
8ae83cdf | 625 | CmdFrm.length = ALIGN(3 + i, 4); |
c81c8b68 | 626 | |
8ae83cdf | 627 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
c81c8b68 GKH |
628 | return -EIO; |
629 | ||
8ae83cdf SR |
630 | if (RspFrm.resp != ACCEPTED) { |
631 | dev_err(&firesat->ud->device, "LNB control failed\n"); | |
c81c8b68 GKH |
632 | return -EINVAL; |
633 | } | |
634 | ||
c81c8b68 GKH |
635 | return 0; |
636 | } | |
637 | ||
8ae83cdf | 638 | int avc_register_remote_control(struct firesat *firesat) |
c81c8b68 GKH |
639 | { |
640 | AVCCmdFrm CmdFrm; | |
641 | ||
c81c8b68 GKH |
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 | ||
8ae83cdf | 657 | return avc_write(firesat, &CmdFrm, NULL); |
c81c8b68 GKH |
658 | } |
659 | ||
612262a5 | 660 | void avc_remote_ctrl_work(struct work_struct *work) |
c81c8b68 | 661 | { |
612262a5 SR |
662 | struct firesat *firesat = |
663 | container_of(work, struct firesat, remote_ctrl_work); | |
664 | ||
665 | /* Should it be rescheduled in failure cases? */ | |
8ae83cdf | 666 | avc_register_remote_control(firesat); |
c81c8b68 | 667 | } |
df4846c3 | 668 | |
8ae83cdf SR |
669 | #if 0 /* FIXME: unused */ |
670 | int avc_tuner_host2ca(struct firesat *firesat) | |
df4846c3 | 671 | { |
df4846c3 HK |
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 = firesat->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 | ||
8ae83cdf | 692 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
df4846c3 HK |
693 | return -EIO; |
694 | ||
695 | return 0; | |
696 | } | |
8ae83cdf | 697 | #endif |
df4846c3 HK |
698 | |
699 | static int get_ca_object_pos(AVCRspFrm *RspFrm) | |
700 | { | |
701 | int length = 1; | |
702 | ||
8ae83cdf | 703 | /* Check length of length field */ |
df4846c3 | 704 | if (RspFrm->operand[7] & 0x80) |
8ae83cdf | 705 | length = (RspFrm->operand[7] & 0x7f) + 1; |
df4846c3 HK |
706 | return length + 7; |
707 | } | |
708 | ||
709 | static int get_ca_object_length(AVCRspFrm *RspFrm) | |
710 | { | |
8ae83cdf | 711 | #if 0 /* FIXME: unused */ |
df4846c3 HK |
712 | int size = 0; |
713 | int i; | |
714 | ||
8ae83cdf SR |
715 | if (RspFrm->operand[7] & 0x80) |
716 | for (i = 0; i < (RspFrm->operand[7] & 0x7f); i++) { | |
df4846c3 HK |
717 | size <<= 8; |
718 | size += RspFrm->operand[8 + i]; | |
719 | } | |
8ae83cdf | 720 | #endif |
df4846c3 HK |
721 | return RspFrm->operand[7]; |
722 | } | |
723 | ||
8ae83cdf | 724 | int avc_ca_app_info(struct firesat *firesat, char *app_info, unsigned int *len) |
df4846c3 HK |
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 = firesat->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 | ||
8ae83cdf | 745 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
df4846c3 HK |
746 | return -EIO; |
747 | ||
8ae83cdf | 748 | /* FIXME: check response code and validate response data */ |
df4846c3 HK |
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]); | |
8ae83cdf | 757 | *len = app_info[3] + 4; |
df4846c3 HK |
758 | |
759 | return 0; | |
760 | } | |
761 | ||
8ae83cdf | 762 | int avc_ca_info(struct firesat *firesat, char *app_info, unsigned int *len) |
df4846c3 HK |
763 | { |
764 | AVCCmdFrm CmdFrm; | |
765 | AVCRspFrm RspFrm; | |
096edfbf | 766 | int pos; |
df4846c3 HK |
767 | |
768 | memset(&CmdFrm, 0, sizeof(AVCCmdFrm)); | |
769 | CmdFrm.cts = AVC; | |
770 | CmdFrm.ctype = STATUS; | |
771 | CmdFrm.sutyp = 0x5; | |
772 | CmdFrm.suid = firesat->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 | ||
8ae83cdf | 783 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
df4846c3 HK |
784 | return -EIO; |
785 | ||
096edfbf | 786 | pos = get_ca_object_pos(&RspFrm); |
df4846c3 HK |
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; | |
096edfbf HK |
791 | app_info[4] = RspFrm.operand[pos + 0]; |
792 | app_info[5] = RspFrm.operand[pos + 1]; | |
8ae83cdf | 793 | *len = app_info[3] + 4; |
df4846c3 HK |
794 | |
795 | return 0; | |
796 | } | |
797 | ||
798 | int avc_ca_reset(struct firesat *firesat) | |
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 = firesat->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 | ||
8ae83cdf | 821 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
df4846c3 HK |
822 | return -EIO; |
823 | ||
824 | return 0; | |
825 | } | |
826 | ||
827 | int avc_ca_pmt(struct firesat *firesat, 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 = firesat->subunit; | |
844 | CmdFrm.opcode = VENDOR; | |
845 | ||
846 | if (msg[0] != LIST_MANAGEMENT_ONLY) { | |
8ae83cdf SR |
847 | dev_info(&firesat->ud->device, |
848 | "forcing list_management to ONLY\n"); | |
81c67b7f | 849 | msg[0] = LIST_MANAGEMENT_ONLY; |
df4846c3 HK |
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) { | |
df4846c3 | 895 | pmt_cmd_id = msg[read_pos++]; |
8ae83cdf SR |
896 | if (pmt_cmd_id != 1 && pmt_cmd_id != 4) |
897 | dev_err(&firesat->ud->device, | |
898 | "invalid pmt_cmd_id %d\n", pmt_cmd_id); | |
899 | ||
df4846c3 HK |
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) { | |
df4846c3 HK |
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++]; | |
8ae83cdf SR |
918 | if (pmt_cmd_id != 1 && pmt_cmd_id != 4) |
919 | dev_err(&firesat->ud->device, | |
920 | "invalid pmt_cmd_id %d " | |
921 | "at stream level\n", pmt_cmd_id); | |
922 | ||
df4846c3 HK |
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 | ||
8ae83cdf | 946 | CmdFrm.length = ALIGN(3 + write_pos, 4); |
df4846c3 | 947 | |
8ae83cdf | 948 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
df4846c3 HK |
949 | return -EIO; |
950 | ||
951 | if (RspFrm.resp != ACCEPTED) { | |
8ae83cdf SR |
952 | dev_err(&firesat->ud->device, |
953 | "CA PMT failed with response 0x%x\n", RspFrm.resp); | |
df4846c3 HK |
954 | return -EFAULT; |
955 | } | |
956 | ||
957 | return 0; | |
df4846c3 HK |
958 | } |
959 | ||
960 | int avc_ca_get_time_date(struct firesat *firesat, 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 = firesat->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 | ||
8ae83cdf | 982 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
df4846c3 HK |
983 | return -EIO; |
984 | ||
8ae83cdf SR |
985 | /* FIXME: check response code and validate response data */ |
986 | ||
df4846c3 HK |
987 | *interval = RspFrm.operand[get_ca_object_pos(&RspFrm)]; |
988 | ||
989 | return 0; | |
990 | } | |
991 | ||
992 | int avc_ca_enter_menu(struct firesat *firesat) | |
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 = firesat->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 | ||
8ae83cdf | 1014 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
df4846c3 HK |
1015 | return -EIO; |
1016 | ||
1017 | return 0; | |
1018 | } | |
1019 | ||
8ae83cdf | 1020 | int avc_ca_get_mmi(struct firesat *firesat, char *mmi_object, unsigned int *len) |
df4846c3 HK |
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 = firesat->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 | ||
8ae83cdf | 1042 | if (avc_write(firesat, &CmdFrm, &RspFrm) < 0) |
df4846c3 HK |
1043 | return -EIO; |
1044 | ||
8ae83cdf SR |
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); | |
df4846c3 HK |
1049 | |
1050 | return 0; | |
1051 | } |