Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,19 @@
config DVB_FIREDTV
tristate "FireDTV and FloppyDTV"
depends on DVB_CORE && FIREWIRE
help
Support for DVB receivers from Digital Everywhere
which are connected via IEEE 1394 (FireWire).
These devices don't have an MPEG decoder built in,
so you need an external software decoder to watch TV.
To compile this driver as a module, say M here:
the module will be called firedtv.
if DVB_FIREDTV
config DVB_FIREDTV_INPUT
def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m)
endif # DVB_FIREDTV

View file

@ -0,0 +1,6 @@
obj-$(CONFIG_DVB_FIREDTV) += firedtv.o
firedtv-y += firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o
firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o
ccflags-y += -Idrivers/media/dvb-core

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,258 @@
/*
* FireDTV driver (formerly known as FireSAT)
*
* Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
* Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/dvb/ca.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <dvbdev.h>
#include "firedtv.h"
#define EN50221_TAG_APP_INFO_ENQUIRY 0x9f8020
#define EN50221_TAG_CA_INFO_ENQUIRY 0x9f8030
#define EN50221_TAG_CA_PMT 0x9f8032
#define EN50221_TAG_ENTER_MENU 0x9f8022
static int fdtv_ca_ready(struct firedtv_tuner_status *stat)
{
return stat->ca_initialization_status == 1 &&
stat->ca_error_flag == 0 &&
stat->ca_dvb_flag == 1 &&
stat->ca_module_present_status == 1;
}
static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat)
{
int flags = 0;
if (stat->ca_module_present_status == 1)
flags |= CA_CI_MODULE_PRESENT;
if (stat->ca_initialization_status == 1 &&
stat->ca_error_flag == 0 &&
stat->ca_dvb_flag == 1)
flags |= CA_CI_MODULE_READY;
return flags;
}
static int fdtv_ca_get_caps(void *arg)
{
struct ca_caps *cap = arg;
cap->slot_num = 1;
cap->slot_type = CA_CI;
cap->descr_num = 1;
cap->descr_type = CA_ECD;
return 0;
}
static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg)
{
struct firedtv_tuner_status stat;
struct ca_slot_info *slot = arg;
int err;
err = avc_tuner_status(fdtv, &stat);
if (err)
return err;
if (slot->num != 0)
return -EACCES;
slot->type = CA_CI;
slot->flags = fdtv_get_ca_flags(&stat);
return 0;
}
static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg)
{
struct ca_msg *reply = arg;
return avc_ca_app_info(fdtv, reply->msg, &reply->length);
}
static int fdtv_ca_info(struct firedtv *fdtv, void *arg)
{
struct ca_msg *reply = arg;
return avc_ca_info(fdtv, reply->msg, &reply->length);
}
static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg)
{
struct ca_msg *reply = arg;
return avc_ca_get_mmi(fdtv, reply->msg, &reply->length);
}
static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg)
{
struct firedtv_tuner_status stat;
int err;
switch (fdtv->ca_last_command) {
case EN50221_TAG_APP_INFO_ENQUIRY:
err = fdtv_ca_app_info(fdtv, arg);
break;
case EN50221_TAG_CA_INFO_ENQUIRY:
err = fdtv_ca_info(fdtv, arg);
break;
default:
err = avc_tuner_status(fdtv, &stat);
if (err)
break;
if (stat.ca_mmi == 1)
err = fdtv_ca_get_mmi(fdtv, arg);
else {
dev_info(fdtv->device, "unhandled CA message 0x%08x\n",
fdtv->ca_last_command);
err = -EACCES;
}
}
fdtv->ca_last_command = 0;
return err;
}
static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
{
struct ca_msg *msg = arg;
int data_pos;
int data_length;
int i;
data_pos = 4;
if (msg->msg[3] & 0x80) {
data_length = 0;
for (i = 0; i < (msg->msg[3] & 0x7f); i++)
data_length = (data_length << 8) + msg->msg[data_pos++];
} else {
data_length = msg->msg[3];
}
return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
}
static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg)
{
struct ca_msg *msg = arg;
int err;
/* Do we need a semaphore for this? */
fdtv->ca_last_command =
(msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2];
switch (fdtv->ca_last_command) {
case EN50221_TAG_CA_PMT:
err = fdtv_ca_pmt(fdtv, arg);
break;
case EN50221_TAG_APP_INFO_ENQUIRY:
/* handled in ca_get_msg */
err = 0;
break;
case EN50221_TAG_CA_INFO_ENQUIRY:
/* handled in ca_get_msg */
err = 0;
break;
case EN50221_TAG_ENTER_MENU:
err = avc_ca_enter_menu(fdtv);
break;
default:
dev_err(fdtv->device, "unhandled CA message 0x%08x\n",
fdtv->ca_last_command);
err = -EACCES;
}
return err;
}
static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct dvb_device *dvbdev = file->private_data;
struct firedtv *fdtv = dvbdev->priv;
struct firedtv_tuner_status stat;
int err;
switch (cmd) {
case CA_RESET:
err = avc_ca_reset(fdtv);
break;
case CA_GET_CAP:
err = fdtv_ca_get_caps(arg);
break;
case CA_GET_SLOT_INFO:
err = fdtv_ca_get_slot_info(fdtv, arg);
break;
case CA_GET_MSG:
err = fdtv_ca_get_msg(fdtv, arg);
break;
case CA_SEND_MSG:
err = fdtv_ca_send_msg(fdtv, arg);
break;
default:
dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd);
err = -EOPNOTSUPP;
}
/* FIXME Is this necessary? */
avc_tuner_status(fdtv, &stat);
return err;
}
static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait)
{
return POLLIN;
}
static const struct file_operations fdtv_ca_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = dvb_generic_ioctl,
.open = dvb_generic_open,
.release = dvb_generic_release,
.poll = fdtv_ca_io_poll,
.llseek = noop_llseek,
};
static struct dvb_device fdtv_ca = {
.users = 1,
.readers = 1,
.writers = 1,
.fops = &fdtv_ca_fops,
.kernel_ioctl = fdtv_ca_ioctl,
};
int fdtv_ca_register(struct firedtv *fdtv)
{
struct firedtv_tuner_status stat;
int err;
if (avc_tuner_status(fdtv, &stat))
return -EINVAL;
if (!fdtv_ca_ready(&stat))
return -EFAULT;
err = dvb_register_device(&fdtv->adapter, &fdtv->cadev,
&fdtv_ca, fdtv, DVB_DEVICE_CA);
if (stat.ca_application_info == 0)
dev_err(fdtv->device, "CaApplicationInfo is not set\n");
if (stat.ca_date_time_request == 1)
avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval);
return err;
}
void fdtv_ca_release(struct firedtv *fdtv)
{
if (fdtv->cadev)
dvb_unregister_device(fdtv->cadev);
}

View file

@ -0,0 +1,248 @@
/*
* FireDTV driver (formerly known as FireSAT)
*
* Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
* Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <dmxdev.h>
#include <dvb_demux.h>
#include <dvbdev.h>
#include <dvb_frontend.h>
#include "firedtv.h"
static int alloc_channel(struct firedtv *fdtv)
{
int i;
for (i = 0; i < 16; i++)
if (!__test_and_set_bit(i, &fdtv->channel_active))
break;
return i;
}
static void collect_channels(struct firedtv *fdtv, int *pidc, u16 pid[])
{
int i, n;
for (i = 0, n = 0; i < 16; i++)
if (test_bit(i, &fdtv->channel_active))
pid[n++] = fdtv->channel_pid[i];
*pidc = n;
}
static inline void dealloc_channel(struct firedtv *fdtv, int i)
{
__clear_bit(i, &fdtv->channel_active);
}
int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct firedtv *fdtv = dvbdmxfeed->demux->priv;
int pidc, c, ret;
u16 pids[16];
switch (dvbdmxfeed->type) {
case DMX_TYPE_TS:
case DMX_TYPE_SEC:
break;
default:
dev_err(fdtv->device, "can't start dmx feed: invalid type %u\n",
dvbdmxfeed->type);
return -EINVAL;
}
if (mutex_lock_interruptible(&fdtv->demux_mutex))
return -EINTR;
if (dvbdmxfeed->type == DMX_TYPE_TS) {
switch (dvbdmxfeed->pes_type) {
case DMX_PES_VIDEO:
case DMX_PES_AUDIO:
case DMX_PES_TELETEXT:
case DMX_PES_PCR:
case DMX_PES_OTHER:
c = alloc_channel(fdtv);
break;
default:
dev_err(fdtv->device,
"can't start dmx feed: invalid pes type %u\n",
dvbdmxfeed->pes_type);
ret = -EINVAL;
goto out;
}
} else {
c = alloc_channel(fdtv);
}
if (c > 15) {
dev_err(fdtv->device, "can't start dmx feed: busy\n");
ret = -EBUSY;
goto out;
}
dvbdmxfeed->priv = (typeof(dvbdmxfeed->priv))(unsigned long)c;
fdtv->channel_pid[c] = dvbdmxfeed->pid;
collect_channels(fdtv, &pidc, pids);
if (dvbdmxfeed->pid == 8192) {
ret = avc_tuner_get_ts(fdtv);
if (ret) {
dealloc_channel(fdtv, c);
dev_err(fdtv->device, "can't get TS\n");
goto out;
}
} else {
ret = avc_tuner_set_pids(fdtv, pidc, pids);
if (ret) {
dealloc_channel(fdtv, c);
dev_err(fdtv->device, "can't set PIDs\n");
goto out;
}
}
out:
mutex_unlock(&fdtv->demux_mutex);
return ret;
}
int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux *demux = dvbdmxfeed->demux;
struct firedtv *fdtv = demux->priv;
int pidc, c, ret;
u16 pids[16];
if (dvbdmxfeed->type == DMX_TYPE_TS &&
!((dvbdmxfeed->ts_type & TS_PACKET) &&
(demux->dmx.frontend->source != DMX_MEMORY_FE))) {
if (dvbdmxfeed->ts_type & TS_DECODER) {
if (dvbdmxfeed->pes_type >= DMX_PES_OTHER ||
!demux->pesfilter[dvbdmxfeed->pes_type])
return -EINVAL;
demux->pids[dvbdmxfeed->pes_type] |= 0x8000;
demux->pesfilter[dvbdmxfeed->pes_type] = NULL;
}
if (!(dvbdmxfeed->ts_type & TS_DECODER &&
dvbdmxfeed->pes_type < DMX_PES_OTHER))
return 0;
}
if (mutex_lock_interruptible(&fdtv->demux_mutex))
return -EINTR;
c = (unsigned long)dvbdmxfeed->priv;
dealloc_channel(fdtv, c);
collect_channels(fdtv, &pidc, pids);
ret = avc_tuner_set_pids(fdtv, pidc, pids);
mutex_unlock(&fdtv->demux_mutex);
return ret;
}
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
int fdtv_dvb_register(struct firedtv *fdtv, const char *name)
{
int err;
err = dvb_register_adapter(&fdtv->adapter, name,
THIS_MODULE, fdtv->device, adapter_nr);
if (err < 0)
goto fail_log;
/*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/
fdtv->demux.dmx.capabilities = 0;
fdtv->demux.priv = fdtv;
fdtv->demux.filternum = 16;
fdtv->demux.feednum = 16;
fdtv->demux.start_feed = fdtv_start_feed;
fdtv->demux.stop_feed = fdtv_stop_feed;
fdtv->demux.write_to_decoder = NULL;
err = dvb_dmx_init(&fdtv->demux);
if (err)
goto fail_unreg_adapter;
fdtv->dmxdev.filternum = 16;
fdtv->dmxdev.demux = &fdtv->demux.dmx;
fdtv->dmxdev.capabilities = 0;
err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter);
if (err)
goto fail_dmx_release;
fdtv->frontend.source = DMX_FRONTEND_0;
err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, &fdtv->frontend);
if (err)
goto fail_dmxdev_release;
err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx,
&fdtv->frontend);
if (err)
goto fail_rem_frontend;
err = dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx);
if (err)
goto fail_disconnect_frontend;
fdtv_frontend_init(fdtv, name);
err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe);
if (err)
goto fail_net_release;
err = fdtv_ca_register(fdtv);
if (err)
dev_info(fdtv->device,
"Conditional Access Module not enabled\n");
return 0;
fail_net_release:
dvb_net_release(&fdtv->dvbnet);
fail_disconnect_frontend:
fdtv->demux.dmx.close(&fdtv->demux.dmx);
fail_rem_frontend:
fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend);
fail_dmxdev_release:
dvb_dmxdev_release(&fdtv->dmxdev);
fail_dmx_release:
dvb_dmx_release(&fdtv->demux);
fail_unreg_adapter:
dvb_unregister_adapter(&fdtv->adapter);
fail_log:
dev_err(fdtv->device, "DVB initialization failed\n");
return err;
}
void fdtv_dvb_unregister(struct firedtv *fdtv)
{
fdtv_ca_release(fdtv);
dvb_unregister_frontend(&fdtv->fe);
dvb_net_release(&fdtv->dvbnet);
fdtv->demux.dmx.close(&fdtv->demux.dmx);
fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend);
dvb_dmxdev_release(&fdtv->dmxdev);
dvb_dmx_release(&fdtv->demux);
dvb_unregister_adapter(&fdtv->adapter);
}

View file

@ -0,0 +1,254 @@
/*
* FireDTV driver (formerly known as FireSAT)
*
* Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
* Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/types.h>
#include <dvb_frontend.h>
#include "firedtv.h"
static int fdtv_dvb_init(struct dvb_frontend *fe)
{
struct firedtv *fdtv = fe->sec_priv;
int err;
/* FIXME - allocate free channel at IRM */
fdtv->isochannel = fdtv->adapter.num;
err = cmp_establish_pp_connection(fdtv, fdtv->subunit,
fdtv->isochannel);
if (err) {
dev_err(fdtv->device,
"could not establish point to point connection\n");
return err;
}
return fdtv_start_iso(fdtv);
}
static int fdtv_sleep(struct dvb_frontend *fe)
{
struct firedtv *fdtv = fe->sec_priv;
fdtv_stop_iso(fdtv);
cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel);
fdtv->isochannel = -1;
return 0;
}
#define LNBCONTROL_DONTCARE 0xff
static int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe,
struct dvb_diseqc_master_cmd *cmd)
{
struct firedtv *fdtv = fe->sec_priv;
return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE,
LNBCONTROL_DONTCARE, 1, cmd);
}
static int fdtv_diseqc_send_burst(struct dvb_frontend *fe,
fe_sec_mini_cmd_t minicmd)
{
return 0;
}
static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
{
struct firedtv *fdtv = fe->sec_priv;
fdtv->tone = tone;
return 0;
}
static int fdtv_set_voltage(struct dvb_frontend *fe,
fe_sec_voltage_t voltage)
{
struct firedtv *fdtv = fe->sec_priv;
fdtv->voltage = voltage;
return 0;
}
static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct firedtv *fdtv = fe->sec_priv;
struct firedtv_tuner_status stat;
if (avc_tuner_status(fdtv, &stat))
return -EINVAL;
if (stat.no_rf)
*status = 0;
else
*status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC |
FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
}
static int fdtv_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct firedtv *fdtv = fe->sec_priv;
struct firedtv_tuner_status stat;
if (avc_tuner_status(fdtv, &stat))
return -EINVAL;
*ber = stat.ber;
return 0;
}
static int fdtv_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
struct firedtv *fdtv = fe->sec_priv;
struct firedtv_tuner_status stat;
if (avc_tuner_status(fdtv, &stat))
return -EINVAL;
*strength = stat.signal_strength << 8;
return 0;
}
static int fdtv_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct firedtv *fdtv = fe->sec_priv;
struct firedtv_tuner_status stat;
if (avc_tuner_status(fdtv, &stat))
return -EINVAL;
/* C/N[dB] = -10 * log10(snr / 65535) */
*snr = stat.carrier_noise_ratio * 257;
return 0;
}
static int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks)
{
return -EOPNOTSUPP;
}
static int fdtv_set_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct firedtv *fdtv = fe->sec_priv;
return avc_tuner_dsd(fdtv, p);
}
void fdtv_frontend_init(struct firedtv *fdtv, const char *name)
{
struct dvb_frontend_ops *ops = &fdtv->fe.ops;
struct dvb_frontend_info *fi = &ops->info;
ops->init = fdtv_dvb_init;
ops->sleep = fdtv_sleep;
ops->set_frontend = fdtv_set_frontend;
ops->read_status = fdtv_read_status;
ops->read_ber = fdtv_read_ber;
ops->read_signal_strength = fdtv_read_signal_strength;
ops->read_snr = fdtv_read_snr;
ops->read_ucblocks = fdtv_read_uncorrected_blocks;
ops->diseqc_send_master_cmd = fdtv_diseqc_send_master_cmd;
ops->diseqc_send_burst = fdtv_diseqc_send_burst;
ops->set_tone = fdtv_set_tone;
ops->set_voltage = fdtv_set_voltage;
switch (fdtv->type) {
case FIREDTV_DVB_S:
ops->delsys[0] = SYS_DVBS;
fi->frequency_min = 950000;
fi->frequency_max = 2150000;
fi->frequency_stepsize = 125;
fi->symbol_rate_min = 1000000;
fi->symbol_rate_max = 40000000;
fi->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;
break;
case FIREDTV_DVB_S2:
ops->delsys[0] = SYS_DVBS;
ops->delsys[1] = SYS_DVBS2;
fi->frequency_min = 950000;
fi->frequency_max = 2150000;
fi->frequency_stepsize = 125;
fi->symbol_rate_min = 1000000;
fi->symbol_rate_max = 40000000;
fi->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_2G_MODULATION;
break;
case FIREDTV_DVB_C:
ops->delsys[0] = SYS_DVBC_ANNEX_A;
fi->frequency_min = 47000000;
fi->frequency_max = 866000000;
fi->frequency_stepsize = 62500;
fi->symbol_rate_min = 870000;
fi->symbol_rate_max = 6900000;
fi->caps = FE_CAN_INVERSION_AUTO |
FE_CAN_QAM_16 |
FE_CAN_QAM_32 |
FE_CAN_QAM_64 |
FE_CAN_QAM_128 |
FE_CAN_QAM_256 |
FE_CAN_QAM_AUTO;
break;
case FIREDTV_DVB_T:
ops->delsys[0] = SYS_DVBT;
fi->frequency_min = 49000000;
fi->frequency_max = 861000000;
fi->frequency_stepsize = 62500;
fi->caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_2_3 |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO;
break;
default:
dev_err(fdtv->device, "no frontend for model type %d\n",
fdtv->type);
}
strcpy(fi->name, name);
fdtv->fe.dvb = &fdtv->adapter;
fdtv->fe.sec_priv = fdtv;
}

View file

@ -0,0 +1,428 @@
/*
* FireDTV driver -- firewire I/O backend
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <asm/page.h>
#include <dvb_demux.h>
#include "firedtv.h"
static LIST_HEAD(node_list);
static DEFINE_SPINLOCK(node_list_lock);
static inline struct fw_device *device_of(struct firedtv *fdtv)
{
return fw_device(fdtv->device->parent);
}
static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len,
int tcode)
{
struct fw_device *device = device_of(fdtv);
int rcode, generation = device->generation;
smp_rmb(); /* node_id vs. generation */
rcode = fw_run_transaction(device->card, tcode, device->node_id,
generation, device->max_speed, addr, data, len);
return rcode != RCODE_COMPLETE ? -EIO : 0;
}
int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data)
{
return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP);
}
int fdtv_read(struct firedtv *fdtv, u64 addr, void *data)
{
return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST);
}
int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
{
return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST);
}
#define ISO_HEADER_SIZE 4
#define CIP_HEADER_SIZE 8
#define MPEG2_TS_HEADER_SIZE 4
#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188)
#define MAX_PACKET_SIZE 1024 /* 776, rounded up to 2^n */
#define PACKETS_PER_PAGE (PAGE_SIZE / MAX_PACKET_SIZE)
#define N_PACKETS 64 /* buffer size */
#define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE)
#define IRQ_INTERVAL 16
struct fdtv_ir_context {
struct fw_iso_context *context;
struct fw_iso_buffer buffer;
int interrupt_packet;
int current_packet;
char *pages[N_PAGES];
};
static int queue_iso(struct fdtv_ir_context *ctx, int index)
{
struct fw_iso_packet p;
p.payload_length = MAX_PACKET_SIZE;
p.interrupt = !(++ctx->interrupt_packet & (IRQ_INTERVAL - 1));
p.skip = 0;
p.header_length = ISO_HEADER_SIZE;
return fw_iso_context_queue(ctx->context, &p, &ctx->buffer,
index * MAX_PACKET_SIZE);
}
static void handle_iso(struct fw_iso_context *context, u32 cycle,
size_t header_length, void *header, void *data)
{
struct firedtv *fdtv = data;
struct fdtv_ir_context *ctx = fdtv->ir_context;
__be32 *h, *h_end;
int length, err, i = ctx->current_packet;
char *p, *p_end;
for (h = header, h_end = h + header_length / 4; h < h_end; h++) {
length = be32_to_cpup(h) >> 16;
if (unlikely(length > MAX_PACKET_SIZE)) {
dev_err(fdtv->device, "length = %d\n", length);
length = MAX_PACKET_SIZE;
}
p = ctx->pages[i / PACKETS_PER_PAGE]
+ (i % PACKETS_PER_PAGE) * MAX_PACKET_SIZE;
p_end = p + length;
for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end;
p += MPEG2_TS_SOURCE_PACKET_SIZE)
dvb_dmx_swfilter_packets(&fdtv->demux, p, 1);
err = queue_iso(ctx, i);
if (unlikely(err))
dev_err(fdtv->device, "requeue failed\n");
i = (i + 1) & (N_PACKETS - 1);
}
fw_iso_context_queue_flush(ctx->context);
ctx->current_packet = i;
}
int fdtv_start_iso(struct firedtv *fdtv)
{
struct fdtv_ir_context *ctx;
struct fw_device *device = device_of(fdtv);
int i, err;
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->context = fw_iso_context_create(device->card,
FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel,
device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv);
if (IS_ERR(ctx->context)) {
err = PTR_ERR(ctx->context);
goto fail_free;
}
err = fw_iso_buffer_init(&ctx->buffer, device->card,
N_PAGES, DMA_FROM_DEVICE);
if (err)
goto fail_context_destroy;
ctx->interrupt_packet = 0;
ctx->current_packet = 0;
for (i = 0; i < N_PAGES; i++)
ctx->pages[i] = page_address(ctx->buffer.pages[i]);
for (i = 0; i < N_PACKETS; i++) {
err = queue_iso(ctx, i);
if (err)
goto fail;
}
err = fw_iso_context_start(ctx->context, -1, 0,
FW_ISO_CONTEXT_MATCH_ALL_TAGS);
if (err)
goto fail;
fdtv->ir_context = ctx;
return 0;
fail:
fw_iso_buffer_destroy(&ctx->buffer, device->card);
fail_context_destroy:
fw_iso_context_destroy(ctx->context);
fail_free:
kfree(ctx);
return err;
}
void fdtv_stop_iso(struct firedtv *fdtv)
{
struct fdtv_ir_context *ctx = fdtv->ir_context;
fw_iso_context_stop(ctx->context);
fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card);
fw_iso_context_destroy(ctx->context);
kfree(ctx);
}
static void handle_fcp(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source, int generation,
unsigned long long offset, void *payload, size_t length,
void *callback_data)
{
struct firedtv *f, *fdtv = NULL;
struct fw_device *device;
unsigned long flags;
int su;
if (length < 2 || (((u8 *)payload)[0] & 0xf0) != 0)
return;
su = ((u8 *)payload)[1] & 0x7;
spin_lock_irqsave(&node_list_lock, flags);
list_for_each_entry(f, &node_list, list) {
device = device_of(f);
if (device->generation != generation)
continue;
smp_rmb(); /* node_id vs. generation */
if (device->card == card &&
device->node_id == source &&
(f->subunit == su || (f->subunit == 0 && su == 0x7))) {
fdtv = f;
break;
}
}
spin_unlock_irqrestore(&node_list_lock, flags);
if (fdtv)
avc_recv(fdtv, payload, length);
}
static struct fw_address_handler fcp_handler = {
.length = CSR_FCP_END - CSR_FCP_RESPONSE,
.address_callback = handle_fcp,
};
static const struct fw_address_region fcp_region = {
.start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE,
.end = CSR_REGISTER_BASE + CSR_FCP_END,
};
static const char * const model_names[] = {
[FIREDTV_UNKNOWN] = "unknown type",
[FIREDTV_DVB_S] = "FireDTV S/CI",
[FIREDTV_DVB_C] = "FireDTV C/CI",
[FIREDTV_DVB_T] = "FireDTV T/CI",
[FIREDTV_DVB_S2] = "FireDTV S2 ",
};
/* Adjust the template string if models with longer names appear. */
#define MAX_MODEL_NAME_LEN sizeof("FireDTV ????")
static int node_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
{
struct firedtv *fdtv;
char name[MAX_MODEL_NAME_LEN];
int name_len, i, err;
fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL);
if (!fdtv)
return -ENOMEM;
dev_set_drvdata(&unit->device, fdtv);
fdtv->device = &unit->device;
fdtv->isochannel = -1;
fdtv->voltage = 0xff;
fdtv->tone = 0xff;
mutex_init(&fdtv->avc_mutex);
init_waitqueue_head(&fdtv->avc_wait);
mutex_init(&fdtv->demux_mutex);
INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work);
name_len = fw_csr_string(unit->directory, CSR_MODEL,
name, sizeof(name));
for (i = ARRAY_SIZE(model_names); --i; )
if (strlen(model_names[i]) <= name_len &&
strncmp(name, model_names[i], name_len) == 0)
break;
fdtv->type = i;
err = fdtv_register_rc(fdtv, &unit->device);
if (err)
goto fail_free;
spin_lock_irq(&node_list_lock);
list_add_tail(&fdtv->list, &node_list);
spin_unlock_irq(&node_list_lock);
err = avc_identify_subunit(fdtv);
if (err)
goto fail;
err = fdtv_dvb_register(fdtv, model_names[fdtv->type]);
if (err)
goto fail;
avc_register_remote_control(fdtv);
return 0;
fail:
spin_lock_irq(&node_list_lock);
list_del(&fdtv->list);
spin_unlock_irq(&node_list_lock);
fdtv_unregister_rc(fdtv);
fail_free:
kfree(fdtv);
return err;
}
static void node_remove(struct fw_unit *unit)
{
struct firedtv *fdtv = dev_get_drvdata(&unit->device);
fdtv_dvb_unregister(fdtv);
spin_lock_irq(&node_list_lock);
list_del(&fdtv->list);
spin_unlock_irq(&node_list_lock);
fdtv_unregister_rc(fdtv);
kfree(fdtv);
}
static void node_update(struct fw_unit *unit)
{
struct firedtv *fdtv = dev_get_drvdata(&unit->device);
if (fdtv->isochannel >= 0)
cmp_establish_pp_connection(fdtv, fdtv->subunit,
fdtv->isochannel);
}
#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \
IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION)
#define DIGITAL_EVERYWHERE_OUI 0x001287
#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d
#define AVC_SW_VERSION_ENTRY 0x010001
static const struct ieee1394_device_id fdtv_id_table[] = {
{
/* FloppyDTV S/CI and FloppyDTV S2 */
.match_flags = MATCH_FLAGS,
.vendor_id = DIGITAL_EVERYWHERE_OUI,
.model_id = 0x000024,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
.version = AVC_SW_VERSION_ENTRY,
}, {
/* FloppyDTV T/CI */
.match_flags = MATCH_FLAGS,
.vendor_id = DIGITAL_EVERYWHERE_OUI,
.model_id = 0x000025,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
.version = AVC_SW_VERSION_ENTRY,
}, {
/* FloppyDTV C/CI */
.match_flags = MATCH_FLAGS,
.vendor_id = DIGITAL_EVERYWHERE_OUI,
.model_id = 0x000026,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
.version = AVC_SW_VERSION_ENTRY,
}, {
/* FireDTV S/CI and FloppyDTV S2 */
.match_flags = MATCH_FLAGS,
.vendor_id = DIGITAL_EVERYWHERE_OUI,
.model_id = 0x000034,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
.version = AVC_SW_VERSION_ENTRY,
}, {
/* FireDTV T/CI */
.match_flags = MATCH_FLAGS,
.vendor_id = DIGITAL_EVERYWHERE_OUI,
.model_id = 0x000035,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
.version = AVC_SW_VERSION_ENTRY,
}, {
/* FireDTV C/CI */
.match_flags = MATCH_FLAGS,
.vendor_id = DIGITAL_EVERYWHERE_OUI,
.model_id = 0x000036,
.specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
.version = AVC_SW_VERSION_ENTRY,
}, {}
};
MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table);
static struct fw_driver fdtv_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "firedtv",
.bus = &fw_bus_type,
},
.probe = node_probe,
.update = node_update,
.remove = node_remove,
.id_table = fdtv_id_table,
};
static int __init fdtv_init(void)
{
int ret;
ret = fw_core_add_address_handler(&fcp_handler, &fcp_region);
if (ret < 0)
return ret;
ret = driver_register(&fdtv_driver.driver);
if (ret < 0)
fw_core_remove_address_handler(&fcp_handler);
return ret;
}
static void __exit fdtv_exit(void)
{
driver_unregister(&fdtv_driver.driver);
fw_core_remove_address_handler(&fcp_handler);
}
module_init(fdtv_init);
module_exit(fdtv_exit);
MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
MODULE_DESCRIPTION("FireDTV DVB Driver");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("FireDTV DVB");

View file

@ -0,0 +1,196 @@
/*
* FireDTV driver (formerly known as FireSAT)
*
* Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
#include <linux/bitops.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include "firedtv.h"
/* fixed table with older keycodes, geared towards MythTV */
static const u16 oldtable[] = {
/* code from device: 0x4501...0x451f */
KEY_ESC,
KEY_F9,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_I,
KEY_0,
KEY_ENTER,
KEY_RED,
KEY_UP,
KEY_GREEN,
KEY_F10,
KEY_SPACE,
KEY_F11,
KEY_YELLOW,
KEY_DOWN,
KEY_BLUE,
KEY_Z,
KEY_P,
KEY_PAGEDOWN,
KEY_LEFT,
KEY_W,
KEY_RIGHT,
KEY_P,
KEY_M,
/* code from device: 0x4540...0x4542 */
KEY_R,
KEY_V,
KEY_C,
};
/* user-modifiable table for a remote as sold in 2008 */
static const u16 keytable[] = {
/* code from device: 0x0300...0x031f */
[0x00] = KEY_POWER,
[0x01] = KEY_SLEEP,
[0x02] = KEY_STOP,
[0x03] = KEY_OK,
[0x04] = KEY_RIGHT,
[0x05] = KEY_1,
[0x06] = KEY_2,
[0x07] = KEY_3,
[0x08] = KEY_LEFT,
[0x09] = KEY_4,
[0x0a] = KEY_5,
[0x0b] = KEY_6,
[0x0c] = KEY_UP,
[0x0d] = KEY_7,
[0x0e] = KEY_8,
[0x0f] = KEY_9,
[0x10] = KEY_DOWN,
[0x11] = KEY_TITLE, /* "OSD" - fixme */
[0x12] = KEY_0,
[0x13] = KEY_F20, /* "16:9" - fixme */
[0x14] = KEY_SCREEN, /* "FULL" - fixme */
[0x15] = KEY_MUTE,
[0x16] = KEY_SUBTITLE,
[0x17] = KEY_RECORD,
[0x18] = KEY_TEXT,
[0x19] = KEY_AUDIO,
[0x1a] = KEY_RED,
[0x1b] = KEY_PREVIOUS,
[0x1c] = KEY_REWIND,
[0x1d] = KEY_PLAYPAUSE,
[0x1e] = KEY_NEXT,
[0x1f] = KEY_VOLUMEUP,
/* code from device: 0x0340...0x0354 */
[0x20] = KEY_CHANNELUP,
[0x21] = KEY_F21, /* "4:3" - fixme */
[0x22] = KEY_TV,
[0x23] = KEY_DVD,
[0x24] = KEY_VCR,
[0x25] = KEY_AUX,
[0x26] = KEY_GREEN,
[0x27] = KEY_YELLOW,
[0x28] = KEY_BLUE,
[0x29] = KEY_CHANNEL, /* "CH.LIST" */
[0x2a] = KEY_VENDOR, /* "CI" - fixme */
[0x2b] = KEY_VOLUMEDOWN,
[0x2c] = KEY_CHANNELDOWN,
[0x2d] = KEY_LAST,
[0x2e] = KEY_INFO,
[0x2f] = KEY_FORWARD,
[0x30] = KEY_LIST,
[0x31] = KEY_FAVORITES,
[0x32] = KEY_MENU,
[0x33] = KEY_EPG,
[0x34] = KEY_EXIT,
};
int fdtv_register_rc(struct firedtv *fdtv, struct device *dev)
{
struct input_dev *idev;
int i, err;
idev = input_allocate_device();
if (!idev)
return -ENOMEM;
fdtv->remote_ctrl_dev = idev;
idev->name = "FireDTV remote control";
idev->dev.parent = dev;
idev->evbit[0] = BIT_MASK(EV_KEY);
idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL);
if (!idev->keycode) {
err = -ENOMEM;
goto fail;
}
idev->keycodesize = sizeof(keytable[0]);
idev->keycodemax = ARRAY_SIZE(keytable);
for (i = 0; i < ARRAY_SIZE(keytable); i++)
set_bit(keytable[i], idev->keybit);
err = input_register_device(idev);
if (err)
goto fail_free_keymap;
return 0;
fail_free_keymap:
kfree(idev->keycode);
fail:
input_free_device(idev);
return err;
}
void fdtv_unregister_rc(struct firedtv *fdtv)
{
cancel_work_sync(&fdtv->remote_ctrl_work);
kfree(fdtv->remote_ctrl_dev->keycode);
input_unregister_device(fdtv->remote_ctrl_dev);
}
void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code)
{
struct input_dev *idev = fdtv->remote_ctrl_dev;
u16 *keycode = idev->keycode;
if (code >= 0x0300 && code <= 0x031f)
code = keycode[code - 0x0300];
else if (code >= 0x0340 && code <= 0x0354)
code = keycode[code - 0x0320];
else if (code >= 0x4501 && code <= 0x451f)
code = oldtable[code - 0x4501];
else if (code >= 0x4540 && code <= 0x4542)
code = oldtable[code - 0x4521];
else {
printk(KERN_DEBUG "firedtv: invalid key code 0x%04x "
"from remote control\n", code);
return;
}
input_report_key(idev, code, 1);
input_sync(idev);
input_report_key(idev, code, 0);
input_sync(idev);
}

View file

@ -0,0 +1,169 @@
/*
* FireDTV driver (formerly known as FireSAT)
*
* Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
* Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
#ifndef _FIREDTV_H
#define _FIREDTV_H
#include <linux/time.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
#include <linux/list.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/spinlock_types.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <demux.h>
#include <dmxdev.h>
#include <dvb_demux.h>
#include <dvb_frontend.h>
#include <dvb_net.h>
#include <dvbdev.h>
struct firedtv_tuner_status {
unsigned active_system:8;
unsigned searching:1;
unsigned moving:1;
unsigned no_rf:1;
unsigned input:1;
unsigned selected_antenna:7;
unsigned ber:32;
unsigned signal_strength:8;
unsigned raster_frequency:2;
unsigned rf_frequency:22;
unsigned man_dep_info_length:8;
unsigned front_end_error:1;
unsigned antenna_error:1;
unsigned front_end_power_status:1;
unsigned power_supply:1;
unsigned carrier_noise_ratio:16;
unsigned power_supply_voltage:8;
unsigned antenna_voltage:8;
unsigned firewire_bus_voltage:8;
unsigned ca_mmi:1;
unsigned ca_pmt_reply:1;
unsigned ca_date_time_request:1;
unsigned ca_application_info:1;
unsigned ca_module_present_status:1;
unsigned ca_dvb_flag:1;
unsigned ca_error_flag:1;
unsigned ca_initialization_status:1;
};
enum model_type {
FIREDTV_UNKNOWN = 0,
FIREDTV_DVB_S = 1,
FIREDTV_DVB_C = 2,
FIREDTV_DVB_T = 3,
FIREDTV_DVB_S2 = 4,
};
struct device;
struct input_dev;
struct fdtv_ir_context;
struct firedtv {
struct device *device;
struct list_head list;
struct dvb_adapter adapter;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dmx_frontend frontend;
struct dvb_net dvbnet;
struct dvb_frontend fe;
struct dvb_device *cadev;
int ca_last_command;
int ca_time_interval;
struct mutex avc_mutex;
wait_queue_head_t avc_wait;
bool avc_reply_received;
struct work_struct remote_ctrl_work;
struct input_dev *remote_ctrl_dev;
enum model_type type;
char subunit;
char isochannel;
struct fdtv_ir_context *ir_context;
fe_sec_voltage_t voltage;
fe_sec_tone_mode_t tone;
struct mutex demux_mutex;
unsigned long channel_active;
u16 channel_pid[16];
int avc_data_length;
u8 avc_data[512];
};
/* firedtv-avc.c */
int avc_recv(struct firedtv *fdtv, void *data, size_t length);
int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat);
struct dtv_frontend_properties;
int avc_tuner_dsd(struct firedtv *fdtv, struct dtv_frontend_properties *params);
int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]);
int avc_tuner_get_ts(struct firedtv *fdtv);
int avc_identify_subunit(struct firedtv *fdtv);
struct dvb_diseqc_master_cmd;
int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst,
char conttone, char nrdiseq,
struct dvb_diseqc_master_cmd *diseqcmd);
void avc_remote_ctrl_work(struct work_struct *work);
int avc_register_remote_control(struct firedtv *fdtv);
int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len);
int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len);
int avc_ca_reset(struct firedtv *fdtv);
int avc_ca_pmt(struct firedtv *fdtv, char *app_info, int length);
int avc_ca_get_time_date(struct firedtv *fdtv, int *interval);
int avc_ca_enter_menu(struct firedtv *fdtv);
int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len);
int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel);
void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel);
/* firedtv-ci.c */
int fdtv_ca_register(struct firedtv *fdtv);
void fdtv_ca_release(struct firedtv *fdtv);
/* firedtv-dvb.c */
int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed);
int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed);
int fdtv_dvb_register(struct firedtv *fdtv, const char *name);
void fdtv_dvb_unregister(struct firedtv *fdtv);
/* firedtv-fe.c */
void fdtv_frontend_init(struct firedtv *fdtv, const char *name);
/* firedtv-fw.c */
int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data);
int fdtv_read(struct firedtv *fdtv, u64 addr, void *data);
int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len);
int fdtv_start_iso(struct firedtv *fdtv);
void fdtv_stop_iso(struct firedtv *fdtv);
/* firedtv-rc.c */
#ifdef CONFIG_DVB_FIREDTV_INPUT
int fdtv_register_rc(struct firedtv *fdtv, struct device *dev);
void fdtv_unregister_rc(struct firedtv *fdtv);
void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code);
#else
static inline int fdtv_register_rc(struct firedtv *fdtv,
struct device *dev) { return 0; }
static inline void fdtv_unregister_rc(struct firedtv *fdtv) {}
static inline void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) {}
#endif
#endif /* _FIREDTV_H */