| // SPDX-License-Identifier: GPL-2.0-or-later | 
| /* | 
|  * Abilis Systems Single DVB-T Receiver | 
|  * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> | 
|  * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> | 
|  */ | 
|   | 
| #include <linux/kernel.h> | 
| #include "as102_drv.h" | 
| #include "as10x_cmd.h" | 
|   | 
| /** | 
|  * as10x_cmd_turn_on - send turn on command to AS10x | 
|  * @adap:   pointer to AS10x bus adapter | 
|  * | 
|  * Return 0 when no error, < 0 in case of error. | 
|  */ | 
| int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) | 
| { | 
|     int error = AS10X_CMD_ERROR; | 
|     struct as10x_cmd_t *pcmd, *prsp; | 
|   | 
|     pcmd = adap->cmd; | 
|     prsp = adap->rsp; | 
|   | 
|     /* prepare command */ | 
|     as10x_cmd_build(pcmd, (++adap->cmd_xid), | 
|             sizeof(pcmd->body.turn_on.req)); | 
|   | 
|     /* fill command */ | 
|     pcmd->body.turn_on.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNON); | 
|   | 
|     /* send command */ | 
|     if (adap->ops->xfer_cmd) { | 
|         error = adap->ops->xfer_cmd(adap, (uint8_t *) pcmd, | 
|                         sizeof(pcmd->body.turn_on.req) + | 
|                         HEADER_SIZE, | 
|                         (uint8_t *) prsp, | 
|                         sizeof(prsp->body.turn_on.rsp) + | 
|                         HEADER_SIZE); | 
|     } | 
|   | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* parse response */ | 
|     error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNON_RSP); | 
|   | 
| out: | 
|     return error; | 
| } | 
|   | 
| /** | 
|  * as10x_cmd_turn_off - send turn off command to AS10x | 
|  * @adap:   pointer to AS10x bus adapter | 
|  * | 
|  * Return 0 on success or negative value in case of error. | 
|  */ | 
| int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) | 
| { | 
|     int error = AS10X_CMD_ERROR; | 
|     struct as10x_cmd_t *pcmd, *prsp; | 
|   | 
|     pcmd = adap->cmd; | 
|     prsp = adap->rsp; | 
|   | 
|     /* prepare command */ | 
|     as10x_cmd_build(pcmd, (++adap->cmd_xid), | 
|             sizeof(pcmd->body.turn_off.req)); | 
|   | 
|     /* fill command */ | 
|     pcmd->body.turn_off.req.proc_id = cpu_to_le16(CONTROL_PROC_TURNOFF); | 
|   | 
|     /* send command */ | 
|     if (adap->ops->xfer_cmd) { | 
|         error = adap->ops->xfer_cmd( | 
|             adap, (uint8_t *) pcmd, | 
|             sizeof(pcmd->body.turn_off.req) + HEADER_SIZE, | 
|             (uint8_t *) prsp, | 
|             sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE); | 
|     } | 
|   | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* parse response */ | 
|     error = as10x_rsp_parse(prsp, CONTROL_PROC_TURNOFF_RSP); | 
|   | 
| out: | 
|     return error; | 
| } | 
|   | 
| /** | 
|  * as10x_cmd_set_tune - send set tune command to AS10x | 
|  * @adap:    pointer to AS10x bus adapter | 
|  * @ptune:   tune parameters | 
|  * | 
|  * Return 0 on success or negative value in case of error. | 
|  */ | 
| int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, | 
|                struct as10x_tune_args *ptune) | 
| { | 
|     int error = AS10X_CMD_ERROR; | 
|     struct as10x_cmd_t *preq, *prsp; | 
|   | 
|     preq = adap->cmd; | 
|     prsp = adap->rsp; | 
|   | 
|     /* prepare command */ | 
|     as10x_cmd_build(preq, (++adap->cmd_xid), | 
|             sizeof(preq->body.set_tune.req)); | 
|   | 
|     /* fill command */ | 
|     preq->body.set_tune.req.proc_id = cpu_to_le16(CONTROL_PROC_SETTUNE); | 
|     preq->body.set_tune.req.args.freq = (__force __u32)cpu_to_le32(ptune->freq); | 
|     preq->body.set_tune.req.args.bandwidth = ptune->bandwidth; | 
|     preq->body.set_tune.req.args.hier_select = ptune->hier_select; | 
|     preq->body.set_tune.req.args.modulation = ptune->modulation; | 
|     preq->body.set_tune.req.args.hierarchy = ptune->hierarchy; | 
|     preq->body.set_tune.req.args.interleaving_mode  = | 
|         ptune->interleaving_mode; | 
|     preq->body.set_tune.req.args.code_rate  = ptune->code_rate; | 
|     preq->body.set_tune.req.args.guard_interval = ptune->guard_interval; | 
|     preq->body.set_tune.req.args.transmission_mode  = | 
|         ptune->transmission_mode; | 
|   | 
|     /* send command */ | 
|     if (adap->ops->xfer_cmd) { | 
|         error = adap->ops->xfer_cmd(adap, | 
|                         (uint8_t *) preq, | 
|                         sizeof(preq->body.set_tune.req) | 
|                         + HEADER_SIZE, | 
|                         (uint8_t *) prsp, | 
|                         sizeof(prsp->body.set_tune.rsp) | 
|                         + HEADER_SIZE); | 
|     } | 
|   | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* parse response */ | 
|     error = as10x_rsp_parse(prsp, CONTROL_PROC_SETTUNE_RSP); | 
|   | 
| out: | 
|     return error; | 
| } | 
|   | 
| /** | 
|  * as10x_cmd_get_tune_status - send get tune status command to AS10x | 
|  * @adap: pointer to AS10x bus adapter | 
|  * @pstatus: pointer to updated status structure of the current tune | 
|  * | 
|  * Return 0 on success or negative value in case of error. | 
|  */ | 
| int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, | 
|                   struct as10x_tune_status *pstatus) | 
| { | 
|     int error = AS10X_CMD_ERROR; | 
|     struct as10x_cmd_t  *preq, *prsp; | 
|   | 
|     preq = adap->cmd; | 
|     prsp = adap->rsp; | 
|   | 
|     /* prepare command */ | 
|     as10x_cmd_build(preq, (++adap->cmd_xid), | 
|             sizeof(preq->body.get_tune_status.req)); | 
|   | 
|     /* fill command */ | 
|     preq->body.get_tune_status.req.proc_id = | 
|         cpu_to_le16(CONTROL_PROC_GETTUNESTAT); | 
|   | 
|     /* send command */ | 
|     if (adap->ops->xfer_cmd) { | 
|         error = adap->ops->xfer_cmd( | 
|             adap, | 
|             (uint8_t *) preq, | 
|             sizeof(preq->body.get_tune_status.req) + HEADER_SIZE, | 
|             (uint8_t *) prsp, | 
|             sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE); | 
|     } | 
|   | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* parse response */ | 
|     error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTUNESTAT_RSP); | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* Response OK -> get response data */ | 
|     pstatus->tune_state = prsp->body.get_tune_status.rsp.sts.tune_state; | 
|     pstatus->signal_strength  = | 
|         le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.signal_strength); | 
|     pstatus->PER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.PER); | 
|     pstatus->BER = le16_to_cpu((__force __le16)prsp->body.get_tune_status.rsp.sts.BER); | 
|   | 
| out: | 
|     return error; | 
| } | 
|   | 
| /** | 
|  * as10x_cmd_get_tps - send get TPS command to AS10x | 
|  * @adap:      pointer to AS10x handle | 
|  * @ptps:      pointer to TPS parameters structure | 
|  * | 
|  * Return 0 on success or negative value in case of error. | 
|  */ | 
| int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) | 
| { | 
|     int error = AS10X_CMD_ERROR; | 
|     struct as10x_cmd_t *pcmd, *prsp; | 
|   | 
|     pcmd = adap->cmd; | 
|     prsp = adap->rsp; | 
|   | 
|     /* prepare command */ | 
|     as10x_cmd_build(pcmd, (++adap->cmd_xid), | 
|             sizeof(pcmd->body.get_tps.req)); | 
|   | 
|     /* fill command */ | 
|     pcmd->body.get_tune_status.req.proc_id = | 
|         cpu_to_le16(CONTROL_PROC_GETTPS); | 
|   | 
|     /* send command */ | 
|     if (adap->ops->xfer_cmd) { | 
|         error = adap->ops->xfer_cmd(adap, | 
|                         (uint8_t *) pcmd, | 
|                         sizeof(pcmd->body.get_tps.req) + | 
|                         HEADER_SIZE, | 
|                         (uint8_t *) prsp, | 
|                         sizeof(prsp->body.get_tps.rsp) + | 
|                         HEADER_SIZE); | 
|     } | 
|   | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* parse response */ | 
|     error = as10x_rsp_parse(prsp, CONTROL_PROC_GETTPS_RSP); | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* Response OK -> get response data */ | 
|     ptps->modulation = prsp->body.get_tps.rsp.tps.modulation; | 
|     ptps->hierarchy = prsp->body.get_tps.rsp.tps.hierarchy; | 
|     ptps->interleaving_mode = prsp->body.get_tps.rsp.tps.interleaving_mode; | 
|     ptps->code_rate_HP = prsp->body.get_tps.rsp.tps.code_rate_HP; | 
|     ptps->code_rate_LP = prsp->body.get_tps.rsp.tps.code_rate_LP; | 
|     ptps->guard_interval = prsp->body.get_tps.rsp.tps.guard_interval; | 
|     ptps->transmission_mode  = prsp->body.get_tps.rsp.tps.transmission_mode; | 
|     ptps->DVBH_mask_HP = prsp->body.get_tps.rsp.tps.DVBH_mask_HP; | 
|     ptps->DVBH_mask_LP = prsp->body.get_tps.rsp.tps.DVBH_mask_LP; | 
|     ptps->cell_ID = le16_to_cpu((__force __le16)prsp->body.get_tps.rsp.tps.cell_ID); | 
|   | 
| out: | 
|     return error; | 
| } | 
|   | 
| /** | 
|  * as10x_cmd_get_demod_stats - send get demod stats command to AS10x | 
|  * @adap:          pointer to AS10x bus adapter | 
|  * @pdemod_stats:  pointer to demod stats parameters structure | 
|  * | 
|  * Return 0 on success or negative value in case of error. | 
|  */ | 
| int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, | 
|                   struct as10x_demod_stats *pdemod_stats) | 
| { | 
|     int error = AS10X_CMD_ERROR; | 
|     struct as10x_cmd_t *pcmd, *prsp; | 
|   | 
|     pcmd = adap->cmd; | 
|     prsp = adap->rsp; | 
|   | 
|     /* prepare command */ | 
|     as10x_cmd_build(pcmd, (++adap->cmd_xid), | 
|             sizeof(pcmd->body.get_demod_stats.req)); | 
|   | 
|     /* fill command */ | 
|     pcmd->body.get_demod_stats.req.proc_id = | 
|         cpu_to_le16(CONTROL_PROC_GET_DEMOD_STATS); | 
|   | 
|     /* send command */ | 
|     if (adap->ops->xfer_cmd) { | 
|         error = adap->ops->xfer_cmd(adap, | 
|                 (uint8_t *) pcmd, | 
|                 sizeof(pcmd->body.get_demod_stats.req) | 
|                 + HEADER_SIZE, | 
|                 (uint8_t *) prsp, | 
|                 sizeof(prsp->body.get_demod_stats.rsp) | 
|                 + HEADER_SIZE); | 
|     } | 
|   | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* parse response */ | 
|     error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_DEMOD_STATS_RSP); | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* Response OK -> get response data */ | 
|     pdemod_stats->frame_count = | 
|         le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.frame_count); | 
|     pdemod_stats->bad_frame_count = | 
|         le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bad_frame_count); | 
|     pdemod_stats->bytes_fixed_by_rs = | 
|         le32_to_cpu((__force __le32)prsp->body.get_demod_stats.rsp.stats.bytes_fixed_by_rs); | 
|     pdemod_stats->mer = | 
|         le16_to_cpu((__force __le16)prsp->body.get_demod_stats.rsp.stats.mer); | 
|     pdemod_stats->has_started = | 
|         prsp->body.get_demod_stats.rsp.stats.has_started; | 
|   | 
| out: | 
|     return error; | 
| } | 
|   | 
| /** | 
|  * as10x_cmd_get_impulse_resp - send get impulse response command to AS10x | 
|  * @adap:     pointer to AS10x bus adapter | 
|  * @is_ready: pointer to value indicating when impulse | 
|  *          response data is ready | 
|  * | 
|  * Return 0 on success or negative value in case of error. | 
|  */ | 
| int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, | 
|                    uint8_t *is_ready) | 
| { | 
|     int error = AS10X_CMD_ERROR; | 
|     struct as10x_cmd_t *pcmd, *prsp; | 
|   | 
|     pcmd = adap->cmd; | 
|     prsp = adap->rsp; | 
|   | 
|     /* prepare command */ | 
|     as10x_cmd_build(pcmd, (++adap->cmd_xid), | 
|             sizeof(pcmd->body.get_impulse_rsp.req)); | 
|   | 
|     /* fill command */ | 
|     pcmd->body.get_impulse_rsp.req.proc_id = | 
|         cpu_to_le16(CONTROL_PROC_GET_IMPULSE_RESP); | 
|   | 
|     /* send command */ | 
|     if (adap->ops->xfer_cmd) { | 
|         error = adap->ops->xfer_cmd(adap, | 
|                     (uint8_t *) pcmd, | 
|                     sizeof(pcmd->body.get_impulse_rsp.req) | 
|                     + HEADER_SIZE, | 
|                     (uint8_t *) prsp, | 
|                     sizeof(prsp->body.get_impulse_rsp.rsp) | 
|                     + HEADER_SIZE); | 
|     } | 
|   | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* parse response */ | 
|     error = as10x_rsp_parse(prsp, CONTROL_PROC_GET_IMPULSE_RESP_RSP); | 
|     if (error < 0) | 
|         goto out; | 
|   | 
|     /* Response OK -> get response data */ | 
|     *is_ready = prsp->body.get_impulse_rsp.rsp.is_ready; | 
|   | 
| out: | 
|     return error; | 
| } | 
|   | 
| /** | 
|  * as10x_cmd_build - build AS10x command header | 
|  * @pcmd:     pointer to AS10x command buffer | 
|  * @xid:      sequence id of the command | 
|  * @cmd_len:  length of the command | 
|  */ | 
| void as10x_cmd_build(struct as10x_cmd_t *pcmd, | 
|              uint16_t xid, uint16_t cmd_len) | 
| { | 
|     pcmd->header.req_id = cpu_to_le16(xid); | 
|     pcmd->header.prog = cpu_to_le16(SERVICE_PROG_ID); | 
|     pcmd->header.version = cpu_to_le16(SERVICE_PROG_VERSION); | 
|     pcmd->header.data_len = cpu_to_le16(cmd_len); | 
| } | 
|   | 
| /** | 
|  * as10x_rsp_parse - Parse command response | 
|  * @prsp:       pointer to AS10x command buffer | 
|  * @proc_id:    id of the command | 
|  * | 
|  * Return 0 on success or negative value in case of error. | 
|  */ | 
| int as10x_rsp_parse(struct as10x_cmd_t *prsp, uint16_t proc_id) | 
| { | 
|     int error; | 
|   | 
|     /* extract command error code */ | 
|     error = prsp->body.common.rsp.error; | 
|   | 
|     if ((error == 0) && | 
|         (le16_to_cpu(prsp->body.common.rsp.proc_id) == proc_id)) { | 
|         return 0; | 
|     } | 
|   | 
|     return AS10X_CMD_ERROR; | 
| } |