1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Abilis Systems Single DVB-T Receiver |
4 | * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> |
5 | * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include "as102_drv.h" |
10 | #include "as10x_cmd.h" |
11 | |
12 | /** |
13 | * as10x_cmd_turn_on - send turn on command to AS10x |
14 | * @adap: pointer to AS10x bus adapter |
15 | * |
16 | * Return 0 when no error, < 0 in case of error. |
17 | */ |
18 | int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) |
19 | { |
20 | int error = AS10X_CMD_ERROR; |
21 | struct as10x_cmd_t *pcmd, *prsp; |
22 | |
23 | pcmd = adap->cmd; |
24 | prsp = adap->rsp; |
25 | |
26 | /* prepare command */ |
27 | as10x_cmd_build(pcmd, proc_id: (++adap->cmd_xid), |
28 | cmd_len: sizeof(pcmd->body.turn_on.req)); |
29 | |
30 | /* fill command */ |
31 | pcmd->body.turn_on.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNON); |
32 | |
33 | /* send command */ |
34 | if (adap->ops->xfer_cmd) { |
35 | error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd, |
36 | sizeof(pcmd->body.turn_on.req) + |
37 | HEADER_SIZE, |
38 | (uint8_t *) prsp, |
39 | sizeof(prsp->body.turn_on.rsp) + |
40 | HEADER_SIZE); |
41 | } |
42 | |
43 | if (error < 0) |
44 | goto out; |
45 | |
46 | /* parse response */ |
47 | error = as10x_rsp_parse(r: prsp, proc_id: CONTROL_PROC_TURNON_RSP); |
48 | |
49 | out: |
50 | return error; |
51 | } |
52 | |
53 | /** |
54 | * as10x_cmd_turn_off - send turn off command to AS10x |
55 | * @adap: pointer to AS10x bus adapter |
56 | * |
57 | * Return 0 on success or negative value in case of error. |
58 | */ |
59 | int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) |
60 | { |
61 | int error = AS10X_CMD_ERROR; |
62 | struct as10x_cmd_t *pcmd, *prsp; |
63 | |
64 | pcmd = adap->cmd; |
65 | prsp = adap->rsp; |
66 | |
67 | /* prepare command */ |
68 | as10x_cmd_build(pcmd, proc_id: (++adap->cmd_xid), |
69 | cmd_len: sizeof(pcmd->body.turn_off.req)); |
70 | |
71 | /* fill command */ |
72 | pcmd->body.turn_off.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNOFF); |
73 | |
74 | /* send command */ |
75 | if (adap->ops->xfer_cmd) { |
76 | error = adap->ops->xfer_cmd( |
77 | adap, (uint8_t *) pcmd, |
78 | sizeof(pcmd->body.turn_off.req) + HEADER_SIZE, |
79 | (uint8_t *) prsp, |
80 | sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE); |
81 | } |
82 | |
83 | if (error < 0) |
84 | goto out; |
85 | |
86 | /* parse response */ |
87 | error = as10x_rsp_parse(r: prsp, proc_id: CONTROL_PROC_TURNOFF_RSP); |
88 | |
89 | out: |
90 | return error; |
91 | } |
92 | |
93 | /** |
94 | * as10x_cmd_set_tune - send set tune command to AS10x |
95 | * @adap: pointer to AS10x bus adapter |
96 | * @ptune: tune parameters |
97 | * |
98 | * Return 0 on success or negative value in case of error. |
99 | */ |
100 | int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, |
101 | struct as10x_tune_args *ptune) |
102 | { |
103 | int error = AS10X_CMD_ERROR; |
104 | struct as10x_cmd_t *preq, *prsp; |
105 | |
106 | preq = adap->cmd; |
107 | prsp = adap->rsp; |
108 | |
109 | /* prepare command */ |
110 | as10x_cmd_build(pcmd: preq, proc_id: (++adap->cmd_xid), |
111 | cmd_len: sizeof(preq->body.set_tune.req)); |
112 | |
113 | /* fill command */ |
114 | preq->body.set_tune.req.proc_id = cpu_to_le16(CONTROL_PROC_SETTUNE); |
115 | preq->body.set_tune.req.args.freq = (__force __u32)cpu_to_le32(ptune->freq); |
116 | preq->body.set_tune.req.args.bandwidth = ptune->bandwidth; |
117 | preq->body.set_tune.req.args.hier_select = ptune->hier_select; |
118 | preq->body.set_tune.req.args.modulation = ptune->modulation; |
119 | preq->body.set_tune.req.args.hierarchy = ptune->hierarchy; |
120 | preq->body.set_tune.req.args.interleaving_mode = |
121 | ptune->interleaving_mode; |
122 | preq->body.set_tune.req.args.code_rate = ptune->code_rate; |
123 | preq->body.set_tune.req.args.guard_interval = ptune->guard_interval; |
124 | preq->body.set_tune.req.args.transmission_mode = |
125 | ptune->transmission_mode; |
126 | |
127 | /* send command */ |
128 | if (adap->ops->xfer_cmd) { |
129 | error = adap->ops->xfer_cmd(adap, |
130 | (uint8_t *) preq, |
131 | sizeof(preq->body.set_tune.req) |
132 | + HEADER_SIZE, |
133 | (uint8_t *) prsp, |
134 | sizeof(prsp->body.set_tune.rsp) |
135 | + HEADER_SIZE); |
136 | } |
137 | |
138 | if (error < 0) |
139 | goto out; |
140 | |
141 | /* parse response */ |
142 | error = as10x_rsp_parse(r: prsp, proc_id: CONTROL_PROC_SETTUNE_RSP); |
143 | |
144 | out: |
145 | return error; |
146 | } |
147 | |
148 | /** |
149 | * as10x_cmd_get_tune_status - send get tune status command to AS10x |
150 | * @adap: pointer to AS10x bus adapter |
151 | * @pstatus: pointer to updated status structure of the current tune |
152 | * |
153 | * Return 0 on success or negative value in case of error. |
154 | */ |
155 | int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, |
156 | struct as10x_tune_status *pstatus) |
157 | { |
158 | int error = AS10X_CMD_ERROR; |
159 | struct as10x_cmd_t *preq, *prsp; |
160 | |
161 | preq = adap->cmd; |
162 | prsp = adap->rsp; |
163 | |
164 | /* prepare command */ |
165 | as10x_cmd_build(pcmd: preq, proc_id: (++adap->cmd_xid), |
166 | cmd_len: sizeof(preq->body.get_tune_status.req)); |
167 | |
168 | /* fill command */ |
169 | preq->body.get_tune_status.req.proc_id = |
170 | cpu_to_le16(CONTROL_PROC_GETTUNESTAT); |
171 | |
172 | /* send command */ |
173 | if (adap->ops->xfer_cmd) { |
174 | error = adap->ops->xfer_cmd( |
175 | adap, |
176 | (uint8_t *) preq, |
177 | sizeof(preq->body.get_tune_status.req) + HEADER_SIZE, |
178 | (uint8_t *) prsp, |
179 | sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE); |
180 | } |
181 | |
182 | if (error < 0) |
183 | goto out; |
184 | |
185 | /* parse response */ |
186 | error = as10x_rsp_parse(r: prsp, proc_id: CONTROL_PROC_GETTUNESTAT_RSP); |
187 | if (error < 0) |
188 | goto out; |
189 | |
190 | /* Response OK -> get response data */ |
191 | pstatus->tune_state = prsp->body.get_tune_status.rsp.sts.tune_state; |
192 | pstatus->signal_strength = |
193 | le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.signal_strength); |
194 | pstatus->PER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.PER); |
195 | pstatus->BER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.BER); |
196 | |
197 | out: |
198 | return error; |
199 | } |
200 | |
201 | /** |
202 | * as10x_cmd_get_tps - send get TPS command to AS10x |
203 | * @adap: pointer to AS10x handle |
204 | * @ptps: pointer to TPS parameters structure |
205 | * |
206 | * Return 0 on success or negative value in case of error. |
207 | */ |
208 | int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) |
209 | { |
210 | int error = AS10X_CMD_ERROR; |
211 | struct as10x_cmd_t *pcmd, *prsp; |
212 | |
213 | pcmd = adap->cmd; |
214 | prsp = adap->rsp; |
215 | |
216 | /* prepare command */ |
217 | as10x_cmd_build(pcmd, proc_id: (++adap->cmd_xid), |
218 | cmd_len: sizeof(pcmd->body.get_tps.req)); |
219 | |
220 | /* fill command */ |
221 | pcmd->body.get_tune_status.req.proc_id = |
222 | cpu_to_le16(CONTROL_PROC_GETTPS); |
223 | |
224 | /* send command */ |
225 | if (adap->ops->xfer_cmd) { |
226 | error = adap->ops->xfer_cmd(adap, |
227 | (uint8_t *) pcmd, |
228 | sizeof(pcmd->body.get_tps.req) + |
229 | HEADER_SIZE, |
230 | (uint8_t *) prsp, |
231 | sizeof(prsp->body.get_tps.rsp) + |
232 | HEADER_SIZE); |
233 | } |
234 | |
235 | if (error < 0) |
236 | goto out; |
237 | |
238 | /* parse response */ |
239 | error = as10x_rsp_parse(r: prsp, proc_id: CONTROL_PROC_GETTPS_RSP); |
240 | if (error < 0) |
241 | goto out; |
242 | |
243 | /* Response OK -> get response data */ |
244 | ptps->modulation = prsp->body.get_tps.rsp.tps.modulation; |
245 | ptps->hierarchy = prsp->body.get_tps.rsp.tps.hierarchy; |
246 | ptps->interleaving_mode = prsp->body.get_tps.rsp.tps.interleaving_mode; |
247 | ptps->code_rate_HP = prsp->body.get_tps.rsp.tps.code_rate_HP; |
248 | ptps->code_rate_LP = prsp->body.get_tps.rsp.tps.code_rate_LP; |
249 | ptps->guard_interval = prsp->body.get_tps.rsp.tps.guard_interval; |
250 | ptps->transmission_mode = prsp->body.get_tps.rsp.tps.transmission_mode; |
251 | ptps->DVBH_mask_HP = prsp->body.get_tps.rsp.tps.DVBH_mask_HP; |
252 | ptps->DVBH_mask_LP = prsp->body.get_tps.rsp.tps.DVBH_mask_LP; |
253 | ptps->cell_ID = le16_to_cpu((__force __le16)prsp->body.get_tps.rsp.tps.cell_ID); |
254 | |
255 | out: |
256 | return error; |
257 | } |
258 | |
259 | /** |
260 | * as10x_cmd_get_demod_stats - send get demod stats command to AS10x |
261 | * @adap: pointer to AS10x bus adapter |
262 | * @pdemod_stats: pointer to demod stats parameters structure |
263 | * |
264 | * Return 0 on success or negative value in case of error. |
265 | */ |
266 | int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, |
267 | struct as10x_demod_stats *pdemod_stats) |
268 | { |
269 | int error = AS10X_CMD_ERROR; |
270 | struct as10x_cmd_t *pcmd, *prsp; |
271 | |
272 | pcmd = adap->cmd; |
273 | prsp = adap->rsp; |
274 | |
275 | /* prepare command */ |
276 | as10x_cmd_build(pcmd, proc_id: (++adap->cmd_xid), |
277 | cmd_len: sizeof(pcmd->body.get_demod_stats.req)); |
278 | |
279 | /* fill command */ |
280 | pcmd->body.get_demod_stats.req.proc_id = |
281 | cpu_to_le16(CONTROL_PROC_GET_DEMOD_STATS); |
282 | |
283 | /* send command */ |
284 | if (adap->ops->xfer_cmd) { |
285 | error = adap->ops->xfer_cmd(adap, |
286 | (uint8_t *) pcmd, |
287 | sizeof(pcmd->body.get_demod_stats.req) |
288 | + HEADER_SIZE, |
289 | (uint8_t *) prsp, |
290 | sizeof(prsp->body.get_demod_stats.rsp) |
291 | + HEADER_SIZE); |
292 | } |
293 | |
294 | if (error < 0) |
295 | goto out; |
296 | |
297 | /* parse response */ |
298 | error = as10x_rsp_parse(r: prsp, proc_id: CONTROL_PROC_GET_DEMOD_STATS_RSP); |
299 | if (error < 0) |
300 | goto out; |
301 | |
302 | /* Response OK -> get response data */ |
303 | pdemod_stats->frame_count = |
304 | le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.frame_count); |
305 | pdemod_stats->bad_frame_count = |
306 | le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bad_frame_count); |
307 | pdemod_stats->bytes_fixed_by_rs = |
308 | le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bytes_fixed_by_rs); |
309 | pdemod_stats->mer = |
310 | le16_to_cpu((__force __le16)prsp->body.get_demod_stats.rsp.stats.mer); |
311 | pdemod_stats->has_started = |
312 | prsp->body.get_demod_stats.rsp.stats.has_started; |
313 | |
314 | out: |
315 | return error; |
316 | } |
317 | |
318 | /** |
319 | * as10x_cmd_get_impulse_resp - send get impulse response command to AS10x |
320 | * @adap: pointer to AS10x bus adapter |
321 | * @is_ready: pointer to value indicating when impulse |
322 | * response data is ready |
323 | * |
324 | * Return 0 on success or negative value in case of error. |
325 | */ |
326 | int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, |
327 | uint8_t *is_ready) |
328 | { |
329 | int error = AS10X_CMD_ERROR; |
330 | struct as10x_cmd_t *pcmd, *prsp; |
331 | |
332 | pcmd = adap->cmd; |
333 | prsp = adap->rsp; |
334 | |
335 | /* prepare command */ |
336 | as10x_cmd_build(pcmd, proc_id: (++adap->cmd_xid), |
337 | cmd_len: sizeof(pcmd->body.get_impulse_rsp.req)); |
338 | |
339 | /* fill command */ |
340 | pcmd->body.get_impulse_rsp.req.proc_id = |
341 | cpu_to_le16(CONTROL_PROC_GET_IMPULSE_RESP); |
342 | |
343 | /* send command */ |
344 | if (adap->ops->xfer_cmd) { |
345 | error = adap->ops->xfer_cmd(adap, |
346 | (uint8_t *) pcmd, |
347 | sizeof(pcmd->body.get_impulse_rsp.req) |
348 | + HEADER_SIZE, |
349 | (uint8_t *) prsp, |
350 | sizeof(prsp->body.get_impulse_rsp.rsp) |
351 | + HEADER_SIZE); |
352 | } |
353 | |
354 | if (error < 0) |
355 | goto out; |
356 | |
357 | /* parse response */ |
358 | error = as10x_rsp_parse(r: prsp, proc_id: CONTROL_PROC_GET_IMPULSE_RESP_RSP); |
359 | if (error < 0) |
360 | goto out; |
361 | |
362 | /* Response OK -> get response data */ |
363 | *is_ready = prsp->body.get_impulse_rsp.rsp.is_ready; |
364 | |
365 | out: |
366 | return error; |
367 | } |
368 | |
369 | /** |
370 | * as10x_cmd_build - build AS10x command header |
371 | * @pcmd: pointer to AS10x command buffer |
372 | * @xid: sequence id of the command |
373 | * @cmd_len: length of the command |
374 | */ |
375 | void as10x_cmd_build(struct as10x_cmd_t *pcmd, |
376 | uint16_t xid, uint16_t cmd_len) |
377 | { |
378 | pcmd->header.req_id = cpu_to_le16(xid); |
379 | pcmd->header.prog = cpu_to_le16(SERVICE_PROG_ID); |
380 | pcmd->header.version = cpu_to_le16(SERVICE_PROG_VERSION); |
381 | pcmd->header.data_len = cpu_to_le16(cmd_len); |
382 | } |
383 | |
384 | /** |
385 | * as10x_rsp_parse - Parse command response |
386 | * @prsp: pointer to AS10x command buffer |
387 | * @proc_id: id of the command |
388 | * |
389 | * Return 0 on success or negative value in case of error. |
390 | */ |
391 | int as10x_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id) |
392 | { |
393 | int error; |
394 | |
395 | /* extract command error code */ |
396 | error = prsp->body.common.rsp.error; |
397 | |
398 | if ((error == 0) && |
399 | (le16_to_cpu(prsp->body.common.rsp.proc_id) == proc_id)) { |
400 | return 0; |
401 | } |
402 | |
403 | return AS10X_CMD_ERROR; |
404 | } |
405 | |