forked from Minki/linux
577a7ad33a
Those documents don't really describe the driver API. Instead, they contain development-specific information. Yet, as the main index file describes the content of it as: "how specific kernel subsystems work from the point of view of a kernel developer" It seems to be the better fit. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
258 lines
6.1 KiB
C
258 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Frontend part of the Linux driver for the WideView/ Yakumo/ Hama/
|
|
* Typhoon/ Yuan DVB-T USB2.0 receiver.
|
|
*
|
|
* Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@posteo.de>
|
|
*
|
|
* see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
|
|
*/
|
|
#include "dtt200u.h"
|
|
|
|
struct dtt200u_fe_state {
|
|
struct dvb_usb_device *d;
|
|
|
|
enum fe_status stat;
|
|
|
|
struct dtv_frontend_properties fep;
|
|
struct dvb_frontend frontend;
|
|
|
|
unsigned char data[80];
|
|
struct mutex data_mutex;
|
|
};
|
|
|
|
static int dtt200u_fe_read_status(struct dvb_frontend *fe,
|
|
enum fe_status *stat)
|
|
{
|
|
struct dtt200u_fe_state *state = fe->demodulator_priv;
|
|
int ret;
|
|
|
|
mutex_lock(&state->data_mutex);
|
|
state->data[0] = GET_TUNE_STATUS;
|
|
|
|
ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 3, 0);
|
|
if (ret < 0) {
|
|
*stat = 0;
|
|
mutex_unlock(&state->data_mutex);
|
|
return ret;
|
|
}
|
|
|
|
switch (state->data[0]) {
|
|
case 0x01:
|
|
*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER |
|
|
FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
|
|
break;
|
|
case 0x00: /* pending */
|
|
*stat = FE_TIMEDOUT; /* during set_frontend */
|
|
break;
|
|
default:
|
|
case 0x02: /* failed */
|
|
*stat = 0;
|
|
break;
|
|
}
|
|
mutex_unlock(&state->data_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
|
|
{
|
|
struct dtt200u_fe_state *state = fe->demodulator_priv;
|
|
int ret;
|
|
|
|
mutex_lock(&state->data_mutex);
|
|
state->data[0] = GET_VIT_ERR_CNT;
|
|
|
|
ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 3, 0);
|
|
if (ret >= 0)
|
|
*ber = (state->data[0] << 16) | (state->data[1] << 8) | state->data[2];
|
|
|
|
mutex_unlock(&state->data_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
|
|
{
|
|
struct dtt200u_fe_state *state = fe->demodulator_priv;
|
|
int ret;
|
|
|
|
mutex_lock(&state->data_mutex);
|
|
state->data[0] = GET_RS_UNCOR_BLK_CNT;
|
|
|
|
ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 2, 0);
|
|
if (ret >= 0)
|
|
*unc = (state->data[0] << 8) | state->data[1];
|
|
|
|
mutex_unlock(&state->data_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
|
|
{
|
|
struct dtt200u_fe_state *state = fe->demodulator_priv;
|
|
int ret;
|
|
|
|
mutex_lock(&state->data_mutex);
|
|
state->data[0] = GET_AGC;
|
|
|
|
ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 1, 0);
|
|
if (ret >= 0)
|
|
*strength = (state->data[0] << 8) | state->data[0];
|
|
|
|
mutex_unlock(&state->data_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
|
|
{
|
|
struct dtt200u_fe_state *state = fe->demodulator_priv;
|
|
int ret;
|
|
|
|
mutex_lock(&state->data_mutex);
|
|
state->data[0] = GET_SNR;
|
|
|
|
ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 1, 0);
|
|
if (ret >= 0)
|
|
*snr = ~((state->data[0] << 8) | state->data[0]);
|
|
|
|
mutex_unlock(&state->data_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int dtt200u_fe_init(struct dvb_frontend* fe)
|
|
{
|
|
struct dtt200u_fe_state *state = fe->demodulator_priv;
|
|
int ret;
|
|
|
|
mutex_lock(&state->data_mutex);
|
|
state->data[0] = SET_INIT;
|
|
|
|
ret = dvb_usb_generic_write(state->d, state->data, 1);
|
|
mutex_unlock(&state->data_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int dtt200u_fe_sleep(struct dvb_frontend* fe)
|
|
{
|
|
return dtt200u_fe_init(fe);
|
|
}
|
|
|
|
static int dtt200u_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
|
|
{
|
|
tune->min_delay_ms = 1500;
|
|
tune->step_size = 0;
|
|
tune->max_drift = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int dtt200u_fe_set_frontend(struct dvb_frontend *fe)
|
|
{
|
|
struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
|
|
struct dtt200u_fe_state *state = fe->demodulator_priv;
|
|
int ret;
|
|
u16 freq = fep->frequency / 250000;
|
|
|
|
mutex_lock(&state->data_mutex);
|
|
state->data[0] = SET_BANDWIDTH;
|
|
switch (fep->bandwidth_hz) {
|
|
case 8000000:
|
|
state->data[1] = 8;
|
|
break;
|
|
case 7000000:
|
|
state->data[1] = 7;
|
|
break;
|
|
case 6000000:
|
|
state->data[1] = 6;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto ret;
|
|
}
|
|
|
|
ret = dvb_usb_generic_write(state->d, state->data, 2);
|
|
if (ret < 0)
|
|
goto ret;
|
|
|
|
state->data[0] = SET_RF_FREQ;
|
|
state->data[1] = freq & 0xff;
|
|
state->data[2] = (freq >> 8) & 0xff;
|
|
ret = dvb_usb_generic_write(state->d, state->data, 3);
|
|
if (ret < 0)
|
|
goto ret;
|
|
|
|
ret:
|
|
mutex_unlock(&state->data_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int dtt200u_fe_get_frontend(struct dvb_frontend* fe,
|
|
struct dtv_frontend_properties *fep)
|
|
{
|
|
struct dtt200u_fe_state *state = fe->demodulator_priv;
|
|
|
|
memcpy(fep, &state->fep, sizeof(struct dtv_frontend_properties));
|
|
return 0;
|
|
}
|
|
|
|
static void dtt200u_fe_release(struct dvb_frontend* fe)
|
|
{
|
|
struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
|
|
kfree(state);
|
|
}
|
|
|
|
static const struct dvb_frontend_ops dtt200u_fe_ops;
|
|
|
|
struct dvb_frontend* dtt200u_fe_attach(struct dvb_usb_device *d)
|
|
{
|
|
struct dtt200u_fe_state* state = NULL;
|
|
|
|
/* allocate memory for the internal state */
|
|
state = kzalloc(sizeof(struct dtt200u_fe_state), GFP_KERNEL);
|
|
if (state == NULL)
|
|
goto error;
|
|
|
|
deb_info("attaching frontend dtt200u\n");
|
|
|
|
state->d = d;
|
|
mutex_init(&state->data_mutex);
|
|
|
|
memcpy(&state->frontend.ops,&dtt200u_fe_ops,sizeof(struct dvb_frontend_ops));
|
|
state->frontend.demodulator_priv = state;
|
|
|
|
return &state->frontend;
|
|
error:
|
|
return NULL;
|
|
}
|
|
|
|
static const struct dvb_frontend_ops dtt200u_fe_ops = {
|
|
.delsys = { SYS_DVBT },
|
|
.info = {
|
|
.name = "WideView USB DVB-T",
|
|
.frequency_min_hz = 44250 * kHz,
|
|
.frequency_max_hz = 867250 * kHz,
|
|
.frequency_stepsize_hz = 250 * kHz,
|
|
.caps = FE_CAN_INVERSION_AUTO |
|
|
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
|
|
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
|
|
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
|
|
FE_CAN_TRANSMISSION_MODE_AUTO |
|
|
FE_CAN_GUARD_INTERVAL_AUTO |
|
|
FE_CAN_RECOVER |
|
|
FE_CAN_HIERARCHY_AUTO,
|
|
},
|
|
|
|
.release = dtt200u_fe_release,
|
|
|
|
.init = dtt200u_fe_init,
|
|
.sleep = dtt200u_fe_sleep,
|
|
|
|
.set_frontend = dtt200u_fe_set_frontend,
|
|
.get_frontend = dtt200u_fe_get_frontend,
|
|
.get_tune_settings = dtt200u_fe_get_tune_settings,
|
|
|
|
.read_status = dtt200u_fe_read_status,
|
|
.read_ber = dtt200u_fe_read_ber,
|
|
.read_signal_strength = dtt200u_fe_read_signal_strength,
|
|
.read_snr = dtt200u_fe_read_snr,
|
|
.read_ucblocks = dtt200u_fe_read_unc_blocks,
|
|
};
|