Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

51
drivers/media/dvb/Kconfig Normal file
View File

@@ -0,0 +1,51 @@
#
# Multimedia device configuration
#
menu "Digital Video Broadcasting Devices"
config DVB
bool "DVB For Linux"
depends on NET && INET
---help---
Support Digital Video Broadcasting hardware. Enable this if you
own a DVB adapter and want to use it or if you compile Linux for
a digital SetTopBox.
API specs and user tools are available from <http://www.linuxtv.org/>.
Please report problems regarding this driver to the LinuxDVB
mailing list.
If unsure say N.
source "drivers/media/dvb/dvb-core/Kconfig"
comment "Supported SAA7146 based PCI Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/ttpci/Kconfig"
comment "Supported USB Adapters"
depends on DVB_CORE && USB && I2C
source "drivers/media/dvb/dvb-usb/Kconfig"
source "drivers/media/dvb/ttusb-budget/Kconfig"
source "drivers/media/dvb/ttusb-dec/Kconfig"
source "drivers/media/dvb/cinergyT2/Kconfig"
comment "Supported FlexCopII (B2C2) Adapters"
depends on DVB_CORE && (PCI || USB) && I2C
source "drivers/media/dvb/b2c2/Kconfig"
comment "Supported BT878 Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/bt8xx/Kconfig"
comment "Supported Pluto2 Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/pluto2/Kconfig"
comment "Supported DVB Frontends"
depends on DVB_CORE
source "drivers/media/dvb/frontends/Kconfig"
endmenu

View File

@@ -0,0 +1,5 @@
#
# Makefile for the kernel multimedia device drivers.
#
obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/

View File

@@ -0,0 +1,40 @@
config DVB_B2C2_FLEXCOP
tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters"
depends on DVB_CORE && I2C
select DVB_PLL
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_MT312 if !DVB_FE_CUSTOMISE
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_BCM3510 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
help
Support for the digital TV receiver chip made by B2C2 Inc. included in
Technisats PCI cards and USB boxes.
Say Y if you own such a device and want to use it.
config DVB_B2C2_FLEXCOP_PCI
tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI"
depends on DVB_B2C2_FLEXCOP && PCI && I2C
help
Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2.
Say Y if you own such a device and want to use it.
config DVB_B2C2_FLEXCOP_USB
tristate "Technisat/B2C2 Air/Sky/Cable2PC USB"
depends on DVB_B2C2_FLEXCOP && USB && I2C
help
Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2,
Say Y if you own such a device and want to use it.
config DVB_B2C2_FLEXCOP_DEBUG
bool "Enable debug for the B2C2 FlexCop drivers"
depends on DVB_B2C2_FLEXCOP
help
Say Y if you want to enable the module option to control debug messages
of all B2C2 FlexCop drivers.

View File

@@ -0,0 +1,12 @@
b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \
flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o \
flexcop-dma.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o
b2c2-flexcop-pci-objs = flexcop-pci.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o
b2c2-flexcop-usb-objs = flexcop-usb.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/

View File

@@ -0,0 +1,165 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-common.h - common header file for device-specific source files also.
*
* see flexcop.c for copyright information.
*/
#ifndef __FLEXCOP_COMMON_H__
#define __FLEXCOP_COMMON_H__
#include <linux/pci.h>
#include <linux/mutex.h>
#include "flexcop-reg.h"
#include "dmxdev.h"
#include "dvb_demux.h"
#include "dvb_filter.h"
#include "dvb_net.h"
#include "dvb_frontend.h"
#define FC_MAX_FEED 256
#ifndef FC_LOG_PREFIX
#warning please define a log prefix for your file, using a default one
#define FC_LOG_PREFIX "b2c2-undef"
#endif
/* Steal from usb.h */
#undef err
#define err(format, arg...) printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg)
#undef info
#define info(format, arg...) printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg)
#undef warn
#define warn(format, arg...) printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg)
struct flexcop_dma {
struct pci_dev *pdev;
u8 *cpu_addr0;
dma_addr_t dma_addr0;
u8 *cpu_addr1;
dma_addr_t dma_addr1;
u32 size; /* size of each address in bytes */
};
/* Control structure for data definitions that are common to
* the B2C2-based PCI and USB devices.
*/
struct flexcop_device {
/* general */
struct device *dev; /* for firmware_class */
#define FC_STATE_DVB_INIT 0x01
#define FC_STATE_I2C_INIT 0x02
#define FC_STATE_FE_INIT 0x04
int init_state;
/* device information */
int has_32_hw_pid_filter;
flexcop_revision_t rev;
flexcop_device_type_t dev_type;
flexcop_bus_t bus_type;
/* dvb stuff */
struct dvb_adapter dvb_adapter;
struct dvb_frontend *fe;
struct dvb_net dvbnet;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend hw_frontend;
struct dmx_frontend mem_frontend;
int (*fe_sleep) (struct dvb_frontend *);
struct i2c_adapter i2c_adap;
struct mutex i2c_mutex;
struct module *owner;
/* options and status */
int extra_feedcount;
int feedcount;
int pid_filtering;
int fullts_streaming_state;
/* bus specific callbacks */
flexcop_ibi_value (*read_ibi_reg) (struct flexcop_device *, flexcop_ibi_register);
int (*write_ibi_reg) (struct flexcop_device *, flexcop_ibi_register, flexcop_ibi_value);
int (*i2c_request) (struct flexcop_device*, flexcop_access_op_t, flexcop_i2c_port_t, u8 chipaddr, u8 addr, u8 *buf, u16 len);
int (*stream_control) (struct flexcop_device*, int);
int (*get_mac_addr) (struct flexcop_device *fc, int extended);
void *bus_specific;
};
/* exported prototypes */
/* from flexcop.c */
void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len);
void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no);
struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len);
void flexcop_device_kfree(struct flexcop_device*);
int flexcop_device_initialize(struct flexcop_device*);
void flexcop_device_exit(struct flexcop_device *fc);
void flexcop_reset_block_300(struct flexcop_device *fc);
/* from flexcop-dma.c */
int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size);
void flexcop_dma_free(struct flexcop_dma *dma);
int flexcop_dma_control_timer_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
int flexcop_dma_control_size_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, flexcop_dma_index_t dma_idx);
int flexcop_dma_xfer_control(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, flexcop_dma_addr_index_t index, int onoff);
int flexcop_dma_config_timer(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, u8 cycles);
/* from flexcop-eeprom.c */
/* the PCI part uses this call to get the MAC address, the USB part has its own */
int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended);
/* from flexcop-i2c.c */
/* the PCI part uses this a i2c_request callback, whereas the usb part has its own
* one. We have it in flexcop-i2c.c, because it is going via the actual
* I2C-channel of the flexcop.
*/
int flexcop_i2c_request(struct flexcop_device*, flexcop_access_op_t,
flexcop_i2c_port_t, u8 chipaddr, u8 addr, u8 *buf, u16 len);
/* from flexcop-sram.c */
int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, flexcop_sram_dest_target_t target);
void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s);
void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill);
/* global prototypes for the flexcop-chip */
/* from flexcop-fe-tuner.c */
int flexcop_frontend_init(struct flexcop_device *card);
void flexcop_frontend_exit(struct flexcop_device *fc);
/* from flexcop-i2c.c */
int flexcop_i2c_init(struct flexcop_device *fc);
void flexcop_i2c_exit(struct flexcop_device *fc);
/* from flexcop-sram.c */
int flexcop_sram_init(struct flexcop_device *fc);
/* from flexcop-misc.c */
void flexcop_determine_revision(struct flexcop_device *fc);
void flexcop_device_name(struct flexcop_device *fc,const char *prefix,const char *suffix);
void flexcop_dump_reg(struct flexcop_device *fc, flexcop_ibi_register reg, int num);
/* from flexcop-hw-filter.c */
int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *dvbdmxfeed, int onoff);
void flexcop_hw_filter_init(struct flexcop_device *fc);
void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff);
void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]);
void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff);
#endif

View File

@@ -0,0 +1,171 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-dma.c - methods for configuring and controlling the DMA of the FlexCop.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size)
{
u8 *tcpu;
dma_addr_t tdma;
if (size % 2) {
err("dma buffersize has to be even.");
return -EINVAL;
}
if ((tcpu = pci_alloc_consistent(pdev, size, &tdma)) != NULL) {
dma->pdev = pdev;
dma->cpu_addr0 = tcpu;
dma->dma_addr0 = tdma;
dma->cpu_addr1 = tcpu + size/2;
dma->dma_addr1 = tdma + size/2;
dma->size = size/2;
return 0;
}
return -ENOMEM;
}
EXPORT_SYMBOL(flexcop_dma_allocate);
void flexcop_dma_free(struct flexcop_dma *dma)
{
pci_free_consistent(dma->pdev, dma->size*2,dma->cpu_addr0, dma->dma_addr0);
memset(dma,0,sizeof(struct flexcop_dma));
}
EXPORT_SYMBOL(flexcop_dma_free);
int flexcop_dma_config(struct flexcop_device *fc,
struct flexcop_dma *dma,
flexcop_dma_index_t dma_idx)
{
flexcop_ibi_value v0x0,v0x4,v0xc;
v0x0.raw = v0x4.raw = v0xc.raw = 0;
v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2;
v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2;
v0x4.dma_0x4_write.dma_addr_size = dma->size / 4;
if ((dma_idx & FC_DMA_1) == dma_idx) {
fc->write_ibi_reg(fc,dma1_000,v0x0);
fc->write_ibi_reg(fc,dma1_004,v0x4);
fc->write_ibi_reg(fc,dma1_00c,v0xc);
} else if ((dma_idx & FC_DMA_2) == dma_idx) {
fc->write_ibi_reg(fc,dma2_010,v0x0);
fc->write_ibi_reg(fc,dma2_014,v0x4);
fc->write_ibi_reg(fc,dma2_01c,v0xc);
} else {
err("either DMA1 or DMA2 can be configured at the within one flexcop_dma_config call.");
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(flexcop_dma_config);
/* start the DMA transfers, but not the DMA IRQs */
int flexcop_dma_xfer_control(struct flexcop_device *fc,
flexcop_dma_index_t dma_idx,
flexcop_dma_addr_index_t index,
int onoff)
{
flexcop_ibi_value v0x0,v0xc;
flexcop_ibi_register r0x0,r0xc;
if ((dma_idx & FC_DMA_1) == dma_idx) {
r0x0 = dma1_000;
r0xc = dma1_00c;
} else if ((dma_idx & FC_DMA_2) == dma_idx) {
r0x0 = dma2_010;
r0xc = dma2_01c;
} else {
err("either transfer DMA1 or DMA2 can be started within one flexcop_dma_xfer_control call.");
return -EINVAL;
}
v0x0 = fc->read_ibi_reg(fc,r0x0);
v0xc = fc->read_ibi_reg(fc,r0xc);
deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw);
deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw);
if (index & FC_DMA_SUBADDR_0)
v0x0.dma_0x0.dma_0start = onoff;
if (index & FC_DMA_SUBADDR_1)
v0xc.dma_0xc.dma_1start = onoff;
fc->write_ibi_reg(fc,r0x0,v0x0);
fc->write_ibi_reg(fc,r0xc,v0xc);
deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw);
deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_xfer_control);
static int flexcop_dma_remap(struct flexcop_device *fc,
flexcop_dma_index_t dma_idx,
int onoff)
{
flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_00c : dma2_01c;
flexcop_ibi_value v = fc->read_ibi_reg(fc,r);
deb_info("%s\n",__FUNCTION__);
v.dma_0xc.remap_enable = onoff;
fc->write_ibi_reg(fc,r,v);
return 0;
}
int flexcop_dma_control_size_irq(struct flexcop_device *fc,
flexcop_dma_index_t no,
int onoff)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208);
if (no & FC_DMA_1)
v.ctrl_208.DMA1_IRQ_Enable_sig = onoff;
if (no & FC_DMA_2)
v.ctrl_208.DMA2_IRQ_Enable_sig = onoff;
fc->write_ibi_reg(fc,ctrl_208,v);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_control_size_irq);
int flexcop_dma_control_timer_irq(struct flexcop_device *fc,
flexcop_dma_index_t no,
int onoff)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208);
if (no & FC_DMA_1)
v.ctrl_208.DMA1_Timer_Enable_sig = onoff;
if (no & FC_DMA_2)
v.ctrl_208.DMA2_Timer_Enable_sig = onoff;
fc->write_ibi_reg(fc,ctrl_208,v);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_control_timer_irq);
/* 1 cycles = 1.97 msec */
int flexcop_dma_config_timer(struct flexcop_device *fc,
flexcop_dma_index_t dma_idx,
u8 cycles)
{
flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014;
flexcop_ibi_value v = fc->read_ibi_reg(fc,r);
flexcop_dma_remap(fc,dma_idx,0);
deb_info("%s\n",__FUNCTION__);
v.dma_0x4_write.dmatimer = cycles;
fc->write_ibi_reg(fc,r,v);
return 0;
}
EXPORT_SYMBOL(flexcop_dma_config_timer);

View File

@@ -0,0 +1,153 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-eeprom.c - eeprom access methods (currently only MAC address reading is used)
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
#if 0
/*EEPROM (Skystar2 has one "24LC08B" chip on board) */
static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len)
{
return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len);
}
static int eeprom_lrc_write(struct adapter *adapter, u32 addr, u32 len, u8 *wbuf, u8 *rbuf, int retries)
{
int i;
for (i = 0; i < retries; i++) {
if (eeprom_write(adapter, addr, wbuf, len) == len) {
if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1)
return 1;
}
}
return 0;
}
/* These functions could be used to unlock SkyStar2 cards. */
static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len)
{
u8 rbuf[20];
u8 wbuf[20];
if (len != 16)
return 0;
memcpy(wbuf, key, len);
wbuf[16] = 0;
wbuf[17] = 0;
wbuf[18] = 0;
wbuf[19] = calc_lrc(wbuf, 19);
return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4);
}
static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len)
{
u8 buf[20];
if (len != 16)
return 0;
if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0)
return 0;
memcpy(key, buf, len);
return 1;
}
static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac)
{
u8 tmp[8];
if (type != 0) {
tmp[0] = mac[0];
tmp[1] = mac[1];
tmp[2] = mac[2];
tmp[3] = mac[5];
tmp[4] = mac[6];
tmp[5] = mac[7];
} else {
tmp[0] = mac[0];
tmp[1] = mac[1];
tmp[2] = mac[2];
tmp[3] = mac[3];
tmp[4] = mac[4];
tmp[5] = mac[5];
}
tmp[6] = 0;
tmp[7] = calc_lrc(tmp, 7);
if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8)
return 1;
return 0;
}
static int flexcop_eeprom_read(struct flexcop_device *fc, u16 addr, u8 *buf, u16 len)
{
return fc->i2c_request(fc,FC_READ,FC_I2C_PORT_EEPROM,0x50,addr,buf,len);
}
#endif
static u8 calc_lrc(u8 *buf, int len)
{
int i;
u8 sum = 0;
for (i = 0; i < len; i++)
sum = sum ^ buf[i];
return sum;
}
static int flexcop_eeprom_request(struct flexcop_device *fc, flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries)
{
int i,ret = 0;
u8 chipaddr = 0x50 | ((addr >> 8) & 3);
for (i = 0; i < retries; i++)
if ((ret = fc->i2c_request(fc,op,FC_I2C_PORT_EEPROM,chipaddr,addr & 0xff,buf,len)) == 0)
break;
return ret;
}
static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr, u8 *buf, u16 len, int retries)
{
int ret = flexcop_eeprom_request(fc,FC_READ,addr,buf,len,retries);
if (ret == 0)
if (calc_lrc(buf, len - 1) != buf[len - 1])
ret = -EINVAL;
return ret;
}
/* JJ's comment about extended == 1: it is not presently used anywhere but was
* added to the low-level functions for possible support of EUI64
*/
int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended)
{
u8 buf[8];
int ret = 0;
if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) {
if (extended != 0) {
err("TODO: extended (EUI64) MAC addresses aren't completely supported yet");
ret = -EINVAL;
/* memcpy(fc->dvb_adapter.proposed_mac,buf,3);
mac[3] = 0xfe;
mac[4] = 0xff;
memcpy(&fc->dvb_adapter.proposed_mac[3],&buf[5],3); */
} else
memcpy(fc->dvb_adapter.proposed_mac,buf,6);
}
return ret;
}
EXPORT_SYMBOL(flexcop_eeprom_check_mac_addr);

View File

@@ -0,0 +1,565 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-fe-tuner.c - methods for attaching a frontend and controlling DiSEqC.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
#include "stv0299.h"
#include "mt352.h"
#include "nxt200x.h"
#include "bcm3510.h"
#include "stv0297.h"
#include "mt312.h"
#include "lgdt330x.h"
#include "lgh06xf.h"
#include "dvb-pll.h"
/* lnb control */
static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
struct flexcop_device *fc = fe->dvb->priv;
flexcop_ibi_value v;
deb_tuner("polarity/voltage = %u\n", voltage);
v = fc->read_ibi_reg(fc, misc_204);
switch (voltage) {
case SEC_VOLTAGE_OFF:
v.misc_204.ACPI1_sig = 1;
break;
case SEC_VOLTAGE_13:
v.misc_204.ACPI1_sig = 0;
v.misc_204.LNB_L_H_sig = 0;
break;
case SEC_VOLTAGE_18:
v.misc_204.ACPI1_sig = 0;
v.misc_204.LNB_L_H_sig = 1;
break;
default:
err("unknown SEC_VOLTAGE value");
return -EINVAL;
}
return fc->write_ibi_reg(fc, misc_204, v);
}
static int flexcop_sleep(struct dvb_frontend* fe)
{
struct flexcop_device *fc = fe->dvb->priv;
/* flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); */
if (fc->fe_sleep)
return fc->fe_sleep(fe);
/* v.misc_204.ACPI3_sig = 1;
fc->write_ibi_reg(fc,misc_204,v);*/
return 0;
}
static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
{
/* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
struct flexcop_device *fc = fe->dvb->priv;
flexcop_ibi_value v;
u16 ax;
v.raw = 0;
deb_tuner("tone = %u\n",tone);
switch (tone) {
case SEC_TONE_ON:
ax = 0x01ff;
break;
case SEC_TONE_OFF:
ax = 0;
break;
default:
err("unknown SEC_TONE value");
return -EINVAL;
}
v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax;
v.lnb_switch_freq_200.LNB_CTLLowCount_sig = ax == 0 ? 0x1ff : ax;
return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
}
static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
{
flexcop_set_tone(fe, SEC_TONE_ON);
udelay(data ? 500 : 1000);
flexcop_set_tone(fe, SEC_TONE_OFF);
udelay(data ? 1000 : 500);
}
static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
{
int i, par = 1, d;
for (i = 7; i >= 0; i--) {
d = (data >> i) & 1;
par ^= d;
flexcop_diseqc_send_bit(fe, d);
}
flexcop_diseqc_send_bit(fe, par);
}
static int flexcop_send_diseqc_msg(struct dvb_frontend* fe, int len, u8 *msg, unsigned long burst)
{
int i;
flexcop_set_tone(fe, SEC_TONE_OFF);
mdelay(16);
for (i = 0; i < len; i++)
flexcop_diseqc_send_byte(fe,msg[i]);
mdelay(16);
if (burst != -1) {
if (burst)
flexcop_diseqc_send_byte(fe, 0xff);
else {
flexcop_set_tone(fe, SEC_TONE_ON);
udelay(12500);
flexcop_set_tone(fe, SEC_TONE_OFF);
}
msleep(20);
}
return 0;
}
static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
{
return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
}
static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
{
return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
}
/* dvb-s stv0299 */
static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
{
u8 aclk = 0;
u8 bclk = 0;
if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
stv0299_writereg (fe, 0x13, aclk);
stv0299_writereg (fe, 0x14, bclk);
stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
stv0299_writereg (fe, 0x20, (ratio >> 8) & 0xff);
stv0299_writereg (fe, 0x21, (ratio ) & 0xf0);
return 0;
}
static int samsung_tbmu24112_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params)
{
u8 buf[4];
u32 div;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
struct flexcop_device *fc = fe->dvb->priv;
div = params->frequency / 125;
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
buf[2] = 0x84; /* 0xC4 */
buf[3] = 0x08;
if (params->frequency < 1500000) buf[3] |= 0x10;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1) {
return -EIO;
}
return 0;
}
static u8 samsung_tbmu24112_inittab[] = {
0x01, 0x15,
0x02, 0x30,
0x03, 0x00,
0x04, 0x7D,
0x05, 0x35,
0x06, 0x02,
0x07, 0x00,
0x08, 0xC3,
0x0C, 0x00,
0x0D, 0x81,
0x0E, 0x23,
0x0F, 0x12,
0x10, 0x7E,
0x11, 0x84,
0x12, 0xB9,
0x13, 0x88,
0x14, 0x89,
0x15, 0xC9,
0x16, 0x00,
0x17, 0x5C,
0x18, 0x00,
0x19, 0x00,
0x1A, 0x00,
0x1C, 0x00,
0x1D, 0x00,
0x1E, 0x00,
0x1F, 0x3A,
0x20, 0x2E,
0x21, 0x80,
0x22, 0xFF,
0x23, 0xC1,
0x28, 0x00,
0x29, 0x1E,
0x2A, 0x14,
0x2B, 0x0F,
0x2C, 0x09,
0x2D, 0x05,
0x31, 0x1F,
0x32, 0x19,
0x33, 0xFE,
0x34, 0x93,
0xff, 0xff,
};
static struct stv0299_config samsung_tbmu24112_config = {
.demod_address = 0x68,
.inittab = samsung_tbmu24112_inittab,
.mclk = 88000000UL,
.invert = 0,
.skip_reinit = 0,
.lock_output = STV0229_LOCKOUTPUT_LK,
.volt13_op0_op1 = STV0299_VOLT13_OP1,
.min_delay_ms = 100,
.set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
};
/* dvb-t mt352 */
static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
{
static u8 mt352_clock_config [] = { 0x89, 0x18, 0x2d };
static u8 mt352_reset [] = { 0x50, 0x80 };
static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
udelay(2000);
mt352_write(fe, mt352_reset, sizeof(mt352_reset));
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
return 0;
}
static int samsung_tdtc9251dh0_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len)
{
u32 div;
unsigned char bs = 0;
if (buf_len < 5)
return -EINVAL;
#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
pllbuf[0] = 0x61;
pllbuf[1] = div >> 8;
pllbuf[2] = div & 0xff;
pllbuf[3] = 0xcc;
pllbuf[4] = bs;
return 5;
}
static struct mt352_config samsung_tdtc9251dh0_config = {
.demod_address = 0x0f,
.demod_init = samsung_tdtc9251dh0_demod_init,
};
static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
{
struct flexcop_device *fc = fe->dvb->priv;
return request_firmware(fw, name, fc->dev);
}
static struct lgdt330x_config air2pc_atsc_hd5000_config = {
.demod_address = 0x59,
.demod_chip = LGDT3303,
.serial_mpeg = 0x04,
.clock_polarity_flip = 1,
};
static struct nxt200x_config samsung_tbmv_config = {
.demod_address = 0x0a,
};
static struct bcm3510_config air2pc_atsc_first_gen_config = {
.demod_address = 0x0f,
.request_firmware = flexcop_fe_request_firmware,
};
static int skystar23_samsung_tbdu18132_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params)
{
u8 buf[4];
u32 div;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
struct flexcop_device *fc = fe->dvb->priv;
div = (params->frequency + (125/2)) / 125;
buf[0] = (div >> 8) & 0x7f;
buf[1] = (div >> 0) & 0xff;
buf[2] = 0x84 | ((div >> 10) & 0x60);
buf[3] = 0x80;
if (params->frequency < 1550000)
buf[3] |= 0x02;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1)
return -EIO;
return 0;
}
static struct mt312_config skystar23_samsung_tbdu18132_config = {
.demod_address = 0x0e,
};
static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct flexcop_device *fc = fe->dvb->priv;
u8 buf[4];
u16 div;
int ret;
/* 62.5 kHz * 10 */
#define REF_FREQ 625
#define FREQ_OFFSET 36125
div = ((fep->frequency/1000 + FREQ_OFFSET ) * 10) / REF_FREQ; // 4 MHz = 4000 KHz
buf[0] = (u8)( div >> 8) & 0x7f;
buf[1] = (u8) div & 0xff;
/* F(osc) = N * Reference Freq. (62.5 kHz)
* byte 2 : 0 N14 N13 N12 N11 N10 N9 N8
* byte 3 : N7 N6 N5 N4 N3 N2 N1 N0
* byte 4 : 1 * * AGD R3 R2 R1 R0
* byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1
* AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 */
buf[2] = 0x95;
// Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5
// 47 - 153 0 * 0 0 0 0 0 1 0x01
// 153 - 430 0 * 0 0 0 0 1 0 0x02
// 430 - 822 0 * 0 0 1 0 0 0 0x08
// 822 - 862 1 * 0 0 1 0 0 0 0x88
if (fep->frequency <= 153000000) buf[3] = 0x01;
else if (fep->frequency <= 430000000) buf[3] = 0x02;
else if (fep->frequency <= 822000000) buf[3] = 0x08;
else buf[3] = 0x88;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
deb_tuner("tuner buffer for %d Hz: %x %x %x %x\n",fep->frequency, buf[0],buf[1],buf[2],buf[3]);
ret = fc->i2c_request(fc, FC_WRITE, FC_I2C_PORT_TUNER, 0x61, buf[0], &buf[1], 3);
deb_tuner("tuner write returned: %d\n",ret);
return 0;
}
static u8 alps_tdee4_stv0297_inittab[] = {
0x80, 0x01,
0x80, 0x00,
0x81, 0x01,
0x81, 0x00,
0x00, 0x48,
0x01, 0x58,
0x03, 0x00,
0x04, 0x00,
0x07, 0x00,
0x08, 0x00,
0x30, 0xff,
0x31, 0x9d,
0x32, 0xff,
0x33, 0x00,
0x34, 0x29,
0x35, 0x55,
0x36, 0x80,
0x37, 0x6e,
0x38, 0x9c,
0x40, 0x1a,
0x41, 0xfe,
0x42, 0x33,
0x43, 0x00,
0x44, 0xff,
0x45, 0x00,
0x46, 0x00,
0x49, 0x04,
0x4a, 0x51,
0x4b, 0xf8,
0x52, 0x30,
0x53, 0x06,
0x59, 0x06,
0x5a, 0x5e,
0x5b, 0x04,
0x61, 0x49,
0x62, 0x0a,
0x70, 0xff,
0x71, 0x04,
0x72, 0x00,
0x73, 0x00,
0x74, 0x0c,
0x80, 0x20,
0x81, 0x00,
0x82, 0x30,
0x83, 0x00,
0x84, 0x04,
0x85, 0x22,
0x86, 0x08,
0x87, 0x1b,
0x88, 0x00,
0x89, 0x00,
0x90, 0x00,
0x91, 0x04,
0xa0, 0x86,
0xa1, 0x00,
0xa2, 0x00,
0xb0, 0x91,
0xb1, 0x0b,
0xc0, 0x5b,
0xc1, 0x10,
0xc2, 0x12,
0xd0, 0x02,
0xd1, 0x00,
0xd2, 0x00,
0xd3, 0x00,
0xd4, 0x02,
0xd5, 0x00,
0xde, 0x00,
0xdf, 0x01,
0xff, 0xff,
};
static struct stv0297_config alps_tdee4_stv0297_config = {
.demod_address = 0x1c,
.inittab = alps_tdee4_stv0297_inittab,
// .invert = 1,
// .pll_set = alps_tdee4_stv0297_pll_set,
};
/* try to figure out the frontend, each card/box can have on of the following list */
int flexcop_frontend_init(struct flexcop_device *fc)
{
struct dvb_frontend_ops *ops;
/* try the sky v2.6 (stv0299/Samsung tbmu24112(sl1935)) */
if ((fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) {
ops = &fc->fe->ops;
ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params;
ops->set_voltage = flexcop_set_voltage;
fc->fe_sleep = ops->sleep;
ops->sleep = flexcop_sleep;
fc->dev_type = FC_SKY;
info("found the stv0299 at i2c address: 0x%02x",samsung_tbmu24112_config.demod_address);
} else
/* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */
if ((fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) {
fc->dev_type = FC_AIR_DVB;
fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs;
info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address);
} else
/* try the air atsc 2nd generation (nxt2002) */
if ((fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC2;
dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, &dvb_pll_samsung_tbmv);
info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address);
} else
/* try the air atsc 3nd generation (lgdt3303) */
if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC3;
dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap);
info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address);
} else
/* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */
if ((fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC1;
info("found the bcm3510 at i2c address: 0x%02x",air2pc_atsc_first_gen_config.demod_address);
} else
/* try the cable dvb (stv0297) */
if ((fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_CABLE;
fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params;
info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address);
} else
/* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */
if ((fc->fe = dvb_attach(vp310_mt312_attach, &skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) {
ops = &fc->fe->ops;
ops->tuner_ops.set_params = skystar23_samsung_tbdu18132_tuner_set_params;
ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
ops->diseqc_send_burst = flexcop_diseqc_send_burst;
ops->set_tone = flexcop_set_tone;
ops->set_voltage = flexcop_set_voltage;
fc->fe_sleep = ops->sleep;
ops->sleep = flexcop_sleep;
fc->dev_type = FC_SKY_OLD;
info("found the vp310 (aka mt312) at i2c address: 0x%02x",skystar23_samsung_tbdu18132_config.demod_address);
}
if (fc->fe == NULL) {
err("no frontend driver found for this B2C2/FlexCop adapter");
return -ENODEV;
} else {
if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
err("frontend registration failed!");
dvb_frontend_detach(fc->fe);
fc->fe = NULL;
return -EINVAL;
}
}
fc->init_state |= FC_STATE_FE_INIT;
return 0;
}
void flexcop_frontend_exit(struct flexcop_device *fc)
{
if (fc->init_state & FC_STATE_FE_INIT) {
dvb_unregister_frontend(fc->fe);
dvb_frontend_detach(fc->fe);
}
fc->init_state &= ~FC_STATE_FE_INIT;
}

View File

@@ -0,0 +1,212 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-hw-filter.c - pid and mac address filtering and corresponding control functions.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,Rcv_Data_sig,onoff);
deb_ts("rcv_data is now: '%s'\n",onoff ? "on" : "off");
}
void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,SMC_Enable_sig,onoff);
}
static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,Null_filter_sig,onoff);
}
void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6])
{
flexcop_ibi_value v418,v41c;
v41c = fc->read_ibi_reg(fc,mac_address_41c);
v418.mac_address_418.MAC1 = mac[0];
v418.mac_address_418.MAC2 = mac[1];
v418.mac_address_418.MAC3 = mac[2];
v418.mac_address_418.MAC6 = mac[3];
v41c.mac_address_41c.MAC7 = mac[4];
v41c.mac_address_41c.MAC8 = mac[5];
fc->write_ibi_reg(fc,mac_address_418,v418);
fc->write_ibi_reg(fc,mac_address_41c,v41c);
}
void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,MAC_filter_Mode_sig,onoff);
}
static void flexcop_pid_group_filter(struct flexcop_device *fc, u16 pid, u16 mask)
{
/* index_reg_310.extra_index_reg need to 0 or 7 to work */
flexcop_ibi_value v30c;
v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid;
v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask;
fc->write_ibi_reg(fc,pid_filter_30c,v30c);
}
static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff)
{
flexcop_set_ibi_value(ctrl_208,Mask_filter_sig,onoff);
}
/* this fancy define reduces the code size of the quite similar PID controlling of
* the first 6 PIDs
*/
#define pid_ctrl(vregname,field,enablefield,trans_field,transval) \
flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \
v208 = fc->read_ibi_reg(fc, ctrl_208); \
\
vpid.vregname.field = onoff ? pid : 0x1fff; \
vpid.vregname.trans_field = transval; \
v208.ctrl_208.enablefield = onoff; \
\
fc->write_ibi_reg(fc,vregname,vpid); \
fc->write_ibi_reg(fc,ctrl_208,v208);
static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_300,Stream1_PID,Stream1_filter_sig,Stream1_trans,0);
}
static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_300,Stream2_PID,Stream2_filter_sig,Stream2_trans,0);
}
static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_304,PCR_PID,PCR_filter_sig,PCR_trans,0);
}
static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_304,PMT_PID,PMT_filter_sig,PMT_trans,0);
}
static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_308,EMM_PID,EMM_filter_sig,EMM_trans,0);
}
static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
{
pid_ctrl(pid_filter_308,ECM_PID,ECM_filter_sig,ECM_trans,0);
}
static void flexcop_pid_control(struct flexcop_device *fc, int index, u16 pid,int onoff)
{
if (pid == 0x2000)
return;
deb_ts("setting pid: %5d %04x at index %d '%s'\n",pid,pid,index,onoff ? "on" : "off");
/* We could use bit magic here to reduce source code size.
* I decided against it, but to use the real register names */
switch (index) {
case 0: flexcop_pid_Stream1_PID_ctrl(fc,pid,onoff); break;
case 1: flexcop_pid_Stream2_PID_ctrl(fc,pid,onoff); break;
case 2: flexcop_pid_PCR_PID_ctrl(fc,pid,onoff); break;
case 3: flexcop_pid_PMT_PID_ctrl(fc,pid,onoff); break;
case 4: flexcop_pid_EMM_PID_ctrl(fc,pid,onoff); break;
case 5: flexcop_pid_ECM_PID_ctrl(fc,pid,onoff); break;
default:
if (fc->has_32_hw_pid_filter && index < 38) {
flexcop_ibi_value vpid,vid;
/* set the index */
vid = fc->read_ibi_reg(fc,index_reg_310);
vid.index_reg_310.index_reg = index - 6;
fc->write_ibi_reg(fc,index_reg_310, vid);
vpid = fc->read_ibi_reg(fc,pid_n_reg_314);
vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff;
vpid.pid_n_reg_314.PID_enable_bit = onoff;
fc->write_ibi_reg(fc,pid_n_reg_314, vpid);
}
break;
}
}
static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc,int onoff)
{
if (fc->fullts_streaming_state != onoff) {
deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling");
flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff));
flexcop_pid_group_filter_ctrl(fc,onoff);
fc->fullts_streaming_state = onoff;
}
return 0;
}
int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *dvbdmxfeed, int onoff)
{
int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32;
fc->feedcount += onoff ? 1 : -1; /* the number of PIDs/Feed currently requested */
if (dvbdmxfeed->index >= max_pid_filter)
fc->extra_feedcount += onoff ? 1 : -1;
/* toggle complete-TS-streaming when:
* - pid_filtering is not enabled and it is the first or last feed requested
* - pid_filtering is enabled,
* - but the number of requested feeds is exceeded
* - or the requested pid is 0x2000 */
if (!fc->pid_filtering && fc->feedcount == onoff)
flexcop_toggle_fullts_streaming(fc,onoff);
if (fc->pid_filtering) {
flexcop_pid_control(fc,dvbdmxfeed->index,dvbdmxfeed->pid,onoff);
if (fc->extra_feedcount > 0)
flexcop_toggle_fullts_streaming(fc,1);
else if (dvbdmxfeed->pid == 0x2000)
flexcop_toggle_fullts_streaming(fc,onoff);
else
flexcop_toggle_fullts_streaming(fc,0);
}
/* if it was the first or last feed request change the stream-status */
if (fc->feedcount == onoff) {
flexcop_rcv_data_ctrl(fc,onoff);
if (fc->stream_control) /* device specific stream control */
fc->stream_control(fc,onoff);
/* feeding stopped -> reset the flexcop filter*/
if (onoff == 0) {
flexcop_reset_block_300(fc);
flexcop_hw_filter_init(fc);
}
}
return 0;
}
void flexcop_hw_filter_init(struct flexcop_device *fc)
{
int i;
flexcop_ibi_value v;
for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++)
flexcop_pid_control(fc,i,0x1fff,0);
flexcop_pid_group_filter(fc, 0, 0x1fe0);
flexcop_pid_group_filter_ctrl(fc,0);
v = fc->read_ibi_reg(fc,pid_filter_308);
v.pid_filter_308.EMM_filter_4 = 1;
v.pid_filter_308.EMM_filter_6 = 0;
fc->write_ibi_reg(fc,pid_filter_308,v);
flexcop_null_filter_ctrl(fc, 1);
}

View File

@@ -0,0 +1,208 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
#define FC_MAX_I2C_RETRIES 100000
static int flexcop_i2c_operation(struct flexcop_device *fc, flexcop_ibi_value *r100)
{
int i;
flexcop_ibi_value r;
r100->tw_sm_c_100.working_start = 1;
deb_i2c("r100 before: %08x\n",r100->raw);
fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero);
fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */
for (i = 0; i < FC_MAX_I2C_RETRIES; i++) {
r = fc->read_ibi_reg(fc, tw_sm_c_100);
if (!r.tw_sm_c_100.no_base_addr_ack_error) {
if (r.tw_sm_c_100.st_done) { /* && !r.tw_sm_c_100.working_start */
*r100 = r;
deb_i2c("i2c success\n");
return 0;
}
} else {
deb_i2c("suffering from an i2c ack_error\n");
return -EREMOTEIO;
}
}
deb_i2c("tried %d times i2c operation, never finished or too many ack errors.\n",i);
return -EREMOTEIO;
}
static int flexcop_i2c_read4(struct flexcop_device *fc, flexcop_ibi_value r100, u8 *buf)
{
flexcop_ibi_value r104;
int len = r100.tw_sm_c_100.total_bytes, /* remember total_bytes is buflen-1 */
ret;
if ((ret = flexcop_i2c_operation(fc,&r100)) != 0) {
/* The Cablestar needs a different kind of i2c-transfer (does not
* support "Repeat Start"):
* wait for the ACK failure,
* and do a subsequent read with the Bit 30 enabled
*/
r100.tw_sm_c_100.no_base_addr_ack_error = 1;
if ((ret = flexcop_i2c_operation(fc,&r100)) != 0) {
deb_i2c("no_base_addr read failed. %d\n",ret);
return ret;
}
}
buf[0] = r100.tw_sm_c_100.data1_reg;
if (len > 0) {
r104 = fc->read_ibi_reg(fc,tw_sm_c_104);
deb_i2c("read: r100: %08x, r104: %08x\n",r100.raw,r104.raw);
/* there is at least one more byte, otherwise we wouldn't be here */
buf[1] = r104.tw_sm_c_104.data2_reg;
if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg;
if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg;
}
return 0;
}
static int flexcop_i2c_write4(struct flexcop_device *fc, flexcop_ibi_value r100, u8 *buf)
{
flexcop_ibi_value r104;
int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */
r104.raw = 0;
/* there is at least one byte, otherwise we wouldn't be here */
r100.tw_sm_c_100.data1_reg = buf[0];
r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0;
r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0;
r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0;
deb_i2c("write: r100: %08x, r104: %08x\n",r100.raw,r104.raw);
/* write the additional i2c data before doing the actual i2c operation */
fc->write_ibi_reg(fc,tw_sm_c_104,r104);
return flexcop_i2c_operation(fc,&r100);
}
int flexcop_i2c_request(struct flexcop_device *fc, flexcop_access_op_t op,
flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u16 len)
{
int ret;
u16 bytes_to_transfer;
flexcop_ibi_value r100;
deb_i2c("op = %d\n",op);
r100.raw = 0;
r100.tw_sm_c_100.chipaddr = chipaddr;
r100.tw_sm_c_100.twoWS_rw = op;
r100.tw_sm_c_100.twoWS_port_reg = port;
while (len != 0) {
bytes_to_transfer = len > 4 ? 4 : len;
r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1;
r100.tw_sm_c_100.baseaddr = addr;
if (op == FC_READ)
ret = flexcop_i2c_read4(fc, r100, buf);
else
ret = flexcop_i2c_write4(fc,r100, buf);
if (ret < 0)
return ret;
buf += bytes_to_transfer;
addr += bytes_to_transfer;
len -= bytes_to_transfer;
};
return 0;
}
/* exported for PCI i2c */
EXPORT_SYMBOL(flexcop_i2c_request);
/* master xfer callback for demodulator */
static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
{
struct flexcop_device *fc = i2c_get_adapdata(i2c_adap);
int i, ret = 0;
if (mutex_lock_interruptible(&fc->i2c_mutex))
return -ERESTARTSYS;
/* reading */
if (num == 2 &&
msgs[0].flags == 0 &&
msgs[1].flags == I2C_M_RD &&
msgs[0].buf != NULL &&
msgs[1].buf != NULL) {
ret = fc->i2c_request(fc, FC_READ, FC_I2C_PORT_DEMOD, msgs[0].addr, msgs[0].buf[0], msgs[1].buf, msgs[1].len);
} else for (i = 0; i < num; i++) { /* writing command */
if (msgs[i].flags != 0 || msgs[i].buf == NULL || msgs[i].len < 2) {
ret = -EINVAL;
break;
}
ret = fc->i2c_request(fc, FC_WRITE, FC_I2C_PORT_DEMOD, msgs[i].addr, msgs[i].buf[0], &msgs[i].buf[1], msgs[i].len - 1);
}
if (ret < 0)
err("i2c master_xfer failed");
else
ret = num;
mutex_unlock(&fc->i2c_mutex);
return ret;
}
static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm flexcop_algo = {
.master_xfer = flexcop_master_xfer,
.functionality = flexcop_i2c_func,
};
int flexcop_i2c_init(struct flexcop_device *fc)
{
int ret;
mutex_init(&fc->i2c_mutex);
memset(&fc->i2c_adap, 0, sizeof(struct i2c_adapter));
strncpy(fc->i2c_adap.name, "B2C2 FlexCop device",I2C_NAME_SIZE);
i2c_set_adapdata(&fc->i2c_adap,fc);
fc->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
fc->i2c_adap.algo = &flexcop_algo;
fc->i2c_adap.algo_data = NULL;
fc->i2c_adap.dev.parent = fc->dev;
if ((ret = i2c_add_adapter(&fc->i2c_adap)) < 0)
return ret;
fc->init_state |= FC_STATE_I2C_INIT;
return 0;
}
void flexcop_i2c_exit(struct flexcop_device *fc)
{
if (fc->init_state & FC_STATE_I2C_INIT)
i2c_del_adapter(&fc->i2c_adap);
fc->init_state &= ~FC_STATE_I2C_INIT;
}

View File

@@ -0,0 +1,80 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-misc.c - miscellaneous functions.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
void flexcop_determine_revision(struct flexcop_device *fc)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204);
switch (v.misc_204.Rev_N_sig_revision_hi) {
case 0x2:
deb_info("found a FlexCopII.\n");
fc->rev = FLEXCOP_II;
break;
case 0x3:
deb_info("found a FlexCopIIb.\n");
fc->rev = FLEXCOP_IIB;
break;
case 0x0:
deb_info("found a FlexCopIII.\n");
fc->rev = FLEXCOP_III;
break;
default:
err("unkown FlexCop Revision: %x. Please report the linux-dvb@linuxtv.org.",v.misc_204.Rev_N_sig_revision_hi);
break;
}
if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps))
deb_info("this FlexCop has the additional 32 hardware pid filter.\n");
else
deb_info("this FlexCop has only the 6 basic main hardware pid filter.\n");
/* bus parts have to decide if hw pid filtering is used or not. */
}
static const char *flexcop_revision_names[] = {
"Unkown chip",
"FlexCopII",
"FlexCopIIb",
"FlexCopIII",
};
static const char *flexcop_device_names[] = {
"Unkown device",
"Air2PC/AirStar 2 DVB-T",
"Air2PC/AirStar 2 ATSC 1st generation",
"Air2PC/AirStar 2 ATSC 2nd generation",
"Sky2PC/SkyStar 2 DVB-S",
"Sky2PC/SkyStar 2 DVB-S (old version)",
"Cable2PC/CableStar 2 DVB-C",
"Air2PC/AirStar 2 ATSC 3rd generation (HD5000)",
};
static const char *flexcop_bus_names[] = {
"USB",
"PCI",
};
void flexcop_device_name(struct flexcop_device *fc,const char *prefix,const
char *suffix)
{
info("%s '%s' at the '%s' bus controlled by a '%s' %s",prefix,
flexcop_device_names[fc->dev_type],flexcop_bus_names[fc->bus_type],
flexcop_revision_names[fc->rev],suffix);
}
void flexcop_dump_reg(struct flexcop_device *fc, flexcop_ibi_register reg, int num)
{
flexcop_ibi_value v;
int i;
for (i = 0; i < num; i++) {
v = fc->read_ibi_reg(fc,reg+4*i);
deb_rdump("0x%03x: %08x, ",reg+4*i, v.raw);
}
deb_rdump("\n");
}
EXPORT_SYMBOL(flexcop_dump_reg);

View File

@@ -0,0 +1,431 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-pci.c - covers the PCI part including DMA transfers.
*
* see flexcop.c for copyright information.
*/
#define FC_LOG_PREFIX "flexcop-pci"
#include "flexcop-common.h"
static int enable_pid_filtering = 1;
module_param(enable_pid_filtering, int, 0444);
MODULE_PARM_DESC(enable_pid_filtering, "enable hardware pid filtering: supported values: 0 (fullts), 1");
static int irq_chk_intv;
module_param(irq_chk_intv, int, 0644);
MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ watchdog (currently just debugging).");
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) \
do { if ((debug & level)) printk(args); } while (0)
#define DEBSTATUS ""
#else
#define dprintk(level,args...)
#define DEBSTATUS " (debugging is not enabled)"
#endif
#define deb_info(args...) dprintk(0x01,args)
#define deb_reg(args...) dprintk(0x02,args)
#define deb_ts(args...) dprintk(0x04,args)
#define deb_irq(args...) dprintk(0x08,args)
#define deb_chk(args...) dprintk(0x10,args)
static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debug level (1=info,2=regs,4=TS,8=irqdma (|-able))." DEBSTATUS);
#define DRIVER_VERSION "0.1"
#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
struct flexcop_pci {
struct pci_dev *pdev;
#define FC_PCI_INIT 0x01
#define FC_PCI_DMA_INIT 0x02
int init_state;
void __iomem *io_mem;
u32 irq;
/* buffersize (at least for DMA1, need to be % 188 == 0,
* this logic is required */
#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188)
#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188)
struct flexcop_dma dma[2];
int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */
u32 last_dma1_cur_pos; /* position of the pointer last time the timer/packet irq occured */
int count;
spinlock_t irq_lock;
unsigned long last_irq;
struct delayed_work irq_check_work;
struct flexcop_device *fc_dev;
};
static int lastwreg,lastwval,lastrreg,lastrval;
static flexcop_ibi_value flexcop_pci_read_ibi_reg (struct flexcop_device *fc, flexcop_ibi_register r)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
flexcop_ibi_value v;
v.raw = readl(fc_pci->io_mem + r);
if (lastrreg != r || lastrval != v.raw) {
lastrreg = r; lastrval = v.raw;
deb_reg("new rd: %3x: %08x\n",r,v.raw);
}
return v;
}
static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register r, flexcop_ibi_value v)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
if (lastwreg != r || lastwval != v.raw) {
lastwreg = r; lastwval = v.raw;
deb_reg("new wr: %3x: %08x\n",r,v.raw);
}
writel(v.raw, fc_pci->io_mem + r);
return 0;
}
static void flexcop_pci_irq_check_work(struct work_struct *work)
{
struct flexcop_pci *fc_pci =
container_of(work, struct flexcop_pci, irq_check_work.work);
struct flexcop_device *fc = fc_pci->fc_dev;
flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714);
flexcop_dump_reg(fc_pci->fc_dev,dma1_000,4);
if (v.sram_dest_reg_714.net_ovflow_error)
deb_chk("sram net_ovflow_error\n");
if (v.sram_dest_reg_714.media_ovflow_error)
deb_chk("sram media_ovflow_error\n");
if (v.sram_dest_reg_714.cai_ovflow_error)
deb_chk("sram cai_ovflow_error\n");
if (v.sram_dest_reg_714.cai_ovflow_error)
deb_chk("sram cai_ovflow_error\n");
schedule_delayed_work(&fc_pci->irq_check_work,
msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv));
}
/* When PID filtering is turned on, we use the timer IRQ, because small amounts
* of data need to be passed to the user space instantly as well. When PID
* filtering is turned off, we use the page-change-IRQ */
static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)
{
struct flexcop_pci *fc_pci = dev_id;
struct flexcop_device *fc = fc_pci->fc_dev;
flexcop_ibi_value v;
irqreturn_t ret = IRQ_HANDLED;
spin_lock_irq(&fc_pci->irq_lock);
v = fc->read_ibi_reg(fc,irq_20c);
/* errors */
if (v.irq_20c.Data_receiver_error)
deb_chk("data receiver error\n");
if (v.irq_20c.Continuity_error_flag)
deb_chk("Contunuity error flag is set\n");
if (v.irq_20c.LLC_SNAP_FLAG_set)
deb_chk("LLC_SNAP_FLAG_set is set\n");
if (v.irq_20c.Transport_Error)
deb_chk("Transport error\n");
if ((fc_pci->count % 1000) == 0)
deb_chk("%d valid irq took place so far\n",fc_pci->count);
if (v.irq_20c.DMA1_IRQ_Status == 1) {
if (fc_pci->active_dma1_addr == 0)
flexcop_pass_dmx_packets(fc_pci->fc_dev,fc_pci->dma[0].cpu_addr0,fc_pci->dma[0].size / 188);
else
flexcop_pass_dmx_packets(fc_pci->fc_dev,fc_pci->dma[0].cpu_addr1,fc_pci->dma[0].size / 188);
deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr);
fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr;
} else if (v.irq_20c.DMA1_Timer_Status == 1) {
/* for the timer IRQ we only can use buffer dmx feeding, because we don't have
* complete TS packets when reading from the DMA memory */
dma_addr_t cur_addr =
fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2;
u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0;
deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, last_cur_pos: %08x ",
jiffies_to_usecs(jiffies - fc_pci->last_irq),
v.raw, (unsigned long long)cur_addr, cur_pos,
fc_pci->last_dma1_cur_pos);
fc_pci->last_irq = jiffies;
/* buffer end was reached, restarted from the beginning
* pass the data from last_cur_pos to the buffer end to the demux
*/
if (cur_pos < fc_pci->last_dma1_cur_pos) {
deb_irq(" end was reached: passing %d bytes ",(fc_pci->dma[0].size*2 - 1) - fc_pci->last_dma1_cur_pos);
flexcop_pass_dmx_data(fc_pci->fc_dev,
fc_pci->dma[0].cpu_addr0 + fc_pci->last_dma1_cur_pos,
(fc_pci->dma[0].size*2) - fc_pci->last_dma1_cur_pos);
fc_pci->last_dma1_cur_pos = 0;
}
if (cur_pos > fc_pci->last_dma1_cur_pos) {
deb_irq(" passing %d bytes ",cur_pos - fc_pci->last_dma1_cur_pos);
flexcop_pass_dmx_data(fc_pci->fc_dev,
fc_pci->dma[0].cpu_addr0 + fc_pci->last_dma1_cur_pos,
cur_pos - fc_pci->last_dma1_cur_pos);
}
deb_irq("\n");
fc_pci->last_dma1_cur_pos = cur_pos;
fc_pci->count++;
} else {
deb_irq("isr for flexcop called, apparently without reason (%08x)\n",v.raw);
ret = IRQ_NONE;
}
spin_unlock_irq(&fc_pci->irq_lock);
return ret;
}
static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff)
{
struct flexcop_pci *fc_pci = fc->bus_specific;
if (onoff) {
flexcop_dma_config(fc,&fc_pci->dma[0],FC_DMA_1);
flexcop_dma_config(fc,&fc_pci->dma[1],FC_DMA_2);
flexcop_dma_config_timer(fc,FC_DMA_1,0);
flexcop_dma_xfer_control(fc,FC_DMA_1,FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1,1);
deb_irq("DMA xfer enabled\n");
fc_pci->last_dma1_cur_pos = 0;
flexcop_dma_control_timer_irq(fc,FC_DMA_1,1);
deb_irq("IRQ enabled\n");
// fc_pci->active_dma1_addr = 0;
// flexcop_dma_control_size_irq(fc,FC_DMA_1,1);
if (irq_chk_intv > 0)
schedule_delayed_work(&fc_pci->irq_check_work,
msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv));
} else {
if (irq_chk_intv > 0)
cancel_delayed_work(&fc_pci->irq_check_work);
flexcop_dma_control_timer_irq(fc,FC_DMA_1,0);
deb_irq("IRQ disabled\n");
// flexcop_dma_control_size_irq(fc,FC_DMA_1,0);
flexcop_dma_xfer_control(fc,FC_DMA_1,FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1,0);
deb_irq("DMA xfer disabled\n");
}
return 0;
}
static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci)
{
int ret;
if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[0],FC_DEFAULT_DMA1_BUFSIZE)) != 0)
return ret;
if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[1],FC_DEFAULT_DMA2_BUFSIZE)) != 0) {
flexcop_dma_free(&fc_pci->dma[0]);
return ret;
}
flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1);
flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2);
fc_pci->init_state |= FC_PCI_DMA_INIT;
return ret;
}
static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci)
{
if (fc_pci->init_state & FC_PCI_DMA_INIT) {
flexcop_dma_free(&fc_pci->dma[0]);
flexcop_dma_free(&fc_pci->dma[1]);
}
fc_pci->init_state &= ~FC_PCI_DMA_INIT;
}
static int flexcop_pci_init(struct flexcop_pci *fc_pci)
{
int ret;
u8 card_rev;
pci_read_config_byte(fc_pci->pdev, PCI_CLASS_REVISION, &card_rev);
info("card revision %x", card_rev);
if ((ret = pci_enable_device(fc_pci->pdev)) != 0)
return ret;
pci_set_master(fc_pci->pdev);
/* enable interrupts */
// pci_write_config_dword(pdev, 0x6c, 0x8000);
if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0)
goto err_pci_disable_device;
fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800);
if (!fc_pci->io_mem) {
err("cannot map io memory\n");
ret = -EIO;
goto err_pci_release_regions;
}
pci_set_drvdata(fc_pci->pdev, fc_pci);
if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr,
IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0)
goto err_pci_iounmap;
spin_lock_init(&fc_pci->irq_lock);
fc_pci->init_state |= FC_PCI_INIT;
return ret;
err_pci_iounmap:
pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
pci_set_drvdata(fc_pci->pdev, NULL);
err_pci_release_regions:
pci_release_regions(fc_pci->pdev);
err_pci_disable_device:
pci_disable_device(fc_pci->pdev);
return ret;
}
static void flexcop_pci_exit(struct flexcop_pci *fc_pci)
{
if (fc_pci->init_state & FC_PCI_INIT) {
free_irq(fc_pci->pdev->irq, fc_pci);
pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
pci_set_drvdata(fc_pci->pdev, NULL);
pci_release_regions(fc_pci->pdev);
pci_disable_device(fc_pci->pdev);
}
fc_pci->init_state &= ~FC_PCI_INIT;
}
static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct flexcop_device *fc;
struct flexcop_pci *fc_pci;
int ret = -ENOMEM;
if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) {
err("out of memory\n");
return -ENOMEM;
}
/* general flexcop init */
fc_pci = fc->bus_specific;
fc_pci->fc_dev = fc;
fc->read_ibi_reg = flexcop_pci_read_ibi_reg;
fc->write_ibi_reg = flexcop_pci_write_ibi_reg;
fc->i2c_request = flexcop_i2c_request;
fc->get_mac_addr = flexcop_eeprom_check_mac_addr;
fc->stream_control = flexcop_pci_stream_control;
if (enable_pid_filtering)
info("will use the HW PID filter.");
else
info("will pass the complete TS to the demuxer.");
fc->pid_filtering = enable_pid_filtering;
fc->bus_type = FC_PCI;
fc->dev = &pdev->dev;
fc->owner = THIS_MODULE;
/* bus specific part */
fc_pci->pdev = pdev;
if ((ret = flexcop_pci_init(fc_pci)) != 0)
goto err_kfree;
/* init flexcop */
if ((ret = flexcop_device_initialize(fc)) != 0)
goto err_pci_exit;
/* init dma */
if ((ret = flexcop_pci_dma_init(fc_pci)) != 0)
goto err_fc_exit;
INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work);
return ret;
err_fc_exit:
flexcop_device_exit(fc);
err_pci_exit:
flexcop_pci_exit(fc_pci);
err_kfree:
flexcop_device_kfree(fc);
return ret;
}
/* in theory every _exit function should be called exactly two times,
* here and in the bail-out-part of the _init-function
*/
static void flexcop_pci_remove(struct pci_dev *pdev)
{
struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);
flexcop_pci_dma_exit(fc_pci);
flexcop_device_exit(fc_pci->fc_dev);
flexcop_pci_exit(fc_pci);
flexcop_device_kfree(fc_pci->fc_dev);
}
static struct pci_device_id flexcop_pci_tbl[] = {
{ PCI_DEVICE(0x13d0, 0x2103) },
/* { PCI_DEVICE(0x13d0, 0x2200) }, ? */
{ },
};
MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl);
static struct pci_driver flexcop_pci_driver = {
.name = "b2c2_flexcop_pci",
.id_table = flexcop_pci_tbl,
.probe = flexcop_pci_probe,
.remove = flexcop_pci_remove,
};
static int __init flexcop_pci_module_init(void)
{
return pci_register_driver(&flexcop_pci_driver);
}
static void __exit flexcop_pci_module_exit(void)
{
pci_unregister_driver(&flexcop_pci_driver);
}
module_init(flexcop_pci_module_init);
module_exit(flexcop_pci_module_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,167 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII
*
* see flexcop.c for copyright information.
*/
#ifndef __FLEXCOP_REG_H__
#define __FLEXCOP_REG_H__
typedef enum {
FLEXCOP_UNK = 0,
FLEXCOP_II,
FLEXCOP_IIB,
FLEXCOP_III,
} flexcop_revision_t;
typedef enum {
FC_UNK = 0,
FC_AIR_DVB,
FC_AIR_ATSC1,
FC_AIR_ATSC2,
FC_SKY,
FC_SKY_OLD,
FC_CABLE,
FC_AIR_ATSC3,
} flexcop_device_type_t;
typedef enum {
FC_USB = 0,
FC_PCI,
} flexcop_bus_t;
/* FlexCop IBI Registers */
#if defined(__LITTLE_ENDIAN)
#include "flexcop_ibi_value_le.h"
#else
#if defined(__BIG_ENDIAN)
#include "flexcop_ibi_value_be.h"
#else
#error no endian defined
#endif
#endif
#define fc_data_Tag_ID_DVB 0x3e
#define fc_data_Tag_ID_ATSC 0x3f
#define fc_data_Tag_ID_IDSB 0x8b
#define fc_key_code_default 0x1
#define fc_key_code_even 0x2
#define fc_key_code_odd 0x3
extern flexcop_ibi_value ibi_zero;
typedef enum {
FC_I2C_PORT_DEMOD = 1,
FC_I2C_PORT_EEPROM = 2,
FC_I2C_PORT_TUNER = 3,
} flexcop_i2c_port_t;
typedef enum {
FC_WRITE = 0,
FC_READ = 1,
} flexcop_access_op_t;
typedef enum {
FC_SRAM_DEST_NET = 1,
FC_SRAM_DEST_CAI = 2,
FC_SRAM_DEST_CAO = 4,
FC_SRAM_DEST_MEDIA = 8
} flexcop_sram_dest_t;
typedef enum {
FC_SRAM_DEST_TARGET_WAN_USB = 0,
FC_SRAM_DEST_TARGET_DMA1 = 1,
FC_SRAM_DEST_TARGET_DMA2 = 2,
FC_SRAM_DEST_TARGET_FC3_CA = 3
} flexcop_sram_dest_target_t;
typedef enum {
FC_SRAM_2_32KB = 0, /* 64KB */
FC_SRAM_1_32KB = 1, /* 32KB - default fow FCII */
FC_SRAM_1_128KB = 2, /* 128KB */
FC_SRAM_1_48KB = 3, /* 48KB - default for FCIII */
} flexcop_sram_type_t;
typedef enum {
FC_WAN_SPEED_4MBITS = 0,
FC_WAN_SPEED_8MBITS = 1,
FC_WAN_SPEED_12MBITS = 2,
FC_WAN_SPEED_16MBITS = 3,
} flexcop_wan_speed_t;
typedef enum {
FC_DMA_1 = 1,
FC_DMA_2 = 2,
} flexcop_dma_index_t;
typedef enum {
FC_DMA_SUBADDR_0 = 1,
FC_DMA_SUBADDR_1 = 2,
} flexcop_dma_addr_index_t;
/* names of the particular registers */
typedef enum {
dma1_000 = 0x000,
dma1_004 = 0x004,
dma1_008 = 0x008,
dma1_00c = 0x00c,
dma2_010 = 0x010,
dma2_014 = 0x014,
dma2_018 = 0x018,
dma2_01c = 0x01c,
tw_sm_c_100 = 0x100,
tw_sm_c_104 = 0x104,
tw_sm_c_108 = 0x108,
tw_sm_c_10c = 0x10c,
tw_sm_c_110 = 0x110,
lnb_switch_freq_200 = 0x200,
misc_204 = 0x204,
ctrl_208 = 0x208,
irq_20c = 0x20c,
sw_reset_210 = 0x210,
misc_214 = 0x214,
mbox_v8_to_host_218 = 0x218,
mbox_host_to_v8_21c = 0x21c,
pid_filter_300 = 0x300,
pid_filter_304 = 0x304,
pid_filter_308 = 0x308,
pid_filter_30c = 0x30c,
index_reg_310 = 0x310,
pid_n_reg_314 = 0x314,
mac_low_reg_318 = 0x318,
mac_high_reg_31c = 0x31c,
data_tag_400 = 0x400,
card_id_408 = 0x408,
card_id_40c = 0x40c,
mac_address_418 = 0x418,
mac_address_41c = 0x41c,
ci_600 = 0x600,
pi_604 = 0x604,
pi_608 = 0x608,
dvb_reg_60c = 0x60c,
sram_ctrl_reg_700 = 0x700,
net_buf_reg_704 = 0x704,
cai_buf_reg_708 = 0x708,
cao_buf_reg_70c = 0x70c,
media_buf_reg_710 = 0x710,
sram_dest_reg_714 = 0x714,
net_buf_reg_718 = 0x718,
wan_ctrl_reg_71c = 0x71c,
} flexcop_ibi_register;
#define flexcop_set_ibi_value(reg,attr,val) { \
flexcop_ibi_value v = fc->read_ibi_reg(fc,reg); \
v.reg.attr = val; \
fc->write_ibi_reg(fc,reg,v); \
}
#endif

View File

@@ -0,0 +1,403 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-sram.c - functions for controlling the SRAM.
*
* see flexcop.c for copyright information.
*/
#include "flexcop.h"
static void flexcop_sram_set_chip (struct flexcop_device *fc, flexcop_sram_type_t type)
{
flexcop_set_ibi_value(wan_ctrl_reg_71c,sram_chip,type);
}
int flexcop_sram_init(struct flexcop_device *fc)
{
switch (fc->rev) {
case FLEXCOP_II:
case FLEXCOP_IIB:
flexcop_sram_set_chip(fc,FC_SRAM_1_32KB);
break;
case FLEXCOP_III:
flexcop_sram_set_chip(fc,FC_SRAM_1_48KB);
break;
default:
return -EINVAL;
}
return 0;
}
int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, flexcop_sram_dest_target_t target)
{
flexcop_ibi_value v;
v = fc->read_ibi_reg(fc,sram_dest_reg_714);
if (fc->rev != FLEXCOP_III && target == FC_SRAM_DEST_TARGET_FC3_CA) {
err("SRAM destination target to available on FlexCopII(b)\n");
return -EINVAL;
}
deb_sram("sram dest: %x target: %x\n",dest, target);
if (dest & FC_SRAM_DEST_NET)
v.sram_dest_reg_714.NET_Dest = target;
if (dest & FC_SRAM_DEST_CAI)
v.sram_dest_reg_714.CAI_Dest = target;
if (dest & FC_SRAM_DEST_CAO)
v.sram_dest_reg_714.CAO_Dest = target;
if (dest & FC_SRAM_DEST_MEDIA)
v.sram_dest_reg_714.MEDIA_Dest = target;
fc->write_ibi_reg(fc,sram_dest_reg_714,v);
udelay(1000); /* TODO delay really necessary */
return 0;
}
EXPORT_SYMBOL(flexcop_sram_set_dest);
void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s)
{
flexcop_set_ibi_value(wan_ctrl_reg_71c,wan_speed_sig,s);
}
EXPORT_SYMBOL(flexcop_wan_set_speed);
void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill)
{
flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714);
v.sram_dest_reg_714.ctrl_usb_wan = usb_wan;
v.sram_dest_reg_714.ctrl_sramdma = sramdma;
v.sram_dest_reg_714.ctrl_maximumfill = maximumfill;
fc->write_ibi_reg(fc,sram_dest_reg_714,v);
}
EXPORT_SYMBOL(flexcop_sram_ctrl);
#if 0
static void flexcop_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
{
int i, retries;
u32 command;
for (i = 0; i < len; i++) {
command = bank | addr | 0x04000000 | (*buf << 0x10);
retries = 2;
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
};
if (retries == 0)
printk("%s: SRAM timeout\n", __FUNCTION__);
write_reg_dw(adapter, 0x700, command);
buf++;
addr++;
}
}
static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
{
int i, retries;
u32 command, value;
for (i = 0; i < len; i++) {
command = bank | addr | 0x04008000;
retries = 10000;
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
};
if (retries == 0)
printk("%s: SRAM timeout\n", __FUNCTION__);
write_reg_dw(adapter, 0x700, command);
retries = 10000;
while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
mdelay(1);
retries--;
};
if (retries == 0)
printk("%s: SRAM timeout\n", __FUNCTION__);
value = read_reg_dw(adapter, 0x700) >> 0x10;
*buf = (value & 0xff);
addr++;
buf++;
}
}
static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
{
u32 bank;
bank = 0;
if (adapter->dw_sram_type == 0x20000) {
bank = (addr & 0x18000) << 0x0d;
}
if (adapter->dw_sram_type == 0x00000) {
if ((addr >> 0x0f) == 0)
bank = 0x20000000;
else
bank = 0x10000000;
}
flex_sram_write(adapter, bank, addr & 0x7fff, buf, len);
}
static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
{
u32 bank;
bank = 0;
if (adapter->dw_sram_type == 0x20000) {
bank = (addr & 0x18000) << 0x0d;
}
if (adapter->dw_sram_type == 0x00000) {
if ((addr >> 0x0f) == 0)
bank = 0x20000000;
else
bank = 0x10000000;
}
flex_sram_read(adapter, bank, addr & 0x7fff, buf, len);
}
static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
{
u32 length;
while (len != 0) {
length = len;
// check if the address range belongs to the same
// 32K memory chip. If not, the data is read from
// one chip at a time.
if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
}
sram_read_chunk(adapter, addr, buf, length);
addr = addr + length;
buf = buf + length;
len = len - length;
}
}
static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
{
u32 length;
while (len != 0) {
length = len;
// check if the address range belongs to the same
// 32K memory chip. If not, the data is written to
// one chip at a time.
if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
}
sram_write_chunk(adapter, addr, buf, length);
addr = addr + length;
buf = buf + length;
len = len - length;
}
}
static void sram_set_size(struct adapter *adapter, u32 mask)
{
write_reg_dw(adapter, 0x71c, (mask | (~0x30000 & read_reg_dw(adapter, 0x71c))));
}
static void sram_init(struct adapter *adapter)
{
u32 tmp;
tmp = read_reg_dw(adapter, 0x71c);
write_reg_dw(adapter, 0x71c, 1);
if (read_reg_dw(adapter, 0x71c) != 0) {
write_reg_dw(adapter, 0x71c, tmp);
adapter->dw_sram_type = tmp & 0x30000;
ddprintk("%s: dw_sram_type = %x\n", __FUNCTION__, adapter->dw_sram_type);
} else {
adapter->dw_sram_type = 0x10000;
ddprintk("%s: dw_sram_type = %x\n", __FUNCTION__, adapter->dw_sram_type);
}
/* return value is never used? */
/* return adapter->dw_sram_type; */
}
static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
{
u8 tmp1, tmp2;
dprintk("%s: mask = %x, addr = %x\n", __FUNCTION__, mask, addr);
sram_set_size(adapter, mask);
sram_init(adapter);
tmp2 = 0xa5;
tmp1 = 0x4f;
sram_write(adapter, addr, &tmp2, 1);
sram_write(adapter, addr + 4, &tmp1, 1);
tmp2 = 0;
mdelay(20);
sram_read(adapter, addr, &tmp2, 1);
sram_read(adapter, addr, &tmp2, 1);
dprintk("%s: wrote 0xa5, read 0x%2x\n", __FUNCTION__, tmp2);
if (tmp2 != 0xa5)
return 0;
tmp2 = 0x5a;
tmp1 = 0xf4;
sram_write(adapter, addr, &tmp2, 1);
sram_write(adapter, addr + 4, &tmp1, 1);
tmp2 = 0;
mdelay(20);
sram_read(adapter, addr, &tmp2, 1);
sram_read(adapter, addr, &tmp2, 1);
dprintk("%s: wrote 0x5a, read 0x%2x\n", __FUNCTION__, tmp2);
if (tmp2 != 0x5a)
return 0;
return 1;
}
static u32 sram_length(struct adapter *adapter)
{
if (adapter->dw_sram_type == 0x10000)
return 32768; // 32K
if (adapter->dw_sram_type == 0x00000)
return 65536; // 64K
if (adapter->dw_sram_type == 0x20000)
return 131072; // 128K
return 32768; // 32K
}
/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory.
- for 128K there are 4x32K chips at bank 0,1,2,3.
- for 64K there are 2x32K chips at bank 1,2.
- for 32K there is one 32K chip at bank 0.
FlexCop works only with one bank at a time. The bank is selected
by bits 28-29 of the 0x700 register.
bank 0 covers addresses 0x00000-0x07fff
bank 1 covers addresses 0x08000-0x0ffff
bank 2 covers addresses 0x10000-0x17fff
bank 3 covers addresses 0x18000-0x1ffff
*/
static int flexcop_sram_detect(struct flexcop_device *fc)
{
flexcop_ibi_value r208,r71c_0,vr71c_1;
r208 = fc->read_ibi_reg(fc, ctrl_208);
fc->write_ibi_reg(fc, ctrl_208, ibi_zero);
r71c_0 = fc->read_ibi_reg(fc, wan_ctrl_reg_71c);
write_reg_dw(adapter, 0x71c, 1);
tmp3 = read_reg_dw(adapter, 0x71c);
dprintk("%s: tmp3 = %x\n", __FUNCTION__, tmp3);
write_reg_dw(adapter, 0x71c, tmp2);
// check for internal SRAM ???
tmp3--;
if (tmp3 != 0) {
sram_set_size(adapter, 0x10000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: sram size = 32K\n", __FUNCTION__);
return 32;
}
if (sram_test_location(adapter, 0x20000, 0x18000) != 0) {
sram_set_size(adapter, 0x20000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: sram size = 128K\n", __FUNCTION__);
return 128;
}
if (sram_test_location(adapter, 0x00000, 0x10000) != 0) {
sram_set_size(adapter, 0x00000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: sram size = 64K\n", __FUNCTION__);
return 64;
}
if (sram_test_location(adapter, 0x10000, 0x00000) != 0) {
sram_set_size(adapter, 0x10000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: sram size = 32K\n", __FUNCTION__);
return 32;
}
sram_set_size(adapter, 0x10000);
sram_init(adapter);
write_reg_dw(adapter, 0x208, tmp);
dprintk("%s: SRAM detection failed. Set to 32K \n", __FUNCTION__);
return 0;
}
static void sll_detect_sram_size(struct adapter *adapter)
{
sram_detect_for_flex2(adapter);
}
#endif

View File

@@ -0,0 +1,575 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop-usb.c - covers the USB part.
*
* see flexcop.c for copyright information.
*/
#define FC_LOG_PREFIX "flexcop_usb"
#include "flexcop-usb.h"
#include "flexcop-common.h"
/* Version information */
#define DRIVER_VERSION "0.1"
#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
/* debug */
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) \
do { if ((debug & level)) { printk(args); } } while (0)
#define debug_dump(b,l,method) {\
int i; \
for (i = 0; i < l; i++) method("%02x ", b[i]); \
method("\n");\
}
#define DEBSTATUS ""
#else
#define dprintk(level,args...)
#define debug_dump(b,l,method)
#define DEBSTATUS " (debugging is not enabled)"
#endif
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
#undef DEBSTATUS
#define deb_info(args...) dprintk(0x01,args)
#define deb_ts(args...) dprintk(0x02,args)
#define deb_ctrl(args...) dprintk(0x04,args)
#define deb_i2c(args...) dprintk(0x08,args)
#define deb_v8(args...) dprintk(0x10,args)
/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
* in the IBI address, to make the V8 code simpler.
* PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (these are the six bits used)
* in general: 0000 0HHH 000L LL00
* IBI ADDRESS FORMAT: RHHH BLLL
*
* where R is the read(1)/write(0) bit, B is the busy bit
* and HHH and LLL are the two sets of three bits from the PCI address.
*/
#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
/*
* DKT 020228
* - forget about this VENDOR_BUFFER_SIZE, read and write register
* deal with DWORD or 4 bytes, that should be should from now on
* - from now on, we don't support anything older than firm 1.00
* I eliminated the write register as a 2 trip of writing hi word and lo word
* and force this to write only 4 bytes at a time.
* NOTE: this should work with all the firmware from 1.00 and newer
*/
static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
{
struct flexcop_usb *fc_usb = fc->bus_specific;
u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | (read ? 0x80 : 0);
int len = usb_control_msg(fc_usb->udev,
read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
request,
request_type, /* 0xc0 read or 0x40 write*/
wAddress,
0,
val,
sizeof(u32),
B2C2_WAIT_FOR_OPERATION_RDW * HZ);
if (len != sizeof(u32)) {
err("error while %s dword from %d (%d).",read ? "reading" : "writing",
wAddress,wRegOffsPCI);
return -EIO;
}
return 0;
}
/*
* DKT 010817 - add support for V8 memory read/write and flash update
*/
static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
flexcop_usb_request_t req, u8 page, u16 wAddress,
u8 *pbBuffer,u32 buflen)
{
// u8 dwRequestType;
u8 request_type = USB_TYPE_VENDOR;
u16 wIndex;
int nWaitTime,pipe,len;
wIndex = page << 8;
switch (req) {
case B2C2_USB_READ_V8_MEM:
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
request_type |= USB_DIR_IN;
// dwRequestType = (u8) RTYPE_READ_V8_MEMORY;
pipe = B2C2_USB_CTRL_PIPE_IN;
break;
case B2C2_USB_WRITE_V8_MEM:
wIndex |= pbBuffer[0];
request_type |= USB_DIR_OUT;
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
// dwRequestType = (u8) RTYPE_WRITE_V8_MEMORY;
pipe = B2C2_USB_CTRL_PIPE_OUT;
break;
case B2C2_USB_FLASH_BLOCK:
request_type |= USB_DIR_OUT;
nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
// dwRequestType = (u8) RTYPE_WRITE_V8_FLASH;
pipe = B2C2_USB_CTRL_PIPE_OUT;
break;
default:
deb_info("unsupported request for v8_mem_req %x.\n",req);
return -EINVAL;
}
deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n",request_type,req,
wAddress,wIndex,buflen);
len = usb_control_msg(fc_usb->udev,pipe,
req,
request_type,
wAddress,
wIndex,
pbBuffer,
buflen,
nWaitTime * HZ);
debug_dump(pbBuffer,len,deb_v8);
return len == buflen ? 0 : -EIO;
}
#define bytes_left_to_read_on_page(paddr,buflen) \
((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,flexcop_usb_request_t req,
flexcop_usb_mem_page_t page_start, u32 addr, int extended, u8 *buf, u32 len)
{
int i,ret = 0;
u16 wMax;
u32 pagechunk = 0;
switch(req) {
case B2C2_USB_READ_V8_MEM: wMax = USB_MEM_READ_MAX; break;
case B2C2_USB_WRITE_V8_MEM: wMax = USB_MEM_WRITE_MAX; break;
case B2C2_USB_FLASH_BLOCK: wMax = USB_FLASH_MAX; break;
default:
return -EINVAL;
break;
}
for (i = 0; i < len;) {
pagechunk = wMax < bytes_left_to_read_on_page(addr,len) ? wMax : bytes_left_to_read_on_page(addr,len);
deb_info("%x\n",(addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended));
if ((ret = flexcop_usb_v8_memory_req(fc_usb,req,
page_start + (addr / V8_MEMORY_PAGE_SIZE), /* actual page */
(addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended),
&buf[i],pagechunk)) < 0)
return ret;
addr += pagechunk;
len -= pagechunk;
}
return 0;
}
static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
{
return flexcop_usb_memory_req(fc->bus_specific,B2C2_USB_READ_V8_MEM,
V8_MEMORY_PAGE_FLASH,0x1f010,1,fc->dvb_adapter.proposed_mac,6);
}
#if 0
static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,
flexcop_usb_utility_function_t func, u8 extra, u16 wIndex,
u16 buflen, u8 *pvBuffer)
{
u16 wValue;
u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;
// u8 dwRequestType = (u8) RTYPE_GENERIC,
int nWaitTime = 2,
pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN,
len;
wValue = (func << 8) | extra;
len = usb_control_msg(fc_usb->udev,pipe,
B2C2_USB_UTILITY,
request_type,
wValue,
wIndex,
pvBuffer,
buflen,
nWaitTime * HZ);
return len == buflen ? 0 : -EIO;
}
#endif
/* usb i2c stuff */
static int flexcop_usb_i2c_req(struct flexcop_usb *fc_usb,
flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
{
u16 wValue, wIndex;
int nWaitTime,pipe,len;
// u8 dwRequestType;
u8 request_type = USB_TYPE_VENDOR;
switch (func) {
case USB_FUNC_I2C_WRITE:
case USB_FUNC_I2C_MULTIWRITE:
case USB_FUNC_I2C_REPEATWRITE:
/* DKT 020208 - add this to support special case of DiSEqC */
case USB_FUNC_I2C_CHECKWRITE:
pipe = B2C2_USB_CTRL_PIPE_OUT;
nWaitTime = 2;
// dwRequestType = (u8) RTYPE_GENERIC;
request_type |= USB_DIR_OUT;
break;
case USB_FUNC_I2C_READ:
case USB_FUNC_I2C_REPEATREAD:
pipe = B2C2_USB_CTRL_PIPE_IN;
nWaitTime = 2;
// dwRequestType = (u8) RTYPE_GENERIC;
request_type |= USB_DIR_IN;
break;
default:
deb_info("unsupported function for i2c_req %x\n",func);
return -EINVAL;
}
wValue = (func << 8 ) | (port << 4);
wIndex = (chipaddr << 8 ) | addr;
deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",func,request_type,req,
wValue & 0xff, wValue >> 8, wIndex & 0xff, wIndex >> 8);
len = usb_control_msg(fc_usb->udev,pipe,
req,
request_type,
wValue,
wIndex,
buf,
buflen,
nWaitTime * HZ);
return len == buflen ? 0 : -EREMOTEIO;
}
/* actual bus specific access functions, make sure prototype are/will be equal to pci */
static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register reg)
{
flexcop_ibi_value val;
val.raw = 0;
flexcop_usb_readwrite_dw(fc,reg, &val.raw, 1);
return val;
}
static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register reg, flexcop_ibi_value val)
{
return flexcop_usb_readwrite_dw(fc,reg, &val.raw, 0);
}
static int flexcop_usb_i2c_request(struct flexcop_device *fc, flexcop_access_op_t op,
flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u16 len)
{
if (op == FC_READ)
return flexcop_usb_i2c_req(fc->bus_specific,B2C2_USB_I2C_REQUEST,USB_FUNC_I2C_READ,port,chipaddr,addr,buf,len);
else
return flexcop_usb_i2c_req(fc->bus_specific,B2C2_USB_I2C_REQUEST,USB_FUNC_I2C_WRITE,port,chipaddr,addr,buf,len);
}
static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, u8 *buffer, int buffer_length)
{
u8 *b;
int l;
deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", fc_usb->tmp_buffer_length, buffer_length);
if (fc_usb->tmp_buffer_length > 0) {
memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, buffer_length);
fc_usb->tmp_buffer_length += buffer_length;
b = fc_usb->tmp_buffer;
l = fc_usb->tmp_buffer_length;
} else {
b=buffer;
l=buffer_length;
}
while (l >= 190) {
if (*b == 0xff)
switch (*(b+1) & 0x03) {
case 0x01: /* media packet */
if ( *(b+2) == 0x47 )
flexcop_pass_dmx_packets(fc_usb->fc_dev, b+2, 1);
else
deb_ts("not ts packet %02x %02x %02x %02x \n", *(b+2), *(b+3), *(b+4), *(b+5) );
b += 190;
l -= 190;
break;
default:
deb_ts("wrong packet type\n");
l = 0;
break;
}
else {
deb_ts("wrong header\n");
l = 0;
}
}
if (l>0)
memcpy(fc_usb->tmp_buffer, b, l);
fc_usb->tmp_buffer_length = l;
}
static void flexcop_usb_urb_complete(struct urb *urb)
{
struct flexcop_usb *fc_usb = urb->context;
int i;
if (urb->actual_length > 0)
deb_ts("urb completed, bufsize: %d actlen; %d\n",urb->transfer_buffer_length, urb->actual_length);
for (i = 0; i < urb->number_of_packets; i++) {
if (urb->iso_frame_desc[i].status < 0) {
err("iso frame descriptor %d has an error: %d\n",i,urb->iso_frame_desc[i].status);
} else
if (urb->iso_frame_desc[i].actual_length > 0) {
deb_ts("passed %d bytes to the demux\n",urb->iso_frame_desc[i].actual_length);
flexcop_usb_process_frame(fc_usb,
urb->transfer_buffer + urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
}
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
usb_submit_urb(urb,GFP_ATOMIC);
}
static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
{
/* submit/kill iso packets */
return 0;
}
static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
{
int i;
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
if (fc_usb->iso_urb[i] != NULL) {
deb_ts("unlinking/killing urb no. %d\n",i);
usb_kill_urb(fc_usb->iso_urb[i]);
usb_free_urb(fc_usb->iso_urb[i]);
}
if (fc_usb->iso_buffer != NULL)
pci_free_consistent(NULL,fc_usb->buffer_size, fc_usb->iso_buffer, fc_usb->dma_addr);
}
static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
{
u16 frame_size = fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize;
int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size,i,j,ret;
int buffer_offset = 0;
deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
B2C2_USB_NUM_ISO_URB, B2C2_USB_FRAMES_PER_ISO, frame_size,bufsize);
fc_usb->iso_buffer = pci_alloc_consistent(NULL,bufsize,&fc_usb->dma_addr);
if (fc_usb->iso_buffer == NULL)
return -ENOMEM;
memset(fc_usb->iso_buffer, 0, bufsize);
fc_usb->buffer_size = bufsize;
/* creating iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
if (!(fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,GFP_ATOMIC))) {
ret = -ENOMEM;
goto urb_error;
}
/* initialising and submitting iso urbs */
for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
int frame_offset = 0;
struct urb *urb = fc_usb->iso_urb[i];
deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset);
urb->dev = fc_usb->udev;
urb->context = fc_usb;
urb->complete = flexcop_usb_urb_complete;
urb->pipe = B2C2_USB_DATA_PIPE;
urb->transfer_flags = URB_ISO_ASAP;
urb->interval = 1;
urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",i,j,frame_offset);
urb->iso_frame_desc[j].offset = frame_offset;
urb->iso_frame_desc[j].length = frame_size;
frame_offset += frame_size;
}
if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
err("submitting urb %d failed with %d.",i,ret);
goto urb_error;
}
deb_ts("submitted urb no. %d.\n",i);
}
/* SRAM */
flexcop_sram_set_dest(fc_usb->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET |
FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_WAN_USB);
flexcop_wan_set_speed(fc_usb->fc_dev,FC_WAN_SPEED_8MBITS);
flexcop_sram_ctrl(fc_usb->fc_dev,1,1,1);
return 0;
urb_error:
flexcop_usb_transfer_exit(fc_usb);
return ret;
}
static int flexcop_usb_init(struct flexcop_usb *fc_usb)
{
/* use the alternate setting with the larges buffer */
usb_set_interface(fc_usb->udev,0,1);
switch (fc_usb->udev->speed) {
case USB_SPEED_LOW:
err("cannot handle USB speed because it is to sLOW.");
return -ENODEV;
break;
case USB_SPEED_FULL:
info("running at FULL speed.");
break;
case USB_SPEED_HIGH:
info("running at HIGH speed.");
break;
case USB_SPEED_UNKNOWN: /* fall through */
default:
err("cannot handle USB speed because it is unkown.");
return -ENODEV;
}
usb_set_intfdata(fc_usb->uintf, fc_usb);
return 0;
}
static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
{
usb_set_intfdata(fc_usb->uintf, NULL);
}
static int flexcop_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct flexcop_usb *fc_usb = NULL;
struct flexcop_device *fc = NULL;
int ret;
if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
err("out of memory\n");
return -ENOMEM;
}
/* general flexcop init */
fc_usb = fc->bus_specific;
fc_usb->fc_dev = fc;
fc->read_ibi_reg = flexcop_usb_read_ibi_reg;
fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
fc->i2c_request = flexcop_usb_i2c_request;
fc->get_mac_addr = flexcop_usb_get_mac_addr;
fc->stream_control = flexcop_usb_stream_control;
fc->pid_filtering = 1;
fc->bus_type = FC_USB;
fc->dev = &udev->dev;
fc->owner = THIS_MODULE;
/* bus specific part */
fc_usb->udev = udev;
fc_usb->uintf = intf;
if ((ret = flexcop_usb_init(fc_usb)) != 0)
goto err_kfree;
/* init flexcop */
if ((ret = flexcop_device_initialize(fc)) != 0)
goto err_usb_exit;
/* xfer init */
if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
goto err_fc_exit;
info("%s successfully initialized and connected.",DRIVER_NAME);
return 0;
err_fc_exit:
flexcop_device_exit(fc);
err_usb_exit:
flexcop_usb_exit(fc_usb);
err_kfree:
flexcop_device_kfree(fc);
return ret;
}
static void flexcop_usb_disconnect(struct usb_interface *intf)
{
struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
flexcop_usb_transfer_exit(fc_usb);
flexcop_device_exit(fc_usb->fc_dev);
flexcop_usb_exit(fc_usb);
flexcop_device_kfree(fc_usb->fc_dev);
info("%s successfully deinitialized and disconnected.",DRIVER_NAME);
}
static struct usb_device_id flexcop_usb_table [] = {
{ USB_DEVICE(0x0af7, 0x0101) },
{ }
};
MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver flexcop_usb_driver = {
.name = "b2c2_flexcop_usb",
.probe = flexcop_usb_probe,
.disconnect = flexcop_usb_disconnect,
.id_table = flexcop_usb_table,
};
/* module stuff */
static int __init flexcop_usb_module_init(void)
{
int result;
if ((result = usb_register(&flexcop_usb_driver))) {
err("usb_register failed. (%d)",result);
return result;
}
return 0;
}
static void __exit flexcop_usb_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&flexcop_usb_driver);
}
module_init(flexcop_usb_module_init);
module_exit(flexcop_usb_module_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,119 @@
#ifndef __FLEXCOP_USB_H_INCLUDED__
#define __FLEXCOP_USB_H_INCLUDED__
#include <linux/usb.h>
/* transfer parameters */
#define B2C2_USB_FRAMES_PER_ISO 4
#define B2C2_USB_NUM_ISO_URB 4
#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev,0)
#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev,0)
#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev,0x81)
struct flexcop_usb {
struct usb_device *udev;
struct usb_interface *uintf;
u8 *iso_buffer;
int buffer_size;
dma_addr_t dma_addr;
struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
struct flexcop_device *fc_dev;
u8 tmp_buffer[1023+190];
int tmp_buffer_length;
};
#if 0
/* request types TODO What is its use?*/
typedef enum {
/* something is wrong with this part
RTYPE_READ_DW = (1 << 6),
RTYPE_WRITE_DW_1 = (3 << 6),
RTYPE_READ_V8_MEMORY = (6 << 6),
RTYPE_WRITE_V8_MEMORY = (7 << 6),
RTYPE_WRITE_V8_FLASH = (8 << 6),
RTYPE_GENERIC = (9 << 6),
*/
} flexcop_usb_request_type_t;
#endif
/* request */
typedef enum {
B2C2_USB_WRITE_V8_MEM = 0x04,
B2C2_USB_READ_V8_MEM = 0x05,
B2C2_USB_READ_REG = 0x08,
B2C2_USB_WRITE_REG = 0x0A,
/* B2C2_USB_WRITEREGLO = 0x0A, */
B2C2_USB_WRITEREGHI = 0x0B,
B2C2_USB_FLASH_BLOCK = 0x10,
B2C2_USB_I2C_REQUEST = 0x11,
B2C2_USB_UTILITY = 0x12,
} flexcop_usb_request_t;
/* function definition for I2C_REQUEST */
typedef enum {
USB_FUNC_I2C_WRITE = 0x01,
USB_FUNC_I2C_MULTIWRITE = 0x02,
USB_FUNC_I2C_READ = 0x03,
USB_FUNC_I2C_REPEATWRITE = 0x04,
USB_FUNC_GET_DESCRIPTOR = 0x05,
USB_FUNC_I2C_REPEATREAD = 0x06,
/* DKT 020208 - add this to support special case of DiSEqC */
USB_FUNC_I2C_CHECKWRITE = 0x07,
USB_FUNC_I2C_CHECKRESULT = 0x08,
} flexcop_usb_i2c_function_t;
/*
* function definition for UTILITY request 0x12
* DKT 020304 - new utility function
*/
typedef enum {
UTILITY_SET_FILTER = 0x01,
UTILITY_DATA_ENABLE = 0x02,
UTILITY_FLEX_MULTIWRITE = 0x03,
UTILITY_SET_BUFFER_SIZE = 0x04,
UTILITY_FLEX_OPERATOR = 0x05,
UTILITY_FLEX_RESET300_START = 0x06,
UTILITY_FLEX_RESET300_STOP = 0x07,
UTILITY_FLEX_RESET300 = 0x08,
UTILITY_SET_ISO_SIZE = 0x09,
UTILITY_DATA_RESET = 0x0A,
UTILITY_GET_DATA_STATUS = 0x10,
UTILITY_GET_V8_REG = 0x11,
/* DKT 020326 - add function for v1.14 */
UTILITY_SRAM_WRITE = 0x12,
UTILITY_SRAM_READ = 0x13,
UTILITY_SRAM_TESTFILL = 0x14,
UTILITY_SRAM_TESTSET = 0x15,
UTILITY_SRAM_TESTVERIFY = 0x16,
} flexcop_usb_utility_function_t;
#define B2C2_WAIT_FOR_OPERATION_RW 1*HZ /* 1 s */
#define B2C2_WAIT_FOR_OPERATION_RDW 3*HZ /* 3 s */
#define B2C2_WAIT_FOR_OPERATION_WDW 1*HZ /* 1 s */
#define B2C2_WAIT_FOR_OPERATION_V8READ 3*HZ /* 3 s */
#define B2C2_WAIT_FOR_OPERATION_V8WRITE 3*HZ /* 3 s */
#define B2C2_WAIT_FOR_OPERATION_V8FLASH 3*HZ /* 3 s */
typedef enum {
V8_MEMORY_PAGE_DVB_CI = 0x20,
V8_MEMORY_PAGE_DVB_DS = 0x40,
V8_MEMORY_PAGE_MULTI2 = 0x60,
V8_MEMORY_PAGE_FLASH = 0x80
} flexcop_usb_mem_page_t;
#define V8_MEMORY_EXTENDED (1 << 15)
#define USB_MEM_READ_MAX 32
#define USB_MEM_WRITE_MAX 1
#define USB_FLASH_MAX 8
#define V8_MEMORY_PAGE_SIZE 0x8000 // 32K
#define V8_MEMORY_PAGE_MASK 0x7FFF
#endif

View File

@@ -0,0 +1,311 @@
/*
* flexcop.c - driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* Copyright (C) 2004-5 Patrick Boettcher <patrick.boettcher@desy.de>
*
* based on the skystar2-driver
* Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
*
* Acknowledgements:
* John Jurrius from BBTI, Inc. for extensive support with
* code examples and data books
*
* Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
*
* Contributions to the skystar2-driver have been done by
* Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
* Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
* Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac filtering)
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "flexcop.h"
#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de"
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define DEBSTATUS ""
#else
#define DEBSTATUS " (debugging is not enabled)"
#endif
int b2c2_flexcop_debug;
module_param_named(debug, b2c2_flexcop_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram,32=reg (|-able))." DEBSTATUS);
#undef DEBSTATUS
/* global zero for ibi values */
flexcop_ibi_value ibi_zero;
static int flexcop_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct flexcop_device *fc = dvbdmxfeed->demux->priv;
return flexcop_pid_feed_control(fc,dvbdmxfeed,1);
}
static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct flexcop_device *fc = dvbdmxfeed->demux->priv;
return flexcop_pid_feed_control(fc,dvbdmxfeed,0);
}
static int flexcop_dvb_init(struct flexcop_device *fc)
{
int ret;
if ((ret = dvb_register_adapter(&fc->dvb_adapter,"FlexCop Digital TV device",fc->owner,fc->dev)) < 0) {
err("error registering DVB adapter");
return ret;
}
fc->dvb_adapter.priv = fc;
fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
fc->demux.priv = fc;
fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED;
fc->demux.start_feed = flexcop_dvb_start_feed;
fc->demux.stop_feed = flexcop_dvb_stop_feed;
fc->demux.write_to_decoder = NULL;
if ((ret = dvb_dmx_init(&fc->demux)) < 0) {
err("dvb_dmx failed: error %d",ret);
goto err_dmx;
}
fc->hw_frontend.source = DMX_FRONTEND_0;
fc->dmxdev.filternum = fc->demux.feednum;
fc->dmxdev.demux = &fc->demux.dmx;
fc->dmxdev.capabilities = 0;
if ((ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter)) < 0) {
err("dvb_dmxdev_init failed: error %d",ret);
goto err_dmx_dev;
}
if ((ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend)) < 0) {
err("adding hw_frontend to dmx failed: error %d",ret);
goto err_dmx_add_hw_frontend;
}
fc->mem_frontend.source = DMX_MEMORY_FE;
if ((ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend)) < 0) {
err("adding mem_frontend to dmx failed: error %d",ret);
goto err_dmx_add_mem_frontend;
}
if ((ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend)) < 0) {
err("connect frontend failed: error %d",ret);
goto err_connect_frontend;
}
dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx);
fc->init_state |= FC_STATE_DVB_INIT;
return 0;
err_connect_frontend:
fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend);
err_dmx_add_mem_frontend:
fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->hw_frontend);
err_dmx_add_hw_frontend:
dvb_dmxdev_release(&fc->dmxdev);
err_dmx_dev:
dvb_dmx_release(&fc->demux);
err_dmx:
dvb_unregister_adapter(&fc->dvb_adapter);
return ret;
}
static void flexcop_dvb_exit(struct flexcop_device *fc)
{
if (fc->init_state & FC_STATE_DVB_INIT) {
dvb_net_release(&fc->dvbnet);
fc->demux.dmx.close(&fc->demux.dmx);
fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend);
fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->hw_frontend);
dvb_dmxdev_release(&fc->dmxdev);
dvb_dmx_release(&fc->demux);
dvb_unregister_adapter(&fc->dvb_adapter);
deb_info("deinitialized dvb stuff\n");
}
fc->init_state &= ~FC_STATE_DVB_INIT;
}
/* these methods are necessary to achieve the long-term-goal of hiding the
* struct flexcop_device from the bus-parts */
void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len)
{
dvb_dmx_swfilter(&fc->demux, buf, len);
}
EXPORT_SYMBOL(flexcop_pass_dmx_data);
void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no)
{
dvb_dmx_swfilter_packets(&fc->demux, buf, no);
}
EXPORT_SYMBOL(flexcop_pass_dmx_packets);
static void flexcop_reset(struct flexcop_device *fc)
{
flexcop_ibi_value v210,v204;
/* reset the flexcop itself */
fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
v210.raw = 0;
v210.sw_reset_210.reset_block_000 = 1;
v210.sw_reset_210.reset_block_100 = 1;
v210.sw_reset_210.reset_block_200 = 1;
v210.sw_reset_210.reset_block_300 = 1;
v210.sw_reset_210.reset_block_400 = 1;
v210.sw_reset_210.reset_block_500 = 1;
v210.sw_reset_210.reset_block_600 = 1;
v210.sw_reset_210.reset_block_700 = 1;
v210.sw_reset_210.Block_reset_enable = 0xb2;
v210.sw_reset_210.Special_controls = 0xc259;
fc->write_ibi_reg(fc,sw_reset_210,v210);
msleep(1);
/* reset the periphical devices */
v204 = fc->read_ibi_reg(fc,misc_204);
v204.misc_204.Per_reset_sig = 0;
fc->write_ibi_reg(fc,misc_204,v204);
msleep(1);
v204.misc_204.Per_reset_sig = 1;
fc->write_ibi_reg(fc,misc_204,v204);
}
void flexcop_reset_block_300(struct flexcop_device *fc)
{
flexcop_ibi_value v208_save = fc->read_ibi_reg(fc,ctrl_208),
v210 = fc->read_ibi_reg(fc,sw_reset_210);
deb_rdump("208: %08x, 210: %08x\n",v208_save.raw,v210.raw);
fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
v210.sw_reset_210.reset_block_300 = 1;
v210.sw_reset_210.Block_reset_enable = 0xb2;
fc->write_ibi_reg(fc,sw_reset_210,v210);
msleep(1);
fc->write_ibi_reg(fc,ctrl_208,v208_save);
}
EXPORT_SYMBOL(flexcop_reset_block_300);
struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
{
void *bus;
struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device), GFP_KERNEL);
if (!fc) {
err("no memory");
return NULL;
}
bus = kzalloc(bus_specific_len, GFP_KERNEL);
if (!bus) {
err("no memory");
kfree(fc);
return NULL;
}
fc->bus_specific = bus;
return fc;
}
EXPORT_SYMBOL(flexcop_device_kmalloc);
void flexcop_device_kfree(struct flexcop_device *fc)
{
kfree(fc->bus_specific);
kfree(fc);
}
EXPORT_SYMBOL(flexcop_device_kfree);
int flexcop_device_initialize(struct flexcop_device *fc)
{
int ret;
ibi_zero.raw = 0;
flexcop_reset(fc);
flexcop_determine_revision(fc);
flexcop_sram_init(fc);
flexcop_hw_filter_init(fc);
flexcop_smc_ctrl(fc, 0);
if ((ret = flexcop_dvb_init(fc)))
goto error;
/* do the MAC address reading after initializing the dvb_adapter */
if (fc->get_mac_addr(fc, 0) == 0) {
u8 *b = fc->dvb_adapter.proposed_mac;
info("MAC address = %02x:%02x:%02x:%02x:%02x:%02x", b[0],b[1],b[2],b[3],b[4],b[5]);
flexcop_set_mac_filter(fc,b);
flexcop_mac_filter_ctrl(fc,1);
} else
warn("reading of MAC address failed.\n");
if ((ret = flexcop_i2c_init(fc)))
goto error;
if ((ret = flexcop_frontend_init(fc)))
goto error;
flexcop_device_name(fc,"initialization of","complete");
return 0;
error:
flexcop_device_exit(fc);
return ret;
}
EXPORT_SYMBOL(flexcop_device_initialize);
void flexcop_device_exit(struct flexcop_device *fc)
{
flexcop_frontend_exit(fc);
flexcop_i2c_exit(fc);
flexcop_dvb_exit(fc);
}
EXPORT_SYMBOL(flexcop_device_exit);
static int flexcop_module_init(void)
{
info(DRIVER_NAME " loaded successfully");
return 0;
}
static void flexcop_module_cleanup(void)
{
info(DRIVER_NAME " unloaded successfully");
}
module_init(flexcop_module_init);
module_exit(flexcop_module_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,31 @@
/*
* This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* flexcop.h - private header file for all flexcop-chip-source files.
*
* see flexcop.c for copyright information.
*/
#ifndef __FLEXCOP_H__
#define __FLEXCOP_H___
#define FC_LOG_PREFIX "b2c2-flexcop"
#include "flexcop-common.h"
extern int b2c2_flexcop_debug;
/* debug */
#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
#define dprintk(level,args...) \
do { if ((b2c2_flexcop_debug & level)) printk(args); } while (0)
#else
#define dprintk(level,args...)
#endif
#define deb_info(args...) dprintk(0x01,args)
#define deb_tuner(args...) dprintk(0x02,args)
#define deb_i2c(args...) dprintk(0x04,args)
#define deb_ts(args...) dprintk(0x08,args)
#define deb_sram(args...) dprintk(0x10,args)
#define deb_rdump(args...) dprintk(0x20,args)
#endif

View File

@@ -0,0 +1,458 @@
/* This file is part of linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* register descriptions
*
* see flexcop.c for copyright information.
*/
/* This file is automatically generated, do not edit things here. */
#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__
#define __FLEXCOP_IBI_VALUE_INCLUDED__
typedef union {
u32 raw;
struct {
u32 dma_address0 :30;
u32 dma_0No_update : 1;
u32 dma_0start : 1;
} dma_0x0;
struct {
u32 dma_addr_size :24;
u32 DMA_maxpackets : 8;
} dma_0x4_remap;
struct {
u32 dma_addr_size :24;
u32 unused : 1;
u32 dma1timer : 7;
} dma_0x4_read;
struct {
u32 dma_addr_size :24;
u32 dmatimer : 7;
u32 unused : 1;
} dma_0x4_write;
struct {
u32 dma_cur_addr :30;
u32 unused : 2;
} dma_0x8;
struct {
u32 dma_address1 :30;
u32 remap_enable : 1;
u32 dma_1start : 1;
} dma_0xc;
struct {
u32 st_done : 1;
u32 no_base_addr_ack_error : 1;
u32 twoWS_port_reg : 2;
u32 total_bytes : 2;
u32 twoWS_rw : 1;
u32 working_start : 1;
u32 data1_reg : 8;
u32 baseaddr : 8;
u32 reserved1 : 1;
u32 chipaddr : 7;
} tw_sm_c_100;
struct {
u32 unused : 6;
u32 force_stop : 1;
u32 exlicit_stops : 1;
u32 data4_reg : 8;
u32 data3_reg : 8;
u32 data2_reg : 8;
} tw_sm_c_104;
struct {
u32 reserved2 :19;
u32 tlo1 : 5;
u32 reserved1 : 2;
u32 thi1 : 6;
} tw_sm_c_108;
struct {
u32 reserved2 :19;
u32 tlo1 : 5;
u32 reserved1 : 2;
u32 thi1 : 6;
} tw_sm_c_10c;
struct {
u32 reserved2 :19;
u32 tlo1 : 5;
u32 reserved1 : 2;
u32 thi1 : 6;
} tw_sm_c_110;
struct {
u32 LNB_CTLPrescaler_sig : 2;
u32 LNB_CTLLowCount_sig :15;
u32 LNB_CTLHighCount_sig :15;
} lnb_switch_freq_200;
struct {
u32 Rev_N_sig_reserved2 : 1;
u32 Rev_N_sig_caps : 1;
u32 Rev_N_sig_reserved1 : 2;
u32 Rev_N_sig_revision_hi : 4;
u32 reserved :20;
u32 Per_reset_sig : 1;
u32 LNB_L_H_sig : 1;
u32 ACPI3_sig : 1;
u32 ACPI1_sig : 1;
} misc_204;
struct {
u32 unused : 9;
u32 Mailbox_from_V8_Enable_sig : 1;
u32 DMA2_Size_IRQ_Enable_sig : 1;
u32 DMA1_Size_IRQ_Enable_sig : 1;
u32 DMA2_Timer_Enable_sig : 1;
u32 DMA2_IRQ_Enable_sig : 1;
u32 DMA1_Timer_Enable_sig : 1;
u32 DMA1_IRQ_Enable_sig : 1;
u32 Rcv_Data_sig : 1;
u32 MAC_filter_Mode_sig : 1;
u32 Multi2_Enable_sig : 1;
u32 Per_CA_Enable_sig : 1;
u32 SMC_Enable_sig : 1;
u32 CA_Enable_sig : 1;
u32 WAN_CA_Enable_sig : 1;
u32 WAN_Enable_sig : 1;
u32 Mask_filter_sig : 1;
u32 Null_filter_sig : 1;
u32 ECM_filter_sig : 1;
u32 EMM_filter_sig : 1;
u32 PMT_filter_sig : 1;
u32 PCR_filter_sig : 1;
u32 Stream2_filter_sig : 1;
u32 Stream1_filter_sig : 1;
} ctrl_208;
struct {
u32 reserved :21;
u32 Transport_Error : 1;
u32 LLC_SNAP_FLAG_set : 1;
u32 Continuity_error_flag : 1;
u32 Data_receiver_error : 1;
u32 Mailbox_from_V8_Status_sig : 1;
u32 DMA2_Size_IRQ_Status : 1;
u32 DMA1_Size_IRQ_Status : 1;
u32 DMA2_Timer_Status : 1;
u32 DMA2_IRQ_Status : 1;
u32 DMA1_Timer_Status : 1;
u32 DMA1_IRQ_Status : 1;
} irq_20c;
struct {
u32 Special_controls :16;
u32 Block_reset_enable : 8;
u32 reset_block_700 : 1;
u32 reset_block_600 : 1;
u32 reset_block_500 : 1;
u32 reset_block_400 : 1;
u32 reset_block_300 : 1;
u32 reset_block_200 : 1;
u32 reset_block_100 : 1;
u32 reset_block_000 : 1;
} sw_reset_210;
struct {
u32 unused2 :20;
u32 polarity_PS_ERR_sig : 1;
u32 polarity_PS_SYNC_sig : 1;
u32 polarity_PS_VALID_sig : 1;
u32 polarity_PS_CLK_sig : 1;
u32 unused1 : 3;
u32 s2p_sel_sig : 1;
u32 section_pkg_enable_sig : 1;
u32 halt_V8_sig : 1;
u32 v2WS_oe_sig : 1;
u32 vuart_oe_sig : 1;
} misc_214;
struct {
u32 Mailbox_from_V8 :32;
} mbox_v8_to_host_218;
struct {
u32 sysramaccess_busmuster : 1;
u32 sysramaccess_write : 1;
u32 unused : 7;
u32 sysramaccess_addr :15;
u32 sysramaccess_data : 8;
} mbox_host_to_v8_21c;
struct {
u32 debug_fifo_problem : 1;
u32 debug_flag_write_status00 : 1;
u32 Stream2_trans : 1;
u32 Stream2_PID :13;
u32 debug_flag_pid_saved : 1;
u32 MAC_Multicast_filter : 1;
u32 Stream1_trans : 1;
u32 Stream1_PID :13;
} pid_filter_300;
struct {
u32 reserved : 2;
u32 PMT_trans : 1;
u32 PMT_PID :13;
u32 debug_overrun2 : 1;
u32 debug_overrun3 : 1;
u32 PCR_trans : 1;
u32 PCR_PID :13;
} pid_filter_304;
struct {
u32 reserved : 2;
u32 ECM_trans : 1;
u32 ECM_PID :13;
u32 EMM_filter_6 : 1;
u32 EMM_filter_4 : 1;
u32 EMM_trans : 1;
u32 EMM_PID :13;
} pid_filter_308;
struct {
u32 unused2 : 3;
u32 Group_mask :13;
u32 unused1 : 2;
u32 Group_trans : 1;
u32 Group_PID :13;
} pid_filter_30c_ext_ind_0_7;
struct {
u32 unused :15;
u32 net_master_read :17;
} pid_filter_30c_ext_ind_1;
struct {
u32 unused :15;
u32 net_master_write :17;
} pid_filter_30c_ext_ind_2;
struct {
u32 unused :15;
u32 next_net_master_write :17;
} pid_filter_30c_ext_ind_3;
struct {
u32 reserved2 : 5;
u32 stack_read :10;
u32 reserved1 : 6;
u32 state_write :10;
u32 unused1 : 1;
} pid_filter_30c_ext_ind_4;
struct {
u32 unused :22;
u32 stack_cnt :10;
} pid_filter_30c_ext_ind_5;
struct {
u32 unused : 4;
u32 data_size_reg :12;
u32 write_status4 : 2;
u32 write_status1 : 2;
u32 pid_fsm_save_reg300 : 2;
u32 pid_fsm_save_reg4 : 2;
u32 pid_fsm_save_reg3 : 2;
u32 pid_fsm_save_reg2 : 2;
u32 pid_fsm_save_reg1 : 2;
u32 pid_fsm_save_reg0 : 2;
} pid_filter_30c_ext_ind_6;
struct {
u32 unused :22;
u32 pass_alltables : 1;
u32 AB_select : 1;
u32 extra_index_reg : 3;
u32 index_reg : 5;
} index_reg_310;
struct {
u32 reserved :17;
u32 PID_enable_bit : 1;
u32 PID_trans : 1;
u32 PID :13;
} pid_n_reg_314;
struct {
u32 reserved : 6;
u32 HighAB_bit : 1;
u32 Enable_bit : 1;
u32 A6_byte : 8;
u32 A5_byte : 8;
u32 A4_byte : 8;
} mac_low_reg_318;
struct {
u32 reserved : 8;
u32 A3_byte : 8;
u32 A2_byte : 8;
u32 A1_byte : 8;
} mac_high_reg_31c;
struct {
u32 data_Tag_ID :16;
u32 reserved :16;
} data_tag_400;
struct {
u32 Card_IDbyte3 : 8;
u32 Card_IDbyte4 : 8;
u32 Card_IDbyte5 : 8;
u32 Card_IDbyte6 : 8;
} card_id_408;
struct {
u32 Card_IDbyte1 : 8;
u32 Card_IDbyte2 : 8;
} card_id_40c;
struct {
u32 MAC6 : 8;
u32 MAC3 : 8;
u32 MAC2 : 8;
u32 MAC1 : 8;
} mac_address_418;
struct {
u32 reserved :16;
u32 MAC8 : 8;
u32 MAC7 : 8;
} mac_address_41c;
struct {
u32 reserved :21;
u32 txbuffempty : 1;
u32 ReceiveByteFrameError : 1;
u32 ReceiveDataReady : 1;
u32 transmitter_data_byte : 8;
} ci_600;
struct {
u32 pi_component_reg : 3;
u32 pi_rw : 1;
u32 pi_ha :20;
u32 pi_d : 8;
} pi_604;
struct {
u32 pi_busy_n : 1;
u32 pi_wait_n : 1;
u32 pi_timeout_status : 1;
u32 pi_CiMax_IRQ_n : 1;
u32 config_cclk : 1;
u32 config_cs_n : 1;
u32 config_wr_n : 1;
u32 config_Prog_n : 1;
u32 config_Init_stat : 1;
u32 config_Done_stat : 1;
u32 pcmcia_b_mod_pwr_n : 1;
u32 pcmcia_a_mod_pwr_n : 1;
u32 reserved : 3;
u32 Timer_addr : 5;
u32 unused : 1;
u32 timer_data : 7;
u32 Timer_Load_req : 1;
u32 Timer_Read_req : 1;
u32 oncecycle_read : 1;
u32 serialReset : 1;
} pi_608;
struct {
u32 reserved : 6;
u32 rw_flag : 1;
u32 dvb_en : 1;
u32 key_array_row : 5;
u32 key_array_col : 3;
u32 key_code : 2;
u32 key_enable : 1;
u32 PID :13;
} dvb_reg_60c;
struct {
u32 start_sram_ibi : 1;
u32 reserved2 : 1;
u32 ce_pin_reg : 1;
u32 oe_pin_reg : 1;
u32 reserved1 : 3;
u32 sc_xfer_bit : 1;
u32 sram_data : 8;
u32 sram_rw : 1;
u32 sram_addr :15;
} sram_ctrl_reg_700;
struct {
u32 net_addr_write :16;
u32 net_addr_read :16;
} net_buf_reg_704;
struct {
u32 cai_cnt : 4;
u32 reserved2 : 6;
u32 cai_write :11;
u32 reserved1 : 5;
u32 cai_read :11;
} cai_buf_reg_708;
struct {
u32 cao_cnt : 4;
u32 reserved2 : 6;
u32 cap_write :11;
u32 reserved1 : 5;
u32 cao_read :11;
} cao_buf_reg_70c;
struct {
u32 media_cnt : 4;
u32 reserved2 : 6;
u32 media_write :11;
u32 reserved1 : 5;
u32 media_read :11;
} media_buf_reg_710;
struct {
u32 reserved :17;
u32 ctrl_maximumfill : 1;
u32 ctrl_sramdma : 1;
u32 ctrl_usb_wan : 1;
u32 cao_ovflow_error : 1;
u32 cai_ovflow_error : 1;
u32 media_ovflow_error : 1;
u32 net_ovflow_error : 1;
u32 MEDIA_Dest : 2;
u32 CAO_Dest : 2;
u32 CAI_Dest : 2;
u32 NET_Dest : 2;
} sram_dest_reg_714;
struct {
u32 reserved3 :11;
u32 net_addr_write : 1;
u32 reserved2 : 3;
u32 net_addr_read : 1;
u32 reserved1 : 4;
u32 net_cnt :12;
} net_buf_reg_718;
struct {
u32 reserved3 : 4;
u32 wan_pkt_frame : 4;
u32 reserved2 : 4;
u32 sram_memmap : 2;
u32 sram_chip : 2;
u32 wan_wait_state : 8;
u32 reserved1 : 6;
u32 wan_speed_sig : 2;
} wan_ctrl_reg_71c;
} flexcop_ibi_value;
#endif

View File

@@ -0,0 +1,458 @@
/* This file is part of linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
*
* register descriptions
*
* see flexcop.c for copyright information.
*/
/* This file is automatically generated, do not edit things here. */
#ifndef __FLEXCOP_IBI_VALUE_INCLUDED__
#define __FLEXCOP_IBI_VALUE_INCLUDED__
typedef union {
u32 raw;
struct {
u32 dma_0start : 1;
u32 dma_0No_update : 1;
u32 dma_address0 :30;
} dma_0x0;
struct {
u32 DMA_maxpackets : 8;
u32 dma_addr_size :24;
} dma_0x4_remap;
struct {
u32 dma1timer : 7;
u32 unused : 1;
u32 dma_addr_size :24;
} dma_0x4_read;
struct {
u32 unused : 1;
u32 dmatimer : 7;
u32 dma_addr_size :24;
} dma_0x4_write;
struct {
u32 unused : 2;
u32 dma_cur_addr :30;
} dma_0x8;
struct {
u32 dma_1start : 1;
u32 remap_enable : 1;
u32 dma_address1 :30;
} dma_0xc;
struct {
u32 chipaddr : 7;
u32 reserved1 : 1;
u32 baseaddr : 8;
u32 data1_reg : 8;
u32 working_start : 1;
u32 twoWS_rw : 1;
u32 total_bytes : 2;
u32 twoWS_port_reg : 2;
u32 no_base_addr_ack_error : 1;
u32 st_done : 1;
} tw_sm_c_100;
struct {
u32 data2_reg : 8;
u32 data3_reg : 8;
u32 data4_reg : 8;
u32 exlicit_stops : 1;
u32 force_stop : 1;
u32 unused : 6;
} tw_sm_c_104;
struct {
u32 thi1 : 6;
u32 reserved1 : 2;
u32 tlo1 : 5;
u32 reserved2 :19;
} tw_sm_c_108;
struct {
u32 thi1 : 6;
u32 reserved1 : 2;
u32 tlo1 : 5;
u32 reserved2 :19;
} tw_sm_c_10c;
struct {
u32 thi1 : 6;
u32 reserved1 : 2;
u32 tlo1 : 5;
u32 reserved2 :19;
} tw_sm_c_110;
struct {
u32 LNB_CTLHighCount_sig :15;
u32 LNB_CTLLowCount_sig :15;
u32 LNB_CTLPrescaler_sig : 2;
} lnb_switch_freq_200;
struct {
u32 ACPI1_sig : 1;
u32 ACPI3_sig : 1;
u32 LNB_L_H_sig : 1;
u32 Per_reset_sig : 1;
u32 reserved :20;
u32 Rev_N_sig_revision_hi : 4;
u32 Rev_N_sig_reserved1 : 2;
u32 Rev_N_sig_caps : 1;
u32 Rev_N_sig_reserved2 : 1;
} misc_204;
struct {
u32 Stream1_filter_sig : 1;
u32 Stream2_filter_sig : 1;
u32 PCR_filter_sig : 1;
u32 PMT_filter_sig : 1;
u32 EMM_filter_sig : 1;
u32 ECM_filter_sig : 1;
u32 Null_filter_sig : 1;
u32 Mask_filter_sig : 1;
u32 WAN_Enable_sig : 1;
u32 WAN_CA_Enable_sig : 1;
u32 CA_Enable_sig : 1;
u32 SMC_Enable_sig : 1;
u32 Per_CA_Enable_sig : 1;
u32 Multi2_Enable_sig : 1;
u32 MAC_filter_Mode_sig : 1;
u32 Rcv_Data_sig : 1;
u32 DMA1_IRQ_Enable_sig : 1;
u32 DMA1_Timer_Enable_sig : 1;
u32 DMA2_IRQ_Enable_sig : 1;
u32 DMA2_Timer_Enable_sig : 1;
u32 DMA1_Size_IRQ_Enable_sig : 1;
u32 DMA2_Size_IRQ_Enable_sig : 1;
u32 Mailbox_from_V8_Enable_sig : 1;
u32 unused : 9;
} ctrl_208;
struct {
u32 DMA1_IRQ_Status : 1;
u32 DMA1_Timer_Status : 1;
u32 DMA2_IRQ_Status : 1;
u32 DMA2_Timer_Status : 1;
u32 DMA1_Size_IRQ_Status : 1;
u32 DMA2_Size_IRQ_Status : 1;
u32 Mailbox_from_V8_Status_sig : 1;
u32 Data_receiver_error : 1;
u32 Continuity_error_flag : 1;
u32 LLC_SNAP_FLAG_set : 1;
u32 Transport_Error : 1;
u32 reserved :21;
} irq_20c;
struct {
u32 reset_block_000 : 1;
u32 reset_block_100 : 1;
u32 reset_block_200 : 1;
u32 reset_block_300 : 1;
u32 reset_block_400 : 1;
u32 reset_block_500 : 1;
u32 reset_block_600 : 1;
u32 reset_block_700 : 1;
u32 Block_reset_enable : 8;
u32 Special_controls :16;
} sw_reset_210;
struct {
u32 vuart_oe_sig : 1;
u32 v2WS_oe_sig : 1;
u32 halt_V8_sig : 1;
u32 section_pkg_enable_sig : 1;
u32 s2p_sel_sig : 1;
u32 unused1 : 3;
u32 polarity_PS_CLK_sig : 1;
u32 polarity_PS_VALID_sig : 1;
u32 polarity_PS_SYNC_sig : 1;
u32 polarity_PS_ERR_sig : 1;
u32 unused2 :20;
} misc_214;
struct {
u32 Mailbox_from_V8 :32;
} mbox_v8_to_host_218;
struct {
u32 sysramaccess_data : 8;
u32 sysramaccess_addr :15;
u32 unused : 7;
u32 sysramaccess_write : 1;
u32 sysramaccess_busmuster : 1;
} mbox_host_to_v8_21c;
struct {
u32 Stream1_PID :13;
u32 Stream1_trans : 1;
u32 MAC_Multicast_filter : 1;
u32 debug_flag_pid_saved : 1;
u32 Stream2_PID :13;
u32 Stream2_trans : 1;
u32 debug_flag_write_status00 : 1;
u32 debug_fifo_problem : 1;
} pid_filter_300;
struct {
u32 PCR_PID :13;
u32 PCR_trans : 1;
u32 debug_overrun3 : 1;
u32 debug_overrun2 : 1;
u32 PMT_PID :13;
u32 PMT_trans : 1;
u32 reserved : 2;
} pid_filter_304;
struct {
u32 EMM_PID :13;
u32 EMM_trans : 1;
u32 EMM_filter_4 : 1;
u32 EMM_filter_6 : 1;
u32 ECM_PID :13;
u32 ECM_trans : 1;
u32 reserved : 2;
} pid_filter_308;
struct {
u32 Group_PID :13;
u32 Group_trans : 1;
u32 unused1 : 2;
u32 Group_mask :13;
u32 unused2 : 3;
} pid_filter_30c_ext_ind_0_7;
struct {
u32 net_master_read :17;
u32 unused :15;
} pid_filter_30c_ext_ind_1;
struct {
u32 net_master_write :17;
u32 unused :15;
} pid_filter_30c_ext_ind_2;
struct {
u32 next_net_master_write :17;
u32 unused :15;
} pid_filter_30c_ext_ind_3;
struct {
u32 unused1 : 1;
u32 state_write :10;
u32 reserved1 : 6;
u32 stack_read :10;
u32 reserved2 : 5;
} pid_filter_30c_ext_ind_4;
struct {
u32 stack_cnt :10;
u32 unused :22;
} pid_filter_30c_ext_ind_5;
struct {
u32 pid_fsm_save_reg0 : 2;
u32 pid_fsm_save_reg1 : 2;
u32 pid_fsm_save_reg2 : 2;
u32 pid_fsm_save_reg3 : 2;
u32 pid_fsm_save_reg4 : 2;
u32 pid_fsm_save_reg300 : 2;
u32 write_status1 : 2;
u32 write_status4 : 2;
u32 data_size_reg :12;
u32 unused : 4;
} pid_filter_30c_ext_ind_6;
struct {
u32 index_reg : 5;
u32 extra_index_reg : 3;
u32 AB_select : 1;
u32 pass_alltables : 1;
u32 unused :22;
} index_reg_310;
struct {
u32 PID :13;
u32 PID_trans : 1;
u32 PID_enable_bit : 1;
u32 reserved :17;
} pid_n_reg_314;
struct {
u32 A4_byte : 8;
u32 A5_byte : 8;
u32 A6_byte : 8;
u32 Enable_bit : 1;
u32 HighAB_bit : 1;
u32 reserved : 6;
} mac_low_reg_318;
struct {
u32 A1_byte : 8;
u32 A2_byte : 8;
u32 A3_byte : 8;
u32 reserved : 8;
} mac_high_reg_31c;
struct {
u32 reserved :16;
u32 data_Tag_ID :16;
} data_tag_400;
struct {
u32 Card_IDbyte6 : 8;
u32 Card_IDbyte5 : 8;
u32 Card_IDbyte4 : 8;
u32 Card_IDbyte3 : 8;
} card_id_408;
struct {
u32 Card_IDbyte2 : 8;
u32 Card_IDbyte1 : 8;
} card_id_40c;
struct {
u32 MAC1 : 8;
u32 MAC2 : 8;
u32 MAC3 : 8;
u32 MAC6 : 8;
} mac_address_418;
struct {
u32 MAC7 : 8;
u32 MAC8 : 8;
u32 reserved :16;
} mac_address_41c;
struct {
u32 transmitter_data_byte : 8;
u32 ReceiveDataReady : 1;
u32 ReceiveByteFrameError : 1;
u32 txbuffempty : 1;
u32 reserved :21;
} ci_600;
struct {
u32 pi_d : 8;
u32 pi_ha :20;
u32 pi_rw : 1;
u32 pi_component_reg : 3;
} pi_604;
struct {
u32 serialReset : 1;
u32 oncecycle_read : 1;
u32 Timer_Read_req : 1;
u32 Timer_Load_req : 1;
u32 timer_data : 7;
u32 unused : 1;
u32 Timer_addr : 5;
u32 reserved : 3;
u32 pcmcia_a_mod_pwr_n : 1;
u32 pcmcia_b_mod_pwr_n : 1;
u32 config_Done_stat : 1;
u32 config_Init_stat : 1;
u32 config_Prog_n : 1;
u32 config_wr_n : 1;
u32 config_cs_n : 1;
u32 config_cclk : 1;
u32 pi_CiMax_IRQ_n : 1;
u32 pi_timeout_status : 1;
u32 pi_wait_n : 1;
u32 pi_busy_n : 1;
} pi_608;
struct {
u32 PID :13;
u32 key_enable : 1;
u32 key_code : 2;
u32 key_array_col : 3;
u32 key_array_row : 5;
u32 dvb_en : 1;
u32 rw_flag : 1;
u32 reserved : 6;
} dvb_reg_60c;
struct {
u32 sram_addr :15;
u32 sram_rw : 1;
u32 sram_data : 8;
u32 sc_xfer_bit : 1;
u32 reserved1 : 3;
u32 oe_pin_reg : 1;
u32 ce_pin_reg : 1;
u32 reserved2 : 1;
u32 start_sram_ibi : 1;
} sram_ctrl_reg_700;
struct {
u32 net_addr_read :16;
u32 net_addr_write :16;
} net_buf_reg_704;
struct {
u32 cai_read :11;
u32 reserved1 : 5;
u32 cai_write :11;
u32 reserved2 : 6;
u32 cai_cnt : 4;
} cai_buf_reg_708;
struct {
u32 cao_read :11;
u32 reserved1 : 5;
u32 cap_write :11;
u32 reserved2 : 6;
u32 cao_cnt : 4;
} cao_buf_reg_70c;
struct {
u32 media_read :11;
u32 reserved1 : 5;
u32 media_write :11;
u32 reserved2 : 6;
u32 media_cnt : 4;
} media_buf_reg_710;
struct {
u32 NET_Dest : 2;
u32 CAI_Dest : 2;
u32 CAO_Dest : 2;
u32 MEDIA_Dest : 2;
u32 net_ovflow_error : 1;
u32 media_ovflow_error : 1;
u32 cai_ovflow_error : 1;
u32 cao_ovflow_error : 1;
u32 ctrl_usb_wan : 1;
u32 ctrl_sramdma : 1;
u32 ctrl_maximumfill : 1;
u32 reserved :17;
} sram_dest_reg_714;
struct {
u32 net_cnt :12;
u32 reserved1 : 4;
u32 net_addr_read : 1;
u32 reserved2 : 3;
u32 net_addr_write : 1;
u32 reserved3 :11;
} net_buf_reg_718;
struct {
u32 wan_speed_sig : 2;
u32 reserved1 : 6;
u32 wan_wait_state : 8;
u32 sram_chip : 2;
u32 sram_memmap : 2;
u32 reserved2 : 4;
u32 wan_pkt_frame : 4;
u32 reserved3 : 4;
} wan_ctrl_reg_71c;
} flexcop_ibi_value;
#endif

View File

@@ -0,0 +1,23 @@
config DVB_BT8XX
tristate "BT8xx based PCI cards"
depends on DVB_CORE && PCI && I2C && VIDEO_BT848
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_SP887X if !DVB_FE_CUSTOMISE
select DVB_NXT6000 if !DVB_FE_CUSTOMISE
select DVB_CX24110 if !DVB_FE_CUSTOMISE
select DVB_OR51211 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select FW_LOADER
help
Support for PCI cards based on the Bt8xx PCI bridge. Examples are
the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards,
the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and
some AVerMedia cards.
Since these cards have no MPEG decoder onboard, they transmit
only compressed MPEG data over the PCI bus, so you need
an external software decoder to watch TV on your computer.
Say Y if you own such a device and want to use it.

View File

@@ -0,0 +1,3 @@
obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/video/bt8xx -Idrivers/media/dvb/frontends

View File

@@ -0,0 +1,636 @@
/*
* bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card
*
* Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de>
*
* large parts based on the bttv driver
* Copyright (C) 1996,97,98 Ralph Metzler (rjkm@metzlerbros.de)
* & Marcus Metzler (mocm@metzlerbros.de)
* (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include "dmxdev.h"
#include "dvbdev.h"
#include "bt878.h"
#include "dst_priv.h"
/**************************************/
/* Miscellaneous utility definitions */
/**************************************/
static unsigned int bt878_verbose = 1;
static unsigned int bt878_debug;
module_param_named(verbose, bt878_verbose, int, 0444);
MODULE_PARM_DESC(verbose,
"verbose startup messages, default is 1 (yes)");
module_param_named(debug, bt878_debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging, default is 0 (off).");
int bt878_num;
struct bt878 bt878[BT878_MAX];
EXPORT_SYMBOL(bt878_num);
EXPORT_SYMBOL(bt878);
#define btwrite(dat,adr) bmtwrite((dat), (bt->bt878_mem+(adr)))
#define btread(adr) bmtread(bt->bt878_mem+(adr))
#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
#if defined(dprintk)
#undef dprintk
#endif
#define dprintk if(bt878_debug) printk
static void bt878_mem_free(struct bt878 *bt)
{
if (bt->buf_cpu) {
pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu,
bt->buf_dma);
bt->buf_cpu = NULL;
}
if (bt->risc_cpu) {
pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu,
bt->risc_dma);
bt->risc_cpu = NULL;
}
}
static int bt878_mem_alloc(struct bt878 *bt)
{
if (!bt->buf_cpu) {
bt->buf_size = 128 * 1024;
bt->buf_cpu =
pci_alloc_consistent(bt->dev, bt->buf_size,
&bt->buf_dma);
if (!bt->buf_cpu)
return -ENOMEM;
memset(bt->buf_cpu, 0, bt->buf_size);
}
if (!bt->risc_cpu) {
bt->risc_size = PAGE_SIZE;
bt->risc_cpu =
pci_alloc_consistent(bt->dev, bt->risc_size,
&bt->risc_dma);
if (!bt->risc_cpu) {
bt878_mem_free(bt);
return -ENOMEM;
}
memset(bt->risc_cpu, 0, bt->risc_size);
}
return 0;
}
/* RISC instructions */
#define RISC_WRITE (0x01 << 28)
#define RISC_JUMP (0x07 << 28)
#define RISC_SYNC (0x08 << 28)
/* RISC bits */
#define RISC_WR_SOL (1 << 27)
#define RISC_WR_EOL (1 << 26)
#define RISC_IRQ (1 << 24)
#define RISC_STATUS(status) ((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16))
#define RISC_SYNC_RESYNC (1 << 15)
#define RISC_SYNC_FM1 0x06
#define RISC_SYNC_VRO 0x0C
#define RISC_FLUSH() bt->risc_pos = 0
#define RISC_INSTR(instr) bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr)
static int bt878_make_risc(struct bt878 *bt)
{
bt->block_bytes = bt->buf_size >> 4;
bt->block_count = 1 << 4;
bt->line_bytes = bt->block_bytes;
bt->line_count = bt->block_count;
while (bt->line_bytes > 4095) {
bt->line_bytes >>= 1;
bt->line_count <<= 1;
}
if (bt->line_count > 255) {
printk("bt878: buffer size error!\n");
return -EINVAL;
}
return 0;
}
static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin)
{
u32 buf_pos = 0;
u32 line;
RISC_FLUSH();
RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin);
RISC_INSTR(0);
dprintk("bt878: risc len lines %u, bytes per line %u\n",
bt->line_count, bt->line_bytes);
for (line = 0; line < bt->line_count; line++) {
// At the beginning of every block we issue an IRQ with previous (finished) block number set
if (!(buf_pos % bt->block_bytes))
RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL |
RISC_IRQ |
RISC_STATUS(((buf_pos /
bt->block_bytes) +
(bt->block_count -
1)) %
bt->block_count) | bt->
line_bytes);
else
RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL |
bt->line_bytes);
RISC_INSTR(bt->buf_dma + buf_pos);
buf_pos += bt->line_bytes;
}
RISC_INSTR(RISC_SYNC | op_sync_orin | RISC_SYNC_VRO);
RISC_INSTR(0);
RISC_INSTR(RISC_JUMP);
RISC_INSTR(bt->risc_dma);
btwrite((bt->line_count << 16) | bt->line_bytes, BT878_APACK_LEN);
}
/*****************************/
/* Start/Stop grabbing funcs */
/*****************************/
void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
u32 irq_err_ignore)
{
u32 int_mask;
dprintk("bt878 debug: bt878_start (ctl=%8.8x)\n", controlreg);
/* complete the writing of the risc dma program now we have
* the card specifics
*/
bt878_risc_program(bt, op_sync_orin);
controlreg &= ~0x1f;
controlreg |= 0x1b;
btwrite(bt->risc_dma, BT878_ARISC_START);
/* original int mask had :
* 6 2 8 4 0
* 1111 1111 1000 0000 0000
* SCERR|OCERR|PABORT|RIPERR|FDSR|FTRGT|FBUS|RISCI
* Hacked for DST to:
* SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI
*/
int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT |
BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT |
BT878_AFBUS | BT878_ARISCI;
/* ignore pesky bits */
int_mask &= ~irq_err_ignore;
btwrite(int_mask, BT878_AINT_MASK);
btwrite(controlreg, BT878_AGPIO_DMA_CTL);
}
void bt878_stop(struct bt878 *bt)
{
u32 stat;
int i = 0;
dprintk("bt878 debug: bt878_stop\n");
btwrite(0, BT878_AINT_MASK);
btand(~0x13, BT878_AGPIO_DMA_CTL);
do {
stat = btread(BT878_AINT_STAT);
if (!(stat & BT878_ARISC_EN))
break;
i++;
} while (i < 500);
dprintk("bt878(%d) debug: bt878_stop, i=%d, stat=0x%8.8x\n",
bt->nr, i, stat);
}
EXPORT_SYMBOL(bt878_start);
EXPORT_SYMBOL(bt878_stop);
/*****************************/
/* Interrupt service routine */
/*****************************/
static irqreturn_t bt878_irq(int irq, void *dev_id)
{
u32 stat, astat, mask;
int count;
struct bt878 *bt;
bt = (struct bt878 *) dev_id;
count = 0;
while (1) {
stat = btread(BT878_AINT_STAT);
mask = btread(BT878_AINT_MASK);
if (!(astat = (stat & mask)))
return IRQ_NONE; /* this interrupt is not for me */
/* dprintk("bt878(%d) debug: irq count %d, stat 0x%8.8x, mask 0x%8.8x\n",bt->nr,count,stat,mask); */
btwrite(astat, BT878_AINT_STAT); /* try to clear interupt condition */
if (astat & (BT878_ASCERR | BT878_AOCERR)) {
if (bt878_verbose) {
printk("bt878(%d): irq%s%s risc_pc=%08x\n",
bt->nr,
(astat & BT878_ASCERR) ? " SCERR" :
"",
(astat & BT878_AOCERR) ? " OCERR" :
"", btread(BT878_ARISC_PC));
}
}
if (astat & (BT878_APABORT | BT878_ARIPERR | BT878_APPERR)) {
if (bt878_verbose) {
printk
("bt878(%d): irq%s%s%s risc_pc=%08x\n",
bt->nr,
(astat & BT878_APABORT) ? " PABORT" :
"",
(astat & BT878_ARIPERR) ? " RIPERR" :
"",
(astat & BT878_APPERR) ? " PPERR" :
"", btread(BT878_ARISC_PC));
}
}
if (astat & (BT878_AFDSR | BT878_AFTRGT | BT878_AFBUS)) {
if (bt878_verbose) {
printk
("bt878(%d): irq%s%s%s risc_pc=%08x\n",
bt->nr,
(astat & BT878_AFDSR) ? " FDSR" : "",
(astat & BT878_AFTRGT) ? " FTRGT" :
"",
(astat & BT878_AFBUS) ? " FBUS" : "",
btread(BT878_ARISC_PC));
}
}
if (astat & BT878_ARISCI) {
bt->finished_block = (stat & BT878_ARISCS) >> 28;
tasklet_schedule(&bt->tasklet);
break;
}
count++;
if (count > 20) {
btwrite(0, BT878_AINT_MASK);
printk(KERN_ERR
"bt878(%d): IRQ lockup, cleared int mask\n",
bt->nr);
break;
}
}
return IRQ_HANDLED;
}
int
bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp)
{
int retval;
retval = 0;
if (mutex_lock_interruptible(&bt->gpio_lock))
return -ERESTARTSYS;
/* special gpio signal */
switch (cmd) {
case DST_IG_ENABLE:
// dprintk("dvb_bt8xx: dst enable mask 0x%02x enb 0x%02x \n", mp->dstg.enb.mask, mp->dstg.enb.enable);
retval = bttv_gpio_enable(bt->bttv_nr,
mp->enb.mask,
mp->enb.enable);
break;
case DST_IG_WRITE:
// dprintk("dvb_bt8xx: dst write gpio mask 0x%02x out 0x%02x\n", mp->dstg.outp.mask, mp->dstg.outp.highvals);
retval = bttv_write_gpio(bt->bttv_nr,
mp->outp.mask,
mp->outp.highvals);
break;
case DST_IG_READ:
/* read */
retval = bttv_read_gpio(bt->bttv_nr, &mp->rd.value);
// dprintk("dvb_bt8xx: dst read gpio 0x%02x\n", (unsigned)mp->dstg.rd.value);
break;
case DST_IG_TS:
/* Set packet size */
bt->TS_Size = mp->psize;
break;
default:
retval = -EINVAL;
break;
}
mutex_unlock(&bt->gpio_lock);
return retval;
}
EXPORT_SYMBOL(bt878_device_control);
static struct cards card_list[] __devinitdata = {
{ 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
{ 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" },
{ 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" },
{ 0x002611bd, BTTV_BOARD_TWINHAN_DST, "Pinnacle PCTV SAT CI" },
{ 0x00011822, BTTV_BOARD_TWINHAN_DST, "Twinhan VisionPlus DVB" },
{ 0xfc00270f, BTTV_BOARD_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" },
{ 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" },
{ 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" },
{ 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" },
{ 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" },
{ 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV" },
{ 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" },
{ 0, -1, NULL }
};
/***********************/
/* PCI device handling */
/***********************/
static int __devinit bt878_probe(struct pci_dev *dev,
const struct pci_device_id *pci_id)
{
int result = 0, has_dvb = 0, i;
unsigned char lat;
struct bt878 *bt;
#if defined(__powerpc__)
unsigned int cmd;
#endif
unsigned int cardid;
unsigned short id;
struct cards *dvb_cards;
printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n",
bt878_num);
if (bt878_num >= BT878_MAX) {
printk(KERN_ERR "bt878: Too many devices inserted\n");
result = -ENOMEM;
goto fail0;
}
if (pci_enable_device(dev))
return -EIO;
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &id);
cardid = id << 16;
pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &id);
cardid |= id;
for (i = 0, dvb_cards = card_list; i < ARRAY_SIZE(card_list); i++, dvb_cards++) {
if (cardid == dvb_cards->pci_id) {
printk("%s: card id=[0x%x],[ %s ] has DVB functions.\n",
__func__, cardid, dvb_cards->name);
has_dvb = 1;
}
}
if (!has_dvb) {
printk("%s: card id=[0x%x], Unknown card.\nExiting..\n", __func__, cardid);
result = -EINVAL;
goto fail0;
}
bt = &bt878[bt878_num];
bt->dev = dev;
bt->nr = bt878_num;
bt->shutdown = 0;
bt->id = dev->device;
bt->irq = dev->irq;
bt->bt878_adr = pci_resource_start(dev, 0);
if (!request_mem_region(pci_resource_start(dev, 0),
pci_resource_len(dev, 0), "bt878")) {
result = -EBUSY;
goto fail0;
}
pci_read_config_byte(dev, PCI_CLASS_REVISION, &bt->revision);
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
printk(KERN_INFO "bt878(%d): Bt%x (rev %d) at %02x:%02x.%x, ",
bt878_num, bt->id, bt->revision, dev->bus->number,
PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
printk("irq: %d, latency: %d, memory: 0x%lx\n",
bt->irq, lat, bt->bt878_adr);
#if defined(__powerpc__)
/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
/* response on cards with no firmware is not enabled by OF */
pci_read_config_dword(dev, PCI_COMMAND, &cmd);
cmd = (cmd | PCI_COMMAND_MEMORY);
pci_write_config_dword(dev, PCI_COMMAND, cmd);
#endif
#ifdef __sparc__
bt->bt878_mem = (unsigned char *) bt->bt878_adr;
#else
bt->bt878_mem = ioremap(bt->bt878_adr, 0x1000);
#endif
/* clear interrupt mask */
btwrite(0, BT848_INT_MASK);
result = request_irq(bt->irq, bt878_irq,
IRQF_SHARED | IRQF_DISABLED, "bt878",
(void *) bt);
if (result == -EINVAL) {
printk(KERN_ERR "bt878(%d): Bad irq number or handler\n",
bt878_num);
goto fail1;
}
if (result == -EBUSY) {
printk(KERN_ERR
"bt878(%d): IRQ %d busy, change your PnP config in BIOS\n",
bt878_num, bt->irq);
goto fail1;
}
if (result < 0)
goto fail1;
pci_set_master(dev);
pci_set_drvdata(dev, bt);
/* if(init_bt878(btv) < 0) {
bt878_remove(dev);
return -EIO;
}
*/
if ((result = bt878_mem_alloc(bt))) {
printk("bt878: failed to allocate memory!\n");
goto fail2;
}
bt878_make_risc(bt);
btwrite(0, BT878_AINT_MASK);
bt878_num++;
return 0;
fail2:
free_irq(bt->irq, bt);
fail1:
release_mem_region(pci_resource_start(bt->dev, 0),
pci_resource_len(bt->dev, 0));
fail0:
pci_disable_device(dev);
return result;
}
static void __devexit bt878_remove(struct pci_dev *pci_dev)
{
u8 command;
struct bt878 *bt = pci_get_drvdata(pci_dev);
if (bt878_verbose)
printk("bt878(%d): unloading\n", bt->nr);
/* turn off all capturing, DMA and IRQs */
btand(~0x13, BT878_AGPIO_DMA_CTL);
/* first disable interrupts before unmapping the memory! */
btwrite(0, BT878_AINT_MASK);
btwrite(~0U, BT878_AINT_STAT);
/* disable PCI bus-mastering */
pci_read_config_byte(bt->dev, PCI_COMMAND, &command);
/* Should this be &=~ ?? */
command &= ~PCI_COMMAND_MASTER;
pci_write_config_byte(bt->dev, PCI_COMMAND, command);
free_irq(bt->irq, bt);
printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt->bt878_mem);
if (bt->bt878_mem)
iounmap(bt->bt878_mem);
release_mem_region(pci_resource_start(bt->dev, 0),
pci_resource_len(bt->dev, 0));
/* wake up any waiting processes
because shutdown flag is set, no new processes (in this queue)
are expected
*/
bt->shutdown = 1;
bt878_mem_free(bt);
pci_set_drvdata(pci_dev, NULL);
pci_disable_device(pci_dev);
return;
}
static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BROOKTREE_878,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
};
MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
static struct pci_driver bt878_pci_driver = {
.name = "bt878",
.id_table = bt878_pci_tbl,
.probe = bt878_probe,
.remove = bt878_remove,
};
static int bt878_pci_driver_registered;
/*******************************/
/* Module management functions */
/*******************************/
static int bt878_init_module(void)
{
bt878_num = 0;
bt878_pci_driver_registered = 0;
printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n",
(BT878_VERSION_CODE >> 16) & 0xff,
(BT878_VERSION_CODE >> 8) & 0xff,
BT878_VERSION_CODE & 0xff);
/*
bt878_check_chipset();
*/
/* later we register inside of bt878_find_audio_dma()
* because we may want to ignore certain cards */
bt878_pci_driver_registered = 1;
return pci_register_driver(&bt878_pci_driver);
}
static void bt878_cleanup_module(void)
{
if (bt878_pci_driver_registered) {
bt878_pci_driver_registered = 0;
pci_unregister_driver(&bt878_pci_driver);
}
return;
}
module_init(bt878_init_module);
module_exit(bt878_cleanup_module);
//MODULE_AUTHOR("XXX");
MODULE_LICENSE("GPL");
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View File

@@ -0,0 +1,166 @@
/*
bt878.h - Bt878 audio module (register offsets)
Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de>
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _BT878_H_
#define _BT878_H_
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include "bt848.h"
#include "bttv.h"
#define BT878_VERSION_CODE 0x000000
#define BT878_AINT_STAT 0x100
#define BT878_ARISCS (0xf<<28)
#define BT878_ARISC_EN (1<<27)
#define BT878_ASCERR (1<<19)
#define BT878_AOCERR (1<<18)
#define BT878_APABORT (1<<17)
#define BT878_ARIPERR (1<<16)
#define BT878_APPERR (1<<15)
#define BT878_AFDSR (1<<14)
#define BT878_AFTRGT (1<<13)
#define BT878_AFBUS (1<<12)
#define BT878_ARISCI (1<<11)
#define BT878_AOFLOW (1<<3)
#define BT878_AINT_MASK 0x104
#define BT878_AGPIO_DMA_CTL 0x10c
#define BT878_A_GAIN (0xf<<28)
#define BT878_A_G2X (1<<27)
#define BT878_A_PWRDN (1<<26)
#define BT878_A_SEL (3<<24)
#define BT878_DA_SCE (1<<23)
#define BT878_DA_LRI (1<<22)
#define BT878_DA_MLB (1<<21)
#define BT878_DA_LRD (0x1f<<16)
#define BT878_DA_DPM (1<<15)
#define BT878_DA_SBR (1<<14)
#define BT878_DA_ES2 (1<<13)
#define BT878_DA_LMT (1<<12)
#define BT878_DA_SDR (0xf<<8)
#define BT878_DA_IOM (3<<6)
#define BT878_DA_APP (1<<5)
#define BT878_ACAP_EN (1<<4)
#define BT878_PKTP (3<<2)
#define BT878_RISC_EN (1<<1)
#define BT878_FIFO_EN 1
#define BT878_APACK_LEN 0x110
#define BT878_AFP_LEN (0xff<<16)
#define BT878_ALP_LEN 0xfff
#define BT878_ARISC_START 0x114
#define BT878_ARISC_PC 0x120
/* BT878 FUNCTION 0 REGISTERS */
#define BT878_GPIO_DMA_CTL 0x10c
/* Interrupt register */
#define BT878_INT_STAT 0x100
#define BT878_INT_MASK 0x104
#define BT878_I2CRACK (1<<25)
#define BT878_I2CDONE (1<<8)
#define BT878_MAX 4
#define BT878_RISC_SYNC_MASK (1 << 15)
#define BTTV_BOARD_UNKNOWN 0x00
#define BTTV_BOARD_PINNACLESAT 0x5e
#define BTTV_BOARD_NEBULA_DIGITV 0x68
#define BTTV_BOARD_PC_HDTV 0x70
#define BTTV_BOARD_TWINHAN_DST 0x71
#define BTTV_BOARD_AVDVBT_771 0x7b
#define BTTV_BOARD_AVDVBT_761 0x7c
#define BTTV_BOARD_DVICO_DVBT_LITE 0x80
#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87
struct cards {
__u32 pci_id;
__u16 card_id;
char *name;
};
extern int bt878_num;
struct bt878 {
struct mutex gpio_lock;
unsigned int nr;
unsigned int bttv_nr;
struct i2c_adapter *adapter;
struct pci_dev *dev;
unsigned int id;
unsigned int TS_Size;
unsigned char revision;
unsigned int irq;
unsigned long bt878_adr;
volatile void __iomem *bt878_mem; /* function 1 */
volatile u32 finished_block;
volatile u32 last_block;
u32 block_count;
u32 block_bytes;
u32 line_bytes;
u32 line_count;
u32 buf_size;
u8 *buf_cpu;
dma_addr_t buf_dma;
u32 risc_size;
u32 *risc_cpu;
dma_addr_t risc_dma;
u32 risc_pos;
struct tasklet_struct tasklet;
int shutdown;
};
extern struct bt878 bt878[BT878_MAX];
void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
u32 irq_err_ignore);
void bt878_stop(struct bt878 *bt);
#if defined(__powerpc__) /* big-endian */
extern __inline__ void io_st_le32(volatile unsigned __iomem *addr, unsigned val)
{
__asm__ __volatile__("stwbrx %1,0,%2":"=m"(*addr):"r"(val),
"r"(addr));
__asm__ __volatile__("eieio":::"memory");
}
#define bmtwrite(dat,adr) io_st_le32((adr),(dat))
#define bmtread(adr) ld_le32((adr))
#else
#define bmtwrite(dat,adr) writel((dat), (adr))
#define bmtread(adr) readl(adr)
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,719 @@
/*
CA-driver for TwinHan DST Frontend/Card
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/dvb/ca.h>
#include "dvbdev.h"
#include "dvb_frontend.h"
#include "dst_ca.h"
#include "dst_common.h"
#define DST_CA_ERROR 0
#define DST_CA_NOTICE 1
#define DST_CA_INFO 2
#define DST_CA_DEBUG 3
#define dprintk(x, y, z, format, arg...) do { \
if (z) { \
if ((x > DST_CA_ERROR) && (x > y)) \
printk(KERN_ERR "%s: " format "\n", __FUNCTION__ , ##arg); \
else if ((x > DST_CA_NOTICE) && (x > y)) \
printk(KERN_NOTICE "%s: " format "\n", __FUNCTION__ , ##arg); \
else if ((x > DST_CA_INFO) && (x > y)) \
printk(KERN_INFO "%s: " format "\n", __FUNCTION__ , ##arg); \
else if ((x > DST_CA_DEBUG) && (x > y)) \
printk(KERN_DEBUG "%s: " format "\n", __FUNCTION__ , ##arg); \
} else { \
if (x > y) \
printk(format, ## arg); \
} \
} while(0)
static unsigned int verbose = 5;
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
/* Need some more work */
static int ca_set_slot_descr(void)
{
/* We could make this more graceful ? */
return -EOPNOTSUPP;
}
/* Need some more work */
static int ca_set_pid(void)
{
/* We could make this more graceful ? */
return -EOPNOTSUPP;
}
static void put_command_and_length(u8 *data, int command, int length)
{
data[0] = (command >> 16) & 0xff;
data[1] = (command >> 8) & 0xff;
data[2] = command & 0xff;
data[3] = length;
}
static void put_checksum(u8 *check_string, int length)
{
dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum.");
dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length);
check_string[length] = dst_check_sum (check_string, length);
dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]);
}
static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read)
{
u8 reply;
mutex_lock(&state->dst_mutex);
dst_comm_init(state);
msleep(65);
if (write_dst(state, data, len)) {
dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover");
dst_error_recovery(state);
goto error;
}
if ((dst_pio_disable(state)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed.");
goto error;
}
if (read_dst(state, &reply, GET_ACK) < 0) {
dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
dst_error_recovery(state);
goto error;
}
if (read) {
if (! dst_wait_dst_ready(state, LONG_DELAY)) {
dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready");
goto error;
}
if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */
dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
dst_error_recovery(state);
goto error;
}
}
mutex_unlock(&state->dst_mutex);
return 0;
error:
mutex_unlock(&state->dst_mutex);
return -EIO;
}
static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read)
{
u8 dst_ca_comm_err = 0;
while (dst_ca_comm_err < RETRIES) {
dprintk(verbose, DST_CA_NOTICE, 1, " Put Command");
if (dst_ci_command(state, data, ca_string, len, read)) { // If error
dst_error_recovery(state);
dst_ca_comm_err++; // work required here.
} else {
break;
}
}
if(dst_ca_comm_err == RETRIES)
return -1;
return 0;
}
static int ca_get_app_info(struct dst_state *state)
{
int length, str_length;
static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff};
put_checksum(&command[0], command[0]);
if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
return -1;
}
dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================");
dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]",
state->messages[7], (state->messages[8] << 8) | state->messages[9],
(state->messages[10] << 8) | state->messages[11], __FUNCTION__, (char *)(&state->messages[12]));
dprintk(verbose, DST_CA_INFO, 1, " ==================================================================================================");
// Transform dst message to correct application_info message
length = state->messages[5];
str_length = length - 6;
if (str_length < 0) {
str_length = 0;
dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering.");
}
// First, the command and length fields
put_command_and_length(&state->messages[0], CA_APP_INFO, length);
// Copy application_type, application_manufacturer and manufacturer_code
memcpy(&state->messages[4], &state->messages[7], 5);
// Set string length and copy string
state->messages[9] = str_length;
memcpy(&state->messages[10], &state->messages[12], str_length);
return 0;
}
static int ca_get_ca_info(struct dst_state *state)
{
int srcPtr, dstPtr, i, num_ids;
static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff};
const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7;
put_checksum(&slot_command[0], slot_command[0]);
if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
return -1;
}
dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
// Print raw data
dprintk(verbose, DST_CA_INFO, 0, " DST data = [");
for (i = 0; i < state->messages[0] + 1; i++) {
dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]);
}
dprintk(verbose, DST_CA_INFO, 0, "]\n");
// Set the command and length of the output
num_ids = state->messages[in_num_ids_pos];
if (num_ids >= 100) {
num_ids = 100;
dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering.");
}
put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2);
dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = [");
srcPtr = in_system_id_pos;
dstPtr = out_system_id_pos;
for(i = 0; i < num_ids; i++) {
dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]);
// Append to output
state->messages[dstPtr + 0] = state->messages[srcPtr + 0];
state->messages[dstPtr + 1] = state->messages[srcPtr + 1];
srcPtr += 2;
dstPtr += 2;
}
dprintk(verbose, DST_CA_INFO, 0, "]\n");
return 0;
}
static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg)
{
int i;
u8 slot_cap[256];
static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff};
put_checksum(&slot_command[0], slot_command[0]);
if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
return -1;
}
dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !");
/* Will implement the rest soon */
dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]);
dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
for (i = 0; i < slot_cap[0] + 1; i++)
dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]);
dprintk(verbose, DST_CA_INFO, 0, "\n");
p_ca_caps->slot_num = 1;
p_ca_caps->slot_type = 1;
p_ca_caps->descr_num = slot_cap[7];
p_ca_caps->descr_type = 1;
if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps)))
return -EFAULT;
return 0;
}
/* Need some more work */
static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
{
return -EOPNOTSUPP;
}
static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg)
{
int i;
static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
u8 *slot_info = state->messages;
put_checksum(&slot_command[0], 7);
if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
return -1;
}
dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
/* Will implement the rest soon */
dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]);
dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
for (i = 0; i < 8; i++)
dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]);
dprintk(verbose, DST_CA_INFO, 0, "\n");
if (slot_info[4] & 0x80) {
p_ca_slot_info->flags = CA_CI_MODULE_PRESENT;
p_ca_slot_info->num = 1;
p_ca_slot_info->type = CA_CI;
} else if (slot_info[4] & 0x40) {
p_ca_slot_info->flags = CA_CI_MODULE_READY;
p_ca_slot_info->num = 1;
p_ca_slot_info->type = CA_CI;
} else
p_ca_slot_info->flags = 0;
if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info)))
return -EFAULT;
return 0;
}
static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
{
u8 i = 0;
u32 command = 0;
if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg)))
return -EFAULT;
if (p_ca_message->msg) {
dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%02x %02x %02x]", p_ca_message->msg[0], p_ca_message->msg[1], p_ca_message->msg[2]);
for (i = 0; i < 3; i++) {
command = command | p_ca_message->msg[i];
if (i < 2)
command = command << 8;
}
dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command);
switch (command) {
case CA_APP_INFO:
memcpy(p_ca_message->msg, state->messages, 128);
if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
return -EFAULT;
break;
case CA_INFO:
memcpy(p_ca_message->msg, state->messages, 128);
if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
return -EFAULT;
break;
}
}
return 0;
}
static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length)
{
if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) {
hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */
hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */
} else {
if (length > 247) {
dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !");
return -1;
}
hw_buffer->msg[0] = (length & 0xff) + 7;
hw_buffer->msg[1] = 0x40;
hw_buffer->msg[2] = 0x03;
hw_buffer->msg[3] = 0x00;
hw_buffer->msg[4] = 0x03;
hw_buffer->msg[5] = length & 0xff;
hw_buffer->msg[6] = 0x00;
/*
* Need to compute length for EN50221 section 8.3.2, for the time being
* assuming 8.3.2 is not applicable
*/
memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length);
}
return 0;
}
static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply)
{
if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed.");
dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST.");
rdc_reset_state(state);
return -1;
}
dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success.");
return 0;
}
static u32 asn_1_decode(u8 *asn_1_array)
{
u8 length_field = 0, word_count = 0, count = 0;
u32 length = 0;
length_field = asn_1_array[0];
dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field);
if (length_field < 0x80) {
length = length_field & 0x7f;
dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length);
} else {
word_count = length_field & 0x7f;
for (count = 0; count < word_count; count++) {
length = length << 8;
length += asn_1_array[count + 1];
dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length);
}
}
return length;
}
static int debug_string(u8 *msg, u32 length, u32 offset)
{
u32 i;
dprintk(verbose, DST_CA_DEBUG, 0, " String=[ ");
for (i = offset; i < length; i++)
dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]);
dprintk(verbose, DST_CA_DEBUG, 0, "]\n");
return 0;
}
static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query)
{
u32 length = 0;
u8 tag_length = 8;
length = asn_1_decode(&p_ca_message->msg[3]);
dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length);
debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */
memset(hw_buffer->msg, '\0', length);
handle_dst_tag(state, p_ca_message, hw_buffer, length);
put_checksum(hw_buffer->msg, hw_buffer->msg[0]);
debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */
write_to_8820(state, hw_buffer, (length + tag_length), reply);
return 0;
}
/* Board supports CA PMT reply ? */
static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
{
int ca_pmt_reply_test = 0;
/* Do test board */
/* Not there yet but soon */
/* CA PMT Reply capable */
if (ca_pmt_reply_test) {
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
return -1;
}
/* Process CA PMT Reply */
/* will implement soon */
dprintk(verbose, DST_CA_ERROR, 1, " Not there yet");
}
/* CA PMT Reply not capable */
if (!ca_pmt_reply_test) {
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
return -1;
}
dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !");
/* put a dummy message */
}
return 0;
}
static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
{
int i = 0;
unsigned int ca_message_header_len;
u32 command = 0;
struct ca_msg *hw_buffer;
int result = 0;
if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
return -ENOMEM;
}
dprintk(verbose, DST_CA_DEBUG, 1, " ");
if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) {
result = -EFAULT;
goto free_mem_and_exit;
}
if (p_ca_message->msg) {
ca_message_header_len = p_ca_message->length; /* Restore it back when you are done */
/* EN50221 tag */
command = 0;
for (i = 0; i < 3; i++) {
command = command | p_ca_message->msg[i];
if (i < 2)
command = command << 8;
}
dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command);
switch (command) {
case CA_PMT:
dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT");
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !");
break;
case CA_PMT_REPLY:
dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY");
/* Have to handle the 2 basic types of cards here */
if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !");
break;
case CA_APP_INFO_ENQUIRY: // only for debugging
dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information");
if ((ca_get_app_info(state)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !");
break;
case CA_INFO_ENQUIRY:
dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information");
if ((ca_get_ca_info(state)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !");
break;
}
}
free_mem_and_exit:
kfree (hw_buffer);
return result;
}
static int dst_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long ioctl_arg)
{
struct dvb_device* dvbdev = (struct dvb_device*) file->private_data;
struct dst_state* state = (struct dst_state*) dvbdev->priv;
struct ca_slot_info *p_ca_slot_info;
struct ca_caps *p_ca_caps;
struct ca_msg *p_ca_message;
void __user *arg = (void __user *)ioctl_arg;
int result = 0;
p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL);
p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL);
p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL);
if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) {
dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
result = -ENOMEM;
goto free_mem_and_exit;
}
/* We have now only the standard ioctl's, the driver is upposed to handle internals. */
switch (cmd) {
case CA_SEND_MSG:
dprintk(verbose, DST_CA_INFO, 1, " Sending message");
if ((ca_send_message(state, p_ca_message, arg)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !");
result = -1;
goto free_mem_and_exit;
}
break;
case CA_GET_MSG:
dprintk(verbose, DST_CA_INFO, 1, " Getting message");
if ((ca_get_message(state, p_ca_message, arg)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !");
break;
case CA_RESET:
dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST");
dst_error_bailout(state);
msleep(4000);
break;
case CA_GET_SLOT_INFO:
dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info");
if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !");
break;
case CA_GET_CAP:
dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities");
if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !");
break;
case CA_GET_DESCR_INFO:
dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description");
if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !");
break;
case CA_SET_DESCR:
dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler");
if ((ca_set_slot_descr()) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !");
break;
case CA_SET_PID:
dprintk(verbose, DST_CA_INFO, 1, " Setting PID");
if ((ca_set_pid()) < 0) {
dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !");
result = -1;
goto free_mem_and_exit;
}
dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !");
default:
result = -EOPNOTSUPP;
};
free_mem_and_exit:
kfree (p_ca_message);
kfree (p_ca_slot_info);
kfree (p_ca_caps);
return result;
}
static int dst_ca_open(struct inode *inode, struct file *file)
{
dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file);
try_module_get(THIS_MODULE);
return 0;
}
static int dst_ca_release(struct inode *inode, struct file *file)
{
dprintk(verbose, DST_CA_DEBUG, 1, " Device closed.");
module_put(THIS_MODULE);
return 0;
}
static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
{
ssize_t bytes_read = 0;
dprintk(verbose, DST_CA_DEBUG, 1, " Device read.");
return bytes_read;
}
static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)
{
dprintk(verbose, DST_CA_DEBUG, 1, " Device write.");
return 0;
}
static struct file_operations dst_ca_fops = {
.owner = THIS_MODULE,
.ioctl = dst_ca_ioctl,
.open = dst_ca_open,
.release = dst_ca_release,
.read = dst_ca_read,
.write = dst_ca_write
};
static struct dvb_device dvbdev_ca = {
.priv = NULL,
.users = 1,
.readers = 1,
.writers = 1,
.fops = &dst_ca_fops
};
struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
{
struct dvb_device *dvbdev;
dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device");
if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) {
dst->dst_ca = dvbdev;
return dst->dst_ca;
}
return NULL;
}
EXPORT_SYMBOL(dst_ca_attach);
MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver");
MODULE_AUTHOR("Manu Abraham");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,58 @@
/*
CA-driver for TwinHan DST Frontend/Card
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DST_CA_H_
#define _DST_CA_H_
#define RETRIES 5
#define CA_APP_INFO_ENQUIRY 0x9f8020
#define CA_APP_INFO 0x9f8021
#define CA_ENTER_MENU 0x9f8022
#define CA_INFO_ENQUIRY 0x9f8030
#define CA_INFO 0x9f8031
#define CA_PMT 0x9f8032
#define CA_PMT_REPLY 0x9f8033
#define CA_CLOSE_MMI 0x9f8800
#define CA_DISPLAY_CONTROL 0x9f8801
#define CA_DISPLAY_REPLY 0x9f8802
#define CA_TEXT_LAST 0x9f8803
#define CA_TEXT_MORE 0x9f8804
#define CA_KEYPAD_CONTROL 0x9f8805
#define CA_KEYPRESS 0x9f8806
#define CA_ENQUIRY 0x9f8807
#define CA_ANSWER 0x9f8808
#define CA_MENU_LAST 0x9f8809
#define CA_MENU_MORE 0x9f880a
#define CA_MENU_ANSWER 0x9f880b
#define CA_LIST_LAST 0x9f880c
#define CA_LIST_MORE 0x9f880d
struct dst_ca_private {
struct dst_state *dst;
struct dvb_device *dvbdev;
};
#endif

View File

@@ -0,0 +1,188 @@
/*
Frontend-driver for TwinHan DST Frontend
Copyright (C) 2003 Jamie Honan
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DST_COMMON_H
#define DST_COMMON_H
#include <linux/smp_lock.h>
#include <linux/dvb/frontend.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include "bt878.h"
#include "dst_ca.h"
#define NO_DELAY 0
#define LONG_DELAY 1
#define DEVICE_INIT 2
#define DELAY 1
#define DST_TYPE_IS_SAT 0
#define DST_TYPE_IS_TERR 1
#define DST_TYPE_IS_CABLE 2
#define DST_TYPE_IS_ATSC 3
#define DST_TYPE_HAS_TS188 1
#define DST_TYPE_HAS_TS204 2
#define DST_TYPE_HAS_SYMDIV 4
#define DST_TYPE_HAS_FW_1 8
#define DST_TYPE_HAS_FW_2 16
#define DST_TYPE_HAS_FW_3 32
#define DST_TYPE_HAS_FW_BUILD 64
#define DST_TYPE_HAS_OBS_REGS 128
#define DST_TYPE_HAS_INC_COUNT 256
#define DST_TYPE_HAS_MULTI_FE 512
#define DST_TYPE_HAS_NEWTUNE_2 1024
#define DST_TYPE_HAS_DBOARD 2048
#define DST_TYPE_HAS_VLF 4096
/* Card capability list */
#define DST_TYPE_HAS_MAC 1
#define DST_TYPE_HAS_DISEQC3 2
#define DST_TYPE_HAS_DISEQC4 4
#define DST_TYPE_HAS_DISEQC5 8
#define DST_TYPE_HAS_MOTO 16
#define DST_TYPE_HAS_CA 32
#define DST_TYPE_HAS_ANALOG 64 /* Analog inputs */
#define DST_TYPE_HAS_SESSION 128
#define TUNER_TYPE_MULTI 1
#define TUNER_TYPE_UNKNOWN 2
/* DVB-S */
#define TUNER_TYPE_L64724 4
#define TUNER_TYPE_STV0299 8
#define TUNER_TYPE_MB86A15 16
/* DVB-T */
#define TUNER_TYPE_TDA10046 32
/* ATSC */
#define TUNER_TYPE_NXT200x 64
#define RDC_8820_PIO_0_DISABLE 0
#define RDC_8820_PIO_0_ENABLE 1
#define RDC_8820_INT 2
#define RDC_8820_RESET 4
/* DST Communication */
#define GET_REPLY 1
#define NO_REPLY 0
#define GET_ACK 1
#define FIXED_COMM 8
#define ACK 0xff
struct dst_state {
struct i2c_adapter* i2c;
struct bt878* bt;
/* configuration settings */
const struct dst_config* config;
struct dvb_frontend frontend;
/* private ASIC data */
u8 tx_tuna[10];
u8 rx_tuna[10];
u8 rxbuffer[10];
u8 diseq_flags;
u8 dst_type;
u32 type_flags;
u32 frequency; /* intermediate frequency in kHz for QPSK */
fe_spectral_inversion_t inversion;
u32 symbol_rate; /* symbol rate in Symbols per second */
fe_code_rate_t fec;
fe_sec_voltage_t voltage;
fe_sec_tone_mode_t tone;
u32 decode_freq;
u8 decode_lock;
u16 decode_strength;
u16 decode_snr;
unsigned long cur_jiff;
u8 k22;
fe_bandwidth_t bandwidth;
u32 dst_hw_cap;
u8 dst_fw_version;
fe_sec_mini_cmd_t minicmd;
fe_modulation_t modulation;
u8 messages[256];
u8 mac_address[8];
u8 fw_version[8];
u8 card_info[8];
u8 vendor[8];
u8 board_info[8];
u32 tuner_type;
char *tuner_name;
struct mutex dst_mutex;
u8 fw_name[8];
struct dvb_device *dst_ca;
};
struct tuner_types {
u32 tuner_type;
char *tuner_name;
char *board_name;
char *fw_name;
};
struct dst_types {
char *device_id;
int offset;
u8 dst_type;
u32 type_flags;
u32 dst_feature;
u32 tuner_type;
};
struct dst_config
{
/* the ASIC i2c address */
u8 demod_address;
};
int rdc_reset_state(struct dst_state *state);
int rdc_8820_reset(struct dst_state *state);
int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode);
int dst_pio_enable(struct dst_state *state);
int dst_pio_disable(struct dst_state *state);
int dst_error_recovery(struct dst_state* state);
int dst_error_bailout(struct dst_state *state);
int dst_comm_init(struct dst_state* state);
int write_dst(struct dst_state *state, u8 * data, u8 len);
int read_dst(struct dst_state *state, u8 * ret, u8 len);
u8 dst_check_sum(u8 * buf, u32 len);
struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter);
struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh, int delay);
int dst_command(struct dst_state* state, u8 * data, u8 len);
#endif // DST_COMMON_H

View File

@@ -0,0 +1,35 @@
/*
* dst-bt878.h: part of the DST driver for the TwinHan DST Frontend
*
* Copyright (C) 2003 Jamie Honan
*/
struct dst_gpio_enable {
u32 mask;
u32 enable;
};
struct dst_gpio_output {
u32 mask;
u32 highvals;
};
struct dst_gpio_read {
unsigned long value;
};
union dst_gpio_packet {
struct dst_gpio_enable enb;
struct dst_gpio_output outp;
struct dst_gpio_read rd;
int psize;
};
#define DST_IG_ENABLE 0
#define DST_IG_WRITE 1
#define DST_IG_READ 2
#define DST_IG_TS 3
struct bt878;
int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp);

View File

@@ -0,0 +1,960 @@
/*
* Bt8xx based DVB adapter driver
*
* Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.org>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include "dmxdev.h"
#include "dvbdev.h"
#include "dvb_demux.h"
#include "dvb_frontend.h"
#include "dvb-bt8xx.h"
#include "bt878.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
#define dprintk( args... ) \
do \
if (debug) printk(KERN_DEBUG args); \
while (0)
#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
static void dvb_bt8xx_task(unsigned long data)
{
struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data;
//printk("%d ", card->bt->finished_block);
while (card->bt->last_block != card->bt->finished_block) {
(card->bt->TS_Size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter)
(&card->demux,
&card->bt->buf_cpu[card->bt->last_block *
card->bt->block_bytes],
card->bt->block_bytes);
card->bt->last_block = (card->bt->last_block + 1) %
card->bt->block_count;
}
}
static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux*dvbdmx = dvbdmxfeed->demux;
struct dvb_bt8xx_card *card = dvbdmx->priv;
int rc;
dprintk("dvb_bt8xx: start_feed\n");
if (!dvbdmx->dmx.frontend)
return -EINVAL;
mutex_lock(&card->lock);
card->nfeeds++;
rc = card->nfeeds;
if (card->nfeeds == 1)
bt878_start(card->bt, card->gpio_mode,
card->op_sync_orin, card->irq_err_ignore);
mutex_unlock(&card->lock);
return rc;
}
static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
struct dvb_bt8xx_card *card = dvbdmx->priv;
dprintk("dvb_bt8xx: stop_feed\n");
if (!dvbdmx->dmx.frontend)
return -EINVAL;
mutex_lock(&card->lock);
card->nfeeds--;
if (card->nfeeds == 0)
bt878_stop(card->bt);
mutex_unlock(&card->lock);
return 0;
}
static int is_pci_slot_eq(struct pci_dev* adev, struct pci_dev* bdev)
{
if ((adev->subsystem_vendor == bdev->subsystem_vendor) &&
(adev->subsystem_device == bdev->subsystem_device) &&
(adev->bus->number == bdev->bus->number) &&
(PCI_SLOT(adev->devfn) == PCI_SLOT(bdev->devfn)))
return 1;
return 0;
}
static struct bt878 __devinit *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci_dev* bttv_pci_dev)
{
unsigned int card_nr;
/* Hmm, n squared. Hope n is small */
for (card_nr = 0; card_nr < bt878_num; card_nr++)
if (is_pci_slot_eq(bt878[card_nr].dev, bttv_pci_dev))
return &bt878[card_nr];
return NULL;
}
static int thomson_dtt7579_demod_init(struct dvb_frontend* fe)
{
static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 };
static u8 mt352_reset [] = { 0x50, 0x80 };
static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 };
static u8 mt352_gpp_ctl_cfg [] = { 0x8C, 0x33 };
static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
udelay(2000);
mt352_write(fe, mt352_reset, sizeof(mt352_reset));
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
return 0;
}
static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len)
{
u32 div;
unsigned char bs = 0;
unsigned char cp = 0;
if (buf_len < 5)
return -EINVAL;
div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
if (params->frequency < 542000000)
cp = 0xb4;
else if (params->frequency < 771000000)
cp = 0xbc;
else
cp = 0xf4;
if (params->frequency == 0)
bs = 0x03;
else if (params->frequency < 443250000)
bs = 0x02;
else
bs = 0x08;
pllbuf[0] = 0x60;
pllbuf[1] = div >> 8;
pllbuf[2] = div & 0xff;
pllbuf[3] = cp;
pllbuf[4] = bs;
return 5;
}
static struct mt352_config thomson_dtt7579_config = {
.demod_address = 0x0f,
.demod_init = thomson_dtt7579_demod_init,
};
static struct zl10353_config thomson_dtt7579_zl10353_config = {
.demod_address = 0x0f,
};
static int cx24108_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
u32 freq = params->frequency;
int i, a, n, pump;
u32 band, pll;
u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000,
1576000,1718000,1856000,2036000,2150000};
u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000,
0x00102000,0x00104000,0x00108000,0x00110000,
0x00120000,0x00140000};
#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */
printk("cx24108 debug: entering SetTunerFreq, freq=%d\n",freq);
/* This is really the bit driving the tuner chip cx24108 */
if (freq<950000)
freq = 950000; /* kHz */
else if (freq>2150000)
freq = 2150000; /* satellite IF is 950..2150MHz */
/* decide which VCO to use for the input frequency */
for(i = 1; (i < ARRAY_SIZE(osci)) && (osci[i] < freq); i++);
printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq);
band=bandsel[i];
/* the gain values must be set by SetSymbolrate */
/* compute the pll divider needed, from Conexant data sheet,
resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
depending on the divider bit. It is set to /4 on the 2 lowest
bands */
n=((i<=2?2:1)*freq*10L)/(XTAL/100);
a=n%32; n/=32; if(a==0) n--;
pump=(freq<(osci[i-1]+osci[i])/2);
pll=0xf8000000|
((pump?1:2)<<(14+11))|
((n&0x1ff)<<(5+11))|
((a&0x1f)<<11);
/* everything is shifted left 11 bits to left-align the bits in the
32bit word. Output to the tuner goes MSB-aligned, after all */
printk("cx24108 debug: pump=%d, n=%d, a=%d\n",pump,n,a);
cx24110_pll_write(fe,band);
/* set vga and vca to their widest-band settings, as a precaution.
SetSymbolrate might not be called to set this up */
cx24110_pll_write(fe,0x500c0000);
cx24110_pll_write(fe,0x83f1f800);
cx24110_pll_write(fe,pll);
//writereg(client,0x56,0x7f);
return 0;
}
static int pinnsat_tuner_init(struct dvb_frontend* fe)
{
struct dvb_bt8xx_card *card = fe->dvb->priv;
bttv_gpio_enable(card->bttv_nr, 1, 1); /* output */
bttv_write_gpio(card->bttv_nr, 1, 1); /* relay on */
return 0;
}
static int pinnsat_tuner_sleep(struct dvb_frontend* fe)
{
struct dvb_bt8xx_card *card = fe->dvb->priv;
bttv_write_gpio(card->bttv_nr, 1, 0); /* relay off */
return 0;
}
static struct cx24110_config pctvsat_config = {
.demod_address = 0x55,
};
static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
u8 cfg, cpump, band_select;
u8 data[4];
u32 div;
struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) };
div = (36000000 + params->frequency + 83333) / 166666;
cfg = 0x88;
if (params->frequency < 175000000)
cpump = 2;
else if (params->frequency < 390000000)
cpump = 1;
else if (params->frequency < 470000000)
cpump = 2;
else if (params->frequency < 750000000)
cpump = 2;
else
cpump = 3;
if (params->frequency < 175000000)
band_select = 0x0e;
else if (params->frequency < 470000000)
band_select = 0x05;
else
band_select = 0x03;
data[0] = (div >> 8) & 0x7f;
data[1] = div & 0xff;
data[2] = ((div >> 10) & 0x60) | cfg;
data[3] = (cpump << 6) | band_select;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
i2c_transfer(card->i2c_adapter, &msg, 1);
return (div * 166666 - 36000000);
}
static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
{
struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv;
return request_firmware(fw, name, &bt->bt->dev->dev);
}
static struct sp887x_config microtune_mt7202dtf_config = {
.demod_address = 0x70,
.request_firmware = microtune_mt7202dtf_request_firmware,
};
static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
{
static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d };
static u8 mt352_reset [] = { 0x50, 0x80 };
static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
0x00, 0xFF, 0x00, 0x40, 0x40 };
static u8 mt352_av771_extra[] = { 0xB5, 0x7A };
static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
udelay(2000);
mt352_write(fe, mt352_reset, sizeof(mt352_reset));
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg));
udelay(2000);
mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra));
mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
return 0;
}
static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len)
{
u32 div;
unsigned char bs = 0;
unsigned char cp = 0;
if (buf_len < 5) return -EINVAL;
div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
if (params->frequency < 150000000)
cp = 0xB4;
else if (params->frequency < 173000000)
cp = 0xBC;
else if (params->frequency < 250000000)
cp = 0xB4;
else if (params->frequency < 400000000)
cp = 0xBC;
else if (params->frequency < 420000000)
cp = 0xF4;
else if (params->frequency < 470000000)
cp = 0xFC;
else if (params->frequency < 600000000)
cp = 0xBC;
else if (params->frequency < 730000000)
cp = 0xF4;
else
cp = 0xFC;
if (params->frequency < 150000000)
bs = 0x01;
else if (params->frequency < 173000000)
bs = 0x01;
else if (params->frequency < 250000000)
bs = 0x02;
else if (params->frequency < 400000000)
bs = 0x02;
else if (params->frequency < 420000000)
bs = 0x02;
else if (params->frequency < 470000000)
bs = 0x02;
else if (params->frequency < 600000000)
bs = 0x08;
else if (params->frequency < 730000000)
bs = 0x08;
else
bs = 0x08;
pllbuf[0] = 0x61;
pllbuf[1] = div >> 8;
pllbuf[2] = div & 0xff;
pllbuf[3] = cp;
pllbuf[4] = bs;
return 5;
}
static struct mt352_config advbt771_samsung_tdtc9251dh0_config = {
.demod_address = 0x0f,
.demod_init = advbt771_samsung_tdtc9251dh0_demod_init,
};
static struct dst_config dst_config = {
.demod_address = 0x55,
};
static int or51211_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
{
struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv;
return request_firmware(fw, name, &bt->bt->dev->dev);
}
static void or51211_setmode(struct dvb_frontend * fe, int mode)
{
struct dvb_bt8xx_card *bt = fe->dvb->priv;
bttv_write_gpio(bt->bttv_nr, 0x0002, mode); /* Reset */
msleep(20);
}
static void or51211_reset(struct dvb_frontend * fe)
{
struct dvb_bt8xx_card *bt = fe->dvb->priv;
/* RESET DEVICE
* reset is controled by GPIO-0
* when set to 0 causes reset and when to 1 for normal op
* must remain reset for 128 clock cycles on a 50Mhz clock
* also PRM1 PRM2 & PRM4 are controled by GPIO-1,GPIO-2 & GPIO-4
* We assume that the reset has be held low long enough or we
* have been reset by a power on. When the driver is unloaded
* reset set to 0 so if reloaded we have been reset.
*/
/* reset & PRM1,2&4 are outputs */
int ret = bttv_gpio_enable(bt->bttv_nr, 0x001F, 0x001F);
if (ret != 0)
printk(KERN_WARNING "or51211: Init Error - Can't Reset DVR (%i)\n", ret);
bttv_write_gpio(bt->bttv_nr, 0x001F, 0x0000); /* Reset */
msleep(20);
/* Now set for normal operation */
bttv_write_gpio(bt->bttv_nr, 0x0001F, 0x0001);
/* wait for operation to begin */
msleep(500);
}
static void or51211_sleep(struct dvb_frontend * fe)
{
struct dvb_bt8xx_card *bt = fe->dvb->priv;
bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000);
}
static struct or51211_config or51211_config = {
.demod_address = 0x15,
.request_firmware = or51211_request_firmware,
.setmode = or51211_setmode,
.reset = or51211_reset,
.sleep = or51211_sleep,
};
static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
u8 buf[4];
u32 div;
struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) };
div = (params->frequency + 36166667) / 166667;
buf[0] = (div >> 8) & 0x7F;
buf[1] = div & 0xFF;
buf[2] = 0x85;
if ((params->frequency >= 47000000) && (params->frequency < 153000000))
buf[3] = 0x01;
else if ((params->frequency >= 153000000) && (params->frequency < 430000000))
buf[3] = 0x02;
else if ((params->frequency >= 430000000) && (params->frequency < 824000000))
buf[3] = 0x0C;
else if ((params->frequency >= 824000000) && (params->frequency < 863000000))
buf[3] = 0x8C;
else
return -EINVAL;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
i2c_transfer(card->i2c_adapter, &msg, 1);
return 0;
}
static struct nxt6000_config vp3021_alps_tded4_config = {
.demod_address = 0x0a,
.clock_inversion = 1,
};
static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe)
{
static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d };
static u8 mt352_reset [] = { 0x50, 0x80 };
static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg [] = { 0x67, 0x20, 0xa0 };
static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
udelay(2000);
mt352_write(fe, mt352_reset, sizeof(mt352_reset));
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg));
mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
return 0;
}
static int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len)
{
u32 div;
struct dvb_ofdm_parameters *op = &params->u.ofdm;
if (buf_len < 5)
return -EINVAL;
div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
pllbuf[0] = 0x61;
pllbuf[1] = (div >> 8) & 0x7F;
pllbuf[2] = div & 0xFF;
pllbuf[3] = 0x85;
dprintk("frequency %u, div %u\n", params->frequency, div);
if (params->frequency < 470000000)
pllbuf[4] = 0x02;
else if (params->frequency > 823000000)
pllbuf[4] = 0x88;
else
pllbuf[4] = 0x08;
if (op->bandwidth == 8)
pllbuf[4] |= 0x04;
return 5;
}
static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt)
{
/*
* Reset the frontend, must be called before trying
* to initialise the MT352 or mt352_attach
* will fail. Same goes for the nxt6000 frontend.
*
*/
int ret = bttv_gpio_enable(bt->bttv_nr, 0x08, 0x08);
if (ret != 0)
printk(KERN_WARNING "digitv_alps_tded4: Init Error - Can't Reset DVR (%i)\n", ret);
/* Pulse the reset line */
bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */
bttv_write_gpio(bt->bttv_nr, 0x08, 0x00); /* Low */
msleep(100);
bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */
}
static struct mt352_config digitv_alps_tded4_config = {
.demod_address = 0x0a,
.demod_init = digitv_alps_tded4_demod_init,
};
static struct lgdt330x_config tdvs_tua6034_config = {
.demod_address = 0x0e,
.demod_chip = LGDT3303,
.serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
};
static void lgdt330x_reset(struct dvb_bt8xx_card *bt)
{
/* Set pin 27 of the lgdt3303 chip high to reset the frontend */
/* Pulse the reset line */
bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */
bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000000); /* Low */
msleep(100);
bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */
msleep(100);
}
static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
{
struct dst_state* state = NULL;
switch(type) {
case BTTV_BOARD_DVICO_DVBT_LITE:
card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter);
if (card->fe == NULL)
card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config,
card->i2c_adapter);
if (card->fe != NULL) {
card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs;
card->fe->ops.info.frequency_min = 174000000;
card->fe->ops.info.frequency_max = 862000000;
}
break;
case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE:
lgdt330x_reset(card);
card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
if (card->fe != NULL) {
dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter);
dprintk ("dvb_bt8xx: lgdt330x detected\n");
}
break;
case BTTV_BOARD_NEBULA_DIGITV:
/*
* It is possible to determine the correct frontend using the I2C bus (see the Nebula SDK);
* this would be a cleaner solution than trying each frontend in turn.
*/
/* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */
digitv_alps_tded4_reset(card);
card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter);
if (card->fe != NULL) {
card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params;
dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n");
break;
}
/* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */
digitv_alps_tded4_reset(card);
card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter);
if (card->fe != NULL) {
card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs;
dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n");
}
break;
case BTTV_BOARD_AVDVBT_761:
card->fe = dvb_attach(sp887x_attach, &microtune_mt7202dtf_config, card->i2c_adapter);
if (card->fe) {
card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params;
}
break;
case BTTV_BOARD_AVDVBT_771:
card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter);
if (card->fe != NULL) {
card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs;
card->fe->ops.info.frequency_min = 174000000;
card->fe->ops.info.frequency_max = 862000000;
}
break;
case BTTV_BOARD_TWINHAN_DST:
/* DST is not a frontend driver !!! */
state = kmalloc(sizeof (struct dst_state), GFP_KERNEL);
if (!state) {
printk("dvb_bt8xx: No memory\n");
break;
}
/* Setup the Card */
state->config = &dst_config;
state->i2c = card->i2c_adapter;
state->bt = card->bt;
state->dst_ca = NULL;
/* DST is not a frontend, attaching the ASIC */
if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) {
printk("%s: Could not find a Twinhan DST.\n", __FUNCTION__);
break;
}
/* Attach other DST peripherals if any */
/* Conditional Access device */
card->fe = &state->frontend;
if (state->dst_hw_cap & DST_TYPE_HAS_CA)
dvb_attach(dst_ca_attach, state, &card->dvb_adapter);
break;
case BTTV_BOARD_PINNACLESAT:
card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter);
if (card->fe) {
card->fe->ops.tuner_ops.init = pinnsat_tuner_init;
card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep;
card->fe->ops.tuner_ops.set_params = cx24108_tuner_set_params;
}
break;
case BTTV_BOARD_PC_HDTV:
card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter);
break;
}
if (card->fe == NULL)
printk("dvb-bt8xx: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
card->bt->dev->vendor,
card->bt->dev->device,
card->bt->dev->subsystem_vendor,
card->bt->dev->subsystem_device);
else
if (dvb_register_frontend(&card->dvb_adapter, card->fe)) {
printk("dvb-bt8xx: Frontend registration failed!\n");
dvb_frontend_detach(card->fe);
card->fe = NULL;
}
}
static int __devinit dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
{
int result;
if ((result = dvb_register_adapter(&card->dvb_adapter, card->card_name, THIS_MODULE, &card->bt->dev->dev)) < 0) {
printk("dvb_bt8xx: dvb_register_adapter failed (errno = %d)\n", result);
return result;
}
card->dvb_adapter.priv = card;
card->bt->adapter = card->i2c_adapter;
memset(&card->demux, 0, sizeof(struct dvb_demux));
card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING;
card->demux.priv = card;
card->demux.filternum = 256;
card->demux.feednum = 256;
card->demux.start_feed = dvb_bt8xx_start_feed;
card->demux.stop_feed = dvb_bt8xx_stop_feed;
card->demux.write_to_decoder = NULL;
if ((result = dvb_dmx_init(&card->demux)) < 0) {
printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
card->dmxdev.filternum = 256;
card->dmxdev.demux = &card->demux.dmx;
card->dmxdev.capabilities = 0;
if ((result = dvb_dmxdev_init(&card->dmxdev, &card->dvb_adapter)) < 0) {
printk("dvb_bt8xx: dvb_dmxdev_init failed (errno = %d)\n", result);
dvb_dmx_release(&card->demux);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
card->fe_hw.source = DMX_FRONTEND_0;
if ((result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw)) < 0) {
printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
card->fe_mem.source = DMX_MEMORY_FE;
if ((result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem)) < 0) {
printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
if ((result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw)) < 0) {
printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem);
card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
dvb_unregister_adapter(&card->dvb_adapter);
return result;
}
dvb_net_init(&card->dvb_adapter, &card->dvbnet, &card->demux.dmx);
tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card);
frontend_init(card, type);
return 0;
}
static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub)
{
struct dvb_bt8xx_card *card;
struct pci_dev* bttv_pci_dev;
int ret;
if (!(card = kzalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL)))
return -ENOMEM;
mutex_init(&card->lock);
card->bttv_nr = sub->core->nr;
strncpy(card->card_name, sub->core->name, sizeof(sub->core->name));
card->i2c_adapter = &sub->core->i2c_adap;
switch(sub->core->type) {
case BTTV_BOARD_PINNACLESAT:
card->gpio_mode = 0x0400c060;
/* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR,
BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */
card->op_sync_orin = BT878_RISC_SYNC_MASK;
card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
break;
case BTTV_BOARD_DVICO_DVBT_LITE:
card->gpio_mode = 0x0400C060;
card->op_sync_orin = BT878_RISC_SYNC_MASK;
card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
/* 26, 15, 14, 6, 5
* A_PWRDN DA_DPM DA_SBR DA_IOM_DA
* DA_APP(parallel) */
break;
case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE:
card->gpio_mode = 0x0400c060;
card->op_sync_orin = BT878_RISC_SYNC_MASK;
card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
break;
case BTTV_BOARD_NEBULA_DIGITV:
case BTTV_BOARD_AVDVBT_761:
card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5);
card->op_sync_orin = BT878_RISC_SYNC_MASK;
card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
/* A_PWRDN DA_SBR DA_APP (high speed serial) */
break;
case BTTV_BOARD_AVDVBT_771: //case 0x07711461:
card->gpio_mode = 0x0400402B;
card->op_sync_orin = BT878_RISC_SYNC_MASK;
card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
/* A_PWRDN DA_SBR DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/
break;
case BTTV_BOARD_TWINHAN_DST:
card->gpio_mode = 0x2204f2c;
card->op_sync_orin = BT878_RISC_SYNC_MASK;
card->irq_err_ignore = BT878_APABORT | BT878_ARIPERR |
BT878_APPERR | BT878_AFBUS;
/* 25,21,14,11,10,9,8,3,2 then
* 0x33 = 5,4,1,0
* A_SEL=SML, DA_MLB, DA_SBR,
* DA_SDR=f, fifo trigger = 32 DWORDS
* IOM = 0 == audio A/D
* DPM = 0 == digital audio mode
* == async data parallel port
* then 0x33 (13 is set by start_capture)
* DA_APP = async data parallel port,
* ACAP_EN = 1,
* RISC+FIFO ENABLE */
break;
case BTTV_BOARD_PC_HDTV:
card->gpio_mode = 0x0100EC7B;
card->op_sync_orin = BT878_RISC_SYNC_MASK;
card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
break;
default:
printk(KERN_WARNING "dvb_bt8xx: Unknown bttv card type: %d.\n",
sub->core->type);
kfree(card);
return -ENODEV;
}
dprintk("dvb_bt8xx: identified card%d as %s\n", card->bttv_nr, card->card_name);
if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) {
printk("dvb_bt8xx: no pci device for card %d\n", card->bttv_nr);
kfree(card);
return -EFAULT;
}
if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) {
printk("dvb_bt8xx: unable to determine DMA core of card %d,\n",
card->bttv_nr);
printk("dvb_bt8xx: if you have the ALSA bt87x audio driver "
"installed, try removing it.\n");
kfree(card);
return -EFAULT;
}
mutex_init(&card->bt->gpio_lock);
card->bt->bttv_nr = sub->core->nr;
if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) {
kfree(card);
return ret;
}
dev_set_drvdata(&sub->dev, card);
return 0;
}
static void dvb_bt8xx_remove(struct bttv_sub_device *sub)
{
struct dvb_bt8xx_card *card = dev_get_drvdata(&sub->dev);
dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr);
bt878_stop(card->bt);
tasklet_kill(&card->bt->tasklet);
dvb_net_release(&card->dvbnet);
card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem);
card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
if (card->fe) {
dvb_unregister_frontend(card->fe);
dvb_frontend_detach(card->fe);
}
dvb_unregister_adapter(&card->dvb_adapter);
kfree(card);
}
static struct bttv_sub_driver driver = {
.drv = {
.name = "dvb-bt8xx",
},
.probe = dvb_bt8xx_probe,
.remove = dvb_bt8xx_remove,
/* FIXME:
* .shutdown = dvb_bt8xx_shutdown,
* .suspend = dvb_bt8xx_suspend,
* .resume = dvb_bt8xx_resume,
*/
};
static int __init dvb_bt8xx_init(void)
{
return bttv_sub_register(&driver, "dvb");
}
static void __exit dvb_bt8xx_exit(void)
{
bttv_sub_unregister(&driver);
}
module_init(dvb_bt8xx_init);
module_exit(dvb_bt8xx_exit);
MODULE_DESCRIPTION("Bt8xx based DVB adapter driver");
MODULE_AUTHOR("Florian Schirmer <jolt@tuxbox.org>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,63 @@
/*
* Bt8xx based DVB adapter driver
*
* Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.org>
* Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de>
* Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH
* Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef DVB_BT8XX_H
#define DVB_BT8XX_H
#include <linux/i2c.h>
#include <linux/mutex.h>
#include "dvbdev.h"
#include "dvb_net.h"
#include "bttv.h"
#include "mt352.h"
#include "sp887x.h"
#include "dst_common.h"
#include "nxt6000.h"
#include "cx24110.h"
#include "or51211.h"
#include "lgdt330x.h"
#include "lgh06xf.h"
#include "zl10353.h"
struct dvb_bt8xx_card {
struct mutex lock;
int nfeeds;
char card_name[32];
struct dvb_adapter dvb_adapter;
struct bt878 *bt;
unsigned int bttv_nr;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
u32 gpio_mode;
u32 op_sync_orin;
u32 irq_err_ignore;
struct i2c_adapter *i2c_adapter;
struct dvb_net dvbnet;
struct dvb_frontend* fe;
};
#endif /* DVB_BT8XX_H */

View File

@@ -0,0 +1,85 @@
config DVB_CINERGYT2
tristate "Terratec CinergyT2/qanu USB2 DVB-T receiver"
depends on DVB_CORE && USB
help
Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
Say Y if you own such a device and want to use it.
config DVB_CINERGYT2_TUNING
bool "sophisticated fine-tuning for CinergyT2 cards"
depends on DVB_CINERGYT2
help
Here you can fine-tune some parameters of the CinergyT2 driver.
Normally you don't need to touch this, but in exotic setups you
may fine-tune your setup and adjust e.g. DMA buffer sizes for
a particular application.
config DVB_CINERGYT2_STREAM_URB_COUNT
int "Number of queued USB Request Blocks for Highspeed Stream Transfers"
depends on DVB_CINERGYT2_TUNING
default "32"
help
USB Request Blocks for Highspeed Stream transfers are scheduled in
a queue for the Host Controller.
Usually the default value is a safe choice.
You may increase this number if you are using this device in a
Server Environment with many high-traffic USB Highspeed devices
sharing the same USB bus.
config DVB_CINERGYT2_STREAM_BUF_SIZE
int "Size of URB Stream Buffers for Highspeed Transfers"
depends on DVB_CINERGYT2_TUNING
default "512"
help
Should be a multiple of native buffer size of 512 bytes.
Default value is a safe choice.
You may increase this number if you are using this device in a
Server Environment with many high-traffic USB Highspeed devices
sharing the same USB bus.
config DVB_CINERGYT2_QUERY_INTERVAL
int "Status update interval [milliseconds]"
depends on DVB_CINERGYT2_TUNING
default "250"
help
This is the interval for status readouts from the demodulator.
You may try lower values if you need more responsive signal quality
measurements.
Please keep in mind that these updates cause traffic on the tuner
control bus and thus may or may not affect reception sensitivity.
The default value should be a safe choice for common applications.
config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
bool "Register the onboard IR Remote Control Receiver as Input Device"
depends on DVB_CINERGYT2_TUNING
default y
help
Enable this option if you want to use the onboard Infrared Remote
Control Receiver as Linux-Input device.
Right now only the keycode table for the default Remote Control
delivered with the device is supported, please see the driver
source code to find out how to add support for other controls.
config DVB_CINERGYT2_RC_QUERY_INTERVAL
int "Infrared Remote Controller update interval [milliseconds]"
depends on DVB_CINERGYT2_TUNING && DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
default "50"
help
If you have a very fast-repeating remote control you can try lower
values, for normal consumer receivers the default value should be
a safe choice.

View File

@@ -0,0 +1,3 @@
obj-$(CONFIG_DVB_CINERGYT2) += cinergyT2.o
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
config DVB_CORE
tristate "DVB Core Support"
depends on DVB
select CRC32
help
DVB core utility functions for device handling, software fallbacks etc.
Say Y when you have a DVB card and want to use it. Say Y if your want
to build your drivers outside the kernel, but need the DVB core. All
in-kernel drivers will select this automatically if needed.
If unsure say N.
config DVB_CORE_ATTACH
bool "Load and attach frontend modules as needed"
depends on DVB_CORE
depends on MODULES
help
Remove the static dependency of DVB card drivers on all
frontend modules for all possible card variants. Instead,
allow the card drivers to only load the frontend modules
they require. This saves several KBytes of memory.
Note: You will need module-init-tools v3.2 or later for this feature.
If unsure say Y.

View File

@@ -0,0 +1,9 @@
#
# Makefile for the kernel DVB device drivers.
#
dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
dvb_ca_en50221.o dvb_frontend.o \
dvb_net.o dvb_ringbuffer.o dvb_math.o
obj-$(CONFIG_DVB_CORE) += dvb-core.o

View File

@@ -0,0 +1,278 @@
/*
* demux.h
*
* Copyright (c) 2002 Convergence GmbH
*
* based on code:
* Copyright (c) 2000 Nokia Research Center
* Tampere, FINLAND
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef __DEMUX_H
#define __DEMUX_H
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/time.h>
#include <linux/dvb/dmx.h>
/*--------------------------------------------------------------------------*/
/* Common definitions */
/*--------------------------------------------------------------------------*/
/*
* DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter.
*/
#ifndef DMX_MAX_FILTER_SIZE
#define DMX_MAX_FILTER_SIZE 18
#endif
/*
* DMX_MAX_SECFEED_SIZE: Maximum length (in bytes) of a private section feed filter.
*/
#ifndef DMX_MAX_SECTION_SIZE
#define DMX_MAX_SECTION_SIZE 4096
#endif
#ifndef DMX_MAX_SECFEED_SIZE
#define DMX_MAX_SECFEED_SIZE (DMX_MAX_SECTION_SIZE + 188)
#endif
/*
* enum dmx_success: Success codes for the Demux Callback API.
*/
enum dmx_success {
DMX_OK = 0, /* Received Ok */
DMX_LENGTH_ERROR, /* Incorrect length */
DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
DMX_CRC_ERROR, /* Incorrect CRC */
DMX_FRAME_ERROR, /* Frame alignment error */
DMX_FIFO_ERROR, /* Receiver FIFO overrun */
DMX_MISSED_ERROR /* Receiver missed packet */
} ;
/*--------------------------------------------------------------------------*/
/* TS packet reception */
/*--------------------------------------------------------------------------*/
/* TS filter type for set() */
#define TS_PACKET 1 /* send TS packets (188 bytes) to callback (default) */
#define TS_PAYLOAD_ONLY 2 /* in case TS_PACKET is set, only send the TS
payload (<=184 bytes per packet) to callback */
#define TS_DECODER 4 /* send stream to built-in decoder (if present) */
/* PES type for filters which write to built-in decoder */
/* these should be kept identical to the types in dmx.h */
enum dmx_ts_pes
{ /* also send packets to decoder (if it exists) */
DMX_TS_PES_AUDIO0,
DMX_TS_PES_VIDEO0,
DMX_TS_PES_TELETEXT0,
DMX_TS_PES_SUBTITLE0,
DMX_TS_PES_PCR0,
DMX_TS_PES_AUDIO1,
DMX_TS_PES_VIDEO1,
DMX_TS_PES_TELETEXT1,
DMX_TS_PES_SUBTITLE1,
DMX_TS_PES_PCR1,
DMX_TS_PES_AUDIO2,
DMX_TS_PES_VIDEO2,
DMX_TS_PES_TELETEXT2,
DMX_TS_PES_SUBTITLE2,
DMX_TS_PES_PCR2,
DMX_TS_PES_AUDIO3,
DMX_TS_PES_VIDEO3,
DMX_TS_PES_TELETEXT3,
DMX_TS_PES_SUBTITLE3,
DMX_TS_PES_PCR3,
DMX_TS_PES_OTHER
};
#define DMX_TS_PES_AUDIO DMX_TS_PES_AUDIO0
#define DMX_TS_PES_VIDEO DMX_TS_PES_VIDEO0
#define DMX_TS_PES_TELETEXT DMX_TS_PES_TELETEXT0
#define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0
#define DMX_TS_PES_PCR DMX_TS_PES_PCR0
struct dmx_ts_feed {
int is_filtering; /* Set to non-zero when filtering in progress */
struct dmx_demux *parent; /* Back-pointer */
void *priv; /* Pointer to private data of the API client */
int (*set) (struct dmx_ts_feed *feed,
u16 pid,
int type,
enum dmx_ts_pes pes_type,
size_t circular_buffer_size,
struct timespec timeout);
int (*start_filtering) (struct dmx_ts_feed* feed);
int (*stop_filtering) (struct dmx_ts_feed* feed);
};
/*--------------------------------------------------------------------------*/
/* Section reception */
/*--------------------------------------------------------------------------*/
struct dmx_section_filter {
u8 filter_value [DMX_MAX_FILTER_SIZE];
u8 filter_mask [DMX_MAX_FILTER_SIZE];
u8 filter_mode [DMX_MAX_FILTER_SIZE];
struct dmx_section_feed* parent; /* Back-pointer */
void* priv; /* Pointer to private data of the API client */
};
struct dmx_section_feed {
int is_filtering; /* Set to non-zero when filtering in progress */
struct dmx_demux* parent; /* Back-pointer */
void* priv; /* Pointer to private data of the API client */
int check_crc;
u32 crc_val;
u8 *secbuf;
u8 secbuf_base[DMX_MAX_SECFEED_SIZE];
u16 secbufp, seclen, tsfeedp;
int (*set) (struct dmx_section_feed* feed,
u16 pid,
size_t circular_buffer_size,
int check_crc);
int (*allocate_filter) (struct dmx_section_feed* feed,
struct dmx_section_filter** filter);
int (*release_filter) (struct dmx_section_feed* feed,
struct dmx_section_filter* filter);
int (*start_filtering) (struct dmx_section_feed* feed);
int (*stop_filtering) (struct dmx_section_feed* feed);
};
/*--------------------------------------------------------------------------*/
/* Callback functions */
/*--------------------------------------------------------------------------*/
typedef int (*dmx_ts_cb) ( const u8 * buffer1,
size_t buffer1_length,
const u8 * buffer2,
size_t buffer2_length,
struct dmx_ts_feed* source,
enum dmx_success success);
typedef int (*dmx_section_cb) ( const u8 * buffer1,
size_t buffer1_len,
const u8 * buffer2,
size_t buffer2_len,
struct dmx_section_filter * source,
enum dmx_success success);
/*--------------------------------------------------------------------------*/
/* DVB Front-End */
/*--------------------------------------------------------------------------*/
enum dmx_frontend_source {
DMX_MEMORY_FE,
DMX_FRONTEND_0,
DMX_FRONTEND_1,
DMX_FRONTEND_2,
DMX_FRONTEND_3,
DMX_STREAM_0, /* external stream input, e.g. LVDS */
DMX_STREAM_1,
DMX_STREAM_2,
DMX_STREAM_3
};
struct dmx_frontend {
struct list_head connectivity_list; /* List of front-ends that can
be connected to a particular
demux */
enum dmx_frontend_source source;
};
/*--------------------------------------------------------------------------*/
/* MPEG-2 TS Demux */
/*--------------------------------------------------------------------------*/
/*
* Flags OR'ed in the capabilities field of struct dmx_demux.
*/
#define DMX_TS_FILTERING 1
#define DMX_PES_FILTERING 2
#define DMX_SECTION_FILTERING 4
#define DMX_MEMORY_BASED_FILTERING 8 /* write() available */
#define DMX_CRC_CHECKING 16
#define DMX_TS_DESCRAMBLING 32
/*
* Demux resource type identifier.
*/
/*
* DMX_FE_ENTRY(): Casts elements in the list of registered
* front-ends from the generic type struct list_head
* to the type * struct dmx_frontend
*.
*/
#define DMX_FE_ENTRY(list) list_entry(list, struct dmx_frontend, connectivity_list)
struct dmx_demux {
u32 capabilities; /* Bitfield of capability flags */
struct dmx_frontend* frontend; /* Front-end connected to the demux */
void* priv; /* Pointer to private data of the API client */
int (*open) (struct dmx_demux* demux);
int (*close) (struct dmx_demux* demux);
int (*write) (struct dmx_demux* demux, const char* buf, size_t count);
int (*allocate_ts_feed) (struct dmx_demux* demux,
struct dmx_ts_feed** feed,
dmx_ts_cb callback);
int (*release_ts_feed) (struct dmx_demux* demux,
struct dmx_ts_feed* feed);
int (*allocate_section_feed) (struct dmx_demux* demux,
struct dmx_section_feed** feed,
dmx_section_cb callback);
int (*release_section_feed) (struct dmx_demux* demux,
struct dmx_section_feed* feed);
int (*add_frontend) (struct dmx_demux* demux,
struct dmx_frontend* frontend);
int (*remove_frontend) (struct dmx_demux* demux,
struct dmx_frontend* frontend);
struct list_head* (*get_frontends) (struct dmx_demux* demux);
int (*connect_frontend) (struct dmx_demux* demux,
struct dmx_frontend* frontend);
int (*disconnect_frontend) (struct dmx_demux* demux);
int (*get_pes_pids) (struct dmx_demux* demux, u16 *pids);
int (*get_caps) (struct dmx_demux* demux, struct dmx_caps *caps);
int (*set_source) (struct dmx_demux* demux, const dmx_source_t *src);
int (*get_stc) (struct dmx_demux* demux, unsigned int num,
u64 *stc, unsigned int *base);
};
#endif /* #ifndef __DEMUX_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,108 @@
/*
* dmxdev.h
*
* Copyright (C) 2000 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef _DMXDEV_H_
#define _DMXDEV_H_
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/dvb/dmx.h>
#include "dvbdev.h"
#include "demux.h"
#include "dvb_ringbuffer.h"
enum dmxdev_type {
DMXDEV_TYPE_NONE,
DMXDEV_TYPE_SEC,
DMXDEV_TYPE_PES,
};
enum dmxdev_state {
DMXDEV_STATE_FREE,
DMXDEV_STATE_ALLOCATED,
DMXDEV_STATE_SET,
DMXDEV_STATE_GO,
DMXDEV_STATE_DONE,
DMXDEV_STATE_TIMEDOUT
};
struct dmxdev_filter {
union {
struct dmx_section_filter *sec;
} filter;
union {
struct dmx_ts_feed *ts;
struct dmx_section_feed *sec;
} feed;
union {
struct dmx_sct_filter_params sec;
struct dmx_pes_filter_params pes;
} params;
enum dmxdev_type type;
enum dmxdev_state state;
struct dmxdev *dev;
struct dvb_ringbuffer buffer;
struct mutex mutex;
/* only for sections */
struct timer_list timer;
int todo;
u8 secheader[3];
};
struct dmxdev {
struct dvb_device *dvbdev;
struct dvb_device *dvr_dvbdev;
struct dmxdev_filter *filter;
struct dmx_demux *demux;
int filternum;
int capabilities;
#define DMXDEV_CAP_DUPLEX 1
struct dmx_frontend *dvr_orig_fe;
struct dvb_ringbuffer dvr_buffer;
#define DVR_BUFFER_SIZE (10*188*1024)
struct mutex mutex;
spinlock_t lock;
};
int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *);
void dvb_dmxdev_release(struct dmxdev *dmxdev);
#endif /* _DMXDEV_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
/*
* dvb_ca.h: generic DVB functions for EN50221 CA interfaces
*
* Copyright (C) 2004 Andrew de Quincey
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _DVB_CA_EN50221_H_
#define _DVB_CA_EN50221_H_
#include <linux/list.h>
#include <linux/dvb/ca.h>
#include "dvbdev.h"
#define DVB_CA_EN50221_POLL_CAM_PRESENT 1
#define DVB_CA_EN50221_POLL_CAM_CHANGED 2
#define DVB_CA_EN50221_POLL_CAM_READY 4
#define DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE 1
#define DVB_CA_EN50221_FLAG_IRQ_FR 2
#define DVB_CA_EN50221_FLAG_IRQ_DA 4
#define DVB_CA_EN50221_CAMCHANGE_REMOVED 0
#define DVB_CA_EN50221_CAMCHANGE_INSERTED 1
/* Structure describing a CA interface */
struct dvb_ca_en50221 {
/* the module owning this structure */
struct module* owner;
/* NOTE: the read_*, write_* and poll_slot_status functions must use locks as
* they may be called from several threads at once */
/* functions for accessing attribute memory on the CAM */
int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value);
/* functions for accessing the control interface on the CAM */
int (*read_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address);
int (*write_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value);
/* Functions for controlling slots */
int (*slot_reset)(struct dvb_ca_en50221* ca, int slot);
int (*slot_shutdown)(struct dvb_ca_en50221* ca, int slot);
int (*slot_ts_enable)(struct dvb_ca_en50221* ca, int slot);
/*
* Poll slot status.
* Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set
*/
int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot, int open);
/* private data, used by caller */
void* data;
/* Opaque data used by the dvb_ca core. Do not modify! */
void* private;
};
/* ******************************************************************************** */
/* Functions for reporting IRQ events */
/**
* A CAMCHANGE IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
* @param change_type One of the DVB_CA_CAMCHANGE_* values
*/
void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type);
/**
* A CAMREADY IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
*/
void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot);
/**
* An FR or a DA IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
*/
void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot);
/* ******************************************************************************** */
/* Initialisation/shutdown functions */
/**
* Initialise a new DVB CA device.
*
* @param dvb_adapter DVB adapter to attach the new CA device to.
* @param ca The dvb_ca instance.
* @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*).
* @param slot_count Number of slots supported.
*
* @return 0 on success, nonzero on failure
*/
extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count);
/**
* Release a DVB CA device.
*
* @param ca The associated dvb_ca instance.
*/
extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
/*
* dvb_demux.h: DVB kernel demux API
*
* Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler
* for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef _DVB_DEMUX_H_
#define _DVB_DEMUX_H_
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include "demux.h"
#define DMX_TYPE_TS 0
#define DMX_TYPE_SEC 1
#define DMX_TYPE_PES 2
#define DMX_STATE_FREE 0
#define DMX_STATE_ALLOCATED 1
#define DMX_STATE_SET 2
#define DMX_STATE_READY 3
#define DMX_STATE_GO 4
#define DVB_DEMUX_MASK_MAX 18
struct dvb_demux_filter {
struct dmx_section_filter filter;
u8 maskandmode[DMX_MAX_FILTER_SIZE];
u8 maskandnotmode[DMX_MAX_FILTER_SIZE];
int doneq;
struct dvb_demux_filter *next;
struct dvb_demux_feed *feed;
int index;
int state;
int type;
u16 hw_handle;
struct timer_list timer;
};
#define DMX_FEED_ENTRY(pos) list_entry(pos, struct dvb_demux_feed, list_head)
struct dvb_demux_feed {
union {
struct dmx_ts_feed ts;
struct dmx_section_feed sec;
} feed;
union {
dmx_ts_cb ts;
dmx_section_cb sec;
} cb;
struct dvb_demux *demux;
void *priv;
int type;
int state;
u16 pid;
u8 *buffer;
int buffer_size;
struct timespec timeout;
struct dvb_demux_filter *filter;
int ts_type;
enum dmx_ts_pes pes_type;
int cc;
int pusi_seen; /* prevents feeding of garbage from previous section */
u16 peslen;
struct list_head list_head;
unsigned int index; /* a unique index for each feed (can be used as hardware pid filter index) */
};
struct dvb_demux {
struct dmx_demux dmx;
void *priv;
int filternum;
int feednum;
int (*start_feed)(struct dvb_demux_feed *feed);
int (*stop_feed)(struct dvb_demux_feed *feed);
int (*write_to_decoder)(struct dvb_demux_feed *feed,
const u8 *buf, size_t len);
u32 (*check_crc32)(struct dvb_demux_feed *feed,
const u8 *buf, size_t len);
void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
const u8 *src, size_t len);
int users;
#define MAX_DVB_DEMUX_USERS 10
struct dvb_demux_filter *filter;
struct dvb_demux_feed *feed;
struct list_head frontend_list;
struct dvb_demux_feed *pesfilter[DMX_TS_PES_OTHER];
u16 pids[DMX_TS_PES_OTHER];
int playing;
int recording;
#define DMX_MAX_PID 0x2000
struct list_head feed_list;
u8 tsbuf[204];
int tsbufp;
struct mutex mutex;
spinlock_t lock;
};
int dvb_dmx_init(struct dvb_demux *dvbdemux);
void dvb_dmx_release(struct dvb_demux *dvbdemux);
void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf,
size_t count);
void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count);
void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf,
size_t count);
#endif /* _DVB_DEMUX_H_ */

View File

@@ -0,0 +1,603 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include "dvb_filter.h"
#if 0
static unsigned int bitrates[3][16] =
{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
{0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0},
{0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}};
#endif
static u32 freq[4] = {480, 441, 320, 0};
static unsigned int ac3_bitrates[32] =
{32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640,
0,0,0,0,0,0,0,0,0,0,0,0,0};
static u32 ac3_frames[3][32] =
{{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024,
1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0},
{69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114,
1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0},
{96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344,
1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}};
#if 0
static void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv,
void (*pes_write)(u8 *buf, int count, void *data),
void *priv)
{
dvb_filter_ipack_init(pa, IPACKS, pes_write);
dvb_filter_ipack_init(pv, IPACKS, pes_write);
pa->pid = pida;
pv->pid = pidv;
pa->data = priv;
pv->data = priv;
}
#endif
#if 0
static void ts_to_pes(ipack *p, u8 *buf) // don't need count (=188)
{
u8 off = 0;
if (!buf || !p ){
printk("NULL POINTER IDIOT\n");
return;
}
if (buf[1]&PAY_START) {
if (p->plength == MMAX_PLENGTH-6 && p->found>6){
p->plength = p->found-6;
p->found = 0;
send_ipack(p);
dvb_filter_ipack_reset(p);
}
}
if (buf[3] & ADAPT_FIELD) { // adaptation field?
off = buf[4] + 1;
if (off+4 > 187) return;
}
dvb_filter_instant_repack(buf+4+off, TS_SIZE-4-off, p);
}
#endif
#if 0
/* needs 5 byte input, returns picture coding type*/
static int read_picture_header(u8 *headr, struct mpg_picture *pic, int field, int pr)
{
u8 pct;
if (pr) printk( "Pic header: ");
pic->temporal_reference[field] = (( headr[0] << 2 ) |
(headr[1] & 0x03) )& 0x03ff;
if (pr) printk( " temp ref: 0x%04x", pic->temporal_reference[field]);
pct = ( headr[1] >> 2 ) & 0x07;
pic->picture_coding_type[field] = pct;
if (pr) {
switch(pct){
case I_FRAME:
printk( " I-FRAME");
break;
case B_FRAME:
printk( " B-FRAME");
break;
case P_FRAME:
printk( " P-FRAME");
break;
}
}
pic->vinfo.vbv_delay = (( headr[1] >> 5 ) | ( headr[2] << 3) |
( (headr[3] & 0x1F) << 11) ) & 0xffff;
if (pr) printk( " vbv delay: 0x%04x", pic->vinfo.vbv_delay);
pic->picture_header_parameter = ( headr[3] & 0xe0 ) |
((headr[4] & 0x80) >> 3);
if ( pct == B_FRAME ){
pic->picture_header_parameter |= ( headr[4] >> 3 ) & 0x0f;
}
if (pr) printk( " pic head param: 0x%x",
pic->picture_header_parameter);
return pct;
}
#endif
#if 0
/* needs 4 byte input */
static int read_gop_header(u8 *headr, struct mpg_picture *pic, int pr)
{
if (pr) printk("GOP header: ");
pic->time_code = (( headr[0] << 17 ) | ( headr[1] << 9) |
( headr[2] << 1 ) | (headr[3] &0x01)) & 0x1ffffff;
if (pr) printk(" time: %d:%d.%d ", (headr[0]>>2)& 0x1F,
((headr[0]<<4)& 0x30)| ((headr[1]>>4)& 0x0F),
((headr[1]<<3)& 0x38)| ((headr[2]>>5)& 0x0F));
if ( ( headr[3] & 0x40 ) != 0 ){
pic->closed_gop = 1;
} else {
pic->closed_gop = 0;
}
if (pr) printk("closed: %d", pic->closed_gop);
if ( ( headr[3] & 0x20 ) != 0 ){
pic->broken_link = 1;
} else {
pic->broken_link = 0;
}
if (pr) printk(" broken: %d\n", pic->broken_link);
return 0;
}
#endif
#if 0
/* needs 8 byte input */
static int read_sequence_header(u8 *headr, struct dvb_video_info *vi, int pr)
{
int sw;
int form = -1;
if (pr) printk("Reading sequence header\n");
vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4);
vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]);
sw = (int)((headr[3]&0xF0) >> 4) ;
switch( sw ){
case 1:
if (pr)
printk("Videostream: ASPECT: 1:1");
vi->aspect_ratio = 100;
break;
case 2:
if (pr)
printk("Videostream: ASPECT: 4:3");
vi->aspect_ratio = 133;
break;
case 3:
if (pr)
printk("Videostream: ASPECT: 16:9");
vi->aspect_ratio = 177;
break;
case 4:
if (pr)
printk("Videostream: ASPECT: 2.21:1");
vi->aspect_ratio = 221;
break;
case 5 ... 15:
if (pr)
printk("Videostream: ASPECT: reserved");
vi->aspect_ratio = 0;
break;
default:
vi->aspect_ratio = 0;
return -1;
}
if (pr)
printk(" Size = %dx%d",vi->horizontal_size,vi->vertical_size);
sw = (int)(headr[3]&0x0F);
switch ( sw ) {
case 1:
if (pr)
printk(" FRate: 23.976 fps");
vi->framerate = 23976;
form = -1;
break;
case 2:
if (pr)
printk(" FRate: 24 fps");
vi->framerate = 24000;
form = -1;
break;
case 3:
if (pr)
printk(" FRate: 25 fps");
vi->framerate = 25000;
form = VIDEO_MODE_PAL;
break;
case 4:
if (pr)
printk(" FRate: 29.97 fps");
vi->framerate = 29970;
form = VIDEO_MODE_NTSC;
break;
case 5:
if (pr)
printk(" FRate: 30 fps");
vi->framerate = 30000;
form = VIDEO_MODE_NTSC;
break;
case 6:
if (pr)
printk(" FRate: 50 fps");
vi->framerate = 50000;
form = VIDEO_MODE_PAL;
break;
case 7:
if (pr)
printk(" FRate: 60 fps");
vi->framerate = 60000;
form = VIDEO_MODE_NTSC;
break;
}
vi->bit_rate = (headr[4] << 10) | (headr[5] << 2) | (headr[6] & 0x03);
vi->vbv_buffer_size
= (( headr[6] & 0xF8) >> 3 ) | (( headr[7] & 0x1F )<< 5);
if (pr){
printk(" BRate: %d Mbit/s",4*(vi->bit_rate)/10000);
printk(" vbvbuffer %d",16*1024*(vi->vbv_buffer_size));
printk("\n");
}
vi->video_format = form;
return 0;
}
#endif
#if 0
static int get_vinfo(u8 *mbuf, int count, struct dvb_video_info *vi, int pr)
{
u8 *headr;
int found = 0;
int c = 0;
while (found < 4 && c+4 < count){
u8 *b;
b = mbuf+c;
if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01
&& b[3] == 0xb3) found = 4;
else {
c++;
}
}
if (! found) return -1;
c += 4;
if (c+12 >= count) return -1;
headr = mbuf+c;
if (read_sequence_header(headr, vi, pr) < 0) return -1;
vi->off = c-4;
return 0;
}
#endif
#if 0
static int get_ainfo(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr)
{
u8 *headr;
int found = 0;
int c = 0;
int fr = 0;
while (found < 2 && c < count){
u8 b[2];
memcpy( b, mbuf+c, 2);
if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8)
found = 2;
else {
c++;
}
}
if (!found) return -1;
if (c+3 >= count) return -1;
headr = mbuf+c;
ai->layer = (headr[1] & 0x06) >> 1;
if (pr)
printk("Audiostream: Layer: %d", 4-ai->layer);
ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000;
if (pr){
if (ai->bit_rate == 0)
printk(" Bit rate: free");
else if (ai->bit_rate == 0xf)
printk(" BRate: reserved");
else
printk(" BRate: %d kb/s", ai->bit_rate/1000);
}
fr = (headr[2] & 0x0c ) >> 2;
ai->frequency = freq[fr]*100;
if (pr){
if (ai->frequency == 3)
printk(" Freq: reserved\n");
else
printk(" Freq: %d kHz\n",ai->frequency);
}
ai->off = c;
return 0;
}
#endif
int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr)
{
u8 *headr;
int found = 0;
int c = 0;
u8 frame = 0;
int fr = 0;
while ( !found && c < count){
u8 *b = mbuf+c;
if ( b[0] == 0x0b && b[1] == 0x77 )
found = 1;
else {
c++;
}
}
if (!found) return -1;
if (pr)
printk("Audiostream: AC3");
ai->off = c;
if (c+5 >= count) return -1;
ai->layer = 0; // 0 for AC3
headr = mbuf+c+2;
frame = (headr[2]&0x3f);
ai->bit_rate = ac3_bitrates[frame >> 1]*1000;
if (pr)
printk(" BRate: %d kb/s", (int) ai->bit_rate/1000);
ai->frequency = (headr[2] & 0xc0 ) >> 6;
fr = (headr[2] & 0xc0 ) >> 6;
ai->frequency = freq[fr]*100;
if (pr) printk (" Freq: %d Hz\n", (int) ai->frequency);
ai->framesize = ac3_frames[fr][frame >> 1];
if ((frame & 1) && (fr == 1)) ai->framesize++;
ai->framesize = ai->framesize << 1;
if (pr) printk (" Framesize %d\n",(int) ai->framesize);
return 0;
}
EXPORT_SYMBOL(dvb_filter_get_ac3info);
#if 0
static u8 *skip_pes_header(u8 **bufp)
{
u8 *inbuf = *bufp;
u8 *buf = inbuf;
u8 *pts = NULL;
int skip = 0;
static const int mpeg1_skip_table[16] = {
1, 0xffff, 5, 10, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
};
if ((inbuf[6] & 0xc0) == 0x80){ /* mpeg2 */
if (buf[7] & PTS_ONLY)
pts = buf+9;
else pts = NULL;
buf = inbuf + 9 + inbuf[8];
} else { /* mpeg1 */
for (buf = inbuf + 6; *buf == 0xff; buf++)
if (buf == inbuf + 6 + 16) {
break;
}
if ((*buf & 0xc0) == 0x40)
buf += 2;
skip = mpeg1_skip_table [*buf >> 4];
if (skip == 5 || skip == 10) pts = buf;
else pts = NULL;
buf += mpeg1_skip_table [*buf >> 4];
}
*bufp = buf;
return pts;
}
#endif
#if 0
static void initialize_quant_matrix( u32 *matrix )
{
int i;
matrix[0] = 0x08101013;
matrix[1] = 0x10131616;
matrix[2] = 0x16161616;
matrix[3] = 0x1a181a1b;
matrix[4] = 0x1b1b1a1a;
matrix[5] = 0x1a1a1b1b;
matrix[6] = 0x1b1d1d1d;
matrix[7] = 0x2222221d;
matrix[8] = 0x1d1d1b1b;
matrix[9] = 0x1d1d2020;
matrix[10] = 0x22222526;
matrix[11] = 0x25232322;
matrix[12] = 0x23262628;
matrix[13] = 0x28283030;
matrix[14] = 0x2e2e3838;
matrix[15] = 0x3a454553;
for ( i = 16 ; i < 32 ; i++ )
matrix[i] = 0x10101010;
}
#endif
#if 0
static void initialize_mpg_picture(struct mpg_picture *pic)
{
int i;
/* set MPEG1 */
pic->mpeg1_flag = 1;
pic->profile_and_level = 0x4A ; /* MP@LL */
pic->progressive_sequence = 1;
pic->low_delay = 0;
pic->sequence_display_extension_flag = 0;
for ( i = 0 ; i < 4 ; i++ ){
pic->frame_centre_horizontal_offset[i] = 0;
pic->frame_centre_vertical_offset[i] = 0;
}
pic->last_frame_centre_horizontal_offset = 0;
pic->last_frame_centre_vertical_offset = 0;
pic->picture_display_extension_flag[0] = 0;
pic->picture_display_extension_flag[1] = 0;
pic->sequence_header_flag = 0;
pic->gop_flag = 0;
pic->sequence_end_flag = 0;
}
#endif
#if 0
static void mpg_set_picture_parameter( int32_t field_type, struct mpg_picture *pic )
{
int16_t last_h_offset;
int16_t last_v_offset;
int16_t *p_h_offset;
int16_t *p_v_offset;
if ( pic->mpeg1_flag ){
pic->picture_structure[field_type] = VIDEO_FRAME_PICTURE;
pic->top_field_first = 0;
pic->repeat_first_field = 0;
pic->progressive_frame = 1;
pic->picture_coding_parameter = 0x000010;
}
/* Reset flag */
pic->picture_display_extension_flag[field_type] = 0;
last_h_offset = pic->last_frame_centre_horizontal_offset;
last_v_offset = pic->last_frame_centre_vertical_offset;
if ( field_type == FIRST_FIELD ){
p_h_offset = pic->frame_centre_horizontal_offset;
p_v_offset = pic->frame_centre_vertical_offset;
*p_h_offset = last_h_offset;
*(p_h_offset + 1) = last_h_offset;
*(p_h_offset + 2) = last_h_offset;
*p_v_offset = last_v_offset;
*(p_v_offset + 1) = last_v_offset;
*(p_v_offset + 2) = last_v_offset;
} else {
pic->frame_centre_horizontal_offset[3] = last_h_offset;
pic->frame_centre_vertical_offset[3] = last_v_offset;
}
}
#endif
#if 0
static void init_mpg_picture( struct mpg_picture *pic, int chan, int32_t field_type)
{
pic->picture_header = 0;
pic->sequence_header_data
= ( INIT_HORIZONTAL_SIZE << 20 )
| ( INIT_VERTICAL_SIZE << 8 )
| ( INIT_ASPECT_RATIO << 4 )
| ( INIT_FRAME_RATE );
pic->mpeg1_flag = 0;
pic->vinfo.horizontal_size
= INIT_DISP_HORIZONTAL_SIZE;
pic->vinfo.vertical_size
= INIT_DISP_VERTICAL_SIZE;
pic->picture_display_extension_flag[field_type]
= 0;
pic->pts_flag[field_type] = 0;
pic->sequence_gop_header = 0;
pic->picture_header = 0;
pic->sequence_header_flag = 0;
pic->gop_flag = 0;
pic->sequence_end_flag = 0;
pic->sequence_display_extension_flag = 0;
pic->last_frame_centre_horizontal_offset = 0;
pic->last_frame_centre_vertical_offset = 0;
pic->channel = chan;
}
#endif
void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid,
dvb_filter_pes2ts_cb_t *cb, void *priv)
{
unsigned char *buf=p2ts->buf;
buf[0]=0x47;
buf[1]=(pid>>8);
buf[2]=pid&0xff;
p2ts->cc=0;
p2ts->cb=cb;
p2ts->priv=priv;
}
EXPORT_SYMBOL(dvb_filter_pes2ts_init);
int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes,
int len, int payload_start)
{
unsigned char *buf=p2ts->buf;
int ret=0, rest;
//len=6+((pes[4]<<8)|pes[5]);
if (payload_start)
buf[1]|=0x40;
else
buf[1]&=~0x40;
while (len>=184) {
buf[3]=0x10|((p2ts->cc++)&0x0f);
memcpy(buf+4, pes, 184);
if ((ret=p2ts->cb(p2ts->priv, buf)))
return ret;
len-=184; pes+=184;
buf[1]&=~0x40;
}
if (!len)
return 0;
buf[3]=0x30|((p2ts->cc++)&0x0f);
rest=183-len;
if (rest) {
buf[5]=0x00;
if (rest-1)
memset(buf+6, 0xff, rest-1);
}
buf[4]=rest;
memcpy(buf+5+rest, pes, len);
return p2ts->cb(p2ts->priv, buf);
}
EXPORT_SYMBOL(dvb_filter_pes2ts);

View File

@@ -0,0 +1,246 @@
/*
* dvb_filter.h
*
* Copyright (C) 2003 Convergence GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _DVB_FILTER_H_
#define _DVB_FILTER_H_
#include <linux/slab.h>
#include "demux.h"
typedef int (dvb_filter_pes2ts_cb_t) (void *, unsigned char *);
struct dvb_filter_pes2ts {
unsigned char buf[188];
unsigned char cc;
dvb_filter_pes2ts_cb_t *cb;
void *priv;
};
void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid,
dvb_filter_pes2ts_cb_t *cb, void *priv);
int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes,
int len, int payload_start);
#define PROG_STREAM_MAP 0xBC
#define PRIVATE_STREAM1 0xBD
#define PADDING_STREAM 0xBE
#define PRIVATE_STREAM2 0xBF
#define AUDIO_STREAM_S 0xC0
#define AUDIO_STREAM_E 0xDF
#define VIDEO_STREAM_S 0xE0
#define VIDEO_STREAM_E 0xEF
#define ECM_STREAM 0xF0
#define EMM_STREAM 0xF1
#define DSM_CC_STREAM 0xF2
#define ISO13522_STREAM 0xF3
#define PROG_STREAM_DIR 0xFF
#define DVB_PICTURE_START 0x00
#define DVB_USER_START 0xb2
#define DVB_SEQUENCE_HEADER 0xb3
#define DVB_SEQUENCE_ERROR 0xb4
#define DVB_EXTENSION_START 0xb5
#define DVB_SEQUENCE_END 0xb7
#define DVB_GOP_START 0xb8
#define DVB_EXCEPT_SLICE 0xb0
#define SEQUENCE_EXTENSION 0x01
#define SEQUENCE_DISPLAY_EXTENSION 0x02
#define PICTURE_CODING_EXTENSION 0x08
#define QUANT_MATRIX_EXTENSION 0x03
#define PICTURE_DISPLAY_EXTENSION 0x07
#define I_FRAME 0x01
#define B_FRAME 0x02
#define P_FRAME 0x03
/* Initialize sequence_data */
#define INIT_HORIZONTAL_SIZE 720
#define INIT_VERTICAL_SIZE 576
#define INIT_ASPECT_RATIO 0x02
#define INIT_FRAME_RATE 0x03
#define INIT_DISP_HORIZONTAL_SIZE 540
#define INIT_DISP_VERTICAL_SIZE 576
//flags2
#define PTS_DTS_FLAGS 0xC0
#define ESCR_FLAG 0x20
#define ES_RATE_FLAG 0x10
#define DSM_TRICK_FLAG 0x08
#define ADD_CPY_FLAG 0x04
#define PES_CRC_FLAG 0x02
#define PES_EXT_FLAG 0x01
//pts_dts flags
#define PTS_ONLY 0x80
#define PTS_DTS 0xC0
#define TS_SIZE 188
#define TRANS_ERROR 0x80
#define PAY_START 0x40
#define TRANS_PRIO 0x20
#define PID_MASK_HI 0x1F
//flags
#define TRANS_SCRMBL1 0x80
#define TRANS_SCRMBL2 0x40
#define ADAPT_FIELD 0x20
#define PAYLOAD 0x10
#define COUNT_MASK 0x0F
// adaptation flags
#define DISCON_IND 0x80
#define RAND_ACC_IND 0x40
#define ES_PRI_IND 0x20
#define PCR_FLAG 0x10
#define OPCR_FLAG 0x08
#define SPLICE_FLAG 0x04
#define TRANS_PRIV 0x02
#define ADAP_EXT_FLAG 0x01
// adaptation extension flags
#define LTW_FLAG 0x80
#define PIECE_RATE 0x40
#define SEAM_SPLICE 0x20
#define MAX_PLENGTH 0xFFFF
#define MMAX_PLENGTH (256*MAX_PLENGTH)
#ifndef IPACKS
#define IPACKS 2048
#endif
struct ipack {
int size;
int found;
u8 *buf;
u8 cid;
u32 plength;
u8 plen[2];
u8 flag1;
u8 flag2;
u8 hlength;
u8 pts[5];
u16 *pid;
int mpeg;
u8 check;
int which;
int done;
void *data;
void (*func)(u8 *buf, int size, void *priv);
int count;
int repack_subids;
};
struct dvb_video_info {
u32 horizontal_size;
u32 vertical_size;
u32 aspect_ratio;
u32 framerate;
u32 video_format;
u32 bit_rate;
u32 comp_bit_rate;
u32 vbv_buffer_size;
s16 vbv_delay;
u32 CSPF;
u32 off;
};
#define OFF_SIZE 4
#define FIRST_FIELD 0
#define SECOND_FIELD 1
#define VIDEO_FRAME_PICTURE 0x03
struct mpg_picture {
int channel;
struct dvb_video_info vinfo;
u32 *sequence_gop_header;
u32 *picture_header;
s32 time_code;
int low_delay;
int closed_gop;
int broken_link;
int sequence_header_flag;
int gop_flag;
int sequence_end_flag;
u8 profile_and_level;
s32 picture_coding_parameter;
u32 matrix[32];
s8 matrix_change_flag;
u8 picture_header_parameter;
/* bit 0 - 2: bwd f code
bit 3 : fpb vector
bit 4 - 6: fwd f code
bit 7 : fpf vector */
int mpeg1_flag;
int progressive_sequence;
int sequence_display_extension_flag;
u32 sequence_header_data;
s16 last_frame_centre_horizontal_offset;
s16 last_frame_centre_vertical_offset;
u32 pts[2]; /* [0] 1st field, [1] 2nd field */
int top_field_first;
int repeat_first_field;
int progressive_frame;
int bank;
int forward_bank;
int backward_bank;
int compress;
s16 frame_centre_horizontal_offset[OFF_SIZE];
/* [0-2] 1st field, [3] 2nd field */
s16 frame_centre_vertical_offset[OFF_SIZE];
/* [0-2] 1st field, [3] 2nd field */
s16 temporal_reference[2];
/* [0] 1st field, [1] 2nd field */
s8 picture_coding_type[2];
/* [0] 1st field, [1] 2nd field */
s8 picture_structure[2];
/* [0] 1st field, [1] 2nd field */
s8 picture_display_extension_flag[2];
/* [0] 1st field, [1] 2nd field */
/* picture_display_extenion() 0:no 1:exit*/
s8 pts_flag[2];
/* [0] 1st field, [1] 2nd field */
};
struct dvb_audio_info {
int layer;
u32 bit_rate;
u32 frequency;
u32 mode;
u32 mode_extension ;
u32 emphasis;
u32 framesize;
u32 off;
};
int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,169 @@
/*
* dvb_frontend.h
*
* Copyright (C) 2001 convergence integrated media GmbH
* Copyright (C) 2004 convergence GmbH
*
* Written by Ralph Metzler
* Overhauled by Holger Waechtler
* Kernel I2C stuff by Michael Hunold <hunold@convergence.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef _DVB_FRONTEND_H_
#define _DVB_FRONTEND_H_
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/dvb/frontend.h>
#include "dvbdev.h"
struct dvb_frontend_tune_settings {
int min_delay_ms;
int step_size;
int max_drift;
struct dvb_frontend_parameters parameters;
};
struct dvb_frontend;
struct dvb_tuner_info {
char name[128];
u32 frequency_min;
u32 frequency_max;
u32 frequency_step;
u32 bandwidth_min;
u32 bandwidth_max;
u32 bandwidth_step;
};
struct dvb_tuner_ops {
struct dvb_tuner_info info;
int (*release)(struct dvb_frontend *fe);
int (*init)(struct dvb_frontend *fe);
int (*sleep)(struct dvb_frontend *fe);
/** This is for simple PLLs - set all parameters in one go. */
int (*set_params)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
/** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */
int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len);
int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
#define TUNER_STATUS_LOCKED 1
int (*get_status)(struct dvb_frontend *fe, u32 *status);
/** These are provided seperately from set_params in order to facilitate silicon
* tuners which require sophisticated tuning loops, controlling each parameter seperately. */
int (*set_frequency)(struct dvb_frontend *fe, u32 frequency);
int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
};
struct dvb_frontend_ops {
struct dvb_frontend_info info;
void (*release)(struct dvb_frontend* fe);
void (*release_sec)(struct dvb_frontend* fe);
int (*init)(struct dvb_frontend* fe);
int (*sleep)(struct dvb_frontend* fe);
int (*write)(struct dvb_frontend* fe, u8* buf, int len);
/* if this is set, it overrides the default swzigzag */
int (*tune)(struct dvb_frontend* fe,
struct dvb_frontend_parameters* params,
unsigned int mode_flags,
int *delay,
fe_status_t *status);
/* get frontend tuning algorithm from the module */
int (*get_frontend_algo)(struct dvb_frontend *fe);
/* these two are only used for the swzigzag code */
int (*set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings);
int (*get_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
int (*read_status)(struct dvb_frontend* fe, fe_status_t* status);
int (*read_ber)(struct dvb_frontend* fe, u32* ber);
int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength);
int (*read_snr)(struct dvb_frontend* fe, u16* snr);
int (*read_ucblocks)(struct dvb_frontend* fe, u32* ucblocks);
int (*diseqc_reset_overload)(struct dvb_frontend* fe);
int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd);
int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply);
int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd);
int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone);
int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg);
int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd);
int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable);
int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
struct dvb_tuner_ops tuner_ops;
};
#define MAX_EVENT 8
struct dvb_fe_events {
struct dvb_frontend_event events[MAX_EVENT];
int eventw;
int eventr;
int overflow;
wait_queue_head_t wait_queue;
struct semaphore sem;
};
struct dvb_frontend {
struct dvb_frontend_ops ops;
struct dvb_adapter *dvb;
void* demodulator_priv;
void* tuner_priv;
void* frontend_priv;
void* sec_priv;
};
extern int dvb_register_frontend(struct dvb_adapter* dvb,
struct dvb_frontend* fe);
extern int dvb_unregister_frontend(struct dvb_frontend* fe);
extern void dvb_frontend_detach(struct dvb_frontend* fe);
extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec);
extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime);
#endif

View File

@@ -0,0 +1,145 @@
/*
* dvb-math provides some complex fixed-point math
* operations shared between the dvb related stuff
*
* Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/bug.h>
#include "dvb_math.h"
static const unsigned short logtable[256] = {
0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6,
0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37,
0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f,
0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41,
0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1,
0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142,
0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68,
0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355,
0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c,
0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490,
0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3,
0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507,
0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe,
0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca,
0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c,
0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7,
0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c,
0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c,
0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a,
0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065,
0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730,
0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc,
0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469,
0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9,
0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c,
0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765,
0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83,
0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387,
0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973,
0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47
};
unsigned int intlog2(u32 value)
{
/**
* returns: log2(value) * 2^24
* wrong result if value = 0 (log2(0) is undefined)
*/
unsigned int msb;
unsigned int logentry;
unsigned int significand;
unsigned int interpolation;
if (unlikely(value == 0)) {
WARN_ON(1);
return 0;
}
/* first detect the msb (count begins at 0) */
msb = fls(value) - 1;
/**
* now we use a logtable after the following method:
*
* log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24
* where x = msb and therefore 1 <= y < 2
* first y is determined by shifting the value left
* so that msb is bit 31
* 0x00231f56 -> 0x8C7D5800
* the result is y * 2^31 -> "significand"
* then the highest 9 bits are used for a table lookup
* the highest bit is discarded because it's always set
* the highest nine bits in our example are 100011000
* so we would use the entry 0x18
*/
significand = value << (31 - msb);
logentry = (significand >> 23) & 0xff;
/**
* last step we do is interpolation because of the
* limitations of the log table the error is that part of
* the significand which isn't used for lookup then we
* compute the ratio between the error and the next table entry
* and interpolate it between the log table entry used and the
* next one the biggest error possible is 0x7fffff
* (in our example it's 0x7D5800)
* needed value for next table entry is 0x800000
* so the interpolation is
* (error / 0x800000) * (logtable_next - logtable_current)
* in the implementation the division is moved to the end for
* better accuracy there is also an overflow correction if
* logtable_next is 256
*/
interpolation = ((significand & 0x7fffff) *
((logtable[(logentry + 1) & 0xff] -
logtable[logentry]) & 0xffff)) >> 15;
/* now we return the result */
return ((msb << 24) + (logtable[logentry] << 8) + interpolation);
}
EXPORT_SYMBOL(intlog2);
unsigned int intlog10(u32 value)
{
/**
* returns: log10(value) * 2^24
* wrong result if value = 0 (log10(0) is undefined)
*/
u64 log;
if (unlikely(value == 0)) {
WARN_ON(1);
return 0;
}
log = intlog2(value);
/**
* we use the following method:
* log10(x) = log2(x) * log10(2)
*/
return (log * 646456993) >> 31;
}
EXPORT_SYMBOL(intlog10);

View File

@@ -0,0 +1,58 @@
/*
* dvb-math provides some complex fixed-point math
* operations shared between the dvb related stuff
*
* Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __DVB_MATH_H
#define __DVB_MATH_H
#include <linux/types.h>
/**
* computes log2 of a value; the result is shifted left by 24 bits
*
* to use rational values you can use the following method:
* intlog2(value) = intlog2(value * 2^x) - x * 2^24
*
* example: intlog2(8) will give 3 << 24 = 3 * 2^24
* example: intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24
* example: intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24
*
* @param value The value (must be != 0)
* @return log2(value) * 2^24
*/
extern unsigned int intlog2(u32 value);
/**
* computes log10 of a value; the result is shifted left by 24 bits
*
* to use rational values you can use the following method:
* intlog10(value) = intlog10(value * 10^x) - x * 2^24
*
* example: intlog10(1000) will give 3 << 24 = 3 * 2^24
* due to the implementation intlog10(1000) might be not exactly 3 * 2^24
*
* look at intlog2 for similar examples
*
* @param value The value (must be != 0)
* @return log10(value) * 2^24
*/
extern unsigned int intlog10(u32 value);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
/*
* dvb_net.h
*
* Copyright (C) 2001 Ralph Metzler for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef _DVB_NET_H_
#define _DVB_NET_H_
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include "dvbdev.h"
#define DVB_NET_DEVICES_MAX 10
struct dvb_net {
struct dvb_device *dvbdev;
struct net_device *device[DVB_NET_DEVICES_MAX];
int state[DVB_NET_DEVICES_MAX];
struct dmx_demux *demux;
};
void dvb_net_release(struct dvb_net *);
int dvb_net_init(struct dvb_adapter *, struct dvb_net *, struct dmx_demux *);
#endif

View File

@@ -0,0 +1,271 @@
/*
*
* dvb_ringbuffer.c: ring buffer implementation for the dvb driver
*
* Copyright (C) 2003 Oliver Endriss
* Copyright (C) 2004 Andrew de Quincey
*
* based on code originally found in av7110.c & dvb_ci.c:
* Copyright (C) 1999-2003 Ralph Metzler
* & Marcus Metzler for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include "dvb_ringbuffer.h"
#define PKT_READY 0
#define PKT_DISPOSED 1
void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
{
rbuf->pread=rbuf->pwrite=0;
rbuf->data=data;
rbuf->size=len;
rbuf->error=0;
init_waitqueue_head(&rbuf->queue);
spin_lock_init(&(rbuf->lock));
}
int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
{
return (rbuf->pread==rbuf->pwrite);
}
ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
{
ssize_t free;
free = rbuf->pread - rbuf->pwrite;
if (free <= 0)
free += rbuf->size;
return free-1;
}
ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
{
ssize_t avail;
avail = rbuf->pwrite - rbuf->pread;
if (avail < 0)
avail += rbuf->size;
return avail;
}
void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
{
rbuf->pread = rbuf->pwrite;
rbuf->error = 0;
}
void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)
{
unsigned long flags;
spin_lock_irqsave(&rbuf->lock, flags);
dvb_ringbuffer_flush(rbuf);
spin_unlock_irqrestore(&rbuf->lock, flags);
wake_up(&rbuf->queue);
}
ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, int usermem)
{
size_t todo = len;
size_t split;
split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
if (split > 0) {
if (!usermem)
memcpy(buf, rbuf->data+rbuf->pread, split);
else
if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
return -EFAULT;
buf += split;
todo -= split;
rbuf->pread = 0;
}
if (!usermem)
memcpy(buf, rbuf->data+rbuf->pread, todo);
else
if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
return -EFAULT;
rbuf->pread = (rbuf->pread + todo) % rbuf->size;
return len;
}
ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)
{
size_t todo = len;
size_t split;
split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
if (split > 0) {
memcpy(rbuf->data+rbuf->pwrite, buf, split);
buf += split;
todo -= split;
rbuf->pwrite = 0;
}
memcpy(rbuf->data+rbuf->pwrite, buf, todo);
rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
return len;
}
ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
{
int status;
ssize_t oldpwrite = rbuf->pwrite;
DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
status = dvb_ringbuffer_write(rbuf, buf, len);
if (status < 0) rbuf->pwrite = oldpwrite;
return status;
}
ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
int offset, u8* buf, size_t len, int usermem)
{
size_t todo;
size_t split;
size_t pktlen;
pktlen = rbuf->data[idx] << 8;
pktlen |= rbuf->data[(idx + 1) % rbuf->size];
if (offset > pktlen) return -EINVAL;
if ((offset + len) > pktlen) len = pktlen - offset;
idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
todo = len;
split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
if (split > 0) {
if (!usermem)
memcpy(buf, rbuf->data+idx, split);
else
if (copy_to_user(buf, rbuf->data+idx, split))
return -EFAULT;
buf += split;
todo -= split;
idx = 0;
}
if (!usermem)
memcpy(buf, rbuf->data+idx, todo);
else
if (copy_to_user(buf, rbuf->data+idx, todo))
return -EFAULT;
return len;
}
void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
{
size_t pktlen;
rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
// clean up disposed packets
while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
} else {
// first packet is not disposed, so we stop cleaning now
break;
}
}
}
ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
{
int consumed;
int curpktlen;
int curpktstatus;
if (idx == -1) {
idx = rbuf->pread;
} else {
curpktlen = rbuf->data[idx] << 8;
curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
}
consumed = (idx - rbuf->pread) % rbuf->size;
while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
curpktlen = rbuf->data[idx] << 8;
curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
if (curpktstatus == PKT_READY) {
*pktlen = curpktlen;
return idx;
}
consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
}
// no packets available
return -1;
}
EXPORT_SYMBOL(dvb_ringbuffer_init);
EXPORT_SYMBOL(dvb_ringbuffer_empty);
EXPORT_SYMBOL(dvb_ringbuffer_free);
EXPORT_SYMBOL(dvb_ringbuffer_avail);
EXPORT_SYMBOL(dvb_ringbuffer_flush);
EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
EXPORT_SYMBOL(dvb_ringbuffer_read);
EXPORT_SYMBOL(dvb_ringbuffer_write);
EXPORT_SYMBOL(dvb_ringbuffer_pkt_write);
EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);

View File

@@ -0,0 +1,174 @@
/*
*
* dvb_ringbuffer.h: ring buffer implementation for the dvb driver
*
* Copyright (C) 2003 Oliver Endriss
* Copyright (C) 2004 Andrew de Quincey
*
* based on code originally found in av7110.c & dvb_ci.c:
* Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _DVB_RINGBUFFER_H_
#define _DVB_RINGBUFFER_H_
#include <linux/spinlock.h>
#include <linux/wait.h>
struct dvb_ringbuffer {
u8 *data;
ssize_t size;
ssize_t pread;
ssize_t pwrite;
int error;
wait_queue_head_t queue;
spinlock_t lock;
};
#define DVB_RINGBUFFER_PKTHDRSIZE 3
/*
** Notes:
** ------
** (1) For performance reasons read and write routines don't check buffer sizes
** and/or number of bytes free/available. This has to be done before these
** routines are called. For example:
**
** *** write <buflen> bytes ***
** free = dvb_ringbuffer_free(rbuf);
** if (free >= buflen)
** count = dvb_ringbuffer_write(rbuf, buffer, buflen);
** else
** ...
**
** *** read min. 1000, max. <bufsize> bytes ***
** avail = dvb_ringbuffer_avail(rbuf);
** if (avail >= 1000)
** count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize), 0);
** else
** ...
**
** (2) If there is exactly one reader and one writer, there is no need
** to lock read or write operations.
** Two or more readers must be locked against each other.
** Flushing the buffer counts as a read operation.
** Two or more writers must be locked against each other.
*/
/* initialize ring buffer, lock and queue */
extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len);
/* test whether buffer is empty */
extern int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf);
/* return the number of free bytes in the buffer */
extern ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf);
/* return the number of bytes waiting in the buffer */
extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf);
/* read routines & macros */
/* ---------------------- */
/* flush buffer */
extern void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf);
/* flush buffer protected by spinlock and wake-up waiting task(s) */
extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf);
/* peek at byte <offs> in the buffer */
#define DVB_RINGBUFFER_PEEK(rbuf,offs) \
(rbuf)->data[((rbuf)->pread+(offs))%(rbuf)->size]
/* advance read ptr by <num> bytes */
#define DVB_RINGBUFFER_SKIP(rbuf,num) \
(rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size
/*
** read <len> bytes from ring buffer into <buf>
** <usermem> specifies whether <buf> resides in user space
** returns number of bytes transferred or -EFAULT
*/
extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
size_t len, int usermem);
/* write routines & macros */
/* ----------------------- */
/* write single byte to ring buffer */
#define DVB_RINGBUFFER_WRITE_BYTE(rbuf,byte) \
{ (rbuf)->data[(rbuf)->pwrite]=(byte); \
(rbuf)->pwrite=((rbuf)->pwrite+1)%(rbuf)->size; }
/*
** write <len> bytes to ring buffer
** <usermem> specifies whether <buf> resides in user space
** returns number of bytes transferred or -EFAULT
*/
extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
size_t len);
/**
* Write a packet into the ringbuffer.
*
* <rbuf> Ringbuffer to write to.
* <buf> Buffer to write.
* <len> Length of buffer (currently limited to 65535 bytes max).
* returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL.
*/
extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf,
size_t len);
/**
* Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this
* does NOT update the read pointer in the ringbuffer. You must use
* dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required.
*
* <rbuf> Ringbuffer concerned.
* <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
* <offset> Offset into packet to read from.
* <buf> Destination buffer for data.
* <len> Size of destination buffer.
* <usermem> Set to 1 if <buf> is in userspace.
* returns Number of bytes read, or -EFAULT.
*/
extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
int offset, u8* buf, size_t len, int usermem);
/**
* Dispose of a packet in the ring buffer.
*
* <rbuf> Ring buffer concerned.
* <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
*/
extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx);
/**
* Get the index of the next packet in a ringbuffer.
*
* <rbuf> Ringbuffer concerned.
* <idx> Previous packet index, or -1 to return the first packet index.
* <pktlen> On success, will be updated to contain the length of the packet in bytes.
* returns Packet index (if >=0), or -1 if no packets available.
*/
extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
#endif /* _DVB_RINGBUFFER_H_ */

View File

@@ -0,0 +1,438 @@
/*
* dvbdev.c
*
* Copyright (C) 2000 Ralph Metzler <ralph@convergence.de>
* & Marcus Metzler <marcus@convergence.de>
* for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include "dvbdev.h"
static int dvbdev_debug;
module_param(dvbdev_debug, int, 0644);
MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
#define dprintk if (dvbdev_debug) printk
static LIST_HEAD(dvb_adapter_list);
static DEFINE_MUTEX(dvbdev_register_lock);
static const char * const dnames[] = {
"video", "audio", "sec", "frontend", "demux", "dvr", "ca",
"net", "osd"
};
#define DVB_MAX_ADAPTERS 8
#define DVB_MAX_IDS 4
#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64)
static struct class *dvb_class;
static struct dvb_device* dvbdev_find_device (int minor)
{
struct list_head *entry;
list_for_each (entry, &dvb_adapter_list) {
struct list_head *entry0;
struct dvb_adapter *adap;
adap = list_entry (entry, struct dvb_adapter, list_head);
list_for_each (entry0, &adap->device_list) {
struct dvb_device *dev;
dev = list_entry (entry0, struct dvb_device, list_head);
if (nums2minor(adap->num, dev->type, dev->id) == minor)
return dev;
}
}
return NULL;
}
static int dvb_device_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev;
dvbdev = dvbdev_find_device (iminor(inode));
if (dvbdev && dvbdev->fops) {
int err = 0;
const struct file_operations *old_fops;
file->private_data = dvbdev;
old_fops = file->f_op;
file->f_op = fops_get(dvbdev->fops);
if(file->f_op->open)
err = file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
return -ENODEV;
}
static struct file_operations dvb_device_fops =
{
.owner = THIS_MODULE,
.open = dvb_device_open,
};
static struct cdev dvb_device_cdev = {
.kobj = {.name = "dvb", },
.owner = THIS_MODULE,
};
int dvb_generic_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
if (!dvbdev)
return -ENODEV;
if (!dvbdev->users)
return -EBUSY;
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
if (!dvbdev->readers)
return -EBUSY;
dvbdev->readers--;
} else {
if (!dvbdev->writers)
return -EBUSY;
dvbdev->writers--;
}
dvbdev->users--;
return 0;
}
EXPORT_SYMBOL(dvb_generic_open);
int dvb_generic_release(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
if (!dvbdev)
return -ENODEV;
if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
dvbdev->readers++;
} else {
dvbdev->writers++;
}
dvbdev->users++;
return 0;
}
EXPORT_SYMBOL(dvb_generic_release);
int dvb_generic_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct dvb_device *dvbdev = file->private_data;
if (!dvbdev)
return -ENODEV;
if (!dvbdev->kernel_ioctl)
return -EINVAL;
return dvb_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl);
}
EXPORT_SYMBOL(dvb_generic_ioctl);
static int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
{
u32 id = 0;
while (id < DVB_MAX_IDS) {
struct list_head *entry;
list_for_each (entry, &adap->device_list) {
struct dvb_device *dev;
dev = list_entry (entry, struct dvb_device, list_head);
if (dev->type == type && dev->id == id)
goto skip;
}
return id;
skip:
id++;
}
return -ENFILE;
}
int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
const struct dvb_device *template, void *priv, int type)
{
struct dvb_device *dvbdev;
struct file_operations *dvbdevfops;
int id;
mutex_lock(&dvbdev_register_lock);
if ((id = dvbdev_get_free_id (adap, type)) < 0){
mutex_unlock(&dvbdev_register_lock);
*pdvbdev = NULL;
printk ("%s: could get find free device id...\n", __FUNCTION__);
return -ENFILE;
}
*pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
if (!dvbdev){
mutex_unlock(&dvbdev_register_lock);
return -ENOMEM;
}
dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
if (!dvbdevfops){
kfree (dvbdev);
mutex_unlock(&dvbdev_register_lock);
return -ENOMEM;
}
memcpy(dvbdev, template, sizeof(struct dvb_device));
dvbdev->type = type;
dvbdev->id = id;
dvbdev->adapter = adap;
dvbdev->priv = priv;
dvbdev->fops = dvbdevfops;
memcpy(dvbdev->fops, template->fops, sizeof(struct file_operations));
dvbdev->fops->owner = adap->module;
list_add_tail (&dvbdev->list_head, &adap->device_list);
mutex_unlock(&dvbdev_register_lock);
class_device_create(dvb_class, NULL, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
adap->device, "dvb%d.%s%d", adap->num, dnames[type], id);
dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
adap->num, dnames[type], id, nums2minor(adap->num, type, id),
nums2minor(adap->num, type, id));
return 0;
}
EXPORT_SYMBOL(dvb_register_device);
void dvb_unregister_device(struct dvb_device *dvbdev)
{
if (!dvbdev)
return;
class_device_destroy(dvb_class, MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num,
dvbdev->type, dvbdev->id)));
list_del (&dvbdev->list_head);
kfree (dvbdev->fops);
kfree (dvbdev);
}
EXPORT_SYMBOL(dvb_unregister_device);
static int dvbdev_get_free_adapter_num (void)
{
int num = 0;
while (num < DVB_MAX_ADAPTERS) {
struct list_head *entry;
list_for_each (entry, &dvb_adapter_list) {
struct dvb_adapter *adap;
adap = list_entry (entry, struct dvb_adapter, list_head);
if (adap->num == num)
goto skip;
}
return num;
skip:
num++;
}
return -ENFILE;
}
int dvb_register_adapter(struct dvb_adapter *adap, const char *name, struct module *module, struct device *device)
{
int num;
mutex_lock(&dvbdev_register_lock);
if ((num = dvbdev_get_free_adapter_num ()) < 0) {
mutex_unlock(&dvbdev_register_lock);
return -ENFILE;
}
memset (adap, 0, sizeof(struct dvb_adapter));
INIT_LIST_HEAD (&adap->device_list);
printk ("DVB: registering new adapter (%s).\n", name);
adap->num = num;
adap->name = name;
adap->module = module;
adap->device = device;
list_add_tail (&adap->list_head, &dvb_adapter_list);
mutex_unlock(&dvbdev_register_lock);
return num;
}
EXPORT_SYMBOL(dvb_register_adapter);
int dvb_unregister_adapter(struct dvb_adapter *adap)
{
mutex_lock(&dvbdev_register_lock);
list_del (&adap->list_head);
mutex_unlock(&dvbdev_register_lock);
return 0;
}
EXPORT_SYMBOL(dvb_unregister_adapter);
/* if the miracle happens and "generic_usercopy()" is included into
the kernel, then this can vanish. please don't make the mistake and
define this as video_usercopy(). this will introduce a dependecy
to the v4l "videodev.o" module, which is unnecessary for some
cards (ie. the budget dvb-cards don't need the v4l module...) */
int dvb_usercopy(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg,
int (*func)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg))
{
char sbuf[128];
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
/* Copy arguments into temp kernel buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_NONE:
/*
* For this command, the pointer is actually an integer
* argument.
*/
parg = (void *) arg;
break;
case _IOC_READ: /* some v4l ioctls are marked wrong ... */
case _IOC_WRITE:
case (_IOC_WRITE | _IOC_READ):
if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
parg = sbuf;
} else {
/* too big to allocate from stack */
mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
if (NULL == mbuf)
return -ENOMEM;
parg = mbuf;
}
err = -EFAULT;
if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
goto out;
break;
}
/* call driver */
if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
err = -EINVAL;
if (err < 0)
goto out;
/* Copy results into user buffer */
switch (_IOC_DIR(cmd))
{
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
err = -EFAULT;
break;
}
out:
kfree(mbuf);
return err;
}
static int __init init_dvbdev(void)
{
int retval;
dev_t dev = MKDEV(DVB_MAJOR, 0);
if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) {
printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
return retval;
}
cdev_init(&dvb_device_cdev, &dvb_device_fops);
if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) {
printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
goto error;
}
dvb_class = class_create(THIS_MODULE, "dvb");
if (IS_ERR(dvb_class)) {
retval = PTR_ERR(dvb_class);
goto error;
}
return 0;
error:
cdev_del(&dvb_device_cdev);
unregister_chrdev_region(dev, MAX_DVB_MINORS);
return retval;
}
static void __exit exit_dvbdev(void)
{
class_destroy(dvb_class);
cdev_del(&dvb_device_cdev);
unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
}
module_init(init_dvbdev);
module_exit(exit_dvbdev);
MODULE_DESCRIPTION("DVB Core Driver");
MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,127 @@
/*
* dvbdev.h
*
* Copyright (C) 2000 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Lesser Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef _DVBDEV_H_
#define _DVBDEV_H_
#include <linux/types.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/smp_lock.h>
#define DVB_MAJOR 212
#define DVB_DEVICE_VIDEO 0
#define DVB_DEVICE_AUDIO 1
#define DVB_DEVICE_SEC 2
#define DVB_DEVICE_FRONTEND 3
#define DVB_DEVICE_DEMUX 4
#define DVB_DEVICE_DVR 5
#define DVB_DEVICE_CA 6
#define DVB_DEVICE_NET 7
#define DVB_DEVICE_OSD 8
struct dvb_adapter {
int num;
struct list_head list_head;
struct list_head device_list;
const char *name;
u8 proposed_mac [6];
void* priv;
struct device *device;
struct module *module;
};
struct dvb_device {
struct list_head list_head;
struct file_operations *fops;
struct dvb_adapter *adapter;
int type;
u32 id;
/* in theory, 'users' can vanish now,
but I don't want to change too much now... */
int readers;
int writers;
int users;
/* don't really need those !? -- FIXME: use video_usercopy */
int (*kernel_ioctl)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg);
void *priv;
};
extern int dvb_register_adapter (struct dvb_adapter *adap, const char *name, struct module *module, struct device *device);
extern int dvb_unregister_adapter (struct dvb_adapter *adap);
extern int dvb_register_device (struct dvb_adapter *adap,
struct dvb_device **pdvbdev,
const struct dvb_device *template,
void *priv,
int type);
extern void dvb_unregister_device (struct dvb_device *dvbdev);
extern int dvb_generic_open (struct inode *inode, struct file *file);
extern int dvb_generic_release (struct inode *inode, struct file *file);
extern int dvb_generic_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
/* we don't mess with video_usercopy() any more,
we simply define out own dvb_usercopy(), which will hopefully become
generic_usercopy() someday... */
extern int dvb_usercopy(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg,
int (*func)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg));
/** generic DVB attach function. */
#ifdef CONFIG_DVB_CORE_ATTACH
#define dvb_attach(FUNCTION, ARGS...) ({ \
void *__r = NULL; \
typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
if (__a) { \
__r = (void *) __a(ARGS); \
if (__r == NULL) \
symbol_put(FUNCTION); \
} else { \
printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \
} \
__r; \
})
#else
#define dvb_attach(FUNCTION, ARGS...) ({ \
FUNCTION(ARGS); \
})
#endif
#endif /* #ifndef _DVBDEV_H_ */

View File

@@ -0,0 +1,210 @@
config DVB_USB
tristate "Support for various USB DVB devices"
depends on DVB_CORE && USB && I2C
select FW_LOADER
select DVB_PLL
help
By enabling this you will be able to choose the various supported
USB1.1 and USB2.0 DVB devices.
Almost every USB device needs a firmware, please look into
<file:Documentation/dvb/README.dvb-usb>.
For a complete list of supported USB devices see the LinuxTV DVB Wiki:
<http://www.linuxtv.org/wiki/index.php/DVB_USB>
Say Y if you own a USB DVB device.
config DVB_USB_DEBUG
bool "Enable extended debug support for all DVB-USB devices"
depends on DVB_USB
help
Say Y if you want to enable debugging. See modinfo dvb-usb (and the
appropriate drivers) for debug levels.
config DVB_USB_A800
tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)"
depends on DVB_USB
select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver.
config DVB_USB_DIBUSB_MB
tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)"
depends on DVB_USB
select DVB_DIB3000MB
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by
DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator.
For an up-to-date list of devices supported by this driver, have a look
on the Linux-DVB Wiki at www.linuxtv.org.
Say Y if you own such a device and want to use it. You should build it as
a module.
config DVB_USB_DIBUSB_MB_FAULTY
bool "Support faulty USB IDs"
depends on DVB_USB_DIBUSB_MB
help
Support for faulty USB IDs due to an invalid EEPROM on some Artec devices.
config DVB_USB_DIBUSB_MC
tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
depends on DVB_USB
select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
Support for USB2.0 DVB-T receivers based on reference designs made by
DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator.
For an up-to-date list of devices supported by this driver, have a look
on the Linux-DVB Wiki at www.linuxtv.org.
Say Y if you own such a device and want to use it. You should build it as
a module.
config DVB_USB_DIB0700
tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)"
depends on DVB_USB
select DVB_DIB7000P
select DVB_DIB7000M
select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
USB bridge is also present in devices having the DiB7700 DVB-T-USB
silicon. This chip can be found in devices offered by Hauppauge,
Avermedia and other big and small companies.
For an up-to-date list of devices supported by this driver, have a look
on the Linux-DVB Wiki at www.linuxtv.org.
Say Y if you own such a device and want to use it. You should build it as
a module.
config DVB_USB_UMT_010
tristate "HanfTek UMT-010 DVB-T USB2.0 support"
depends on DVB_USB
select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver.
config DVB_USB_CXUSB
tristate "Conexant USB2.0 hybrid reference design support"
depends on DVB_USB
select DVB_CX22702 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
help
Say Y here to support the Conexant USB2.0 hybrid reference design.
Currently, only DVB and ATSC modes are supported, analog mode
shall be added in the future. Devices that require this module:
Medion MD95700 hybrid USB2.0 device.
DViCO FusionHDTV (Bluebird) USB2.0 devices
config DVB_USB_M920X
tristate "Uli m920x DVB-T USB2.0 support"
depends on DVB_USB
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
help
Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
Currently, only devices with a product id of
"DTV USB MINI" (in cold state) are supported.
Firmware required.
config DVB_USB_GL861
tristate "Genesys Logic GL861 USB2.0 support"
depends on DVB_USB
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
help
Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
receiver with USB ID 0db0:5581.
config DVB_USB_AU6610
tristate "Alcor Micro AU6610 USB2.0 support"
depends on DVB_USB
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
help
Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver.
config DVB_USB_DIGITV
tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support"
depends on DVB_USB
select DVB_NXT6000 if !DVB_FE_CUSTOMISE
select DVB_MT352 if !DVB_FE_CUSTOMISE
help
Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver.
config DVB_USB_VP7045
tristate "TwinhanDTV Alpha/MagicBoxII, DNTV tinyUSB2, Beetle USB2.0 support"
depends on DVB_USB
help
Say Y here to support the
TwinhanDTV Alpha (stick) (VP-7045),
TwinhanDTV MagicBox II (VP-7046),
DigitalNow TinyUSB 2 DVB-t,
DigitalRise USB 2.0 Ter (Beetle) and
TYPHOON DVB-T USB DRIVE
DVB-T USB2.0 receivers.
config DVB_USB_VP702X
tristate "TwinhanDTV StarBox and clones DVB-S USB2.0 support"
depends on DVB_USB
help
Say Y here to support the
TwinhanDTV StarBox,
DigitalRise USB Starbox and
TYPHOON DVB-S USB 2.0 BOX
DVB-S USB2.0 receivers.
config DVB_USB_GP8PSK
tristate "GENPIX 8PSK->USB module support"
depends on DVB_USB
help
Say Y here to support the
GENPIX 8psk module
DVB-S USB2.0 receivers.
config DVB_USB_NOVA_T_USB2
tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
depends on DVB_USB
select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help
Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
config DVB_USB_TTUSB2
tristate "Pinnacle 400e DVB-S USB2.0 support"
depends on DVB_USB
select DVB_TDA10086 if !DVB_FE_CUSTOMISE
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
select DVB_TDA826X if !DVB_FE_CUSTOMISE
help
Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The
firmware protocol used by this module is similar to the one used by the
old ttusb-driver - that's why the module is called dvb-usb-ttusb2.ko.
config DVB_USB_DTT200U
tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)"
depends on DVB_USB
help
Say Y here to support the WideView/Yakumo/Hama/Typhoon/Yuan DVB-T USB2.0 receiver.
The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan).
The WT-220U and its clones are pen-sized.

View File

@@ -0,0 +1,54 @@
dvb-usb-objs = dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o
obj-$(CONFIG_DVB_USB) += dvb-usb.o
dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o
obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o
dvb-usb-vp702x-objs = vp702x.o vp702x-fe.o
obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o
dvb-usb-gp8psk-objs = gp8psk.o gp8psk-fe.o
obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o
dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o
obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o
dvb-usb-dibusb-common-objs = dibusb-common.o
dvb-usb-a800-objs = a800.o
obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o
dvb-usb-dibusb-mb-objs = dibusb-mb.o
obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o
dvb-usb-dibusb-mc-objs = dibusb-mc.o
obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o
dvb-usb-nova-t-usb2-objs = nova-t-usb2.o
obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o
dvb-usb-umt-010-objs = umt-010.o
obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o
dvb-usb-m920x-objs = m920x.o
obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o
dvb-usb-gl861-objs = gl861.o
obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o
dvb-usb-au6610-objs = au6610.o
obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o
dvb-usb-digitv-objs = digitv.o
obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o
dvb-usb-cxusb-objs = cxusb.o
obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o
dvb-usb-ttusb2-objs = ttusb2.o
obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o
dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o
obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/

View File

@@ -0,0 +1,193 @@
/* DVB USB framework compliant Linux driver for the AVerMedia AverTV DVB-T
* USB2.0 (A800) DVB-T receiver.
*
* Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
*
* Thanks to
* - AVerMedia who kindly provided information and
* - Glen Harris who suffered from my mistakes during development.
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dibusb.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (rc=1 (or-able))." DVB_USB_DEBUG_STATUS);
#define deb_rc(args...) dprintk(debug,0x01,args)
static int a800_power_ctrl(struct dvb_usb_device *d, int onoff)
{
/* do nothing for the AVerMedia */
return 0;
}
/* assure to put cold to 0 for iManufacturer == 1 */
static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc, int *cold)
{
*cold = udev->descriptor.iManufacturer != 1;
return 0;
}
static struct dvb_usb_rc_key a800_rc_keys[] = {
{ 0x02, 0x01, KEY_PROG1 }, /* SOURCE */
{ 0x02, 0x00, KEY_POWER }, /* POWER */
{ 0x02, 0x05, KEY_1 }, /* 1 */
{ 0x02, 0x06, KEY_2 }, /* 2 */
{ 0x02, 0x07, KEY_3 }, /* 3 */
{ 0x02, 0x09, KEY_4 }, /* 4 */
{ 0x02, 0x0a, KEY_5 }, /* 5 */
{ 0x02, 0x0b, KEY_6 }, /* 6 */
{ 0x02, 0x0d, KEY_7 }, /* 7 */
{ 0x02, 0x0e, KEY_8 }, /* 8 */
{ 0x02, 0x0f, KEY_9 }, /* 9 */
{ 0x02, 0x12, KEY_LEFT }, /* L / DISPLAY */
{ 0x02, 0x11, KEY_0 }, /* 0 */
{ 0x02, 0x13, KEY_RIGHT }, /* R / CH RTN */
{ 0x02, 0x17, KEY_PROG2 }, /* SNAP SHOT */
{ 0x02, 0x10, KEY_PROG3 }, /* 16-CH PREV */
{ 0x02, 0x1e, KEY_VOLUMEDOWN }, /* VOL DOWN */
{ 0x02, 0x0c, KEY_ZOOM }, /* FULL SCREEN */
{ 0x02, 0x1f, KEY_VOLUMEUP }, /* VOL UP */
{ 0x02, 0x14, KEY_MUTE }, /* MUTE */
{ 0x02, 0x08, KEY_AUDIO }, /* AUDIO */
{ 0x02, 0x19, KEY_RECORD }, /* RECORD */
{ 0x02, 0x18, KEY_PLAY }, /* PLAY */
{ 0x02, 0x1b, KEY_STOP }, /* STOP */
{ 0x02, 0x1a, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */
{ 0x02, 0x1d, KEY_BACK }, /* << / RED */
{ 0x02, 0x1c, KEY_FORWARD }, /* >> / YELLOW */
{ 0x02, 0x03, KEY_TEXT }, /* TELETEXT */
{ 0x02, 0x04, KEY_EPG }, /* EPG */
{ 0x02, 0x15, KEY_MENU }, /* MENU */
{ 0x03, 0x03, KEY_CHANNELUP }, /* CH UP */
{ 0x03, 0x02, KEY_CHANNELDOWN }, /* CH DOWN */
{ 0x03, 0x01, KEY_FIRST }, /* |<< / GREEN */
{ 0x03, 0x00, KEY_LAST }, /* >>| / BLUE */
};
static int a800_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
u8 key[5];
if (usb_control_msg(d->udev,usb_rcvctrlpipe(d->udev,0),
0x04, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, key, 5,
2000) != 5)
return -ENODEV;
/* call the universal NEC remote processor, to find out the key's state and event */
dvb_usb_nec_rc_key_to_event(d,key,event,state);
if (key[0] != 0)
deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]);
return 0;
}
/* USB Driver stuff */
static struct dvb_usb_device_properties a800_properties;
static int a800_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return dvb_usb_device_init(intf,&a800_properties,THIS_MODULE,NULL);
}
/* do not change the order of the ID table */
static struct usb_device_id a800_table [] = {
/* 00 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB2_COLD) },
/* 01 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB2_WARM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, a800_table);
static struct dvb_usb_device_properties a800_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-avertv-a800-02.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
.pid_filter = dibusb_pid_filter,
.pid_filter_ctrl = dibusb_pid_filter_ctrl,
.frontend_attach = dibusb_dib3000mc_frontend_attach,
.tuner_attach = dibusb_dib3000mc_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x06,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
.size_of_priv = sizeof(struct dibusb_state),
},
},
.power_ctrl = a800_power_ctrl,
.identify_state = a800_identify_state,
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = a800_rc_keys,
.rc_key_map_size = ARRAY_SIZE(a800_rc_keys),
.rc_query = a800_rc_query,
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "AVerMedia AverTV DVB-T USB 2.0 (A800)",
{ &a800_table[0], NULL },
{ &a800_table[1], NULL },
},
}
};
static struct usb_driver a800_driver = {
.name = "dvb_usb_a800",
.probe = a800_probe,
.disconnect = dvb_usb_device_exit,
.id_table = a800_table,
};
/* module stuff */
static int __init a800_module_init(void)
{
int result;
if ((result = usb_register(&a800_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit a800_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&a800_driver);
}
module_init (a800_module_init);
module_exit (a800_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("AVerMedia AverTV DVB-T USB 2.0 (A800)");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,255 @@
/* DVB USB compliant linux driver for Sigmatek DVB-110 DVB-T USB2.0 receiver
*
* Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "au6610.h"
#include "zl10353.h"
#include "qt1010.h"
/* debug */
static int dvb_usb_au6610_debug;
module_param_named(debug, dvb_usb_au6610_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
int ret;
u16 index;
u8 usb_buf[6]; /* enough for all known requests,
read returns 5 and write 6 bytes */
switch (wlen) {
case 1:
index = wbuf[0] << 8;
break;
case 2:
index = wbuf[0] << 8;
index += wbuf[1];
break;
default:
warn("wlen = %x, aborting.", wlen);
return -EINVAL;
}
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
USB_TYPE_VENDOR|USB_DIR_IN, addr, index, usb_buf,
sizeof(usb_buf), AU6610_USB_TIMEOUT);
if (ret < 0)
return ret;
switch (operation) {
case AU6610_REQ_I2C_READ:
case AU6610_REQ_USB_READ:
/* requested value is always 5th byte in buffer */
rbuf[0] = usb_buf[4];
}
return ret;
}
static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u8 request;
u8 wo = (rbuf == NULL || rlen == 0); /* write-only */
if (wo) {
request = AU6610_REQ_I2C_WRITE;
} else { /* rw */
request = AU6610_REQ_I2C_READ;
}
return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen);
}
/* I2C */
static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
if (num > 2)
return -EINVAL;
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, msg[i+1].buf,
msg[i+1].len) < 0)
break;
i++;
} else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, NULL, 0) < 0)
break;
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 au6610_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm au6610_i2c_algo = {
.master_xfer = au6610_i2c_xfer,
.functionality = au6610_i2c_func,
};
/* Callbacks for DVB USB */
static int au6610_identify_state(struct usb_device *udev,
struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc,
int *cold)
{
*cold = 0;
return 0;
}
static struct zl10353_config au6610_zl10353_config = {
.demod_address = 0x1e,
.no_tuner = 1,
.parallel_ts = 1,
};
static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
{
if ((adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
&adap->dev->i2c_adap)) != NULL) {
return 0;
}
return -EIO;
}
static struct qt1010_config au6610_qt1010_config = {
.i2c_address = 0xc4
};
static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
adap->fe, &adap->dev->i2c_adap,
&au6610_qt1010_config) == NULL ? -ENODEV : 0;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties au6610_properties;
static int au6610_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dvb_usb_device *d;
struct usb_host_interface *alt;
int ret;
if (intf->num_altsetting < AU6610_ALTSETTING_COUNT)
return -ENODEV;
if ((ret = dvb_usb_device_init(intf, &au6610_properties, THIS_MODULE, &d)) == 0) {
alt = usb_altnum_to_altsetting(intf, AU6610_ALTSETTING);
if (alt == NULL) {
deb_rc("no alt found!\n");
return -ENODEV;
}
ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
alt->desc.bAlternateSetting);
}
return ret;
}
static struct usb_device_id au6610_table [] = {
{ USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, au6610_table);
static struct dvb_usb_device_properties au6610_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.size_of_priv = 0,
.identify_state = au6610_identify_state,
.num_adapters = 1,
.adapter = {
{
.frontend_attach = au6610_zl10353_frontend_attach,
.tuner_attach = au6610_qt1010_tuner_attach,
.stream = {
.type = USB_ISOC,
.count = 5,
.endpoint = 0x82,
.u = {
.isoc = {
.framesperurb = 40,
.framesize = 942, /* maximum packet size */
.interval = 1.25, /* 125 us */
}
}
},
}
},
.i2c_algo = &au6610_i2c_algo,
.num_device_descs = 1,
.devices = {
{
"Sigmatek DVB-110 DVB-T USB2.0",
{ &au6610_table[0], NULL },
{ NULL },
},
}
};
static struct usb_driver au6610_driver = {
.name = "dvb_usb_au6610",
.probe = au6610_probe,
.disconnect = dvb_usb_device_exit,
.id_table = au6610_table,
};
/* module stuff */
static int __init au6610_module_init(void)
{
int ret;
if ((ret = usb_register(&au6610_driver))) {
err("usb_register failed. Error number %d", ret);
return ret;
}
return 0;
}
static void __exit au6610_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&au6610_driver);
}
module_init (au6610_module_init);
module_exit (au6610_module_exit);
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_DESCRIPTION("Driver Sigmatek DVB-110 DVB-T USB2.0 / AU6610");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,19 @@
#ifndef _DVB_USB_AU6610_H_
#define _DVB_USB_AU6610_H_
#define DVB_USB_LOG_PREFIX "au6610"
#include "dvb-usb.h"
#define deb_rc(args...) dprintk(dvb_usb_au6610_debug,0x01,args)
#define AU6610_REQ_I2C_WRITE 0x14
#define AU6610_REQ_I2C_READ 0x13
#define AU6610_REQ_USB_WRITE 0x16
#define AU6610_REQ_USB_READ 0x15
#define AU6610_USB_TIMEOUT 1000
#define AU6610_ALTSETTING_COUNT 6
#define AU6610_ALTSETTING 5
#endif

View File

@@ -0,0 +1,808 @@
/* DVB USB compliant linux driver for Conexant USB reference design.
*
* The Conexant reference design I saw on their website was only for analogue
* capturing (using the cx25842). The box I took to write this driver (reverse
* engineered) is the one labeled Medion MD95700. In addition to the cx25842
* for analogue capturing it also has a cx22702 DVB-T demodulator on the main
* board. Besides it has a atiremote (X10) and a USB2.0 hub onboard.
*
* Maybe it is a little bit premature to call this driver cxusb, but I assume
* the USB protocol is identical or at least inherited from the reference
* design, so it can be reused for the "analogue-only" device (if it will
* appear at all).
*
* TODO: Use the cx25840-driver for the analogue part
*
* Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
* Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org)
* Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "cxusb.h"
#include "cx22702.h"
#include "lgdt330x.h"
#include "lgh06xf.h"
#include "mt352.h"
#include "mt352_priv.h"
#include "zl10353.h"
/* debug */
int dvb_usb_cxusb_debug;
module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
static int cxusb_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 sndbuf[1+wlen];
memset(sndbuf, 0, 1+wlen);
sndbuf[0] = cmd;
memcpy(&sndbuf[1], wbuf, wlen);
if (wo)
dvb_usb_generic_write(d, sndbuf, 1+wlen);
else
dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
return 0;
}
/* GPIO */
static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
{
struct cxusb_state *st = d->priv;
u8 o[2], i;
if (st->gpio_write_state[GPIO_TUNER] == onoff)
return;
o[0] = GPIO_TUNER;
o[1] = onoff;
cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
if (i != 0x01)
deb_info("gpio_write failed.\n");
st->gpio_write_state[GPIO_TUNER] = onoff;
}
/* I2C */
static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
if (num > 2)
warn("more than two i2c messages at a time is not handled yet. TODO.");
for (i = 0; i < num; i++) {
if (d->udev->descriptor.idVendor == USB_VID_MEDION)
switch (msg[i].addr) {
case 0x63:
cxusb_gpio_tuner(d, 0);
break;
default:
cxusb_gpio_tuner(d, 1);
break;
}
/* read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
u8 obuf[3+msg[i].len], ibuf[1+msg[i+1].len];
obuf[0] = msg[i].len;
obuf[1] = msg[i+1].len;
obuf[2] = msg[i].addr;
memcpy(&obuf[3], msg[i].buf, msg[i].len);
if (cxusb_ctrl_msg(d, CMD_I2C_READ,
obuf, 3+msg[i].len,
ibuf, 1+msg[i+1].len) < 0)
break;
if (ibuf[0] != 0x08)
deb_i2c("i2c read may have failed\n");
memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
i++;
} else { /* write */
u8 obuf[2+msg[i].len], ibuf;
obuf[0] = msg[i].addr;
obuf[1] = msg[i].len;
memcpy(&obuf[2], msg[i].buf, msg[i].len);
if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf,
2+msg[i].len, &ibuf,1) < 0)
break;
if (ibuf != 0x08)
deb_i2c("i2c write may have failed\n");
}
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 cxusb_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm cxusb_i2c_algo = {
.master_xfer = cxusb_i2c_xfer,
.functionality = cxusb_i2c_func,
};
static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff)
{
u8 b = 0;
if (onoff)
return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0);
else
return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0);
}
static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff)
{
u8 b = 0;
if (onoff)
return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0);
else
return 0;
}
static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
u8 buf[2] = { 0x03, 0x00 };
if (onoff)
cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, buf, 2, NULL, 0);
else
cxusb_ctrl_msg(adap->dev, CMD_STREAMING_OFF, NULL, 0, NULL, 0);
return 0;
}
static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
u8 ircode[4];
int i;
cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4);
*event = 0;
*state = REMOTE_NO_KEY_PRESSED;
for (i = 0; i < d->props.rc_key_map_size; i++) {
if (keymap[i].custom == ircode[2] &&
keymap[i].data == ircode[3]) {
*event = keymap[i].event;
*state = REMOTE_KEY_PRESSED;
return 0;
}
}
return 0;
}
static struct dvb_usb_rc_key dvico_mce_rc_keys[] = {
{ 0xfe, 0x02, KEY_TV },
{ 0xfe, 0x0e, KEY_MP3 },
{ 0xfe, 0x1a, KEY_DVD },
{ 0xfe, 0x1e, KEY_FAVORITES },
{ 0xfe, 0x16, KEY_SETUP },
{ 0xfe, 0x46, KEY_POWER2 },
{ 0xfe, 0x0a, KEY_EPG },
{ 0xfe, 0x49, KEY_BACK },
{ 0xfe, 0x4d, KEY_MENU },
{ 0xfe, 0x51, KEY_UP },
{ 0xfe, 0x5b, KEY_LEFT },
{ 0xfe, 0x5f, KEY_RIGHT },
{ 0xfe, 0x53, KEY_DOWN },
{ 0xfe, 0x5e, KEY_OK },
{ 0xfe, 0x59, KEY_INFO },
{ 0xfe, 0x55, KEY_TAB },
{ 0xfe, 0x0f, KEY_PREVIOUSSONG },/* Replay */
{ 0xfe, 0x12, KEY_NEXTSONG }, /* Skip */
{ 0xfe, 0x42, KEY_ENTER }, /* Windows/Start */
{ 0xfe, 0x15, KEY_VOLUMEUP },
{ 0xfe, 0x05, KEY_VOLUMEDOWN },
{ 0xfe, 0x11, KEY_CHANNELUP },
{ 0xfe, 0x09, KEY_CHANNELDOWN },
{ 0xfe, 0x52, KEY_CAMERA },
{ 0xfe, 0x5a, KEY_TUNER }, /* Live */
{ 0xfe, 0x19, KEY_OPEN },
{ 0xfe, 0x0b, KEY_1 },
{ 0xfe, 0x17, KEY_2 },
{ 0xfe, 0x1b, KEY_3 },
{ 0xfe, 0x07, KEY_4 },
{ 0xfe, 0x50, KEY_5 },
{ 0xfe, 0x54, KEY_6 },
{ 0xfe, 0x48, KEY_7 },
{ 0xfe, 0x4c, KEY_8 },
{ 0xfe, 0x58, KEY_9 },
{ 0xfe, 0x13, KEY_ANGLE }, /* Aspect */
{ 0xfe, 0x03, KEY_0 },
{ 0xfe, 0x1f, KEY_ZOOM },
{ 0xfe, 0x43, KEY_REWIND },
{ 0xfe, 0x47, KEY_PLAYPAUSE },
{ 0xfe, 0x4f, KEY_FASTFORWARD },
{ 0xfe, 0x57, KEY_MUTE },
{ 0xfe, 0x0d, KEY_STOP },
{ 0xfe, 0x01, KEY_RECORD },
{ 0xfe, 0x4e, KEY_POWER },
};
static struct dvb_usb_rc_key dvico_portable_rc_keys[] = {
{ 0xfc, 0x02, KEY_SETUP }, /* Profile */
{ 0xfc, 0x43, KEY_POWER2 },
{ 0xfc, 0x06, KEY_EPG },
{ 0xfc, 0x5a, KEY_BACK },
{ 0xfc, 0x05, KEY_MENU },
{ 0xfc, 0x47, KEY_INFO },
{ 0xfc, 0x01, KEY_TAB },
{ 0xfc, 0x42, KEY_PREVIOUSSONG },/* Replay */
{ 0xfc, 0x49, KEY_VOLUMEUP },
{ 0xfc, 0x09, KEY_VOLUMEDOWN },
{ 0xfc, 0x54, KEY_CHANNELUP },
{ 0xfc, 0x0b, KEY_CHANNELDOWN },
{ 0xfc, 0x16, KEY_CAMERA },
{ 0xfc, 0x40, KEY_TUNER }, /* ATV/DTV */
{ 0xfc, 0x45, KEY_OPEN },
{ 0xfc, 0x19, KEY_1 },
{ 0xfc, 0x18, KEY_2 },
{ 0xfc, 0x1b, KEY_3 },
{ 0xfc, 0x1a, KEY_4 },
{ 0xfc, 0x58, KEY_5 },
{ 0xfc, 0x59, KEY_6 },
{ 0xfc, 0x15, KEY_7 },
{ 0xfc, 0x14, KEY_8 },
{ 0xfc, 0x17, KEY_9 },
{ 0xfc, 0x44, KEY_ANGLE }, /* Aspect */
{ 0xfc, 0x55, KEY_0 },
{ 0xfc, 0x07, KEY_ZOOM },
{ 0xfc, 0x0a, KEY_REWIND },
{ 0xfc, 0x08, KEY_PLAYPAUSE },
{ 0xfc, 0x4b, KEY_FASTFORWARD },
{ 0xfc, 0x5b, KEY_MUTE },
{ 0xfc, 0x04, KEY_STOP },
{ 0xfc, 0x56, KEY_RECORD },
{ 0xfc, 0x57, KEY_POWER },
{ 0xfc, 0x41, KEY_UNKNOWN }, /* INPUT */
{ 0xfc, 0x00, KEY_UNKNOWN }, /* HD */
};
static int cxusb_dee1601_demod_init(struct dvb_frontend* fe)
{
static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 };
static u8 reset [] = { RESET, 0x80 };
static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 };
static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
mt352_write(fe, clock_config, sizeof(clock_config));
udelay(200);
mt352_write(fe, reset, sizeof(reset));
mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
mt352_write(fe, agc_cfg, sizeof(agc_cfg));
mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
return 0;
}
static int cxusb_mt352_demod_init(struct dvb_frontend* fe)
{ /* used in both lgz201 and th7579 */
static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x29 };
static u8 reset [] = { RESET, 0x80 };
static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
static u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 };
static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
mt352_write(fe, clock_config, sizeof(clock_config));
udelay(200);
mt352_write(fe, reset, sizeof(reset));
mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
mt352_write(fe, agc_cfg, sizeof(agc_cfg));
mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
return 0;
}
static struct cx22702_config cxusb_cx22702_config = {
.demod_address = 0x63,
.output_mode = CX22702_PARALLEL_OUTPUT,
};
static struct lgdt330x_config cxusb_lgdt3303_config = {
.demod_address = 0x0e,
.demod_chip = LGDT3303,
};
static struct mt352_config cxusb_dee1601_config = {
.demod_address = 0x0f,
.demod_init = cxusb_dee1601_demod_init,
};
static struct zl10353_config cxusb_zl10353_dee1601_config = {
.demod_address = 0x0f,
.parallel_ts = 1,
};
static struct mt352_config cxusb_mt352_config = {
/* used in both lgz201 and th7579 */
.demod_address = 0x0f,
.demod_init = cxusb_mt352_demod_init,
};
/* Callbacks for DVB USB */
static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
u8 bpll[4] = { 0x0b, 0xdc, 0x9c, 0xa0 };
adap->pll_addr = 0x61;
memcpy(adap->pll_init, bpll, 4);
adap->pll_desc = &dvb_pll_fmd1216me;
adap->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c;
adap->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c;
return 0;
}
static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe, 0x61,
NULL, &dvb_pll_thomson_dtt7579);
return 0;
}
static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, &dvb_pll_lg_z201);
return 0;
}
static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe, 0x60,
NULL, &dvb_pll_thomson_dtt7579);
return 0;
}
static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap);
return 0;
}
static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
{
u8 b;
if (usb_set_interface(adap->dev->udev, 0, 6) < 0)
err("set interface failed");
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1);
if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
&adap->dev->i2c_adap)) != NULL)
return 0;
return -EIO;
}
static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
{
if (usb_set_interface(adap->dev->udev, 0, 7) < 0)
err("set interface failed");
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config,
&adap->dev->i2c_adap)) != NULL)
return 0;
return -EIO;
}
static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap)
{
/* used in both lgz201 and th7579 */
if (usb_set_interface(adap->dev->udev, 0, 0) < 0)
err("set interface failed");
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
&adap->dev->i2c_adap)) != NULL)
return 0;
return -EIO;
}
static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
{
if (usb_set_interface(adap->dev->udev, 0, 0) < 0)
err("set interface failed");
cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config,
&adap->dev->i2c_adap)) != NULL) ||
((adap->fe = dvb_attach(zl10353_attach,
&cxusb_zl10353_dee1601_config,
&adap->dev->i2c_adap)) != NULL))
return 0;
return -EIO;
}
/*
* DViCO bluebird firmware needs the "warm" product ID to be patched into the
* firmware file before download.
*/
#define BLUEBIRD_01_ID_OFFSET 6638
static int bluebird_patch_dvico_firmware_download(struct usb_device *udev,
const struct firmware *fw)
{
if (fw->size < BLUEBIRD_01_ID_OFFSET + 4)
return -EINVAL;
if (fw->data[BLUEBIRD_01_ID_OFFSET] == (USB_VID_DVICO & 0xff) &&
fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) {
fw->data[BLUEBIRD_01_ID_OFFSET + 2] =
le16_to_cpu(udev->descriptor.idProduct) + 1;
fw->data[BLUEBIRD_01_ID_OFFSET + 3] =
le16_to_cpu(udev->descriptor.idProduct) >> 8;
return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
}
return -EINVAL;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties cxusb_medion_properties;
static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties;
static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
static int cxusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
if (dvb_usb_device_init(intf,&cxusb_medion_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_dee1601_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_lgz201_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&cxusb_bluebird_dtt7579_properties,THIS_MODULE,NULL) == 0) {
return 0;
}
return -EINVAL;
}
static struct usb_device_id cxusb_table [] = {
{ USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, cxusb_table);
static struct dvb_usb_device_properties cxusb_medion_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.size_of_priv = sizeof(struct cxusb_state),
.num_adapters = 1,
.adapter = {
{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_cx22702_frontend_attach,
.tuner_attach = cxusb_fmd1216me_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 5,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 8192,
}
}
},
},
},
.power_ctrl = cxusb_power_ctrl,
.i2c_algo = &cxusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "Medion MD95700 (MDUSBTV-HYBRID)",
{ NULL },
{ &cxusb_table[0], NULL },
},
}
};
static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.firmware = "dvb-usb-bluebird-01.fw",
.download_firmware = bluebird_patch_dvico_firmware_download,
/* use usb alt setting 0 for EP4 transfer (dvb-t),
use usb alt setting 7 for EP2 transfer (atsc) */
.size_of_priv = sizeof(struct cxusb_state),
.num_adapters = 1,
.adapter = {
{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_lgdt3303_frontend_attach,
.tuner_attach = cxusb_lgh064f_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 5,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 8192,
}
}
},
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
.i2c_algo = &cxusb_i2c_algo,
.rc_interval = 100,
.rc_key_map = dvico_portable_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys),
.rc_query = cxusb_rc_query,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "DViCO FusionHDTV5 USB Gold",
{ &cxusb_table[1], NULL },
{ &cxusb_table[2], NULL },
},
}
};
static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.firmware = "dvb-usb-bluebird-01.fw",
.download_firmware = bluebird_patch_dvico_firmware_download,
/* use usb alt setting 0 for EP4 transfer (dvb-t),
use usb alt setting 7 for EP2 transfer (atsc) */
.size_of_priv = sizeof(struct cxusb_state),
.num_adapters = 1,
.adapter = {
{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_dee1601_frontend_attach,
.tuner_attach = cxusb_dee1601_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 5,
.endpoint = 0x04,
.u = {
.bulk = {
.buffersize = 8192,
}
}
},
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
.i2c_algo = &cxusb_i2c_algo,
.rc_interval = 150,
.rc_key_map = dvico_mce_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dvico_mce_rc_keys),
.rc_query = cxusb_rc_query,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 3,
.devices = {
{ "DViCO FusionHDTV DVB-T Dual USB",
{ &cxusb_table[3], NULL },
{ &cxusb_table[4], NULL },
},
{ "DigitalNow DVB-T Dual USB",
{ &cxusb_table[9], NULL },
{ &cxusb_table[10], NULL },
},
{ "DViCO FusionHDTV DVB-T Dual Digital 2",
{ &cxusb_table[11], NULL },
{ &cxusb_table[12], NULL },
},
}
};
static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.firmware = "dvb-usb-bluebird-01.fw",
.download_firmware = bluebird_patch_dvico_firmware_download,
/* use usb alt setting 0 for EP4 transfer (dvb-t),
use usb alt setting 7 for EP2 transfer (atsc) */
.size_of_priv = sizeof(struct cxusb_state),
.num_adapters = 2,
.adapter = {
{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_mt352_frontend_attach,
.tuner_attach = cxusb_lgz201_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 5,
.endpoint = 0x04,
.u = {
.bulk = {
.buffersize = 8192,
}
}
},
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
.i2c_algo = &cxusb_i2c_algo,
.rc_interval = 100,
.rc_key_map = dvico_portable_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys),
.rc_query = cxusb_rc_query,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "DViCO FusionHDTV DVB-T USB (LGZ201)",
{ &cxusb_table[5], NULL },
{ &cxusb_table[6], NULL },
},
}
};
static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.firmware = "dvb-usb-bluebird-01.fw",
.download_firmware = bluebird_patch_dvico_firmware_download,
/* use usb alt setting 0 for EP4 transfer (dvb-t),
use usb alt setting 7 for EP2 transfer (atsc) */
.size_of_priv = sizeof(struct cxusb_state),
.num_adapters = 1,
.adapter = {
{
.streaming_ctrl = cxusb_streaming_ctrl,
.frontend_attach = cxusb_mt352_frontend_attach,
.tuner_attach = cxusb_dtt7579_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 5,
.endpoint = 0x04,
.u = {
.bulk = {
.buffersize = 8192,
}
}
},
},
},
.power_ctrl = cxusb_bluebird_power_ctrl,
.i2c_algo = &cxusb_i2c_algo,
.rc_interval = 100,
.rc_key_map = dvico_portable_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys),
.rc_query = cxusb_rc_query,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "DViCO FusionHDTV DVB-T USB (TH7579)",
{ &cxusb_table[7], NULL },
{ &cxusb_table[8], NULL },
},
}
};
static struct usb_driver cxusb_driver = {
.name = "dvb_usb_cxusb",
.probe = cxusb_probe,
.disconnect = dvb_usb_device_exit,
.id_table = cxusb_table,
};
/* module stuff */
static int __init cxusb_module_init(void)
{
int result;
if ((result = usb_register(&cxusb_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit cxusb_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&cxusb_driver);
}
module_init (cxusb_module_init);
module_exit (cxusb_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design");
MODULE_VERSION("1.0-alpha");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,35 @@
#ifndef _DVB_USB_CXUSB_H_
#define _DVB_USB_CXUSB_H_
#define DVB_USB_LOG_PREFIX "cxusb"
#include "dvb-usb.h"
extern int dvb_usb_cxusb_debug;
#define deb_info(args...) dprintk(dvb_usb_cxusb_debug,0x01,args)
#define deb_i2c(args...) if (d->udev->descriptor.idVendor == USB_VID_MEDION) \
dprintk(dvb_usb_cxusb_debug,0x01,args)
/* usb commands - some of it are guesses, don't have a reference yet */
#define CMD_I2C_WRITE 0x08
#define CMD_I2C_READ 0x09
#define CMD_GPIO_READ 0x0d
#define CMD_GPIO_WRITE 0x0e
#define GPIO_TUNER 0x02
#define CMD_POWER_OFF 0xdc
#define CMD_POWER_ON 0xde
#define CMD_STREAMING_ON 0x36
#define CMD_STREAMING_OFF 0x37
#define CMD_GET_IR_CODE 0x47
#define CMD_ANALOG 0x50
#define CMD_DIGITAL 0x51
struct cxusb_state {
u8 gpio_write_state[3];
};
#endif

View File

@@ -0,0 +1,54 @@
/* Linux driver for devices based on the DiBcom DiB0700 USB bridge
*
* 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, version 2.
*
* Copyright (C) 2005-6 DiBcom, SA
*/
#ifndef _DIB0700_H_
#define _DIB0700_H_
#define DVB_USB_LOG_PREFIX "dib0700"
#include "dvb-usb.h"
#include "dib07x0.h"
extern int dvb_usb_dib0700_debug;
#define deb_info(args...) dprintk(dvb_usb_dib0700_debug,0x01,args)
#define deb_fw(args...) dprintk(dvb_usb_dib0700_debug,0x02,args)
#define deb_fwdata(args...) dprintk(dvb_usb_dib0700_debug,0x04,args)
#define deb_data(args...) dprintk(dvb_usb_dib0700_debug,0x08,args)
#define REQUEST_I2C_READ 0x2
#define REQUEST_I2C_WRITE 0x3
#define REQUEST_POLL_RC 0x4
#define REQUEST_JUMPRAM 0x8
#define REQUEST_SET_CLOCK 0xB
#define REQUEST_SET_GPIO 0xC
#define REQUEST_ENABLE_VIDEO 0xF
// 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog)
// 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " )
#define REQUEST_GET_VERSION 0x15
struct dib0700_state {
u8 channel_state;
u16 mt2060_if1[2];
u8 is_dib7000pc;
};
extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val);
extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3);
extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw);
extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
extern struct i2c_algorithm dib0700_i2c_algo;
extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc, int *cold);
extern int dib0700_device_count;
extern struct dvb_usb_device_properties dib0700_devices[];
extern struct usb_device_id dib0700_usb_id_table[];
#endif

View File

@@ -0,0 +1,311 @@
/* Linux driver for devices based on the DiBcom DiB0700 USB bridge
*
* 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, version 2.
*
* Copyright (C) 2005-6 DiBcom, SA
*/
#include "dib0700.h"
/* debug */
int dvb_usb_dib0700_debug;
module_param_named(debug,dvb_usb_dib0700_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS);
/* expecting rx buffer: request data[0] data[1] ... data[2] */
static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen)
{
int status;
deb_data(">>> ");
debug_dump(tx,txlen,deb_data);
status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0),
tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen,
USB_CTRL_GET_TIMEOUT);
if (status != txlen)
deb_data("ep 0 write error (status = %d, len: %d)\n",status,txlen);
return status < 0 ? status : 0;
}
/* expecting tx buffer: request data[0] ... data[n] (n <= 4) */
static int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen)
{
u16 index, value;
int status;
if (txlen < 2) {
err("tx buffer length is smaller than 2. Makes no sense.");
return -EINVAL;
}
if (txlen > 4) {
err("tx buffer length is larger than 4. Not supported.");
return -EINVAL;
}
deb_data(">>> ");
debug_dump(tx,txlen,deb_data);
value = ((txlen - 2) << 8) | tx[1];
index = 0;
if (txlen > 2)
index |= (tx[2] << 8);
if (txlen > 3)
index |= tx[3];
/* think about swapping here */
value = le16_to_cpu(value);
index = le16_to_cpu(index);
status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0],
USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen,
USB_CTRL_GET_TIMEOUT);
if (status < 0)
deb_info("ep 0 read error (status = %d)\n",status);
deb_data("<<< ");
debug_dump(rx,rxlen,deb_data);
return status; /* length in case of success */
}
int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val)
{
u8 buf[3] = { REQUEST_SET_GPIO, gpio, ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6) };
return dib0700_ctrl_wr(d,buf,3);
}
/*
* I2C master xfer function
*/
static int dib0700_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i,len;
u8 buf[255];
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
/* fill in the address */
buf[1] = (msg[i].addr << 1);
/* fill the buffer */
memcpy(&buf[2], msg[i].buf, msg[i].len);
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
buf[0] = REQUEST_I2C_READ;
buf[1] |= 1;
/* special thing in the current firmware: when length is zero the read-failed */
if ((len = dib0700_ctrl_rd(d, buf, msg[i].len + 2, msg[i+1].buf, msg[i+1].len)) <= 0) {
deb_info("I2C read failed on address %x\n", msg[i].addr);
break;
}
msg[i+1].len = len;
i++;
} else {
buf[0] = REQUEST_I2C_WRITE;
if (dib0700_ctrl_wr(d, buf, msg[i].len + 2) < 0)
break;
}
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
struct i2c_algorithm dib0700_i2c_algo = {
.master_xfer = dib0700_i2c_xfer,
.functionality = dib0700_i2c_func,
};
int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc, int *cold)
{
u8 b[16];
s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev,0),
REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT);
deb_info("FW GET_VERSION length: %d\n",ret);
*cold = ret <= 0;
deb_info("cold: %d\n", *cold);
return 0;
}
static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv,
u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
{
u8 b[10];
b[0] = REQUEST_SET_CLOCK;
b[1] = (en_pll << 7) | (pll_src << 6) | (pll_range << 5) | (clock_gpio3 << 4);
b[2] = (pll_prediv >> 8) & 0xff; // MSB
b[3] = pll_prediv & 0xff; // LSB
b[4] = (pll_loopdiv >> 8) & 0xff; // MSB
b[5] = pll_loopdiv & 0xff; // LSB
b[6] = (free_div >> 8) & 0xff; // MSB
b[7] = free_div & 0xff; // LSB
b[8] = (dsuScaler >> 8) & 0xff; // MSB
b[9] = dsuScaler & 0xff; // LSB
return dib0700_ctrl_wr(d, b, 10);
}
int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3)
{
switch (clk_MHz) {
case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break;
default: return -EINVAL;
}
return 0;
}
static int dib0700_jumpram(struct usb_device *udev, u32 address)
{
int ret, actlen;
u8 buf[8] = { REQUEST_JUMPRAM, 0, 0, 0,
(address >> 24) & 0xff,
(address >> 16) & 0xff,
(address >> 8) & 0xff,
address & 0xff };
if ((ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x01),buf,8,&actlen,1000)) < 0) {
deb_fw("jumpram to 0x%x failed\n",address);
return ret;
}
if (actlen != 8) {
deb_fw("jumpram to 0x%x failed\n",address);
return -EIO;
}
return 0;
}
int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw)
{
struct hexline hx;
int pos = 0, ret, act_len;
u8 buf[260];
while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) {
deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n",hx.addr, hx.len, hx.chk);
buf[0] = hx.len;
buf[1] = (hx.addr >> 8) & 0xff;
buf[2] = hx.addr & 0xff;
buf[3] = hx.type;
memcpy(&buf[4],hx.data,hx.len);
buf[4+hx.len] = hx.chk;
ret = usb_bulk_msg(udev,
usb_sndbulkpipe(udev, 0x01),
buf,
hx.len + 5,
&act_len,
1000);
if (ret < 0) {
err("firmware download failed at %d with %d",pos,ret);
return ret;
}
}
if (ret == 0) {
/* start the firmware */
if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
info("firmware started successfully.");
msleep(500);
}
} else
ret = -EIO;
return ret;
}
int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct dib0700_state *st = adap->dev->priv;
u8 b[4];
b[0] = REQUEST_ENABLE_VIDEO;
b[1] = 0x00;
b[2] = (0x01 << 4); /* Master mode */
b[3] = 0x00;
deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
if (onoff)
st->channel_state |= 1 << adap->id;
else
st->channel_state &= ~(1 << adap->id);
b[2] |= st->channel_state;
if (st->channel_state) /* if at least one channel is active */
b[1] = (0x01 << 4) | 0x00;
deb_info("data for streaming: %x %x\n",b[1],b[2]);
return dib0700_ctrl_wr(adap->dev, b, 4);
}
static int dib0700_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int i;
for (i = 0; i < dib0700_device_count; i++)
if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE, NULL) == 0)
return 0;
return -ENODEV;
}
static struct usb_driver dib0700_driver = {
.name = "dvb_usb_dib0700",
.probe = dib0700_probe,
.disconnect = dvb_usb_device_exit,
.id_table = dib0700_usb_id_table,
};
/* module stuff */
static int __init dib0700_module_init(void)
{
int result;
info("loaded with support for %d different device-types", dib0700_device_count);
if ((result = usb_register(&dib0700_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit dib0700_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&dib0700_driver);
}
module_init (dib0700_module_init);
module_exit (dib0700_module_exit);
MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,378 @@
/* Linux driver for devices based on the DiBcom DiB0700 USB bridge
*
* 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, version 2.
*
* Copyright (C) 2005-6 DiBcom, SA
*/
#include "dib0700.h"
#include "dib3000mc.h"
#include "dib7000m.h"
#include "dib7000p.h"
#include "mt2060.h"
static int force_lna_activation;
module_param(force_lna_activation, int, 0644);
MODULE_PARM_DESC(force_lna_activation, "force the activation of Low-Noise-Amplifyer(s) (LNA), "
"if applicable for the device (default: 0=automatic/off).");
/* Hauppauge Nova-T 500
* has a LNA on GPIO0 which is enabled by setting 1 */
static struct mt2060_config bristol_mt2060_config[2] = {
{
.i2c_address = 0x60,
.clock_out = 3,
}, {
.i2c_address = 0x61,
}
};
static struct dibx000_agc_config bristol_dib3000p_mt2060_agc_config = {
.band_caps = BAND_VHF | BAND_UHF,
.setup = (1 << 8) | (5 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (2 << 0),
.agc1_max = 42598,
.agc1_min = 17694,
.agc2_max = 45875,
.agc2_min = 0,
.agc1_pt1 = 0,
.agc1_pt2 = 59,
.agc1_slope1 = 0,
.agc1_slope2 = 69,
.agc2_pt1 = 0,
.agc2_pt2 = 59,
.agc2_slope1 = 111,
.agc2_slope2 = 28,
};
static struct dib3000mc_config bristol_dib3000mc_config[2] = {
{ .agc = &bristol_dib3000p_mt2060_agc_config,
.max_time = 0x196,
.ln_adc_level = 0x1cc7,
.output_mpeg2_in_188_bytes = 1,
},
{ .agc = &bristol_dib3000p_mt2060_agc_config,
.max_time = 0x196,
.ln_adc_level = 0x1cc7,
.output_mpeg2_in_188_bytes = 1,
}
};
static int bristol_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_state *st = adap->dev->priv;
if (adap->id == 0) {
dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10);
dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10);
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10);
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10);
if (force_lna_activation)
dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
else
dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 0);
if (dib3000mc_i2c_enumeration(&adap->dev->i2c_adap, 2, DEFAULT_DIB3000P_I2C_ADDRESS, bristol_dib3000mc_config) != 0) {
dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10);
return -ENODEV;
}
}
st->mt2060_if1[adap->id] = 1220;
return (adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap,
(10 + adap->id) << 1, &bristol_dib3000mc_config[adap->id])) == NULL ? -ENODEV : 0;
}
static int bristol_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_state *st = adap->dev->priv;
struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1);
return dvb_attach(mt2060_attach,adap->fe, tun_i2c, &bristol_mt2060_config[adap->id],
st->mt2060_if1[adap->id]) == NULL ? -ENODEV : 0;
}
/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = {
BAND_UHF | BAND_VHF, // band_caps
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
* P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
(0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
712, // inv_gain
41, // time_stabiliz
0, // alpha_level
118, // thlock
0, // wbd_inv
4095, // wbd_ref
0, // wbd_sel
0, // wbd_alpha
42598, // agc1_max
17694, // agc1_min
45875, // agc2_max
2621, // agc2_min
0, // agc1_pt1
76, // agc1_pt2
139, // agc1_pt3
52, // agc1_slope1
59, // agc1_slope2
107, // agc2_pt1
172, // agc2_pt2
57, // agc2_slope1
70, // agc2_slope2
21, // alpha_mant
25, // alpha_exp
28, // beta_mant
48, // beta_exp
1, // perform_agc_softsplit
{ 0, // split_min
107, // split_max
51800, // global_split_min
24700 // global_split_max
},
};
static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = {
BAND_UHF | BAND_VHF,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
* P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
(0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
712, // inv_gain
41, // time_stabiliz
0, // alpha_level
118, // thlock
0, // wbd_inv
4095, // wbd_ref
0, // wbd_sel
0, // wbd_alpha
42598, // agc1_max
16384, // agc1_min
42598, // agc2_max
0, // agc2_min
0, // agc1_pt1
137, // agc1_pt2
255, // agc1_pt3
0, // agc1_slope1
255, // agc1_slope2
0, // agc2_pt1
0, // agc2_pt2
0, // agc2_slope1
41, // agc2_slope2
15, // alpha_mant
25, // alpha_exp
28, // beta_mant
48, // beta_exp
0, // perform_agc_softsplit
};
static struct dibx000_bandwidth_config stk7700p_pll_config = {
60000, 30000, // internal, sampling
1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
0, 0, 1, 1, 0, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
(3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k
60258167, // ifreq
20452225, // timf
};
static struct dib7000m_config stk7700p_dib7000m_config = {
.dvbt_mode = 1,
.output_mpeg2_in_188_bytes = 1,
.quartz_direct = 1,
.agc_config_count = 1,
.agc = &stk7700p_7000m_mt2060_agc_config,
.bw = &stk7700p_pll_config,
.gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB7000M_GPIO_DEFAULT_VALUES,
.gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS,
};
static struct dib7000p_config stk7700p_dib7000p_config = {
.output_mpeg2_in_188_bytes = 1,
.agc = &stk7700p_7000p_mt2060_agc_config,
.bw = &stk7700p_pll_config,
.gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB7000M_GPIO_DEFAULT_VALUES,
.gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS,
};
static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_state *st = adap->dev->priv;
/* unless there is no real power management in DVB - we leave the device on GPIO6 */
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(50);
dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10);
dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10);
dib0700_ctrl_clock(adap->dev, 72, 1);
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(100);
dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
st->mt2060_if1[0] = 1220;
if (dib7000pc_detection(&adap->dev->i2c_adap)) {
adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config);
st->is_dib7000pc = 1;
} else
adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config);
return adap->fe == NULL ? -ENODEV : 0;
}
static struct mt2060_config stk7700p_mt2060_config = {
0x60
};
static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_state *st = adap->dev->priv;
struct i2c_adapter *tun_i2c;
if (st->is_dib7000pc)
tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
else
tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config,
st->mt2060_if1[0]) == NULL ? -ENODEV : 0;
}
struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P_PC) },
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500) },
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) },
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) },
{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) },
{ USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500) },
{ USB_DEVICE(USB_VID_UNIWILL, USB_PID_UNIWILL_STK7700P) },
{ USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P) },
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_2) },
{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_2) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
#define DIB0700_DEFAULT_DEVICE_PROPERTIES \
.caps = DVB_USB_IS_AN_I2C_ADAPTER, \
.usb_ctrl = DEVICE_SPECIFIC, \
.firmware = "dvb-usb-dib0700-01.fw", \
.download_firmware = dib0700_download_firmware, \
.no_reconnect = 1, \
.size_of_priv = sizeof(struct dib0700_state), \
.i2c_algo = &dib0700_i2c_algo, \
.identify_state = dib0700_identify_state
#define DIB0700_DEFAULT_STREAMING_CONFIG(ep) \
.streaming_ctrl = dib0700_streaming_ctrl, \
.stream = { \
.type = USB_BULK, \
.count = 4, \
.endpoint = ep, \
.u = { \
.bulk = { \
.buffersize = 39480, \
} \
} \
}
struct dvb_usb_device_properties dib0700_devices[] = {
{
DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
.adapter = {
{
.frontend_attach = stk7700p_frontend_attach,
.tuner_attach = stk7700p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
},
},
.num_device_descs = 6,
.devices = {
{ "DiBcom STK7700P reference design",
{ &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] },
{ NULL },
},
{ "Hauppauge Nova-T Stick",
{ &dib0700_usb_id_table[4], &dib0700_usb_id_table[9], NULL },
{ NULL },
},
{ "AVerMedia AVerTV DVB-T Volar",
{ &dib0700_usb_id_table[5], &dib0700_usb_id_table[10] },
{ NULL },
},
{ "Compro Videomate U500",
{ &dib0700_usb_id_table[6], NULL },
{ NULL },
},
{ "Uniwill STK7700P based (Hama and others)",
{ &dib0700_usb_id_table[7], NULL },
{ NULL },
},
{ "Leadtek Winfast DTV Dongle (STK7700P based)",
{ &dib0700_usb_id_table[8], NULL },
{ NULL },
}
}
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 2,
.adapter = {
{
.frontend_attach = bristol_frontend_attach,
.tuner_attach = bristol_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}, {
.frontend_attach = bristol_frontend_attach,
.tuner_attach = bristol_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
}
},
.num_device_descs = 1,
.devices = {
{ "Hauppauge Nova-T 500 Dual DVB-T",
{ &dib0700_usb_id_table[2], &dib0700_usb_id_table[3], NULL },
{ NULL },
},
}
}
};
int dib0700_device_count = ARRAY_SIZE(dib0700_devices);

View File

@@ -0,0 +1,21 @@
#ifndef _DIB07X0_H_
#define _DIB07X0_H_
enum dib07x0_gpios {
GPIO0 = 0,
GPIO1 = 2,
GPIO2 = 3,
GPIO3 = 4,
GPIO4 = 5,
GPIO5 = 6,
GPIO6 = 8,
GPIO7 = 10,
GPIO8 = 11,
GPIO9 = 14,
GPIO10 = 15,
};
#define GPIO_IN 0
#define GPIO_OUT 1
#endif

View File

@@ -0,0 +1,452 @@
/* Common methods for dibusb-based-receivers.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dibusb.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info (|-able))." DVB_USB_DEBUG_STATUS);
MODULE_LICENSE("GPL");
#define deb_info(args...) dprintk(debug,0x01,args)
/* common stuff used by the different dibusb modules */
int dibusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.fifo_ctrl != NULL)
if (st->ops.fifo_ctrl(adap->fe,onoff)) {
err("error while controlling the fifo of the demod.");
return -ENODEV;
}
}
return 0;
}
EXPORT_SYMBOL(dibusb_streaming_ctrl);
int dibusb_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff)
{
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.pid_ctrl != NULL)
st->ops.pid_ctrl(adap->fe,index,pid,onoff);
}
return 0;
}
EXPORT_SYMBOL(dibusb_pid_filter);
int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
if (st->ops.pid_parse != NULL)
if (st->ops.pid_parse(adap->fe,onoff) < 0)
err("could not handle pid_parser");
}
return 0;
}
EXPORT_SYMBOL(dibusb_pid_filter_ctrl);
int dibusb_power_ctrl(struct dvb_usb_device *d, int onoff)
{
u8 b[3];
int ret;
b[0] = DIBUSB_REQ_SET_IOCTL;
b[1] = DIBUSB_IOCTL_CMD_POWER_MODE;
b[2] = onoff ? DIBUSB_IOCTL_POWER_WAKEUP : DIBUSB_IOCTL_POWER_SLEEP;
ret = dvb_usb_generic_write(d,b,3);
msleep(10);
return ret;
}
EXPORT_SYMBOL(dibusb_power_ctrl);
int dibusb2_0_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
u8 b[3] = { 0 };
int ret;
if ((ret = dibusb_streaming_ctrl(adap,onoff)) < 0)
return ret;
if (onoff) {
b[0] = DIBUSB_REQ_SET_STREAMING_MODE;
b[1] = 0x00;
if ((ret = dvb_usb_generic_write(adap->dev,b,2)) < 0)
return ret;
}
b[0] = DIBUSB_REQ_SET_IOCTL;
b[1] = onoff ? DIBUSB_IOCTL_CMD_ENABLE_STREAM : DIBUSB_IOCTL_CMD_DISABLE_STREAM;
return dvb_usb_generic_write(adap->dev,b,3);
}
EXPORT_SYMBOL(dibusb2_0_streaming_ctrl);
int dibusb2_0_power_ctrl(struct dvb_usb_device *d, int onoff)
{
if (onoff) {
u8 b[3] = { DIBUSB_REQ_SET_IOCTL, DIBUSB_IOCTL_CMD_POWER_MODE, DIBUSB_IOCTL_POWER_WAKEUP };
return dvb_usb_generic_write(d,b,3);
} else
return 0;
}
EXPORT_SYMBOL(dibusb2_0_power_ctrl);
static int dibusb_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */
/* write only ? */
int wo = (rbuf == NULL || rlen == 0),
len = 2 + wlen + (wo ? 0 : 2);
sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ;
sndbuf[1] = (addr << 1) | (wo ? 0 : 1);
memcpy(&sndbuf[2],wbuf,wlen);
if (!wo) {
sndbuf[wlen+2] = (rlen >> 8) & 0xff;
sndbuf[wlen+3] = rlen & 0xff;
}
return dvb_usb_generic_rw(d,sndbuf,len,rbuf,rlen,0);
}
/*
* I2C master xfer function
*/
static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len,
msg[i+1].buf,msg[i+1].len) < 0)
break;
i++;
} else
if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0)
break;
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 dibusb_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
struct i2c_algorithm dibusb_i2c_algo = {
.master_xfer = dibusb_i2c_xfer,
.functionality = dibusb_i2c_func,
};
EXPORT_SYMBOL(dibusb_i2c_algo);
int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val)
{
u8 wbuf[1] = { offs };
return dibusb_i2c_msg(d, 0x50, wbuf, 1, val, 1);
}
EXPORT_SYMBOL(dibusb_read_eeprom_byte);
/* 3000MC/P stuff */
// Config Adjacent channels Perf -cal22
static struct dibx000_agc_config dib3000p_mt2060_agc_config = {
.band_caps = BAND_VHF | BAND_UHF,
.setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0),
.agc1_max = 48497,
.agc1_min = 23593,
.agc2_max = 46531,
.agc2_min = 24904,
.agc1_pt1 = 0x65,
.agc1_pt2 = 0x69,
.agc1_slope1 = 0x51,
.agc1_slope2 = 0x27,
.agc2_pt1 = 0,
.agc2_pt2 = 0x33,
.agc2_slope1 = 0x35,
.agc2_slope2 = 0x37,
};
static struct dib3000mc_config stk3000p_dib3000p_config = {
&dib3000p_mt2060_agc_config,
.max_time = 0x196,
.ln_adc_level = 0x1cc7,
.output_mpeg2_in_188_bytes = 1,
.agc_command1 = 1,
.agc_command2 = 1,
};
static struct dibx000_agc_config dib3000p_panasonic_agc_config = {
.band_caps = BAND_VHF | BAND_UHF,
.setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0),
.agc1_max = 56361,
.agc1_min = 22282,
.agc2_max = 47841,
.agc2_min = 36045,
.agc1_pt1 = 0x3b,
.agc1_pt2 = 0x6b,
.agc1_slope1 = 0x55,
.agc1_slope2 = 0x1d,
.agc2_pt1 = 0,
.agc2_pt2 = 0x0a,
.agc2_slope1 = 0x95,
.agc2_slope2 = 0x1e,
};
static struct dib3000mc_config mod3000p_dib3000p_config = {
&dib3000p_panasonic_agc_config,
.max_time = 0x51,
.ln_adc_level = 0x1cc7,
.output_mpeg2_in_188_bytes = 1,
.agc_command1 = 1,
.agc_command2 = 1,
};
int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap)
{
if ((adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000P_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL ||
(adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000MC_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL) {
if (adap->priv != NULL) {
struct dibusb_state *st = adap->priv;
st->ops.pid_parse = dib3000mc_pid_parse;
st->ops.pid_ctrl = dib3000mc_pid_control;
}
return 0;
}
return -ENODEV;
}
EXPORT_SYMBOL(dibusb_dib3000mc_frontend_attach);
static struct mt2060_config stk3000p_mt2060_config = {
0x60
};
int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dibusb_state *st = adap->priv;
u8 a,b;
u16 if1 = 1220;
struct i2c_adapter *tun_i2c;
// First IF calibration for Liteon Sticks
if (adap->dev->udev->descriptor.idVendor == USB_VID_LITEON &&
adap->dev->udev->descriptor.idProduct == USB_PID_LITEON_DVB_T_WARM) {
dibusb_read_eeprom_byte(adap->dev,0x7E,&a);
dibusb_read_eeprom_byte(adap->dev,0x7F,&b);
if (a == 0x00)
if1 += b;
else if (a == 0x80)
if1 -= b;
else
warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b);
} else if (adap->dev->udev->descriptor.idVendor == USB_VID_DIBCOM &&
adap->dev->udev->descriptor.idProduct == USB_PID_DIBCOM_MOD3001_WARM) {
u8 desc;
dibusb_read_eeprom_byte(adap->dev, 7, &desc);
if (desc == 2) {
a = 127;
do {
dibusb_read_eeprom_byte(adap->dev, a, &desc);
a--;
} while (a > 7 && (desc == 0xff || desc == 0x00));
if (desc & 0x80)
if1 -= (0xff - desc);
else
if1 += desc;
}
}
tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1);
if (dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) {
/* not found - use panasonic pll parameters */
if (dvb_attach(dvb_pll_attach, adap->fe, 0x60, tun_i2c, &dvb_pll_env57h1xd5) == NULL)
return -ENOMEM;
} else {
st->mt2060_present = 1;
/* set the correct parameters for the dib3000p */
dib3000mc_set_config(adap->fe, &stk3000p_dib3000p_config);
}
return 0;
}
EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach);
/*
* common remote control stuff
*/
struct dvb_usb_rc_key dibusb_rc_keys[] = {
/* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */
{ 0x00, 0x16, KEY_POWER },
{ 0x00, 0x10, KEY_MUTE },
{ 0x00, 0x03, KEY_1 },
{ 0x00, 0x01, KEY_2 },
{ 0x00, 0x06, KEY_3 },
{ 0x00, 0x09, KEY_4 },
{ 0x00, 0x1d, KEY_5 },
{ 0x00, 0x1f, KEY_6 },
{ 0x00, 0x0d, KEY_7 },
{ 0x00, 0x19, KEY_8 },
{ 0x00, 0x1b, KEY_9 },
{ 0x00, 0x15, KEY_0 },
{ 0x00, 0x05, KEY_CHANNELUP },
{ 0x00, 0x02, KEY_CHANNELDOWN },
{ 0x00, 0x1e, KEY_VOLUMEUP },
{ 0x00, 0x0a, KEY_VOLUMEDOWN },
{ 0x00, 0x11, KEY_RECORD },
{ 0x00, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */
{ 0x00, 0x14, KEY_PLAY },
{ 0x00, 0x1a, KEY_STOP },
{ 0x00, 0x40, KEY_REWIND },
{ 0x00, 0x12, KEY_FASTFORWARD },
{ 0x00, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */
{ 0x00, 0x4c, KEY_PAUSE },
{ 0x00, 0x4d, KEY_SCREEN }, /* Full screen mode. */
{ 0x00, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
/* additional keys TwinHan VisionPlus, the Artec seemingly not have */
{ 0x00, 0x0c, KEY_CANCEL }, /* Cancel */
{ 0x00, 0x1c, KEY_EPG }, /* EPG */
{ 0x00, 0x00, KEY_TAB }, /* Tab */
{ 0x00, 0x48, KEY_INFO }, /* Preview */
{ 0x00, 0x04, KEY_LIST }, /* RecordList */
{ 0x00, 0x0f, KEY_TEXT }, /* Teletext */
/* Key codes for the KWorld/ADSTech/JetWay remote. */
{ 0x86, 0x12, KEY_POWER },
{ 0x86, 0x0f, KEY_SELECT }, /* source */
{ 0x86, 0x0c, KEY_UNKNOWN }, /* scan */
{ 0x86, 0x0b, KEY_EPG },
{ 0x86, 0x10, KEY_MUTE },
{ 0x86, 0x01, KEY_1 },
{ 0x86, 0x02, KEY_2 },
{ 0x86, 0x03, KEY_3 },
{ 0x86, 0x04, KEY_4 },
{ 0x86, 0x05, KEY_5 },
{ 0x86, 0x06, KEY_6 },
{ 0x86, 0x07, KEY_7 },
{ 0x86, 0x08, KEY_8 },
{ 0x86, 0x09, KEY_9 },
{ 0x86, 0x0a, KEY_0 },
{ 0x86, 0x18, KEY_ZOOM },
{ 0x86, 0x1c, KEY_UNKNOWN }, /* preview */
{ 0x86, 0x13, KEY_UNKNOWN }, /* snap */
{ 0x86, 0x00, KEY_UNDO },
{ 0x86, 0x1d, KEY_RECORD },
{ 0x86, 0x0d, KEY_STOP },
{ 0x86, 0x0e, KEY_PAUSE },
{ 0x86, 0x16, KEY_PLAY },
{ 0x86, 0x11, KEY_BACK },
{ 0x86, 0x19, KEY_FORWARD },
{ 0x86, 0x14, KEY_UNKNOWN }, /* pip */
{ 0x86, 0x15, KEY_ESC },
{ 0x86, 0x1a, KEY_UP },
{ 0x86, 0x1e, KEY_DOWN },
{ 0x86, 0x1f, KEY_LEFT },
{ 0x86, 0x1b, KEY_RIGHT },
/* Key codes for the DiBcom MOD3000 remote. */
{ 0x80, 0x00, KEY_MUTE },
{ 0x80, 0x01, KEY_TEXT },
{ 0x80, 0x02, KEY_HOME },
{ 0x80, 0x03, KEY_POWER },
{ 0x80, 0x04, KEY_RED },
{ 0x80, 0x05, KEY_GREEN },
{ 0x80, 0x06, KEY_YELLOW },
{ 0x80, 0x07, KEY_BLUE },
{ 0x80, 0x08, KEY_DVD },
{ 0x80, 0x09, KEY_AUDIO },
{ 0x80, 0x0a, KEY_MEDIA }, /* Pictures */
{ 0x80, 0x0b, KEY_VIDEO },
{ 0x80, 0x0c, KEY_BACK },
{ 0x80, 0x0d, KEY_UP },
{ 0x80, 0x0e, KEY_RADIO },
{ 0x80, 0x0f, KEY_EPG },
{ 0x80, 0x10, KEY_LEFT },
{ 0x80, 0x11, KEY_OK },
{ 0x80, 0x12, KEY_RIGHT },
{ 0x80, 0x13, KEY_UNKNOWN }, /* SAP */
{ 0x80, 0x14, KEY_TV },
{ 0x80, 0x15, KEY_DOWN },
{ 0x80, 0x16, KEY_MENU }, /* DVD Menu */
{ 0x80, 0x17, KEY_LAST },
{ 0x80, 0x18, KEY_RECORD },
{ 0x80, 0x19, KEY_STOP },
{ 0x80, 0x1a, KEY_PAUSE },
{ 0x80, 0x1b, KEY_PLAY },
{ 0x80, 0x1c, KEY_PREVIOUS },
{ 0x80, 0x1d, KEY_REWIND },
{ 0x80, 0x1e, KEY_FASTFORWARD },
{ 0x80, 0x1f, KEY_NEXT},
{ 0x80, 0x40, KEY_1 },
{ 0x80, 0x41, KEY_2 },
{ 0x80, 0x42, KEY_3 },
{ 0x80, 0x43, KEY_CHANNELUP },
{ 0x80, 0x44, KEY_4 },
{ 0x80, 0x45, KEY_5 },
{ 0x80, 0x46, KEY_6 },
{ 0x80, 0x47, KEY_CHANNELDOWN },
{ 0x80, 0x48, KEY_7 },
{ 0x80, 0x49, KEY_8 },
{ 0x80, 0x4a, KEY_9 },
{ 0x80, 0x4b, KEY_VOLUMEUP },
{ 0x80, 0x4c, KEY_CLEAR },
{ 0x80, 0x4d, KEY_0 },
{ 0x80, 0x4e, KEY_ENTER },
{ 0x80, 0x4f, KEY_VOLUMEDOWN },
};
EXPORT_SYMBOL(dibusb_rc_keys);
int dibusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
u8 key[5],cmd = DIBUSB_REQ_POLL_REMOTE;
dvb_usb_generic_rw(d,&cmd,1,key,5,0);
dvb_usb_nec_rc_key_to_event(d,key,event,state);
if (key[0] != 0)
deb_info("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]);
return 0;
}
EXPORT_SYMBOL(dibusb_rc_query);

View File

@@ -0,0 +1,444 @@
/* DVB USB compliant linux driver for mobile DVB-T USB devices based on
* reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-B)
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* based on GPL code from DiBcom, which has
* Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dibusb.h"
static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dib3000_config demod_cfg;
struct dibusb_state *st = adap->priv;
demod_cfg.demod_address = 0x8;
if ((adap->fe = dib3000mb_attach(&demod_cfg,&adap->dev->i2c_adap,&st->ops)) == NULL)
return -ENODEV;
adap->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c;
adap->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c;
adap->tuner_pass_ctrl = st->ops.tuner_pass_ctrl;
return 0;
}
static int dibusb_thomson_tuner_attach(struct dvb_usb_adapter *adap)
{
adap->pll_addr = 0x61;
adap->pll_desc = &dvb_pll_tua6010xs;
return 0;
}
/* Some of the Artec 1.1 device aren't equipped with the default tuner
* (Thomson Cable), but with a Panasonic ENV77H11D5. This function figures
* this out. */
static int dibusb_tuner_probe_and_attach(struct dvb_usb_adapter *adap)
{
u8 b[2] = { 0,0 }, b2[1];
int ret = 0;
struct i2c_msg msg[2] = {
{ .flags = 0, .buf = b, .len = 2 },
{ .flags = I2C_M_RD, .buf = b2, .len = 1 },
};
/* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */
msg[0].addr = msg[1].addr = 0x60;
if (adap->tuner_pass_ctrl)
adap->tuner_pass_ctrl(adap->fe,1,msg[0].addr);
if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) {
err("tuner i2c write failed.");
ret = -EREMOTEIO;
}
if (adap->tuner_pass_ctrl)
adap->tuner_pass_ctrl(adap->fe,0,msg[0].addr);
if (b2[0] == 0xfe) {
info("This device has the Thomson Cable onboard. Which is default.");
dibusb_thomson_tuner_attach(adap);
} else {
u8 bpll[4] = { 0x0b, 0xf5, 0x85, 0xab };
info("This device has the Panasonic ENV77H11D5 onboard.");
adap->pll_addr = 0x60;
memcpy(adap->pll_init,bpll,4);
adap->pll_desc = &dvb_pll_tda665x;
}
return ret;
}
/* USB Driver stuff */
static struct dvb_usb_device_properties dibusb1_1_properties;
static struct dvb_usb_device_properties dibusb1_1_an2235_properties;
static struct dvb_usb_device_properties dibusb2_0b_properties;
static struct dvb_usb_device_properties artec_t1_usb2_properties;
static int dibusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
if (dvb_usb_device_init(intf,&dibusb1_1_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&dibusb1_1_an2235_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&dibusb2_0b_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&artec_t1_usb2_properties,THIS_MODULE,NULL) == 0)
return 0;
return -EINVAL;
}
/* do not change the order of the ID table */
static struct usb_device_id dibusb_dib3000mb_table [] = {
/* 00 */ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_AVERMEDIA_DVBT_USB_COLD) },
/* 01 */ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_AVERMEDIA_DVBT_USB_WARM) },
/* 02 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_COLD) },
/* 03 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_WARM) },
/* 04 */ { USB_DEVICE(USB_VID_COMPRO_UNK, USB_PID_COMPRO_DVBU2000_UNK_COLD) },
/* 05 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_COLD) },
/* 06 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_WARM) },
/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_COLD) },
/* 08 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_WARM) },
/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_COLD) },
/* 10 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_WARM) },
/* 11 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_COLD) },
/* 12 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_WARM) },
/* 13 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD) },
/* 14 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM) },
/* 15 */ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7041_COLD) },
/* 16 */ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7041_WARM) },
/* 17 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_COLD) },
/* 18 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_WARM) },
/* 19 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) },
/* 20 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) },
/* 21 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) },
/* 22 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) },
/* 23 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_COLD) },
/* device ID with default DIBUSB2_0-firmware and with the hacked firmware */
/* 24 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_WARM) },
/* 25 */ { USB_DEVICE(USB_VID_KYE, USB_PID_KYE_DVB_T_COLD) },
/* 26 */ { USB_DEVICE(USB_VID_KYE, USB_PID_KYE_DVB_T_WARM) },
/* 27 */ { USB_DEVICE(USB_VID_KWORLD, USB_PID_KWORLD_VSTREAM_COLD) },
/* 28 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) },
/* 29 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_WARM) },
/*
* XXX: As Artec just 'forgot' to program the EEPROM on some Artec T1 devices
* we don't catch these faulty IDs (namely 'Cypress FX1 USB controller') that
* have been left on the device. If you don't have such a device but an Artec
* device that's supposed to work with this driver but is not detected by it,
* free to enable CONFIG_DVB_USB_DIBUSB_MB_FAULTY via your kernel config.
*/
#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY
/* 30 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) },
#endif
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, dibusb_dib3000mb_table);
static struct dvb_usb_device_properties dibusb1_1_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_AN2135,
.firmware = "dvb-usb-dibusb-5.0.0.11.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
.streaming_ctrl = dibusb_streaming_ctrl,
.pid_filter = dibusb_pid_filter,
.pid_filter_ctrl = dibusb_pid_filter_ctrl,
.frontend_attach = dibusb_dib3000mb_frontend_attach,
.tuner_attach = dibusb_tuner_probe_and_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
.size_of_priv = sizeof(struct dibusb_state),
}
},
.power_ctrl = dibusb_power_ctrl,
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
.rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 9,
.devices = {
{ "AVerMedia AverTV DVBT USB1.1",
{ &dibusb_dib3000mb_table[0], NULL },
{ &dibusb_dib3000mb_table[1], NULL },
},
{ "Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)",
{ &dibusb_dib3000mb_table[2], &dibusb_dib3000mb_table[4], NULL},
{ &dibusb_dib3000mb_table[3], NULL },
},
{ "DiBcom USB1.1 DVB-T reference design (MOD3000)",
{ &dibusb_dib3000mb_table[5], NULL },
{ &dibusb_dib3000mb_table[6], NULL },
},
{ "KWorld V-Stream XPERT DTV - DVB-T USB1.1",
{ &dibusb_dib3000mb_table[7], NULL },
{ &dibusb_dib3000mb_table[8], NULL },
},
{ "Grandtec USB1.1 DVB-T",
{ &dibusb_dib3000mb_table[9], &dibusb_dib3000mb_table[11], NULL },
{ &dibusb_dib3000mb_table[10], &dibusb_dib3000mb_table[12], NULL },
},
{ "Unkown USB1.1 DVB-T device ???? please report the name to the author",
{ &dibusb_dib3000mb_table[13], NULL },
{ &dibusb_dib3000mb_table[14], NULL },
},
{ "TwinhanDTV USB-Ter USB1.1 / Magic Box I / HAMA USB1.1 DVB-T device",
{ &dibusb_dib3000mb_table[15], &dibusb_dib3000mb_table[17], NULL},
{ &dibusb_dib3000mb_table[16], &dibusb_dib3000mb_table[18], NULL},
},
{ "Artec T1 USB1.1 TVBOX with AN2135",
{ &dibusb_dib3000mb_table[19], NULL },
{ &dibusb_dib3000mb_table[20], NULL },
},
{ "VideoWalker DVB-T USB",
{ &dibusb_dib3000mb_table[25], NULL },
{ &dibusb_dib3000mb_table[26], NULL },
},
}
};
static struct dvb_usb_device_properties dibusb1_1_an2235_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_AN2235,
.firmware = "dvb-usb-dibusb-an2235-01.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER,
.pid_filter_count = 16,
.streaming_ctrl = dibusb_streaming_ctrl,
.pid_filter = dibusb_pid_filter,
.pid_filter_ctrl = dibusb_pid_filter_ctrl,
.frontend_attach = dibusb_dib3000mb_frontend_attach,
.tuner_attach = dibusb_tuner_probe_and_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
.size_of_priv = sizeof(struct dibusb_state),
},
},
.power_ctrl = dibusb_power_ctrl,
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
.rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY
.num_device_descs = 2,
#else
.num_device_descs = 1,
#endif
.devices = {
{ "Artec T1 USB1.1 TVBOX with AN2235",
{ &dibusb_dib3000mb_table[21], NULL },
{ &dibusb_dib3000mb_table[22], NULL },
},
#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY
{ "Artec T1 USB1.1 TVBOX with AN2235 (faulty USB IDs)",
{ &dibusb_dib3000mb_table[30], NULL },
{ NULL },
},
{ NULL },
#endif
}
};
static struct dvb_usb_device_properties dibusb2_0b_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-adstech-usb2-02.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
.pid_filter = dibusb_pid_filter,
.pid_filter_ctrl = dibusb_pid_filter_ctrl,
.frontend_attach = dibusb_dib3000mb_frontend_attach,
.tuner_attach = dibusb_thomson_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x06,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
.size_of_priv = sizeof(struct dibusb_state),
}
},
.power_ctrl = dibusb2_0_power_ctrl,
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
.rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 2,
.devices = {
{ "KWorld/ADSTech Instant DVB-T USB2.0",
{ &dibusb_dib3000mb_table[23], NULL },
{ &dibusb_dib3000mb_table[24], NULL },
},
{ "KWorld Xpert DVB-T USB2.0",
{ &dibusb_dib3000mb_table[27], NULL },
{ NULL }
},
{ NULL },
}
};
static struct dvb_usb_device_properties artec_t1_usb2_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-dibusb-6.0.0.8.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 16,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
.pid_filter = dibusb_pid_filter,
.pid_filter_ctrl = dibusb_pid_filter_ctrl,
.frontend_attach = dibusb_dib3000mb_frontend_attach,
.tuner_attach = dibusb_tuner_probe_and_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x06,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
.size_of_priv = sizeof(struct dibusb_state),
}
},
.power_ctrl = dibusb2_0_power_ctrl,
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
.rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "Artec T1 USB2.0",
{ &dibusb_dib3000mb_table[28], NULL },
{ &dibusb_dib3000mb_table[29], NULL },
},
{ NULL },
}
};
static struct usb_driver dibusb_driver = {
.name = "dvb_usb_dibusb_mb",
.probe = dibusb_probe,
.disconnect = dvb_usb_device_exit,
.id_table = dibusb_dib3000mb_table,
};
/* module stuff */
static int __init dibusb_module_init(void)
{
int result;
if ((result = usb_register(&dibusb_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit dibusb_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&dibusb_driver);
}
module_init (dibusb_module_init);
module_exit (dibusb_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for DiBcom USB DVB-T devices (DiB3000M-B based)");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,154 @@
/* DVB USB compliant linux driver for mobile DVB-T USB devices based on
* reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-C/P)
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* based on GPL code from DiBcom, which has
* Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dibusb.h"
/* USB Driver stuff */
static struct dvb_usb_device_properties dibusb_mc_properties;
static int dibusb_mc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return dvb_usb_device_init(intf,&dibusb_mc_properties,THIS_MODULE,NULL);
}
/* do not change the order of the ID table */
static struct usb_device_id dibusb_dib3000mc_table [] = {
/* 00 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) },
/* 01 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) },
/* 02 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) },
/* 03 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_WARM) }, // ( ? )
/* 04 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_COLD) },
/* 05 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_WARM) },
/* 06 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_COLD) },
/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_WARM) },
/* 08 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_COLD) },
/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_WARM) },
/* 10 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_COLD) },
/* 11 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_WARM) },
/* 12 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_COLD) },
/* 13 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_WARM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table);
static struct dvb_usb_device_properties dibusb_mc_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-dibusb-6.0.0.8.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
.pid_filter = dibusb_pid_filter,
.pid_filter_ctrl = dibusb_pid_filter_ctrl,
.frontend_attach = dibusb_dib3000mc_frontend_attach,
.tuner_attach = dibusb_dib3000mc_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x06,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
.size_of_priv = sizeof(struct dibusb_state),
}
},
.power_ctrl = dibusb2_0_power_ctrl,
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
.rc_key_map_size = 111, /* FIXME */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 7,
.devices = {
{ "DiBcom USB2.0 DVB-T reference design (MOD3000P)",
{ &dibusb_dib3000mc_table[0], NULL },
{ &dibusb_dib3000mc_table[1], NULL },
},
{ "Artec T1 USB2.0 TVBOX (please check the warm ID)",
{ &dibusb_dib3000mc_table[2], NULL },
{ &dibusb_dib3000mc_table[3], NULL },
},
{ "LITE-ON USB2.0 DVB-T Tuner",
/* Also rebranded as Intuix S800, Toshiba */
{ &dibusb_dib3000mc_table[4], NULL },
{ &dibusb_dib3000mc_table[5], NULL },
},
{ "MSI Digivox Mini SL",
{ &dibusb_dib3000mc_table[6], NULL },
{ &dibusb_dib3000mc_table[7], NULL },
},
{ "GRAND - USB2.0 DVB-T adapter",
{ &dibusb_dib3000mc_table[8], NULL },
{ &dibusb_dib3000mc_table[9], NULL },
},
{ "Artec T14 - USB2.0 DVB-T",
{ &dibusb_dib3000mc_table[10], NULL },
{ &dibusb_dib3000mc_table[11], NULL },
},
{ "Leadtek - USB2.0 Winfast DTV dongle",
{ &dibusb_dib3000mc_table[12], NULL },
{ &dibusb_dib3000mc_table[13], NULL },
},
{ NULL },
}
};
static struct usb_driver dibusb_mc_driver = {
.name = "dvb_usb_dibusb_mc",
.probe = dibusb_mc_probe,
.disconnect = dvb_usb_device_exit,
.id_table = dibusb_dib3000mc_table,
};
/* module stuff */
static int __init dibusb_mc_module_init(void)
{
int result;
if ((result = usb_register(&dibusb_mc_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit dibusb_mc_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&dibusb_mc_driver);
}
module_init (dibusb_mc_module_init);
module_exit (dibusb_mc_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for DiBcom USB2.0 DVB-T (DiB3000M-C/P based) devices");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,130 @@
/* Header file for all dibusb-based-receivers.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#ifndef _DVB_USB_DIBUSB_H_
#define _DVB_USB_DIBUSB_H_
#ifndef DVB_USB_LOG_PREFIX
#define DVB_USB_LOG_PREFIX "dibusb"
#endif
#include "dvb-usb.h"
#include "dib3000.h"
#include "dib3000mc.h"
#include "mt2060.h"
/*
* protocol of all dibusb related devices
*/
/*
* bulk msg to/from endpoint 0x01
*
* general structure:
* request_byte parameter_bytes
*/
#define DIBUSB_REQ_START_READ 0x00
#define DIBUSB_REQ_START_DEMOD 0x01
/*
* i2c read
* bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word
* bulk read: byte_buffer (length_word bytes)
*/
#define DIBUSB_REQ_I2C_READ 0x02
/*
* i2c write
* bulk write: 0x03 (7bit i2c_addr << 1) register_bytes value_bytes
*/
#define DIBUSB_REQ_I2C_WRITE 0x03
/*
* polling the value of the remote control
* bulk write: 0x04
* bulk read: byte_buffer (5 bytes)
*/
#define DIBUSB_REQ_POLL_REMOTE 0x04
/* additional status values for Hauppauge Remote Control Protocol */
#define DIBUSB_RC_HAUPPAUGE_KEY_PRESSED 0x01
#define DIBUSB_RC_HAUPPAUGE_KEY_EMPTY 0x03
/* streaming mode:
* bulk write: 0x05 mode_byte
*
* mode_byte is mostly 0x00
*/
#define DIBUSB_REQ_SET_STREAMING_MODE 0x05
/* interrupt the internal read loop, when blocking */
#define DIBUSB_REQ_INTR_READ 0x06
/* io control
* 0x07 cmd_byte param_bytes
*
* param_bytes can be up to 32 bytes
*
* cmd_byte function parameter name
* 0x00 power mode
* 0x00 sleep
* 0x01 wakeup
*
* 0x01 enable streaming
* 0x02 disable streaming
*
*
*/
#define DIBUSB_REQ_SET_IOCTL 0x07
/* IOCTL commands */
/* change the power mode in firmware */
#define DIBUSB_IOCTL_CMD_POWER_MODE 0x00
#define DIBUSB_IOCTL_POWER_SLEEP 0x00
#define DIBUSB_IOCTL_POWER_WAKEUP 0x01
/* modify streaming of the FX2 */
#define DIBUSB_IOCTL_CMD_ENABLE_STREAM 0x01
#define DIBUSB_IOCTL_CMD_DISABLE_STREAM 0x02
struct dibusb_state {
struct dib_fe_xfer_ops ops;
int mt2060_present;
};
struct dibusb_device_state {
/* for RC5 remote control */
int old_toggle;
int last_repeat_count;
};
extern struct i2c_algorithm dibusb_i2c_algo;
extern int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *);
extern int dibusb_dib3000mc_tuner_attach (struct dvb_usb_adapter *);
extern int dibusb_streaming_ctrl(struct dvb_usb_adapter *, int);
extern int dibusb_pid_filter(struct dvb_usb_adapter *, int, u16, int);
extern int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *, int);
extern int dibusb2_0_streaming_ctrl(struct dvb_usb_adapter *, int);
extern int dibusb_power_ctrl(struct dvb_usb_device *, int);
extern int dibusb2_0_power_ctrl(struct dvb_usb_device *, int);
#define DEFAULT_RC_INTERVAL 150
//#define DEFAULT_RC_INTERVAL 100000
extern struct dvb_usb_rc_key dibusb_rc_keys[];
extern int dibusb_rc_query(struct dvb_usb_device *, u32 *, int *);
extern int dibusb_read_eeprom_byte(struct dvb_usb_device *, u8, u8 *);
#endif

View File

@@ -0,0 +1,347 @@
/* DVB USB compliant linux driver for Nebula Electronics uDigiTV DVB-T USB2.0
* receiver
*
* Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
*
* partly based on the SDK published by Nebula Electronics
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "digitv.h"
#include "mt352.h"
#include "nxt6000.h"
/* debug */
int dvb_usb_digitv_debug;
module_param_named(debug,dvb_usb_digitv_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
static int digitv_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 vv, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 sndbuf[7],rcvbuf[7];
memset(sndbuf,0,7); memset(rcvbuf,0,7);
sndbuf[0] = cmd;
sndbuf[1] = vv;
sndbuf[2] = wo ? wlen : rlen;
if (wo) {
memcpy(&sndbuf[3],wbuf,wlen);
dvb_usb_generic_write(d,sndbuf,7);
} else {
dvb_usb_generic_rw(d,sndbuf,7,rcvbuf,7,10);
memcpy(rbuf,&rcvbuf[3],rlen);
}
return 0;
}
/* I2C */
static int digitv_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
if (num > 2)
warn("more than 2 i2c messages at a time is not handled yet. TODO.");
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
if (digitv_ctrl_msg(d, USB_READ_COFDM, msg[i].buf[0], NULL, 0,
msg[i+1].buf,msg[i+1].len) < 0)
break;
i++;
} else
if (digitv_ctrl_msg(d,USB_WRITE_COFDM, msg[i].buf[0],
&msg[i].buf[1],msg[i].len-1,NULL,0) < 0)
break;
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 digitv_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm digitv_i2c_algo = {
.master_xfer = digitv_i2c_xfer,
.functionality = digitv_i2c_func,
};
/* Callbacks for DVB USB */
static int digitv_identify_state (struct usb_device *udev, struct
dvb_usb_device_properties *props, struct dvb_usb_device_description **desc,
int *cold)
{
*cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0;
return 0;
}
static int digitv_mt352_demod_init(struct dvb_frontend *fe)
{
static u8 reset_buf[] = { 0x89, 0x38, 0x8a, 0x2d, 0x50, 0x80 };
static u8 init_buf[] = { 0x68, 0xa0, 0x8e, 0x40, 0x53, 0x50,
0x67, 0x20, 0x7d, 0x01, 0x7c, 0x00, 0x7a, 0x00,
0x79, 0x20, 0x57, 0x05, 0x56, 0x31, 0x88, 0x0f,
0x75, 0x32 };
int i;
for (i = 0; i < ARRAY_SIZE(reset_buf); i += 2)
mt352_write(fe, &reset_buf[i], 2);
msleep(1);
for (i = 0; i < ARRAY_SIZE(init_buf); i += 2)
mt352_write(fe, &init_buf[i], 2);
return 0;
}
static struct mt352_config digitv_mt352_config = {
.demod_init = digitv_mt352_demod_init,
};
static int digitv_nxt6000_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
u8 b[5];
dvb_usb_tuner_calc_regs(fe,fep,b, 5);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
return digitv_ctrl_msg(adap->dev, USB_WRITE_TUNER, 0, &b[1], 4, NULL, 0);
}
static struct nxt6000_config digitv_nxt6000_config = {
.clock_inversion = 1,
};
static int digitv_frontend_attach(struct dvb_usb_adapter *adap)
{
if ((adap->fe = dvb_attach(mt352_attach, &digitv_mt352_config, &adap->dev->i2c_adap)) != NULL) {
adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
return 0;
}
if ((adap->fe = dvb_attach(nxt6000_attach, &digitv_nxt6000_config, &adap->dev->i2c_adap)) != NULL) {
adap->fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params;
return 0;
}
return -EIO;
}
static int digitv_tuner_attach(struct dvb_usb_adapter *adap)
{
adap->pll_addr = 0x60;
adap->pll_desc = &dvb_pll_tded4;
return 0;
}
static struct dvb_usb_rc_key digitv_rc_keys[] = {
{ 0x5f, 0x55, KEY_0 },
{ 0x6f, 0x55, KEY_1 },
{ 0x9f, 0x55, KEY_2 },
{ 0xaf, 0x55, KEY_3 },
{ 0x5f, 0x56, KEY_4 },
{ 0x6f, 0x56, KEY_5 },
{ 0x9f, 0x56, KEY_6 },
{ 0xaf, 0x56, KEY_7 },
{ 0x5f, 0x59, KEY_8 },
{ 0x6f, 0x59, KEY_9 },
{ 0x9f, 0x59, KEY_TV },
{ 0xaf, 0x59, KEY_AUX },
{ 0x5f, 0x5a, KEY_DVD },
{ 0x6f, 0x5a, KEY_POWER },
{ 0x9f, 0x5a, KEY_MHP }, /* labelled 'Picture' */
{ 0xaf, 0x5a, KEY_AUDIO },
{ 0x5f, 0x65, KEY_INFO },
{ 0x6f, 0x65, KEY_F13 }, /* 16:9 */
{ 0x9f, 0x65, KEY_F14 }, /* 14:9 */
{ 0xaf, 0x65, KEY_EPG },
{ 0x5f, 0x66, KEY_EXIT },
{ 0x6f, 0x66, KEY_MENU },
{ 0x9f, 0x66, KEY_UP },
{ 0xaf, 0x66, KEY_DOWN },
{ 0x5f, 0x69, KEY_LEFT },
{ 0x6f, 0x69, KEY_RIGHT },
{ 0x9f, 0x69, KEY_ENTER },
{ 0xaf, 0x69, KEY_CHANNELUP },
{ 0x5f, 0x6a, KEY_CHANNELDOWN },
{ 0x6f, 0x6a, KEY_VOLUMEUP },
{ 0x9f, 0x6a, KEY_VOLUMEDOWN },
{ 0xaf, 0x6a, KEY_RED },
{ 0x5f, 0x95, KEY_GREEN },
{ 0x6f, 0x95, KEY_YELLOW },
{ 0x9f, 0x95, KEY_BLUE },
{ 0xaf, 0x95, KEY_SUBTITLE },
{ 0x5f, 0x96, KEY_F15 }, /* AD */
{ 0x6f, 0x96, KEY_TEXT },
{ 0x9f, 0x96, KEY_MUTE },
{ 0xaf, 0x96, KEY_REWIND },
{ 0x5f, 0x99, KEY_STOP },
{ 0x6f, 0x99, KEY_PLAY },
{ 0x9f, 0x99, KEY_FASTFORWARD },
{ 0xaf, 0x99, KEY_F16 }, /* chapter */
{ 0x5f, 0x9a, KEY_PAUSE },
{ 0x6f, 0x9a, KEY_PLAY },
{ 0x9f, 0x9a, KEY_RECORD },
{ 0xaf, 0x9a, KEY_F17 }, /* picture in picture */
{ 0x5f, 0xa5, KEY_KPPLUS }, /* zoom in */
{ 0x6f, 0xa5, KEY_KPMINUS }, /* zoom out */
{ 0x9f, 0xa5, KEY_F18 }, /* capture */
{ 0xaf, 0xa5, KEY_F19 }, /* web */
{ 0x5f, 0xa6, KEY_EMAIL },
{ 0x6f, 0xa6, KEY_PHONE },
{ 0x9f, 0xa6, KEY_PC },
};
static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
int i;
u8 key[5];
u8 b[4] = { 0 };
*event = 0;
*state = REMOTE_NO_KEY_PRESSED;
digitv_ctrl_msg(d,USB_READ_REMOTE,0,NULL,0,&key[1],4);
/* Tell the device we've read the remote. Not sure how necessary
this is, but the Nebula SDK does it. */
digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0);
/* if something is inside the buffer, simulate key press */
if (key[1] != 0)
{
for (i = 0; i < d->props.rc_key_map_size; i++) {
if (d->props.rc_key_map[i].custom == key[1] &&
d->props.rc_key_map[i].data == key[2]) {
*event = d->props.rc_key_map[i].event;
*state = REMOTE_KEY_PRESSED;
return 0;
}
}
}
if (key[0] != 0)
deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]);
return 0;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties digitv_properties;
static int digitv_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dvb_usb_device *d;
int ret;
if ((ret = dvb_usb_device_init(intf,&digitv_properties,THIS_MODULE,&d)) == 0) {
u8 b[4] = { 0 };
if (d != NULL) { /* do that only when the firmware is loaded */
b[0] = 1;
digitv_ctrl_msg(d,USB_WRITE_REMOTE_TYPE,0,b,4,NULL,0);
b[0] = 0;
digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0);
}
}
return ret;
}
static struct usb_device_id digitv_table [] = {
{ USB_DEVICE(USB_VID_ANCHOR, USB_PID_NEBULA_DIGITV) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, digitv_table);
static struct dvb_usb_device_properties digitv_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-digitv-02.fw",
.num_adapters = 1,
.adapter = {
{
.frontend_attach = digitv_frontend_attach,
.tuner_attach = digitv_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}
},
.identify_state = digitv_identify_state,
.rc_interval = 1000,
.rc_key_map = digitv_rc_keys,
.rc_key_map_size = ARRAY_SIZE(digitv_rc_keys),
.rc_query = digitv_rc_query,
.i2c_algo = &digitv_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "Nebula Electronics uDigiTV DVB-T USB2.0)",
{ &digitv_table[0], NULL },
{ NULL },
},
{ NULL },
}
};
static struct usb_driver digitv_driver = {
.name = "dvb_usb_digitv",
.probe = digitv_probe,
.disconnect = dvb_usb_device_exit,
.id_table = digitv_table,
};
/* module stuff */
static int __init digitv_module_init(void)
{
int result;
if ((result = usb_register(&digitv_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit digitv_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&digitv_driver);
}
module_init (digitv_module_init);
module_exit (digitv_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for Nebula Electronics uDigiTV DVB-T USB2.0");
MODULE_VERSION("1.0-alpha");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,65 @@
#ifndef _DVB_USB_DIGITV_H_
#define _DVB_USB_DIGITV_H_
#define DVB_USB_LOG_PREFIX "digitv"
#include "dvb-usb.h"
extern int dvb_usb_digitv_debug;
#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args)
/* protocol (from usblogging and the SDK:
*
* Always 7 bytes bulk message(s) for controlling
*
* First byte describes the command. Reads are 2 consecutive transfer (as always).
*
* General structure:
*
* write or first message of a read:
* <cmdbyte> VV <len> B0 B1 B2 B3
*
* second message of a read
* <cmdbyte> VV <len> R0 R1 R2 R3
*
* whereas 0 < len <= 4
*
* I2C address is stored somewhere inside the device.
*
* 0x01 read from EEPROM
* VV = offset; B* = 0; R* = value(s)
*
* 0x02 read register of the COFDM
* VV = register; B* = 0; R* = value(s)
*
* 0x05 write register of the COFDM
* VV = register; B* = value(s);
*
* 0x06 write to the tuner (only for NXT6000)
* VV = 0; B* = PLL data; len = 4;
*
* 0x03 read remote control
* VV = 0; B* = 0; len = 4; R* = key
*
* 0x07 write to the remote (don't know why one should this, resetting ?)
* VV = 0; B* = key; len = 4;
*
* 0x08 write remote type
* VV = 0; B[0] = 0x01, len = 4
*
* 0x09 write device init
* TODO
*/
#define USB_READ_EEPROM 1
#define USB_READ_COFDM 2
#define USB_WRITE_COFDM 5
#define USB_WRITE_TUNER 6
#define USB_READ_REMOTE 3
#define USB_WRITE_REMOTE 7
#define USB_WRITE_REMOTE_TYPE 8
#define USB_DEV_INIT 9
#endif

View File

@@ -0,0 +1,205 @@
/* 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@desy.de>
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dtt200u.h"
struct dtt200u_fe_state {
struct dvb_usb_device *d;
fe_status_t stat;
struct dvb_frontend_parameters fep;
struct dvb_frontend frontend;
};
static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
u8 st = GET_TUNE_STATUS, b[3];
dvb_usb_generic_rw(state->d,&st,1,b,3,0);
switch (b[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;
}
return 0;
}
static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
u8 bw = GET_VIT_ERR_CNT,b[3];
dvb_usb_generic_rw(state->d,&bw,1,b,3,0);
*ber = (b[0] << 16) | (b[1] << 8) | b[2];
return 0;
}
static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
u8 bw = GET_RS_UNCOR_BLK_CNT,b[2];
dvb_usb_generic_rw(state->d,&bw,1,b,2,0);
*unc = (b[0] << 8) | b[1];
return 0;
}
static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
u8 bw = GET_AGC, b;
dvb_usb_generic_rw(state->d,&bw,1,&b,1,0);
*strength = (b << 8) | b;
return 0;
}
static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
u8 bw = GET_SNR,br;
dvb_usb_generic_rw(state->d,&bw,1,&br,1,0);
*snr = ~((br << 8) | br);
return 0;
}
static int dtt200u_fe_init(struct dvb_frontend* fe)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
u8 b = SET_INIT;
return dvb_usb_generic_write(state->d,&b,1);
}
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 dvb_frontend_parameters *fep)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
int i;
fe_status_t st;
u16 freq = fep->frequency / 250000;
u8 bwbuf[2] = { SET_BANDWIDTH, 0 },freqbuf[3] = { SET_RF_FREQ, 0, 0 };
switch (fep->u.ofdm.bandwidth) {
case BANDWIDTH_8_MHZ: bwbuf[1] = 8; break;
case BANDWIDTH_7_MHZ: bwbuf[1] = 7; break;
case BANDWIDTH_6_MHZ: bwbuf[1] = 6; break;
case BANDWIDTH_AUTO: return -EOPNOTSUPP;
default:
return -EINVAL;
}
dvb_usb_generic_write(state->d,bwbuf,2);
freqbuf[1] = freq & 0xff;
freqbuf[2] = (freq >> 8) & 0xff;
dvb_usb_generic_write(state->d,freqbuf,3);
for (i = 0; i < 30; i++) {
msleep(20);
dtt200u_fe_read_status(fe, &st);
if (st & FE_TIMEDOUT)
continue;
}
return 0;
}
static int dtt200u_fe_get_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct dtt200u_fe_state *state = fe->demodulator_priv;
memcpy(fep,&state->fep,sizeof(struct dvb_frontend_parameters));
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 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;
memcpy(&state->frontend.ops,&dtt200u_fe_ops,sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
error:
return NULL;
}
static struct dvb_frontend_ops dtt200u_fe_ops = {
.info = {
.name = "WideView USB DVB-T",
.type = FE_OFDM,
.frequency_min = 44250000,
.frequency_max = 867250000,
.frequency_stepsize = 250000,
.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,
};

View File

@@ -0,0 +1,338 @@
/* DVB USB library compliant Linux driver for the WideView/ Yakumo/ Hama/
* Typhoon/ Yuan DVB-T USB2.0 receiver.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* Thanks to Steve Chang from WideView for providing support for the WT-220U.
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dtt200u.h"
/* debug */
int dvb_usb_dtt200u_debug;
module_param_named(debug,dvb_usb_dtt200u_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2 (or-able))." DVB_USB_DEBUG_STATUS);
static int dtt200u_power_ctrl(struct dvb_usb_device *d, int onoff)
{
u8 b = SET_INIT;
if (onoff)
dvb_usb_generic_write(d,&b,2);
return 0;
}
static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
u8 b_streaming[2] = { SET_STREAMING, onoff };
u8 b_rst_pid = RESET_PID_FILTER;
dvb_usb_generic_write(adap->dev, b_streaming, 2);
if (onoff == 0)
dvb_usb_generic_write(adap->dev, &b_rst_pid, 1);
return 0;
}
static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff)
{
u8 b_pid[4];
pid = onoff ? pid : 0;
b_pid[0] = SET_PID_FILTER;
b_pid[1] = index;
b_pid[2] = pid & 0xff;
b_pid[3] = (pid >> 8) & 0x1f;
return dvb_usb_generic_write(adap->dev, b_pid, 4);
}
/* remote control */
/* key list for the tiny remote control (Yakumo, don't know about the others) */
static struct dvb_usb_rc_key dtt200u_rc_keys[] = {
{ 0x80, 0x01, KEY_MUTE },
{ 0x80, 0x02, KEY_CHANNELDOWN },
{ 0x80, 0x03, KEY_VOLUMEDOWN },
{ 0x80, 0x04, KEY_1 },
{ 0x80, 0x05, KEY_2 },
{ 0x80, 0x06, KEY_3 },
{ 0x80, 0x07, KEY_4 },
{ 0x80, 0x08, KEY_5 },
{ 0x80, 0x09, KEY_6 },
{ 0x80, 0x0a, KEY_7 },
{ 0x80, 0x0c, KEY_ZOOM },
{ 0x80, 0x0d, KEY_0 },
{ 0x80, 0x0e, KEY_SELECT },
{ 0x80, 0x12, KEY_POWER },
{ 0x80, 0x1a, KEY_CHANNELUP },
{ 0x80, 0x1b, KEY_8 },
{ 0x80, 0x1e, KEY_VOLUMEUP },
{ 0x80, 0x1f, KEY_9 },
};
static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
u8 key[5],cmd = GET_RC_CODE;
dvb_usb_generic_rw(d,&cmd,1,key,5,0);
dvb_usb_nec_rc_key_to_event(d,key,event,state);
if (key[0] != 0)
deb_info("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]);
return 0;
}
static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap)
{
adap->fe = dtt200u_fe_attach(adap->dev);
return 0;
}
static struct dvb_usb_device_properties dtt200u_properties;
static struct dvb_usb_device_properties wt220u_fc_properties;
static struct dvb_usb_device_properties wt220u_properties;
static struct dvb_usb_device_properties wt220u_zl0353_properties;
static int dtt200u_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
if (dvb_usb_device_init(intf,&dtt200u_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&wt220u_fc_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&wt220u_zl0353_properties,THIS_MODULE,NULL) == 0)
return 0;
return -ENODEV;
}
static struct usb_device_id dtt200u_usb_table [] = {
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_COLD) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_WARM) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_COLD) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_COLD) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_WARM) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZAP250_COLD) },
{ 0 },
};
MODULE_DEVICE_TABLE(usb, dtt200u_usb_table);
static struct dvb_usb_device_properties dtt200u_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-dtt200u-01.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
.streaming_ctrl = dtt200u_streaming_ctrl,
.pid_filter = dtt200u_pid_filter,
.frontend_attach = dtt200u_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}
},
.power_ctrl = dtt200u_power_ctrl,
.rc_interval = 300,
.rc_key_map = dtt200u_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys),
.rc_query = dtt200u_rc_query,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ .name = "WideView/Yuan/Yakumo/Hama/Typhoon DVB-T USB2.0 (WT-200U)",
.cold_ids = { &dtt200u_usb_table[0], NULL },
.warm_ids = { &dtt200u_usb_table[1], NULL },
},
{ NULL },
}
};
static struct dvb_usb_device_properties wt220u_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-wt220u-02.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
.streaming_ctrl = dtt200u_streaming_ctrl,
.pid_filter = dtt200u_pid_filter,
.frontend_attach = dtt200u_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}
},
.power_ctrl = dtt200u_power_ctrl,
.rc_interval = 300,
.rc_key_map = dtt200u_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys),
.rc_query = dtt200u_rc_query,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)",
.cold_ids = { &dtt200u_usb_table[2], &dtt200u_usb_table[8], NULL },
.warm_ids = { &dtt200u_usb_table[3], NULL },
},
{ NULL },
}
};
static struct dvb_usb_device_properties wt220u_fc_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-wt220u-fc03.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
.streaming_ctrl = dtt200u_streaming_ctrl,
.pid_filter = dtt200u_pid_filter,
.frontend_attach = dtt200u_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x06,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}
},
.power_ctrl = dtt200u_power_ctrl,
.rc_interval = 300,
.rc_key_map = dtt200u_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys),
.rc_query = dtt200u_rc_query,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)",
.cold_ids = { &dtt200u_usb_table[6], NULL },
.warm_ids = { &dtt200u_usb_table[7], NULL },
},
{ NULL },
}
};
static struct dvb_usb_device_properties wt220u_zl0353_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-wt220u-zl0353-01.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
.pid_filter_count = 15,
.streaming_ctrl = dtt200u_streaming_ctrl,
.pid_filter = dtt200u_pid_filter,
.frontend_attach = dtt200u_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}
},
.power_ctrl = dtt200u_power_ctrl,
.rc_interval = 300,
.rc_key_map = dtt200u_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys),
.rc_query = dtt200u_rc_query,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ .name = "WideView WT-220U PenType Receiver (based on ZL353)",
.cold_ids = { &dtt200u_usb_table[4], NULL },
.warm_ids = { &dtt200u_usb_table[5], NULL },
},
{ NULL },
}
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver dtt200u_usb_driver = {
.name = "dvb_usb_dtt200u",
.probe = dtt200u_usb_probe,
.disconnect = dvb_usb_device_exit,
.id_table = dtt200u_usb_table,
};
/* module stuff */
static int __init dtt200u_usb_module_init(void)
{
int result;
if ((result = usb_register(&dtt200u_usb_driver))) {
err("usb_register failed. (%d)",result);
return result;
}
return 0;
}
static void __exit dtt200u_usb_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&dtt200u_usb_driver);
}
module_init(dtt200u_usb_module_init);
module_exit(dtt200u_usb_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D DVB-T USB2.0 devices");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,57 @@
/* Common header file of Linux driver for the WideView/ Yakumo/ Hama/
* Typhoon/ Yuan DVB-T USB2.0 receiver.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#ifndef _DVB_USB_DTT200U_H_
#define _DVB_USB_DTT200U_H_
#define DVB_USB_LOG_PREFIX "dtt200u"
#include "dvb-usb.h"
extern int dvb_usb_dtt200u_debug;
#define deb_info(args...) dprintk(dvb_usb_dtt200u_debug,0x01,args)
#define deb_xfer(args...) dprintk(dvb_usb_dtt200u_debug,0x02,args)
/* guessed protocol description (reverse engineered):
* read
* 00 - USB type 0x02 for usb2.0, 0x01 for usb1.1
* 88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal)
*/
#define GET_SPEED 0x00
#define GET_TUNE_STATUS 0x81
#define GET_RC_CODE 0x84
#define GET_CONFIGURATION 0x88
#define GET_AGC 0x89
#define GET_SNR 0x8a
#define GET_VIT_ERR_CNT 0x8c
#define GET_RS_ERR_CNT 0x8d
#define GET_RS_UNCOR_BLK_CNT 0x8e
/* write
* 01 - init
* 02 - frequency (divided by 250000)
* 03 - bandwidth
* 04 - pid table (index pid(7:0) pid(12:8))
* 05 - reset the pid table
* 08 - transfer switch
*/
#define SET_INIT 0x01
#define SET_RF_FREQ 0x02
#define SET_BANDWIDTH 0x03
#define SET_PID_FILTER 0x04
#define RESET_PID_FILTER 0x05
#define SET_STREAMING 0x08
extern struct dvb_frontend * dtt200u_fe_attach(struct dvb_usb_device *d);
#endif

View File

@@ -0,0 +1,51 @@
/* dvb-usb-common.h is part of the DVB USB library.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* a header file containing prototypes and types for internal use of the dvb-usb-lib
*/
#ifndef _DVB_USB_COMMON_H_
#define _DVB_USB_COMMON_H_
#define DVB_USB_LOG_PREFIX "dvb-usb"
#include "dvb-usb.h"
extern int dvb_usb_debug;
extern int dvb_usb_disable_rc_polling;
#define deb_info(args...) dprintk(dvb_usb_debug,0x001,args)
#define deb_xfer(args...) dprintk(dvb_usb_debug,0x002,args)
#define deb_pll(args...) dprintk(dvb_usb_debug,0x004,args)
#define deb_ts(args...) dprintk(dvb_usb_debug,0x008,args)
#define deb_err(args...) dprintk(dvb_usb_debug,0x010,args)
#define deb_rc(args...) dprintk(dvb_usb_debug,0x020,args)
#define deb_fw(args...) dprintk(dvb_usb_debug,0x040,args)
#define deb_mem(args...) dprintk(dvb_usb_debug,0x080,args)
#define deb_uxfer(args...) dprintk(dvb_usb_debug,0x100,args)
/* commonly used methods */
extern int dvb_usb_download_firmware(struct usb_device *, struct dvb_usb_device_properties *);
extern int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff);
extern int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properties *props);
extern int usb_urb_exit(struct usb_data_stream *stream);
extern int usb_urb_submit(struct usb_data_stream *stream);
extern int usb_urb_kill(struct usb_data_stream *stream);
extern int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap);
extern int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap);
extern int dvb_usb_i2c_init(struct dvb_usb_device *);
extern int dvb_usb_i2c_exit(struct dvb_usb_device *);
extern int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap);
extern int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap);
extern int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap);
extern int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap);
extern int dvb_usb_remote_init(struct dvb_usb_device *);
extern int dvb_usb_remote_exit(struct dvb_usb_device *);
#endif

View File

@@ -0,0 +1,206 @@
/* dvb-usb-dvb.c is part of the DVB USB library.
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for initializing and handling the
* linux-dvb API.
*/
#include "dvb-usb-common.h"
/* does the complete input transfer handling */
static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
{
struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv;
int newfeedcount,ret;
if (adap == NULL)
return -ENODEV;
newfeedcount = adap->feedcount + (onoff ? 1 : -1);
/* stop feed before setting a new pid if there will be no pid anymore */
if (newfeedcount == 0) {
deb_ts("stop feeding\n");
usb_urb_kill(&adap->stream);
if (adap->props.streaming_ctrl != NULL)
if ((ret = adap->props.streaming_ctrl(adap,0)))
err("error while stopping stream.");
}
adap->feedcount = newfeedcount;
/* activate the pid on the device specific pid_filter */
deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n",adap->pid_filtering ?
"yes" : "no", dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ?
"on" : "off");
if (adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER &&
adap->pid_filtering &&
adap->props.pid_filter != NULL)
adap->props.pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid,onoff);
/* start the feed if this was the first feed and there is still a feed
* for reception.
*/
if (adap->feedcount == onoff && adap->feedcount > 0) {
deb_ts("submitting all URBs\n");
usb_urb_submit(&adap->stream);
deb_ts("controlling pid parser\n");
if (adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER &&
adap->props.caps & DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF &&
adap->props.pid_filter_ctrl != NULL)
if (adap->props.pid_filter_ctrl(adap,adap->pid_filtering) < 0)
err("could not handle pid_parser");
deb_ts("start feeding\n");
if (adap->props.streaming_ctrl != NULL)
if (adap->props.streaming_ctrl(adap,1)) {
err("error while enabling fifo.");
return -ENODEV;
}
}
return 0;
}
static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type);
return dvb_usb_ctrl_feed(dvbdmxfeed,1);
}
static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type);
return dvb_usb_ctrl_feed(dvbdmxfeed,0);
}
int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap)
{
int ret;
if ((ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name,
adap->dev->owner, &adap->dev->udev->dev)) < 0) {
deb_info("dvb_register_adapter failed: error %d", ret);
goto err;
}
adap->dvb_adap.priv = adap;
if (adap->dev->props.read_mac_address) {
if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0)
info("MAC address: %02x:%02x:%02x:%02x:%02x:%02x",adap->dvb_adap.proposed_mac[0],
adap->dvb_adap.proposed_mac[1], adap->dvb_adap.proposed_mac[2],
adap->dvb_adap.proposed_mac[3], adap->dvb_adap.proposed_mac[4],
adap->dvb_adap.proposed_mac[5]);
else
err("MAC address reading failed.");
}
adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
adap->demux.priv = adap;
adap->demux.feednum = adap->demux.filternum = adap->max_feed_count;
adap->demux.start_feed = dvb_usb_start_feed;
adap->demux.stop_feed = dvb_usb_stop_feed;
adap->demux.write_to_decoder = NULL;
if ((ret = dvb_dmx_init(&adap->demux)) < 0) {
err("dvb_dmx_init failed: error %d",ret);
goto err_dmx;
}
adap->dmxdev.filternum = adap->demux.filternum;
adap->dmxdev.demux = &adap->demux.dmx;
adap->dmxdev.capabilities = 0;
if ((ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap)) < 0) {
err("dvb_dmxdev_init failed: error %d",ret);
goto err_dmx_dev;
}
dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx);
adap->state |= DVB_USB_ADAP_STATE_DVB;
return 0;
err_dmx_dev:
dvb_dmx_release(&adap->demux);
err_dmx:
dvb_unregister_adapter(&adap->dvb_adap);
err:
return ret;
}
int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap)
{
if (adap->state & DVB_USB_ADAP_STATE_DVB) {
deb_info("unregistering DVB part\n");
dvb_net_release(&adap->dvb_net);
adap->demux.dmx.close(&adap->demux.dmx);
dvb_dmxdev_release(&adap->dmxdev);
dvb_dmx_release(&adap->demux);
dvb_unregister_adapter(&adap->dvb_adap);
adap->state &= ~DVB_USB_ADAP_STATE_DVB;
}
return 0;
}
static int dvb_usb_fe_wakeup(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
dvb_usb_device_power_ctrl(adap->dev, 1);
if (adap->fe_init)
adap->fe_init(fe);
return 0;
}
static int dvb_usb_fe_sleep(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
if (adap->fe_sleep)
adap->fe_sleep(fe);
return dvb_usb_device_power_ctrl(adap->dev, 0);
}
int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
{
if (adap->props.frontend_attach == NULL) {
err("strange: '%s' #%d doesn't want to attach a frontend.",adap->dev->desc->name, adap->id);
return 0;
}
/* re-assign sleep and wakeup functions */
if (adap->props.frontend_attach(adap) == 0 && adap->fe != NULL) {
adap->fe_init = adap->fe->ops.init; adap->fe->ops.init = dvb_usb_fe_wakeup;
adap->fe_sleep = adap->fe->ops.sleep; adap->fe->ops.sleep = dvb_usb_fe_sleep;
if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) {
err("Frontend registration failed.");
dvb_frontend_detach(adap->fe);
adap->fe = NULL;
return -ENODEV;
}
/* only attach the tuner if the demod is there */
if (adap->props.tuner_attach != NULL)
adap->props.tuner_attach(adap);
} else
err("no frontend was attached by '%s'",adap->dev->desc->name);
return 0;
}
int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap)
{
if (adap->fe != NULL) {
dvb_unregister_frontend(adap->fe);
dvb_frontend_detach(adap->fe);
}
return 0;
}

View File

@@ -0,0 +1,146 @@
/* dvb-usb-firmware.c is part of the DVB USB library.
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for downloading the firmware to Cypress FX 1 and 2 based devices.
*
* FIXME: This part does actually not belong to dvb-usb, but to the usb-subsystem.
*/
#include "dvb-usb-common.h"
#include <linux/usb.h>
struct usb_cypress_controller {
int id;
const char *name; /* name of the usb controller */
u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */
};
static struct usb_cypress_controller cypress[] = {
{ .id = DEVICE_SPECIFIC, .name = "Device specific", .cpu_cs_register = 0 },
{ .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 },
{ .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 },
{ .id = CYPRESS_FX2, .name = "Cypress FX2", .cpu_cs_register = 0xe600 },
};
/*
* load a firmware packet to the device
*/
static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len)
{
return usb_control_msg(udev, usb_sndctrlpipe(udev,0),
0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
}
int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type)
{
struct hexline hx;
u8 reset;
int ret,pos=0;
/* stop the CPU */
reset = 1;
if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1)
err("could not stop the USB controller CPU.");
while ((ret = dvb_usb_get_hexline(fw,&hx,&pos)) > 0) {
deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx.addr,hx.len,hx.chk);
ret = usb_cypress_writemem(udev,hx.addr,hx.data,hx.len);
if (ret != hx.len) {
err("error while transferring firmware "
"(transferred size: %d, block size: %d)",
ret,hx.len);
ret = -EINVAL;
break;
}
}
if (ret < 0) {
err("firmware download failed at %d with %d",pos,ret);
return ret;
}
if (ret == 0) {
/* restart the CPU */
reset = 0;
if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) {
err("could not restart the USB controller CPU.");
ret = -EINVAL;
}
} else
ret = -EIO;
return ret;
}
EXPORT_SYMBOL(usb_cypress_load_firmware);
int dvb_usb_download_firmware(struct usb_device *udev, struct dvb_usb_device_properties *props)
{
int ret;
const struct firmware *fw = NULL;
if ((ret = request_firmware(&fw, props->firmware, &udev->dev)) != 0) {
err("did not find the firmware file. (%s) "
"Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)",
props->firmware,ret);
return ret;
}
info("downloading firmware from file '%s'",props->firmware);
switch (props->usb_ctrl) {
case CYPRESS_AN2135:
case CYPRESS_AN2235:
case CYPRESS_FX2:
ret = usb_cypress_load_firmware(udev, fw, props->usb_ctrl);
break;
case DEVICE_SPECIFIC:
if (props->download_firmware)
ret = props->download_firmware(udev,fw);
else {
err("BUG: driver didn't specified a download_firmware-callback, although it claims to have a DEVICE_SPECIFIC one.");
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
break;
}
release_firmware(fw);
return ret;
}
int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx,
int *pos)
{
u8 *b = (u8 *) &fw->data[*pos];
int data_offs = 4;
if (*pos >= fw->size)
return 0;
memset(hx,0,sizeof(struct hexline));
hx->len = b[0];
if ((*pos + hx->len + 4) >= fw->size)
return -EINVAL;
hx->addr = le16_to_cpu( *((u16 *) &b[1]) );
hx->type = b[3];
if (hx->type == 0x04) {
/* b[4] and b[5] are the Extended linear address record data field */
hx->addr |= (b[4] << 24) | (b[5] << 16);
/* hx->len -= 2;
data_offs += 2; */
}
memcpy(hx->data,&b[data_offs],hx->len);
hx->chk = b[hx->len + data_offs];
*pos += hx->len + 5;
return *pos;
}
EXPORT_SYMBOL(dvb_usb_get_hexline);

View File

@@ -0,0 +1,127 @@
/* dvb-usb-i2c.c is part of the DVB USB library.
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for (de-)initializing an I2C adapter.
*/
#include "dvb-usb-common.h"
int dvb_usb_i2c_init(struct dvb_usb_device *d)
{
int ret = 0;
if (!(d->props.caps & DVB_USB_IS_AN_I2C_ADAPTER))
return 0;
if (d->props.i2c_algo == NULL) {
err("no i2c algorithm specified");
return -EINVAL;
}
strncpy(d->i2c_adap.name,d->desc->name,I2C_NAME_SIZE);
#ifdef I2C_ADAP_CLASS_TV_DIGITAL
d->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
#else
d->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
#endif
d->i2c_adap.algo = d->props.i2c_algo;
d->i2c_adap.algo_data = NULL;
d->i2c_adap.dev.parent = &d->udev->dev;
i2c_set_adapdata(&d->i2c_adap, d);
if ((ret = i2c_add_adapter(&d->i2c_adap)) < 0)
err("could not add i2c adapter");
d->state |= DVB_USB_STATE_I2C;
return ret;
}
int dvb_usb_i2c_exit(struct dvb_usb_device *d)
{
if (d->state & DVB_USB_STATE_I2C)
i2c_del_adapter(&d->i2c_adap);
d->state &= ~DVB_USB_STATE_I2C;
return 0;
}
int dvb_usb_tuner_init_i2c(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct i2c_msg msg = { .addr = adap->pll_addr, .flags = 0, .buf = adap->pll_init, .len = 4 };
int ret = 0;
/* if pll_desc is not used */
if (adap->pll_desc == NULL)
return 0;
if (adap->tuner_pass_ctrl)
adap->tuner_pass_ctrl(fe, 1, adap->pll_addr);
deb_pll("pll init: %x\n",adap->pll_addr);
deb_pll("pll-buf: %x %x %x %x\n",adap->pll_init[0], adap->pll_init[1],
adap->pll_init[2], adap->pll_init[3]);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (i2c_transfer (&adap->dev->i2c_adap, &msg, 1) != 1) {
err("tuner i2c write failed for pll_init.");
ret = -EREMOTEIO;
}
msleep(1);
if (adap->tuner_pass_ctrl)
adap->tuner_pass_ctrl(fe,0,adap->pll_addr);
return ret;
}
EXPORT_SYMBOL(dvb_usb_tuner_init_i2c);
int dvb_usb_tuner_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 *b, int buf_len)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
if (buf_len != 5)
return -EINVAL;
if (adap->pll_desc == NULL)
return 0;
deb_pll("pll addr: %x, freq: %d %p\n",adap->pll_addr, fep->frequency, adap->pll_desc);
b[0] = adap->pll_addr;
dvb_pll_configure(adap->pll_desc, &b[1], fep->frequency, fep->u.ofdm.bandwidth);
deb_pll("pll-buf: %x %x %x %x %x\n",b[0],b[1],b[2],b[3],b[4]);
return 5;
}
EXPORT_SYMBOL(dvb_usb_tuner_calc_regs);
int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
int ret = 0;
u8 b[5];
struct i2c_msg msg = { .addr = adap->pll_addr, .flags = 0, .buf = &b[1], .len = 4 };
dvb_usb_tuner_calc_regs(fe,fep,b,5);
if (adap->tuner_pass_ctrl)
adap->tuner_pass_ctrl(fe, 1, adap->pll_addr);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if (i2c_transfer(&adap->dev->i2c_adap, &msg, 1) != 1) {
err("tuner i2c write failed for pll_set.");
ret = -EREMOTEIO;
}
msleep(1);
if (adap->tuner_pass_ctrl)
adap->tuner_pass_ctrl(fe, 0, adap->pll_addr);
return ret;
}
EXPORT_SYMBOL(dvb_usb_tuner_set_params_i2c);

View File

@@ -0,0 +1,144 @@
/* dvb-usb-ids.h is part of the DVB USB library.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) see
* dvb-usb-init.c for copyright information.
*
* a header file containing define's for the USB device supported by the
* various drivers.
*/
#ifndef _DVB_USB_IDS_H_
#define _DVB_USB_IDS_H_
/* Vendor IDs */
#define USB_VID_ADSTECH 0x06e1
#define USB_VID_ALCOR_MICRO 0x058f
#define USB_VID_ANCHOR 0x0547
#define USB_VID_AVERMEDIA 0x07ca
#define USB_VID_COMPRO 0x185b
#define USB_VID_COMPRO_UNK 0x145f
#define USB_VID_CYPRESS 0x04b4
#define USB_VID_DIBCOM 0x10b8
#define USB_VID_DVICO 0x0fe9
#define USB_VID_EMPIA 0xeb1a
#define USB_VID_GENPIX 0x09c0
#define USB_VID_GRANDTEC 0x5032
#define USB_VID_HANFTEK 0x15f4
#define USB_VID_HAUPPAUGE 0x2040
#define USB_VID_HYPER_PALTEK 0x1025
#define USB_VID_KWORLD 0xeb2a
#define USB_VID_KYE 0x0458
#define USB_VID_LEADTEK 0x0413
#define USB_VID_LITEON 0x04ca
#define USB_VID_MEDION 0x1660
#define USB_VID_MSI 0x0db0
#define USB_VID_PINNACLE 0x2304
#define USB_VID_VISIONPLUS 0x13d3
#define USB_VID_TWINHAN 0x1822
#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
#define USB_VID_UNIWILL 0x1584
#define USB_VID_WIDEVIEW 0x14aa
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
#define USB_PID_ADSTECH_USB2_WARM 0xa334
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801
#define USB_PID_COMPRO_DVBU2000_COLD 0xd000
#define USB_PID_COMPRO_DVBU2000_WARM 0xd001
#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c
#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78
#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064
#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9
#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6
#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7
#define USB_PID_DIBCOM_STK7700P 0x1e14
#define USB_PID_DIBCOM_STK7700P_PC 0x1e78
#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
#define USB_PID_UNIWILL_STK7700P 0x6003
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_TWINHAN_VP7041_COLD 0x3201
#define USB_PID_TWINHAN_VP7041_WARM 0x3202
#define USB_PID_TWINHAN_VP7020_COLD 0x3203
#define USB_PID_TWINHAN_VP7020_WARM 0x3204
#define USB_PID_TWINHAN_VP7045_COLD 0x3205
#define USB_PID_TWINHAN_VP7045_WARM 0x3206
#define USB_PID_TWINHAN_VP7021_COLD 0x3207
#define USB_PID_TWINHAN_VP7021_WARM 0x3208
#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
#define USB_PID_ULTIMA_TVBOX_WARM 0x8106
#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107
#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108
#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235
#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109
#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a
#define USB_PID_ARTEC_T14_COLD 0x810b
#define USB_PID_ARTEC_T14_WARM 0x810c
#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f
#define USB_PID_HANFTEK_UMT_010_COLD 0x0001
#define USB_PID_HANFTEK_UMT_010_WARM 0x0015
#define USB_PID_DTT200U_COLD 0x0201
#define USB_PID_DTT200U_WARM 0x0301
#define USB_PID_WT220U_ZAP250_COLD 0x0220
#define USB_PID_WT220U_COLD 0x0222
#define USB_PID_WT220U_WARM 0x0221
#define USB_PID_WT220U_FC_COLD 0x0225
#define USB_PID_WT220U_FC_WARM 0x0226
#define USB_PID_WT220U_ZL0353_COLD 0x022a
#define USB_PID_WT220U_ZL0353_WARM 0x022b
#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300
#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301
#define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941
#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950
#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050
#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060
#define USB_PID_AVERMEDIA_VOLAR 0xa807
#define USB_PID_AVERMEDIA_VOLAR_2 0xb808
#define USB_PID_NEBULA_DIGITV 0x0201
#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820
#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500
#define USB_PID_DVICO_BLUEBIRD_LG064F_WARM 0xd501
#define USB_PID_DVICO_BLUEBIRD_LGZ201_COLD 0xdb00
#define USB_PID_DVICO_BLUEBIRD_LGZ201_WARM 0xdb01
#define USB_PID_DVICO_BLUEBIRD_TH7579_COLD 0xdb10
#define USB_PID_DVICO_BLUEBIRD_TH7579_WARM 0xdb11
#define USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD 0xdb50
#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59
#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55
#define USB_PID_MEDION_MD95700 0x0932
#define USB_PID_MSI_MEGASKY580 0x5580
#define USB_PID_MSI_MEGASKY580_55801 0x5581
#define USB_PID_KYE_DVB_T_COLD 0x701e
#define USB_PID_KYE_DVB_T_WARM 0x701f
#define USB_PID_PCTV_200E 0x020e
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_LITEON_DVB_T_COLD 0xf000
#define USB_PID_LITEON_DVB_T_WARM 0xf001
#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360
#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361
#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6
#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7
#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025
#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00
#define USB_PID_GENPIX_8PSK_COLD 0x0200
#define USB_PID_GENPIX_8PSK_WARM 0x0201
#define USB_PID_SIGMATEK_DVB_110 0x6610
#endif

View File

@@ -0,0 +1,285 @@
/*
* DVB USB library - provides a generic interface for a DVB USB device driver.
*
* dvb-usb-init.c
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dvb-usb-common.h"
/* debug */
int dvb_usb_debug;
module_param_named(debug,dvb_usb_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64,mem=128,uxfer=256 (or-able))." DVB_USB_DEBUG_STATUS);
int dvb_usb_disable_rc_polling;
module_param_named(disable_rc_polling, dvb_usb_disable_rc_polling, int, 0644);
MODULE_PARM_DESC(disable_rc_polling, "disable remote control polling (default: 0).");
static int dvb_usb_force_pid_filter_usage;
module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage, int, 0444);
MODULE_PARM_DESC(disable_rc_polling, "force all dvb-usb-devices to use a PID filter, if any (default: 0).");
static int dvb_usb_adapter_init(struct dvb_usb_device *d)
{
struct dvb_usb_adapter *adap;
int ret,n;
for (n = 0; n < d->props.num_adapters; n++) {
adap = &d->adapter[n];
adap->dev = d;
adap->id = n;
memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties));
/* speed - when running at FULL speed we need a HW PID filter */
if (d->udev->speed == USB_SPEED_FULL && !(adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER)) {
err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)");
return -ENODEV;
}
if ((d->udev->speed == USB_SPEED_FULL && adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) ||
(adap->props.caps & DVB_USB_ADAP_NEED_PID_FILTERING)) {
info("will use the device's hardware PID filter (table count: %d).",adap->props.pid_filter_count);
adap->pid_filtering = 1;
adap->max_feed_count = adap->props.pid_filter_count;
} else {
info("will pass the complete MPEG2 transport stream to the software demuxer.");
adap->pid_filtering = 0;
adap->max_feed_count = 255;
}
if (!adap->pid_filtering &&
dvb_usb_force_pid_filter_usage &&
adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) {
info("pid filter enabled by module option.");
adap->pid_filtering = 1;
adap->max_feed_count = adap->props.pid_filter_count;
}
if (adap->props.size_of_priv > 0) {
adap->priv = kzalloc(adap->props.size_of_priv,GFP_KERNEL);
if (adap->priv == NULL) {
err("no memory for priv for adapter %d.",n);
return -ENOMEM;
}
}
if ((ret = dvb_usb_adapter_stream_init(adap)) ||
(ret = dvb_usb_adapter_dvb_init(adap)) ||
(ret = dvb_usb_adapter_frontend_init(adap))) {
return ret;
}
d->num_adapters_initialized++;
d->state |= DVB_USB_STATE_DVB;
}
/*
* when reloading the driver w/o replugging the device
* sometimes a timeout occures, this helps
*/
if (d->props.generic_bulk_ctrl_endpoint != 0) {
usb_clear_halt(d->udev,usb_sndbulkpipe(d->udev,d->props.generic_bulk_ctrl_endpoint));
usb_clear_halt(d->udev,usb_rcvbulkpipe(d->udev,d->props.generic_bulk_ctrl_endpoint));
}
return 0;
}
static int dvb_usb_adapter_exit(struct dvb_usb_device *d)
{
int n;
for (n = 0; n < d->num_adapters_initialized; n++) {
dvb_usb_adapter_frontend_exit(&d->adapter[n]);
dvb_usb_adapter_dvb_exit(&d->adapter[n]);
dvb_usb_adapter_stream_exit(&d->adapter[n]);
kfree(d->adapter[n].priv);
}
d->num_adapters_initialized = 0;
d->state &= ~DVB_USB_STATE_DVB;
return 0;
}
/* general initialization functions */
static int dvb_usb_exit(struct dvb_usb_device *d)
{
deb_info("state before exiting everything: %x\n",d->state);
dvb_usb_remote_exit(d);
dvb_usb_adapter_exit(d);
dvb_usb_i2c_exit(d);
deb_info("state should be zero now: %x\n",d->state);
d->state = DVB_USB_STATE_INIT;
kfree(d->priv);
kfree(d);
return 0;
}
static int dvb_usb_init(struct dvb_usb_device *d)
{
int ret = 0;
mutex_init(&d->usb_mutex);
mutex_init(&d->i2c_mutex);
d->state = DVB_USB_STATE_INIT;
if (d->props.size_of_priv > 0) {
d->priv = kzalloc(d->props.size_of_priv,GFP_KERNEL);
if (d->priv == NULL) {
err("no memory for priv in 'struct dvb_usb_device'");
return -ENOMEM;
}
}
/* check the capabilities and set appropriate variables */
dvb_usb_device_power_ctrl(d, 1);
if ((ret = dvb_usb_i2c_init(d)) ||
(ret = dvb_usb_adapter_init(d))) {
dvb_usb_exit(d);
return ret;
}
if ((ret = dvb_usb_remote_init(d)))
err("could not initialize remote control.");
dvb_usb_device_power_ctrl(d, 0);
return 0;
}
/* determine the name and the state of the just found USB device */
static struct dvb_usb_device_description * dvb_usb_find_device(struct usb_device *udev,struct dvb_usb_device_properties *props, int *cold)
{
int i,j;
struct dvb_usb_device_description *desc = NULL;
*cold = -1;
for (i = 0; i < props->num_device_descs; i++) {
for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].cold_ids[j] != NULL; j++) {
deb_info("check for cold %x %x\n",props->devices[i].cold_ids[j]->idVendor, props->devices[i].cold_ids[j]->idProduct);
if (props->devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
props->devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
*cold = 1;
desc = &props->devices[i];
break;
}
}
if (desc != NULL)
break;
for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].warm_ids[j] != NULL; j++) {
deb_info("check for warm %x %x\n",props->devices[i].warm_ids[j]->idVendor, props->devices[i].warm_ids[j]->idProduct);
if (props->devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
props->devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
*cold = 0;
desc = &props->devices[i];
break;
}
}
}
if (desc != NULL && props->identify_state != NULL)
props->identify_state(udev,props,&desc,cold);
return desc;
}
int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff)
{
if (onoff)
d->powered++;
else
d->powered--;
if (d->powered == 0 || (onoff && d->powered == 1)) { // when switching from 1 to 0 or from 0 to 1
deb_info("power control: %d\n", onoff);
if (d->props.power_ctrl)
return d->props.power_ctrl(d, onoff);
}
return 0;
}
/*
* USB
*/
int dvb_usb_device_init(struct usb_interface *intf, struct dvb_usb_device_properties
*props, struct module *owner,struct dvb_usb_device **du)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct dvb_usb_device *d = NULL;
struct dvb_usb_device_description *desc = NULL;
int ret = -ENOMEM,cold=0;
if (du != NULL)
*du = NULL;
if ((desc = dvb_usb_find_device(udev,props,&cold)) == NULL) {
deb_err("something went very wrong, device was not found in current device list - let's see what comes next.\n");
return -ENODEV;
}
if (cold) {
info("found a '%s' in cold state, will try to load a firmware",desc->name);
ret = dvb_usb_download_firmware(udev,props);
if (!props->no_reconnect || ret != 0)
return ret;
}
info("found a '%s' in warm state.",desc->name);
d = kzalloc(sizeof(struct dvb_usb_device),GFP_KERNEL);
if (d == NULL) {
err("no memory for 'struct dvb_usb_device'");
return ret;
}
d->udev = udev;
memcpy(&d->props,props,sizeof(struct dvb_usb_device_properties));
d->desc = desc;
d->owner = owner;
usb_set_intfdata(intf, d);
if (du != NULL)
*du = d;
ret = dvb_usb_init(d);
if (ret == 0)
info("%s successfully initialized and connected.",desc->name);
else
info("%s error while loading driver (%d)",desc->name,ret);
return ret;
}
EXPORT_SYMBOL(dvb_usb_device_init);
void dvb_usb_device_exit(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
const char *name = "generic DVB-USB module";
usb_set_intfdata(intf,NULL);
if (d != NULL && d->desc != NULL) {
name = d->desc->name;
dvb_usb_exit(d);
}
info("%s successfully deinitialized and disconnected.",name);
}
EXPORT_SYMBOL(dvb_usb_device_exit);
MODULE_VERSION("1.0");
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("A library module containing commonly used USB and DVB function USB DVB devices");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,198 @@
/* dvb-usb-remote.c is part of the DVB USB library.
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* This file contains functions for initializing the the input-device and for handling remote-control-queries.
*/
#include "dvb-usb-common.h"
#include <linux/usb/input.h>
/* Remote-control poll function - called every dib->rc_query_interval ms to see
* whether the remote control has received anything.
*
* TODO: Fix the repeat rate of the input device.
*/
static void dvb_usb_read_remote_control(struct work_struct *work)
{
struct dvb_usb_device *d =
container_of(work, struct dvb_usb_device, rc_query_work.work);
u32 event;
int state;
/* TODO: need a lock here. We can simply skip checking for the remote control
if we're busy. */
/* when the parameter has been set to 1 via sysfs while the driver was running */
if (dvb_usb_disable_rc_polling)
return;
if (d->props.rc_query(d,&event,&state)) {
err("error while querying for an remote control event.");
goto schedule;
}
switch (state) {
case REMOTE_NO_KEY_PRESSED:
break;
case REMOTE_KEY_PRESSED:
deb_rc("key pressed\n");
d->last_event = event;
case REMOTE_KEY_REPEAT:
deb_rc("key repeated\n");
input_event(d->rc_input_dev, EV_KEY, event, 1);
input_event(d->rc_input_dev, EV_KEY, d->last_event, 0);
input_sync(d->rc_input_dev);
break;
default:
break;
}
/* improved repeat handling ???
switch (state) {
case REMOTE_NO_KEY_PRESSED:
deb_rc("NO KEY PRESSED\n");
if (d->last_state != REMOTE_NO_KEY_PRESSED) {
deb_rc("releasing event %d\n",d->last_event);
input_event(d->rc_input_dev, EV_KEY, d->last_event, 0);
input_sync(d->rc_input_dev);
}
d->last_state = REMOTE_NO_KEY_PRESSED;
d->last_event = 0;
break;
case REMOTE_KEY_PRESSED:
deb_rc("KEY PRESSED\n");
deb_rc("pressing event %d\n",event);
input_event(d->rc_input_dev, EV_KEY, event, 1);
input_sync(d->rc_input_dev);
d->last_event = event;
d->last_state = REMOTE_KEY_PRESSED;
break;
case REMOTE_KEY_REPEAT:
deb_rc("KEY_REPEAT\n");
if (d->last_state != REMOTE_NO_KEY_PRESSED) {
deb_rc("repeating event %d\n",d->last_event);
input_event(d->rc_input_dev, EV_KEY, d->last_event, 2);
input_sync(d->rc_input_dev);
d->last_state = REMOTE_KEY_REPEAT;
}
default:
break;
}
*/
schedule:
schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc_interval));
}
int dvb_usb_remote_init(struct dvb_usb_device *d)
{
struct input_dev *input_dev;
int i;
int err;
if (d->props.rc_key_map == NULL ||
d->props.rc_query == NULL ||
dvb_usb_disable_rc_polling)
return 0;
usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
input_dev->evbit[0] = BIT(EV_KEY);
input_dev->name = "IR-receiver inside an USB DVB receiver";
input_dev->phys = d->rc_phys;
usb_to_input_id(d->udev, &input_dev->id);
input_dev->cdev.dev = &d->udev->dev;
/* set the bits for the keys */
deb_rc("key map size: %d\n", d->props.rc_key_map_size);
for (i = 0; i < d->props.rc_key_map_size; i++) {
deb_rc("setting bit for event %d item %d\n",
d->props.rc_key_map[i].event, i);
set_bit(d->props.rc_key_map[i].event, input_dev->keybit);
}
/* Start the remote-control polling. */
if (d->props.rc_interval < 40)
d->props.rc_interval = 100; /* default */
/* setting these two values to non-zero, we have to manage key repeats */
input_dev->rep[REP_PERIOD] = d->props.rc_interval;
input_dev->rep[REP_DELAY] = d->props.rc_interval + 150;
err = input_register_device(input_dev);
if (err) {
input_free_device(input_dev);
return err;
}
d->rc_input_dev = input_dev;
INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control);
info("schedule remote query interval to %d msecs.", d->props.rc_interval);
schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc_interval));
d->state |= DVB_USB_STATE_REMOTE;
return 0;
}
int dvb_usb_remote_exit(struct dvb_usb_device *d)
{
if (d->state & DVB_USB_STATE_REMOTE) {
cancel_rearming_delayed_work(&d->rc_query_work);
flush_scheduled_work();
input_unregister_device(d->rc_input_dev);
}
d->state &= ~DVB_USB_STATE_REMOTE;
return 0;
}
#define DVB_USB_RC_NEC_EMPTY 0x00
#define DVB_USB_RC_NEC_KEY_PRESSED 0x01
#define DVB_USB_RC_NEC_KEY_REPEATED 0x02
int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d,
u8 keybuf[5], u32 *event, int *state)
{
int i;
struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
*event = 0;
*state = REMOTE_NO_KEY_PRESSED;
switch (keybuf[0]) {
case DVB_USB_RC_NEC_EMPTY:
break;
case DVB_USB_RC_NEC_KEY_PRESSED:
if ((u8) ~keybuf[1] != keybuf[2] ||
(u8) ~keybuf[3] != keybuf[4]) {
deb_err("remote control checksum failed.\n");
break;
}
/* See if we can match the raw key code. */
for (i = 0; i < d->props.rc_key_map_size; i++)
if (keymap[i].custom == keybuf[1] &&
keymap[i].data == keybuf[3]) {
*event = keymap[i].event;
*state = REMOTE_KEY_PRESSED;
return 0;
}
deb_err("key mapping failed - no appropriate key found in keymapping\n");
break;
case DVB_USB_RC_NEC_KEY_REPEATED:
*state = REMOTE_KEY_REPEAT;
break;
default:
deb_err("unkown type of remote status: %d\n",keybuf[0]);
break;
}
return 0;
}
EXPORT_SYMBOL(dvb_usb_nec_rc_key_to_event);

View File

@@ -0,0 +1,95 @@
/* dvb-usb-urb.c is part of the DVB USB library.
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* This file keeps functions for initializing and handling the
* USB and URB stuff.
*/
#include "dvb-usb-common.h"
int dvb_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf,
u16 rlen, int delay_ms)
{
int actlen,ret = -ENOMEM;
if (d->props.generic_bulk_ctrl_endpoint == 0) {
err("endpoint for generic control not specified.");
return -EINVAL;
}
if (wbuf == NULL || wlen == 0)
return -EINVAL;
if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
return ret;
deb_xfer(">>> ");
debug_dump(wbuf,wlen,deb_xfer);
ret = usb_bulk_msg(d->udev,usb_sndbulkpipe(d->udev,
d->props.generic_bulk_ctrl_endpoint), wbuf,wlen,&actlen,
2000);
if (ret)
err("bulk message failed: %d (%d/%d)",ret,wlen,actlen);
else
ret = actlen != wlen ? -1 : 0;
/* an answer is expected, and no error before */
if (!ret && rbuf && rlen) {
if (delay_ms)
msleep(delay_ms);
ret = usb_bulk_msg(d->udev,usb_rcvbulkpipe(d->udev,
d->props.generic_bulk_ctrl_endpoint),rbuf,rlen,&actlen,
2000);
if (ret)
err("recv bulk message failed: %d",ret);
else {
deb_xfer("<<< ");
debug_dump(rbuf,actlen,deb_xfer);
}
}
mutex_unlock(&d->usb_mutex);
return ret;
}
EXPORT_SYMBOL(dvb_usb_generic_rw);
int dvb_usb_generic_write(struct dvb_usb_device *d, u8 *buf, u16 len)
{
return dvb_usb_generic_rw(d,buf,len,NULL,0,0);
}
EXPORT_SYMBOL(dvb_usb_generic_write);
static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buffer, size_t length)
{
struct dvb_usb_adapter *adap = stream->user_priv;
if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB)
dvb_dmx_swfilter(&adap->demux, buffer, length);
}
static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer, size_t length)
{
struct dvb_usb_adapter *adap = stream->user_priv;
if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB)
dvb_dmx_swfilter_204(&adap->demux, buffer, length);
}
int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap)
{
adap->stream.udev = adap->dev->udev;
if (adap->props.caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS)
adap->stream.complete = dvb_usb_data_complete_204;
else
adap->stream.complete = dvb_usb_data_complete;
adap->stream.user_priv = adap;
return usb_urb_init(&adap->stream, &adap->props.stream);
}
int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap)
{
return usb_urb_exit(&adap->stream);
}

View File

@@ -0,0 +1,408 @@
/* dvb-usb.h is part of the DVB USB library.
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* the headerfile, all dvb-usb-drivers have to include.
*
* TODO: clean-up the structures for unused fields and update the comments
*/
#ifndef __DVB_USB_H__
#define __DVB_USB_H__
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/firmware.h>
#include <linux/mutex.h>
#include "dvb_frontend.h"
#include "dvb_demux.h"
#include "dvb_net.h"
#include "dmxdev.h"
#include "dvb-pll.h"
#include "dvb-usb-ids.h"
/* debug */
#ifdef CONFIG_DVB_USB_DEBUG
#define dprintk(var,level,args...) \
do { if ((var & level)) { printk(args); } } while (0)
#define debug_dump(b,l,func) {\
int loop_; \
for (loop_ = 0; loop_ < l; loop_++) func("%02x ", b[loop_]); \
func("\n");\
}
#define DVB_USB_DEBUG_STATUS
#else
#define dprintk(args...)
#define debug_dump(b,l,func)
#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)"
#endif
/* generic log methods - taken from usb.h */
#ifndef DVB_USB_LOG_PREFIX
#define DVB_USB_LOG_PREFIX "dvb-usb (please define a log prefix)"
#endif
#undef err
#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
#undef info
#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
#undef warn
#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
/**
* struct dvb_usb_device_description - name and its according USB IDs
* @name: real name of the box, regardless which DVB USB device class is in use
* @cold_ids: array of struct usb_device_id which describe the device in
* pre-firmware state
* @warm_ids: array of struct usb_device_id which describe the device in
* post-firmware state
*
* Each DVB USB device class can have one or more actual devices, this struct
* assigns a name to it.
*/
struct dvb_usb_device_description {
const char *name;
#define DVB_USB_ID_MAX_NUM 15
struct usb_device_id *cold_ids[DVB_USB_ID_MAX_NUM];
struct usb_device_id *warm_ids[DVB_USB_ID_MAX_NUM];
};
/**
* struct dvb_usb_rc_key - a remote control key and its input-event
* @custom: the vendor/custom part of the key
* @data: the actual key part
* @event: the input event assigned to key identified by custom and data
*/
struct dvb_usb_rc_key {
u8 custom,data;
u32 event;
};
struct dvb_usb_device;
struct dvb_usb_adapter;
struct usb_data_stream;
/**
* Properties of USB streaming - TODO this structure should be somewhere else
* describes the kind of USB transfer used for data-streaming.
* (BULK or ISOC)
*/
struct usb_data_stream_properties {
#define USB_BULK 1
#define USB_ISOC 2
int type;
int count;
int endpoint;
union {
struct {
int buffersize; /* per URB */
} bulk;
struct {
int framesperurb;
int framesize;
int interval;
} isoc;
} u;
};
/**
* struct dvb_usb_adapter_properties - properties of a dvb-usb-adapter.
* A DVB-USB-Adapter is basically a dvb_adapter which is present on a USB-device.
* @caps: capabilities of the DVB USB device.
* @pid_filter_count: number of PID filter position in the optional hardware
* PID-filter.
* @streaming_crtl: called to start and stop the MPEG2-TS streaming of the
* device (not URB submitting/killing).
* @pid_filter_ctrl: called to en/disable the PID filter, if any.
* @pid_filter: called to set/unset a PID for filtering.
* @frontend_attach: called to attach the possible frontends (fill fe-field
* of struct dvb_usb_device).
* @tuner_attach: called to attach the correct tuner and to fill pll_addr,
* pll_desc and pll_init_buf of struct dvb_usb_device).
* @stream: configuration of the USB streaming
*/
struct dvb_usb_adapter_properties {
#define DVB_USB_ADAP_HAS_PID_FILTER 0x01
#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02
#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04
#define DVB_USB_ADAP_RECEIVES_204_BYTE_TS 0x08
int caps;
int pid_filter_count;
int (*streaming_ctrl) (struct dvb_usb_adapter *, int);
int (*pid_filter_ctrl) (struct dvb_usb_adapter *, int);
int (*pid_filter) (struct dvb_usb_adapter *, int, u16, int);
int (*frontend_attach) (struct dvb_usb_adapter *);
int (*tuner_attach) (struct dvb_usb_adapter *);
struct usb_data_stream_properties stream;
int size_of_priv;
};
/**
* struct dvb_usb_device_properties - properties of a dvb-usb-device
* @usb_ctrl: which USB device-side controller is in use. Needed for firmware
* download.
* @firmware: name of the firmware file.
* @download_firmware: called to download the firmware when the usb_ctrl is
* DEVICE_SPECIFIC.
* @no_reconnect: device doesn't do a reconnect after downloading the firmware,
* so do the warm initialization right after it
*
* @size_of_priv: how many bytes shall be allocated for the private field
* of struct dvb_usb_device.
*
* @power_ctrl: called to enable/disable power of the device.
* @read_mac_address: called to read the MAC address of the device.
* @identify_state: called to determine the state (cold or warm), when it
* is not distinguishable by the USB IDs.
*
* @rc_key_map: a hard-wired array of struct dvb_usb_rc_key (NULL to disable
* remote control handling).
* @rc_key_map_size: number of items in @rc_key_map.
* @rc_query: called to query an event event.
* @rc_interval: time in ms between two queries.
*
* @i2c_algo: i2c_algorithm if the device has I2CoverUSB.
*
* @generic_bulk_ctrl_endpoint: most of the DVB USB devices have a generic
* endpoint which received control messages with bulk transfers. When this
* is non-zero, one can use dvb_usb_generic_rw and dvb_usb_generic_write-
* helper functions.
*
* @num_device_descs: number of struct dvb_usb_device_description in @devices
* @devices: array of struct dvb_usb_device_description compatibles with these
* properties.
*/
#define MAX_NO_OF_ADAPTER_PER_DEVICE 2
struct dvb_usb_device_properties {
#define DVB_USB_IS_AN_I2C_ADAPTER 0x01
int caps;
#define DEVICE_SPECIFIC 0
#define CYPRESS_AN2135 1
#define CYPRESS_AN2235 2
#define CYPRESS_FX2 3
int usb_ctrl;
int (*download_firmware) (struct usb_device *, const struct firmware *);
const char firmware[FIRMWARE_NAME_MAX];
int no_reconnect;
int size_of_priv;
int num_adapters;
struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
int (*power_ctrl) (struct dvb_usb_device *, int);
int (*read_mac_address) (struct dvb_usb_device *, u8 []);
int (*identify_state) (struct usb_device *, struct dvb_usb_device_properties *,
struct dvb_usb_device_description **, int *);
/* remote control properties */
#define REMOTE_NO_KEY_PRESSED 0x00
#define REMOTE_KEY_PRESSED 0x01
#define REMOTE_KEY_REPEAT 0x02
struct dvb_usb_rc_key *rc_key_map;
int rc_key_map_size;
int (*rc_query) (struct dvb_usb_device *, u32 *, int *);
int rc_interval;
struct i2c_algorithm *i2c_algo;
int generic_bulk_ctrl_endpoint;
int num_device_descs;
struct dvb_usb_device_description devices[9];
};
/**
* struct usb_data_stream - generic object of an USB stream
* @buf_num: number of buffer allocated.
* @buf_size: size of each buffer in buf_list.
* @buf_list: array containing all allocate buffers for streaming.
* @dma_addr: list of dma_addr_t for each buffer in buf_list.
*
* @urbs_initialized: number of URBs initialized.
* @urbs_submitted: number of URBs submitted.
*/
#define MAX_NO_URBS_FOR_DATA_STREAM 10
struct usb_data_stream {
struct usb_device *udev;
struct usb_data_stream_properties props;
#define USB_STATE_INIT 0x00
#define USB_STATE_URB_BUF 0x01
int state;
void (*complete) (struct usb_data_stream *, u8 *, size_t);
struct urb *urb_list[MAX_NO_URBS_FOR_DATA_STREAM];
int buf_num;
unsigned long buf_size;
u8 *buf_list[MAX_NO_URBS_FOR_DATA_STREAM];
dma_addr_t dma_addr[MAX_NO_URBS_FOR_DATA_STREAM];
int urbs_initialized;
int urbs_submitted;
void *user_priv;
};
/**
* struct dvb_usb_adapter - a DVB adapter on a USB device
* @id: index of this adapter (starting with 0).
*
* @feedcount: number of reqested feeds (used for streaming-activation)
* @pid_filtering: is hardware pid_filtering used or not.
*
* @pll_addr: I2C address of the tuner for programming
* @pll_init: array containing the initialization buffer
* @pll_desc: pointer to the appropriate struct dvb_pll_desc
* @tuner_pass_ctrl: called to (de)activate tuner passthru of the demod or the board
*
* @dvb_adap: device's dvb_adapter.
* @dmxdev: device's dmxdev.
* @demux: device's software demuxer.
* @dvb_net: device's dvb_net interfaces.
* @dvb_frontend: device's frontend.
* @max_feed_count: how many feeds can be handled simultaneously by this
* device
*
* @fe_init: rerouted frontend-init (wakeup) function.
* @fe_sleep: rerouted frontend-sleep function.
*
* @stream: the usb data stream.
*/
struct dvb_usb_adapter {
struct dvb_usb_device *dev;
struct dvb_usb_adapter_properties props;
#define DVB_USB_ADAP_STATE_INIT 0x000
#define DVB_USB_ADAP_STATE_DVB 0x001
int state;
u8 id;
int feedcount;
int pid_filtering;
/* tuner programming information */
u8 pll_addr;
u8 pll_init[4];
struct dvb_pll_desc *pll_desc;
int (*tuner_pass_ctrl) (struct dvb_frontend *, int, u8);
/* dvb */
struct dvb_adapter dvb_adap;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dvb_net dvb_net;
struct dvb_frontend *fe;
int max_feed_count;
int (*fe_init) (struct dvb_frontend *);
int (*fe_sleep) (struct dvb_frontend *);
struct usb_data_stream stream;
void *priv;
};
/**
* struct dvb_usb_device - object of a DVB USB device
* @props: copy of the struct dvb_usb_properties this device belongs to.
* @desc: pointer to the device's struct dvb_usb_device_description.
* @state: initialization and runtime state of the device.
*
* @powered: indicated whether the device is power or not.
* Powered is in/decremented for each call to modify the state.
* @udev: pointer to the device's struct usb_device.
*
* @usb_mutex: semaphore of USB control messages (reading needs two messages)
* @i2c_mutex: semaphore for i2c-transfers
*
* @i2c_adap: device's i2c_adapter if it uses I2CoverUSB
*
* @rc_input_dev: input device for the remote control.
* @rc_query_work: struct work_struct frequent rc queries
* @last_event: last triggered event
* @last_state: last state (no, pressed, repeat)
* @owner: owner of the dvb_adapter
* @priv: private data of the actual driver (allocate by dvb-usb, size defined
* in size_of_priv of dvb_usb_properties).
*/
struct dvb_usb_device {
struct dvb_usb_device_properties props;
struct dvb_usb_device_description *desc;
struct usb_device *udev;
#define DVB_USB_STATE_INIT 0x000
#define DVB_USB_STATE_I2C 0x001
#define DVB_USB_STATE_DVB 0x002
#define DVB_USB_STATE_REMOTE 0x004
int state;
int powered;
/* locking */
struct mutex usb_mutex;
/* i2c */
struct mutex i2c_mutex;
struct i2c_adapter i2c_adap;
int num_adapters_initialized;
struct dvb_usb_adapter adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
/* remote control */
struct input_dev *rc_input_dev;
char rc_phys[64];
struct delayed_work rc_query_work;
u32 last_event;
int last_state;
struct module *owner;
void *priv;
};
extern int dvb_usb_device_init(struct usb_interface *, struct dvb_usb_device_properties *, struct module *, struct dvb_usb_device **);
extern void dvb_usb_device_exit(struct usb_interface *);
/* the generic read/write method for device control */
extern int dvb_usb_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16,int);
extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16);
/* commonly used remote control parsing */
extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *);
/* commonly used pll init and set functions */
extern int dvb_usb_tuner_init_i2c(struct dvb_frontend *);
extern int dvb_usb_tuner_calc_regs(struct dvb_frontend *, struct dvb_frontend_parameters *, u8 *buf, int buf_len);
extern int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *, struct dvb_frontend_parameters *);
/* commonly used firmware download types and function */
struct hexline {
u8 len;
u32 addr;
u8 type;
u8 data[255];
u8 chk;
};
extern int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type);
extern int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx, int *pos);
#endif

View File

@@ -0,0 +1,231 @@
/* DVB USB compliant linux driver for GL861 USB2.0 devices.
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "gl861.h"
#include "zl10353.h"
#include "qt1010.h"
/* debug */
int dvb_usb_gl861_debug;
module_param_named(debug,dvb_usb_gl861_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u16 index;
u16 value = addr << 8;
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 req, type;
if (wo) {
req = GL861_REQ_I2C_WRITE;
type = GL861_WRITE;
} else { /* rw */
req = GL861_REQ_I2C_READ;
type = GL861_READ;
}
switch (wlen) {
case 1:
index = wbuf[0];
break;
case 2:
index = wbuf[0];
value = value + wbuf[1];
break;
default:
warn("wlen = %x, aborting.", wlen);
return -EINVAL;
}
return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type,
value, index, rbuf, rlen, 2000);
}
/* I2C */
static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
if (num > 2)
return -EINVAL;
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, msg[i+1].buf, msg[i+1].len) < 0)
break;
i++;
} else
if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, NULL, 0) < 0)
break;
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 gl861_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm gl861_i2c_algo = {
.master_xfer = gl861_i2c_xfer,
.functionality = gl861_i2c_func,
};
/* Callbacks for DVB USB */
static int gl861_identify_state(struct usb_device *udev,
struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc,
int *cold)
{
*cold = 0;
return 0;
}
static struct zl10353_config gl861_zl10353_config = {
.demod_address = 0x1e,
.no_tuner = 1,
.parallel_ts = 1,
};
static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
{
if ((adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
&adap->dev->i2c_adap)) != NULL) {
return 0;
}
return -EIO;
}
static struct qt1010_config gl861_qt1010_config = {
.i2c_address = 0xc4
};
static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
adap->fe, &adap->dev->i2c_adap,
&gl861_qt1010_config) == NULL ? -ENODEV : 0;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties gl861_properties;
static int gl861_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dvb_usb_device *d;
struct usb_host_interface *alt;
int ret;
if (intf->num_altsetting < 2)
return -ENODEV;
if ((ret = dvb_usb_device_init(intf, &gl861_properties, THIS_MODULE, &d)) == 0) {
alt = usb_altnum_to_altsetting(intf, 0);
if (alt == NULL) {
deb_rc("not alt found!\n");
return -ENODEV;
}
ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
alt->desc.bAlternateSetting);
}
return ret;
}
static struct usb_device_id gl861_table [] = {
{ USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, gl861_table);
static struct dvb_usb_device_properties gl861_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.size_of_priv = 0,
.identify_state = gl861_identify_state,
.num_adapters = 1,
.adapter = {{
.frontend_attach = gl861_frontend_attach,
.tuner_attach = gl861_tuner_attach,
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x81,
.u = {
.bulk = {
.buffersize = 512,
}
}
},
}},
.i2c_algo = &gl861_i2c_algo,
.num_device_descs = 1,
.devices = {
{ "MSI Mega Sky 55801 DVB-T USB2.0",
{ &gl861_table[0], NULL },
{ NULL },
},
}
};
static struct usb_driver gl861_driver = {
.name = "dvb_usb_gl861",
.probe = gl861_probe,
.disconnect = dvb_usb_device_exit,
.id_table = gl861_table,
};
/* module stuff */
static int __init gl861_module_init(void)
{
int ret;
if ((ret = usb_register(&gl861_driver))) {
err("usb_register failed. Error number %d", ret);
return ret;
}
return 0;
}
static void __exit gl861_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&gl861_driver);
}
module_init (gl861_module_init);
module_exit (gl861_module_exit);
MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>");
MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,15 @@
#ifndef _DVB_USB_GL861_H_
#define _DVB_USB_GL861_H_
#define DVB_USB_LOG_PREFIX "gl861"
#include "dvb-usb.h"
#define deb_rc(args...) dprintk(dvb_usb_gl861_debug,0x01,args)
#define GL861_WRITE 0x40
#define GL861_READ 0xc0
#define GL861_REQ_I2C_WRITE 0x01
#define GL861_REQ_I2C_READ 0x02
#endif

View File

@@ -0,0 +1,272 @@
/* DVB USB compliant Linux driver for the
* - GENPIX 8pks/qpsk USB2.0 DVB-S module
*
* Copyright (C) 2006 Alan Nisota (alannisota@gmail.com)
*
* Thanks to GENPIX for the sample code used to implement this module.
*
* This module is based off the vp7045 and vp702x modules
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "gp8psk.h"
struct gp8psk_fe_state {
struct dvb_frontend fe;
struct dvb_usb_device *d;
u16 snr;
unsigned long next_snr_check;
};
static int gp8psk_fe_read_status(struct dvb_frontend* fe, fe_status_t *status)
{
struct gp8psk_fe_state *st = fe->demodulator_priv;
u8 lock;
if (gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0, 0, &lock,1))
return -EINVAL;
if (lock)
*status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER;
else
*status = 0;
return 0;
}
/* not supported by this Frontend */
static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
{
(void) fe;
*ber = 0;
return 0;
}
/* not supported by this Frontend */
static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
{
(void) fe;
*unc = 0;
return 0;
}
static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
{
struct gp8psk_fe_state *st = fe->demodulator_priv;
u8 buf[2];
if (time_after(jiffies,st->next_snr_check)) {
gp8psk_usb_in_op(st->d,GET_SIGNAL_STRENGTH,0,0,buf,2);
*snr = (int)(buf[1]) << 8 | buf[0];
/* snr is reported in dBu*256 */
/* snr / 38.4 ~= 100% strength */
/* snr * 17 returns 100% strength as 65535 */
if (*snr <= 3855)
*snr = (*snr<<4) + *snr; // snr * 17
else
*snr = 65535;
st->next_snr_check = jiffies + (10*HZ)/1000;
} else {
*snr = st->snr;
}
return 0;
}
static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
{
return gp8psk_fe_read_snr(fe, strength);
}
static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
{
tune->min_delay_ms = 800;
return 0;
}
static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct gp8psk_fe_state *state = fe->demodulator_priv;
u8 cmd[10];
u32 freq = fep->frequency * 1000;
cmd[4] = freq & 0xff;
cmd[5] = (freq >> 8) & 0xff;
cmd[6] = (freq >> 16) & 0xff;
cmd[7] = (freq >> 24) & 0xff;
switch(fe->ops.info.type) {
case FE_QPSK:
cmd[0] = fep->u.qpsk.symbol_rate & 0xff;
cmd[1] = (fep->u.qpsk.symbol_rate >> 8) & 0xff;
cmd[2] = (fep->u.qpsk.symbol_rate >> 16) & 0xff;
cmd[3] = (fep->u.qpsk.symbol_rate >> 24) & 0xff;
cmd[8] = ADV_MOD_DVB_QPSK;
cmd[9] = 0x03; /*ADV_MOD_FEC_XXX*/
break;
default:
// other modes are unsuported right now
cmd[0] = 0;
cmd[1] = 0;
cmd[2] = 0;
cmd[3] = 0;
cmd[8] = 0;
cmd[9] = 0;
break;
}
gp8psk_usb_out_op(state->d,TUNE_8PSK,0,0,cmd,10);
state->next_snr_check = jiffies;
return 0;
}
static int gp8psk_fe_get_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
return 0;
}
static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe,
struct dvb_diseqc_master_cmd *m)
{
struct gp8psk_fe_state *st = fe->demodulator_priv;
deb_fe("%s\n",__FUNCTION__);
if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, m->msg[0], 0,
m->msg, m->msg_len)) {
return -EINVAL;
}
return 0;
}
static int gp8psk_fe_send_diseqc_burst (struct dvb_frontend* fe,
fe_sec_mini_cmd_t burst)
{
struct gp8psk_fe_state *st = fe->demodulator_priv;
u8 cmd;
deb_fe("%s\n",__FUNCTION__);
/* These commands are certainly wrong */
cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01;
if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, cmd, 0,
&cmd, 0)) {
return -EINVAL;
}
return 0;
}
static int gp8psk_fe_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
{
struct gp8psk_fe_state* state = fe->demodulator_priv;
if (gp8psk_usb_out_op(state->d,SET_22KHZ_TONE,
(tone == SEC_TONE_ON), 0, NULL, 0)) {
return -EINVAL;
}
return 0;
}
static int gp8psk_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
{
struct gp8psk_fe_state* state = fe->demodulator_priv;
if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE,
voltage == SEC_VOLTAGE_18, 0, NULL, 0)) {
return -EINVAL;
}
return 0;
}
static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd)
{
struct gp8psk_fe_state* state = fe->demodulator_priv;
u8 cmd = sw_cmd & 0x7f;
if (gp8psk_usb_out_op(state->d,SET_DN_SWITCH, cmd, 0,
NULL, 0)) {
return -EINVAL;
}
if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, !!(sw_cmd & 0x80),
0, NULL, 0)) {
return -EINVAL;
}
return 0;
}
static void gp8psk_fe_release(struct dvb_frontend* fe)
{
struct gp8psk_fe_state *state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops gp8psk_fe_ops;
struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d)
{
struct gp8psk_fe_state *s = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL);
if (s == NULL)
goto error;
s->d = d;
memcpy(&s->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops));
s->fe.demodulator_priv = s;
goto success;
error:
return NULL;
success:
return &s->fe;
}
static struct dvb_frontend_ops gp8psk_fe_ops = {
.info = {
.name = "Genpix 8psk-USB DVB-S",
.type = FE_QPSK,
.frequency_min = 950000,
.frequency_max = 2150000,
.frequency_stepsize = 100,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
.symbol_rate_tolerance = 500, /* ppm */
.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
},
.release = gp8psk_fe_release,
.init = NULL,
.sleep = NULL,
.set_frontend = gp8psk_fe_set_frontend,
.get_frontend = gp8psk_fe_get_frontend,
.get_tune_settings = gp8psk_fe_get_tune_settings,
.read_status = gp8psk_fe_read_status,
.read_ber = gp8psk_fe_read_ber,
.read_signal_strength = gp8psk_fe_read_signal_strength,
.read_snr = gp8psk_fe_read_snr,
.read_ucblocks = gp8psk_fe_read_unc_blocks,
.diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg,
.diseqc_send_burst = gp8psk_fe_send_diseqc_burst,
.set_tone = gp8psk_fe_set_tone,
.set_voltage = gp8psk_fe_set_voltage,
.dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd,
};

View File

@@ -0,0 +1,258 @@
/* DVB USB compliant Linux driver for the
* - GENPIX 8pks/qpsk USB2.0 DVB-S module
*
* Copyright (C) 2006 Alan Nisota (alannisota@gmail.com)
*
* Thanks to GENPIX for the sample code used to implement this module.
*
* This module is based off the vp7045 and vp702x modules
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "gp8psk.h"
/* debug */
static char bcm4500_firmware[] = "dvb-usb-gp8psk-02.fw";
int dvb_usb_gp8psk_debug;
module_param_named(debug,dvb_usb_gp8psk_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
{
int ret = 0,try = 0;
if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
return ret;
while (ret >= 0 && ret != blen && try < 3) {
ret = usb_control_msg(d->udev,
usb_rcvctrlpipe(d->udev,0),
req,
USB_TYPE_VENDOR | USB_DIR_IN,
value,index,b,blen,
2000);
deb_info("reading number %d (ret: %d)\n",try,ret);
try++;
}
if (ret < 0 || ret != blen) {
warn("usb in operation failed.");
ret = -EIO;
} else
ret = 0;
deb_xfer("in: req. %x, val: %x, ind: %x, buffer: ",req,value,index);
debug_dump(b,blen,deb_xfer);
mutex_unlock(&d->usb_mutex);
return ret;
}
int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
deb_xfer("out: req. %x, val: %x, ind: %x, buffer: ",req,value,index);
debug_dump(b,blen,deb_xfer);
if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
return ret;
if (usb_control_msg(d->udev,
usb_sndctrlpipe(d->udev,0),
req,
USB_TYPE_VENDOR | USB_DIR_OUT,
value,index,b,blen,
2000) != blen) {
warn("usb out operation failed.");
ret = -EIO;
} else
ret = 0;
mutex_unlock(&d->usb_mutex);
return ret;
}
static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d)
{
int ret;
const struct firmware *fw = NULL;
u8 *ptr, *buf;
if ((ret = request_firmware(&fw, bcm4500_firmware,
&d->udev->dev)) != 0) {
err("did not find the bcm4500 firmware file. (%s) "
"Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)",
bcm4500_firmware,ret);
return ret;
}
ret = -EINVAL;
if (gp8psk_usb_out_op(d, LOAD_BCM4500,1,0,NULL, 0))
goto out_rel_fw;
info("downloaidng bcm4500 firmware from file '%s'",bcm4500_firmware);
ptr = fw->data;
buf = kmalloc(512, GFP_KERNEL | GFP_DMA);
while (ptr[0] != 0xff) {
u16 buflen = ptr[0] + 4;
if (ptr + buflen >= fw->data + fw->size) {
err("failed to load bcm4500 firmware.");
goto out_free;
}
memcpy(buf, ptr, buflen);
if (dvb_usb_generic_write(d, buf, buflen)) {
err("failed to load bcm4500 firmware.");
goto out_free;
}
ptr += buflen;
}
ret = 0;
out_free:
kfree(buf);
out_rel_fw:
release_firmware(fw);
return ret;
}
static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff)
{
u8 status, buf;
if (onoff) {
gp8psk_usb_in_op(d, GET_8PSK_CONFIG,0,0,&status,1);
if (! (status & 0x01)) /* started */
if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1))
return -EINVAL;
if (! (status & 0x02)) /* BCM4500 firmware loaded */
if(gp8psk_load_bcm4500fw(d))
return EINVAL;
if (! (status & 0x04)) /* LNB Power */
if (gp8psk_usb_in_op(d, START_INTERSIL, 1, 0,
&buf, 1))
return EINVAL;
/* Set DVB mode */
if(gp8psk_usb_out_op(d, SET_DVB_MODE, 1, 0, NULL, 0))
return -EINVAL;
gp8psk_usb_in_op(d, GET_8PSK_CONFIG,0,0,&status,1);
} else {
/* Turn off LNB power */
if (gp8psk_usb_in_op(d, START_INTERSIL, 0, 0, &buf, 1))
return EINVAL;
/* Turn off 8psk power */
if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1))
return -EINVAL;
}
return 0;
}
static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
return gp8psk_usb_out_op(adap->dev, ARM_TRANSFER, onoff, 0 , NULL, 0);
}
static int gp8psk_frontend_attach(struct dvb_usb_adapter *adap)
{
adap->fe = gp8psk_fe_attach(adap->dev);
return 0;
}
static struct dvb_usb_device_properties gp8psk_properties;
static int gp8psk_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return dvb_usb_device_init(intf,&gp8psk_properties,THIS_MODULE,NULL);
}
static struct usb_device_id gp8psk_usb_table [] = {
{ USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_COLD) },
{ USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_WARM) },
{ 0 },
};
MODULE_DEVICE_TABLE(usb, gp8psk_usb_table);
static struct dvb_usb_device_properties gp8psk_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-gp8psk-01.fw",
.num_adapters = 1,
.adapter = {
{
.streaming_ctrl = gp8psk_streaming_ctrl,
.frontend_attach = gp8psk_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x82,
.u = {
.bulk = {
.buffersize = 8192,
}
}
},
}
},
.power_ctrl = gp8psk_power_ctrl,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ .name = "Genpix 8PSK-USB DVB-S USB2.0 receiver",
.cold_ids = { &gp8psk_usb_table[0], NULL },
.warm_ids = { &gp8psk_usb_table[1], NULL },
},
{ NULL },
}
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver gp8psk_usb_driver = {
.name = "dvb_usb_gp8psk",
.probe = gp8psk_usb_probe,
.disconnect = dvb_usb_device_exit,
.id_table = gp8psk_usb_table,
};
/* module stuff */
static int __init gp8psk_usb_module_init(void)
{
int result;
if ((result = usb_register(&gp8psk_usb_driver))) {
err("usb_register failed. (%d)",result);
return result;
}
return 0;
}
static void __exit gp8psk_usb_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&gp8psk_usb_driver);
}
module_init(gp8psk_usb_module_init);
module_exit(gp8psk_usb_module_exit);
MODULE_AUTHOR("Alan Nisota <alannisota@gamil.com>");
MODULE_DESCRIPTION("Driver for Genpix 8psk-USB DVB-S USB2.0");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,79 @@
/* DVB USB compliant Linux driver for the
* - GENPIX 8pks/qpsk USB2.0 DVB-S module
*
* Copyright (C) 2006 Alan Nisota (alannisota@gmail.com)
*
* Thanks to GENPIX for the sample code used to implement this module.
*
* This module is based off the vp7045 and vp702x modules
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#ifndef _DVB_USB_GP8PSK_H_
#define _DVB_USB_GP8PSK_H_
#define DVB_USB_LOG_PREFIX "gp8psk"
#include "dvb-usb.h"
extern int dvb_usb_gp8psk_debug;
#define deb_info(args...) dprintk(dvb_usb_gp8psk_debug,0x01,args)
#define deb_xfer(args...) dprintk(dvb_usb_gp8psk_debug,0x02,args)
#define deb_rc(args...) dprintk(dvb_usb_gp8psk_debug,0x04,args)
#define deb_fe(args...) dprintk(dvb_usb_gp8psk_debug,0x08,args)
/* gp8psk commands */
/* Twinhan Vendor requests */
#define TH_COMMAND_IN 0xC0
#define TH_COMMAND_OUT 0xC1
/* command bytes */
#define GET_8PSK_CONFIG 0x80
#define SET_8PSK_CONFIG 0x81
#define ARM_TRANSFER 0x85
#define TUNE_8PSK 0x86
#define GET_SIGNAL_STRENGTH 0x87
#define LOAD_BCM4500 0x88
#define BOOT_8PSK 0x89
#define START_INTERSIL 0x8A
#define SET_LNB_VOLTAGE 0x8B
#define SET_22KHZ_TONE 0x8C
#define SEND_DISEQC_COMMAND 0x8D
#define SET_DVB_MODE 0x8E
#define SET_DN_SWITCH 0x8F
#define GET_SIGNAL_LOCK 0x90
/* Satellite modulation modes */
#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */
#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */
#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */
#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */
#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */
#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */
#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */
#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */
#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */
#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */
#define GET_USB_SPEED 0x07
#define USB_SPEED_LOW 0
#define USB_SPEED_FULL 1
#define USB_SPEED_HIGH 2
#define RESET_FX2 0x13
#define FW_VERSION_READ 0x0B
#define VENDOR_STRING_READ 0x0C
#define PRODUCT_STRING_READ 0x0D
#define FW_BCD_VERSION_READ 0x14
extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d);
extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen);
#endif

View File

@@ -0,0 +1,541 @@
/* DVB USB compliant linux driver for MSI Mega Sky 580 DVB-T USB2.0 receiver
*
* Copyright (C) 2006 Aapo Tahkola (aet@rasterburn.org)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "m920x.h"
#include "mt352.h"
#include "mt352_priv.h"
#include "qt1010.h"
/* debug */
static int dvb_usb_m920x_debug;
module_param_named(debug,dvb_usb_m920x_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
static struct dvb_usb_rc_key megasky_rc_keys [] = {
{ 0x0, 0x12, KEY_POWER },
{ 0x0, 0x1e, KEY_CYCLEWINDOWS }, /* min/max */
{ 0x0, 0x02, KEY_CHANNELUP },
{ 0x0, 0x05, KEY_CHANNELDOWN },
{ 0x0, 0x03, KEY_VOLUMEUP },
{ 0x0, 0x06, KEY_VOLUMEDOWN },
{ 0x0, 0x04, KEY_MUTE },
{ 0x0, 0x07, KEY_OK }, /* TS */
{ 0x0, 0x08, KEY_STOP },
{ 0x0, 0x09, KEY_MENU }, /* swap */
{ 0x0, 0x0a, KEY_REWIND },
{ 0x0, 0x1b, KEY_PAUSE },
{ 0x0, 0x1f, KEY_FASTFORWARD },
{ 0x0, 0x0c, KEY_RECORD },
{ 0x0, 0x0d, KEY_CAMERA }, /* screenshot */
{ 0x0, 0x0e, KEY_COFFEE }, /* "MTS" */
};
static inline int m9206_read(struct usb_device *udev, u8 request, u16 value,\
u16 index, void *data, int size)
{
int ret;
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
request, USB_TYPE_VENDOR | USB_DIR_IN,
value, index, data, size, 2000);
if (ret < 0)
return ret;
if (ret != size)
return -EIO;
return 0;
}
static inline int m9206_write(struct usb_device *udev, u8 request,
u16 value, u16 index)
{
int ret;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
request, USB_TYPE_VENDOR | USB_DIR_OUT,
value, index, NULL, 0, 2000);
return ret;
}
static int m9206_rc_init(struct usb_device *udev)
{
int ret = 0;
/* Remote controller init. */
if ((ret = m9206_write(udev, M9206_CORE, 0xa8, M9206_RC_INIT2)) != 0)
return ret;
if ((ret = m9206_write(udev, M9206_CORE, 0x51, M9206_RC_INIT1)) != 0)
return ret;
return ret;
}
static int m9206_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
struct m9206_state *m = d->priv;
int i, ret = 0;
u8 rc_state[2];
if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0)
goto unlock;
if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0)
goto unlock;
for (i = 0; i < ARRAY_SIZE(megasky_rc_keys); i++)
if (megasky_rc_keys[i].data == rc_state[1]) {
*event = megasky_rc_keys[i].event;
switch(rc_state[0]) {
case 0x80:
*state = REMOTE_NO_KEY_PRESSED;
goto unlock;
case 0x93:
case 0x92:
m->rep_count = 0;
*state = REMOTE_KEY_PRESSED;
goto unlock;
case 0x91:
/* For comfort. */
if (++m->rep_count > 2)
*state = REMOTE_KEY_REPEAT;
goto unlock;
default:
deb_rc("Unexpected rc response %x\n", rc_state[0]);
*state = REMOTE_NO_KEY_PRESSED;
goto unlock;
}
}
if (rc_state[1] != 0)
deb_rc("Unknown rc key %x\n", rc_state[1]);
*state = REMOTE_NO_KEY_PRESSED;
unlock:
return ret;
}
/* I2C */
static int m9206_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct m9206_state *m = d->priv;
int i;
int ret = 0;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
if (num > 2)
return -EINVAL;
for (i = 0; i < num; i++) {
if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].addr, 0x80)) != 0)
goto unlock;
if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[0], 0x0)) != 0)
goto unlock;
if (i + 1 < num && msg[i + 1].flags & I2C_M_RD) {
int i2c_i;
for (i2c_i = 0; i2c_i < M9206_I2C_MAX; i2c_i++)
if (msg[i].addr == m->i2c_r[i2c_i].addr)
break;
if (i2c_i >= M9206_I2C_MAX) {
deb_rc("No magic for i2c addr!\n");
ret = -EINVAL;
goto unlock;
}
if ((ret = m9206_write(d->udev, M9206_I2C, m->i2c_r[i2c_i].magic, 0x80)) != 0)
goto unlock;
if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x60, msg[i + 1].buf, msg[i + 1].len)) != 0)
goto unlock;
i++;
} else {
if (msg[i].len != 2)
return -EINVAL;
if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[1], 0x40)) != 0)
goto unlock;
}
}
ret = i;
unlock:
mutex_unlock(&d->i2c_mutex);
return ret;
}
static u32 m9206_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm m9206_i2c_algo = {
.master_xfer = m9206_i2c_xfer,
.functionality = m9206_i2c_func,
};
static int m9206_set_filter(struct dvb_usb_adapter *adap, int type, int idx,
int pid)
{
int ret = 0;
if (pid >= 0x8000)
return -EINVAL;
pid |= 0x8000;
if ((ret = m9206_write(adap->dev->udev, M9206_FILTER, pid, (type << 8) | (idx * 4) )) != 0)
return ret;
if ((ret = m9206_write(adap->dev->udev, M9206_FILTER, 0, (type << 8) | (idx * 4) )) != 0)
return ret;
return ret;
}
static int m9206_update_filters(struct dvb_usb_adapter *adap)
{
struct m9206_state *m = adap->dev->priv;
int enabled = m->filtering_enabled;
int i, ret = 0, filter = 0;
for (i = 0; i < M9206_MAX_FILTERS; i++)
if (m->filters[i] == 8192)
enabled = 0;
/* Disable all filters */
if ((ret = m9206_set_filter(adap, 0x81, 1, enabled)) != 0)
return ret;
for (i = 0; i < M9206_MAX_FILTERS; i++)
if ((ret = m9206_set_filter(adap, 0x81, i + 2, 0)) != 0)
return ret;
if ((ret = m9206_set_filter(adap, 0x82, 0, 0x0)) != 0)
return ret;
/* Set */
if (enabled) {
for (i = 0; i < M9206_MAX_FILTERS; i++) {
if (m->filters[i] == 0)
continue;
if ((ret = m9206_set_filter(adap, 0x81, filter + 2, m->filters[i])) != 0)
return ret;
filter++;
}
}
if ((ret = m9206_set_filter(adap, 0x82, 0, 0x02f5)) != 0)
return ret;
return ret;
}
static int m9206_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct m9206_state *m = adap->dev->priv;
m->filtering_enabled = onoff ? 1 : 0;
return m9206_update_filters(adap);
}
static int m9206_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
int onoff)
{
struct m9206_state *m = adap->dev->priv;
m->filters[index] = onoff ? pid : 0;
return m9206_update_filters(adap);
}
static int m9206_firmware_download(struct usb_device *udev,
const struct firmware *fw)
{
u16 value, index, size;
u8 read[4], *buff;
int i, pass, ret = 0;
buff = kmalloc(65536, GFP_KERNEL);
if ((ret = m9206_read(udev, M9206_FILTER, 0x0, 0x8000, read, 4)) != 0)
goto done;
deb_rc("%x %x %x %x\n", read[0], read[1], read[2], read[3]);
if ((ret = m9206_read(udev, M9206_FW, 0x0, 0x0, read, 1)) != 0)
goto done;
deb_rc("%x\n", read[0]);
for (pass = 0; pass < 2; pass++) {
for (i = 0; i + (sizeof(u16) * 3) < fw->size;) {
value = le16_to_cpu(*(u16 *)(fw->data + i));
i += sizeof(u16);
index = le16_to_cpu(*(u16 *)(fw->data + i));
i += sizeof(u16);
size = le16_to_cpu(*(u16 *)(fw->data + i));
i += sizeof(u16);
if (pass == 1) {
/* Will stall if using fw->data ... */
memcpy(buff, fw->data + i, size);
ret = usb_control_msg(udev, usb_sndctrlpipe(udev,0),
M9206_FW,
USB_TYPE_VENDOR | USB_DIR_OUT,
value, index, buff, size, 20);
if (ret != size) {
deb_rc("error while uploading fw!\n");
ret = -EIO;
goto done;
}
msleep(3);
}
i += size;
}
if (i != fw->size) {
ret = -EINVAL;
goto done;
}
}
msleep(36);
/* m9206 will disconnect itself from the bus after this. */
(void) m9206_write(udev, M9206_CORE, 0x01, M9206_FW_GO);
deb_rc("firmware uploaded!\n");
done:
kfree(buff);
return ret;
}
/* Callbacks for DVB USB */
static int megasky_identify_state(struct usb_device *udev,
struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc,
int *cold)
{
struct usb_host_interface *alt;
alt = usb_altnum_to_altsetting(usb_ifnum_to_if(udev, 0), 1);
*cold = (alt == NULL) ? 1 : 0;
return 0;
}
static int megasky_mt352_demod_init(struct dvb_frontend *fe)
{
u8 config[] = { CONFIG, 0x3d };
u8 clock[] = { CLOCK_CTL, 0x30 };
u8 reset[] = { RESET, 0x80 };
u8 adc_ctl[] = { ADC_CTL_1, 0x40 };
u8 agc[] = { AGC_TARGET, 0x1c, 0x20 };
u8 sec_agc[] = { 0x69, 0x00, 0xff, 0xff, 0x40, 0xff, 0x00, 0x40, 0x40 };
u8 unk1[] = { 0x93, 0x1a };
u8 unk2[] = { 0xb5, 0x7a };
mt352_write(fe, config, ARRAY_SIZE(config));
mt352_write(fe, clock, ARRAY_SIZE(clock));
mt352_write(fe, reset, ARRAY_SIZE(reset));
mt352_write(fe, adc_ctl, ARRAY_SIZE(adc_ctl));
mt352_write(fe, agc, ARRAY_SIZE(agc));
mt352_write(fe, sec_agc, ARRAY_SIZE(sec_agc));
mt352_write(fe, unk1, ARRAY_SIZE(unk1));
mt352_write(fe, unk2, ARRAY_SIZE(unk2));
deb_rc("Demod init!\n");
return 0;
}
static struct mt352_config megasky_mt352_config = {
.demod_address = 0x1e,
.no_tuner = 1,
.demod_init = megasky_mt352_demod_init,
};
static int megasky_mt352_frontend_attach(struct dvb_usb_adapter *adap)
{
struct m9206_state *m = adap->dev->priv;
deb_rc("megasky_frontend_attach!\n");
m->i2c_r[M9206_I2C_DEMOD].addr = megasky_mt352_config.demod_address;
m->i2c_r[M9206_I2C_DEMOD].magic = 0x1f;
if ((adap->fe = dvb_attach(mt352_attach, &megasky_mt352_config, &adap->dev->i2c_adap)) == NULL)
return -EIO;
return 0;
}
static struct qt1010_config megasky_qt1010_config = {
.i2c_address = 0xc4
};
static int megasky_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
{
struct m9206_state *m = adap->dev->priv;
m->i2c_r[M9206_I2C_TUNER].addr = megasky_qt1010_config.i2c_address;
m->i2c_r[M9206_I2C_TUNER].magic = 0xc5;
if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap,
&megasky_qt1010_config) == NULL)
return -ENODEV;
return 0;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties megasky_properties;
static int m920x_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dvb_usb_device *d;
struct usb_host_interface *alt;
int ret;
if ((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) {
deb_rc("probed!\n");
alt = usb_altnum_to_altsetting(intf, 1);
if (alt == NULL) {
deb_rc("not alt found!\n");
return -ENODEV;
}
ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
alt->desc.bAlternateSetting);
if (ret < 0)
return ret;
deb_rc("Changed to alternate setting!\n");
if ((ret = m9206_rc_init(d->udev)) != 0)
return ret;
}
return ret;
}
static struct usb_device_id m920x_table [] = {
{ USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, m920x_table);
static struct dvb_usb_device_properties megasky_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.firmware = "dvb-usb-megasky-02.fw",
.download_firmware = m9206_firmware_download,
.rc_interval = 100,
.rc_key_map = megasky_rc_keys,
.rc_key_map_size = ARRAY_SIZE(megasky_rc_keys),
.rc_query = m9206_rc_query,
.size_of_priv = sizeof(struct m9206_state),
.identify_state = megasky_identify_state,
.num_adapters = 1,
.adapter = {{
.caps = DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 8,
.pid_filter = m9206_pid_filter,
.pid_filter_ctrl = m9206_pid_filter_ctrl,
.frontend_attach = megasky_mt352_frontend_attach,
.tuner_attach = megasky_qt1010_tuner_attach,
.stream = {
.type = USB_BULK,
.count = 8,
.endpoint = 0x81,
.u = {
.bulk = {
.buffersize = 512,
}
}
},
}},
.i2c_algo = &m9206_i2c_algo,
.num_device_descs = 1,
.devices = {
{ "MSI Mega Sky 580 DVB-T USB2.0",
{ &m920x_table[0], NULL },
{ NULL },
},
}
};
static struct usb_driver m920x_driver = {
.name = "dvb_usb_m920x",
.probe = m920x_probe,
.disconnect = dvb_usb_device_exit,
.id_table = m920x_table,
};
/* module stuff */
static int __init m920x_module_init(void)
{
int ret;
if ((ret = usb_register(&m920x_driver))) {
err("usb_register failed. Error number %d", ret);
return ret;
}
return 0;
}
static void __exit m920x_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&m920x_driver);
}
module_init (m920x_module_init);
module_exit (m920x_module_exit);
MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>");
MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / Uli m920x");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,35 @@
#ifndef _DVB_USB_M920X_H_
#define _DVB_USB_M920X_H_
#define DVB_USB_LOG_PREFIX "m920x"
#include "dvb-usb.h"
#define deb_rc(args...) dprintk(dvb_usb_m920x_debug,0x01,args)
#define M9206_CORE 0x22
#define M9206_RC_STATE 0xff51
#define M9206_RC_KEY 0xff52
#define M9206_RC_INIT1 0xff54
#define M9206_RC_INIT2 0xff55
#define M9206_FW_GO 0xff69
#define M9206_I2C 0x23
#define M9206_FILTER 0x25
#define M9206_FW 0x30
#define M9206_MAX_FILTERS 8
#define M9206_I2C_TUNER 0
#define M9206_I2C_DEMOD 1
#define M9206_I2C_MAX 2
struct m9206_state {
u16 filters[M9206_MAX_FILTERS];
int filtering_enabled;
int rep_count;
struct {
unsigned char addr;
unsigned char magic;
}i2c_r[M9206_I2C_MAX];
};
#endif

View File

@@ -0,0 +1,244 @@
/* DVB USB framework compliant Linux driver for the Hauppauge WinTV-NOVA-T usb2
* DVB-T receiver.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dibusb.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=rc,2=eeprom (|-able))." DVB_USB_DEBUG_STATUS);
#define deb_rc(args...) dprintk(debug,0x01,args)
#define deb_ee(args...) dprintk(debug,0x02,args)
/* Hauppauge NOVA-T USB2 keys */
static struct dvb_usb_rc_key haupp_rc_keys [] = {
{ 0x1e, 0x00, KEY_0 },
{ 0x1e, 0x01, KEY_1 },
{ 0x1e, 0x02, KEY_2 },
{ 0x1e, 0x03, KEY_3 },
{ 0x1e, 0x04, KEY_4 },
{ 0x1e, 0x05, KEY_5 },
{ 0x1e, 0x06, KEY_6 },
{ 0x1e, 0x07, KEY_7 },
{ 0x1e, 0x08, KEY_8 },
{ 0x1e, 0x09, KEY_9 },
{ 0x1e, 0x0a, KEY_KPASTERISK },
{ 0x1e, 0x0b, KEY_RED },
{ 0x1e, 0x0c, KEY_RADIO },
{ 0x1e, 0x0d, KEY_MENU },
{ 0x1e, 0x0e, KEY_GRAVE }, /* # */
{ 0x1e, 0x0f, KEY_MUTE },
{ 0x1e, 0x10, KEY_VOLUMEUP },
{ 0x1e, 0x11, KEY_VOLUMEDOWN },
{ 0x1e, 0x12, KEY_CHANNEL },
{ 0x1e, 0x14, KEY_UP },
{ 0x1e, 0x15, KEY_DOWN },
{ 0x1e, 0x16, KEY_LEFT },
{ 0x1e, 0x17, KEY_RIGHT },
{ 0x1e, 0x18, KEY_VIDEO },
{ 0x1e, 0x19, KEY_AUDIO },
{ 0x1e, 0x1a, KEY_MEDIA },
{ 0x1e, 0x1b, KEY_EPG },
{ 0x1e, 0x1c, KEY_TV },
{ 0x1e, 0x1e, KEY_NEXT },
{ 0x1e, 0x1f, KEY_BACK },
{ 0x1e, 0x20, KEY_CHANNELUP },
{ 0x1e, 0x21, KEY_CHANNELDOWN },
{ 0x1e, 0x24, KEY_LAST }, /* Skip backwards */
{ 0x1e, 0x25, KEY_OK },
{ 0x1e, 0x29, KEY_BLUE},
{ 0x1e, 0x2e, KEY_GREEN },
{ 0x1e, 0x30, KEY_PAUSE },
{ 0x1e, 0x32, KEY_REWIND },
{ 0x1e, 0x34, KEY_FASTFORWARD },
{ 0x1e, 0x35, KEY_PLAY },
{ 0x1e, 0x36, KEY_STOP },
{ 0x1e, 0x37, KEY_RECORD },
{ 0x1e, 0x38, KEY_YELLOW },
{ 0x1e, 0x3b, KEY_GOTO },
{ 0x1e, 0x3d, KEY_POWER },
};
/* Firmware bug? sometimes, when a new key is pressed, the previous pressed key
* is delivered. No workaround yet, maybe a new firmware.
*/
static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
u8 key[5],cmd[2] = { DIBUSB_REQ_POLL_REMOTE, 0x35 }, data,toggle,custom;
u16 raw;
int i;
struct dibusb_device_state *st = d->priv;
dvb_usb_generic_rw(d,cmd,2,key,5,0);
*state = REMOTE_NO_KEY_PRESSED;
switch (key[0]) {
case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED:
raw = ((key[1] << 8) | key[2]) >> 3;
toggle = !!(raw & 0x800);
data = raw & 0x3f;
custom = (raw >> 6) & 0x1f;
deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",key[1],key[2],key[3],custom,data,toggle);
for (i = 0; i < ARRAY_SIZE(haupp_rc_keys); i++) {
if (haupp_rc_keys[i].data == data &&
haupp_rc_keys[i].custom == custom) {
deb_rc("c: %x, d: %x\n",haupp_rc_keys[i].data,haupp_rc_keys[i].custom);
*event = haupp_rc_keys[i].event;
*state = REMOTE_KEY_PRESSED;
if (st->old_toggle == toggle) {
if (st->last_repeat_count++ < 2)
*state = REMOTE_NO_KEY_PRESSED;
} else {
st->last_repeat_count = 0;
st->old_toggle = toggle;
}
break;
}
}
break;
case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY:
default:
break;
}
return 0;
}
static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6])
{
int i;
u8 b;
mac[0] = 0x00;
mac[1] = 0x0d;
mac[2] = 0xfe;
/* this is a complete guess, but works for my box */
for (i = 136; i < 139; i++) {
dibusb_read_eeprom_byte(d,i, &b);
mac[5 - (i - 136)] = b;
}
return 0;
}
/* USB Driver stuff */
static struct dvb_usb_device_properties nova_t_properties;
static int nova_t_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return dvb_usb_device_init(intf,&nova_t_properties,THIS_MODULE,NULL);
}
/* do not change the order of the ID table */
static struct usb_device_id nova_t_table [] = {
/* 00 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) },
/* 01 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, nova_t_table);
static struct dvb_usb_device_properties nova_t_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-nova-t-usb2-02.fw",
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter_count = 32,
.streaming_ctrl = dibusb2_0_streaming_ctrl,
.pid_filter = dibusb_pid_filter,
.pid_filter_ctrl = dibusb_pid_filter_ctrl,
.frontend_attach = dibusb_dib3000mc_frontend_attach,
.tuner_attach = dibusb_dib3000mc_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x06,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
.size_of_priv = sizeof(struct dibusb_state),
}
},
.size_of_priv = sizeof(struct dibusb_device_state),
.power_ctrl = dibusb2_0_power_ctrl,
.read_mac_address = nova_t_read_mac_address,
.rc_interval = 100,
.rc_key_map = haupp_rc_keys,
.rc_key_map_size = ARRAY_SIZE(haupp_rc_keys),
.rc_query = nova_t_rc_query,
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "Hauppauge WinTV-NOVA-T usb2",
{ &nova_t_table[0], NULL },
{ &nova_t_table[1], NULL },
},
{ NULL },
}
};
static struct usb_driver nova_t_driver = {
.name = "dvb_usb_nova_t_usb2",
.probe = nova_t_probe,
.disconnect = dvb_usb_device_exit,
.id_table = nova_t_table,
};
/* module stuff */
static int __init nova_t_module_init(void)
{
int result;
if ((result = usb_register(&nova_t_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit nova_t_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&nova_t_driver);
}
module_init (nova_t_module_init);
module_exit (nova_t_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Hauppauge WinTV-NOVA-T usb2");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,270 @@
/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones
* (e.g. Pinnacle 400e DVB-S USB2.0).
*
* The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes.
*
* TDA8263 + TDA10086
*
* I2C addresses:
* 0x08 - LNBP21PD - LNB power supply
* 0x0e - TDA10086 - Demodulator
* 0x50 - FX2 eeprom
* 0x60 - TDA8263 - Tuner
* 0x78 ???
*
* Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
* Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
* Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.org>
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#define DVB_USB_LOG_PREFIX "ttusb2"
#include "dvb-usb.h"
#include "ttusb2.h"
#include "tda826x.h"
#include "tda10086.h"
#include "lnbp21.h"
/* debug */
static int dvb_usb_ttusb2_debug;
#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args)
module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS);
struct ttusb2_state {
u8 id;
};
static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd,
u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
struct ttusb2_state *st = d->priv;
u8 s[wlen+4],r[64] = { 0 };
int ret = 0;
memset(s,0,wlen+4);
s[0] = 0xaa;
s[1] = ++st->id;
s[2] = cmd;
s[3] = wlen;
memcpy(&s[4],wbuf,wlen);
ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0);
if (ret != 0 ||
r[0] != 0x55 ||
r[1] != s[1] ||
r[2] != cmd ||
(rlen > 0 && r[3] != rlen)) {
warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]);
return -EIO;
}
if (rlen > 0)
memcpy(rbuf, &r[4], rlen);
return 0;
}
static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
static u8 obuf[60], ibuf[60];
int i,read;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
if (num > 2)
warn("more than 2 i2c messages at a time is not handled yet. TODO.");
for (i = 0; i < num; i++) {
read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
obuf[0] = (msg[i].addr << 1) | read;
obuf[1] = msg[i].len;
/* read request */
if (read)
obuf[2] = msg[i+1].len;
else
obuf[2] = 0;
memcpy(&obuf[3],msg[i].buf,msg[i].len);
if (ttusb2_msg(d, CMD_I2C_XFER, obuf, msg[i].len+3, ibuf, obuf[2] + 3) < 0) {
err("i2c transfer failed.");
break;
}
if (read) {
memcpy(msg[i+1].buf,&ibuf[3],msg[i+1].len);
i++;
}
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 ttusb2_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm ttusb2_i2c_algo = {
.master_xfer = ttusb2_i2c_xfer,
.functionality = ttusb2_i2c_func,
};
/* Callbacks for DVB USB */
static int ttusb2_identify_state (struct usb_device *udev, struct
dvb_usb_device_properties *props, struct dvb_usb_device_description **desc,
int *cold)
{
*cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0;
return 0;
}
static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff)
{
u8 b = onoff;
ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0);
return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0);
}
static struct tda10086_config tda10086_config = {
.demod_address = 0x0e,
.invert = 0,
};
static int ttusb2_frontend_attach(struct dvb_usb_adapter *adap)
{
if (usb_set_interface(adap->dev->udev,0,3) < 0)
err("set interface to alts=3 failed");
if ((adap->fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) {
deb_info("TDA10086 attach failed\n");
return -ENODEV;
}
return 0;
}
static int ttusb2_tuner_attach(struct dvb_usb_adapter *adap)
{
if (dvb_attach(tda826x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
deb_info("TDA8263 attach failed\n");
return -ENODEV;
}
if (dvb_attach(lnbp21_attach, adap->fe, &adap->dev->i2c_adap, 0, 0) == NULL) {
deb_info("LNBP21 attach failed\n");
return -ENODEV;
}
return 0;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties ttusb2_properties;
static int ttusb2_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return dvb_usb_device_init(intf,&ttusb2_properties,THIS_MODULE,NULL);
}
static struct usb_device_id ttusb2_table [] = {
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, ttusb2_table);
static struct dvb_usb_device_properties ttusb2_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-pctv-400e-01.fw",
.size_of_priv = sizeof(struct ttusb2_state),
.num_adapters = 1,
.adapter = {
{
.streaming_ctrl = NULL, // ttusb2_streaming_ctrl,
.frontend_attach = ttusb2_frontend_attach,
.tuner_attach = ttusb2_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_ISOC,
.count = 5,
.endpoint = 0x02,
.u = {
.isoc = {
.framesperurb = 4,
.framesize = 940,
.interval = 1,
}
}
}
}
},
.power_ctrl = ttusb2_power_ctrl,
.identify_state = ttusb2_identify_state,
.i2c_algo = &ttusb2_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "Pinnacle 400e DVB-S USB2.0",
{ &ttusb2_table[0], NULL },
{ NULL },
},
}
};
static struct usb_driver ttusb2_driver = {
.name = "dvb_usb_ttusb2",
.probe = ttusb2_probe,
.disconnect = dvb_usb_device_exit,
.id_table = ttusb2_table,
};
/* module stuff */
static int __init ttusb2_module_init(void)
{
int result;
if ((result = usb_register(&ttusb2_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit ttusb2_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&ttusb2_driver);
}
module_init (ttusb2_module_init);
module_exit (ttusb2_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,70 @@
/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones
* (e.g. Pinnacle 400e DVB-S USB2.0).
*
* Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
* Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
* Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.de>
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#ifndef _DVB_USB_TTUSB2_H_
#define _DVB_USB_TTUSB2_H_
/* TTUSB protocol
*
* always to messages (out/in)
* out message:
* 0xaa <id> <cmdbyte> <datalen> <data...>
*
* in message (complete block is always 0x40 bytes long)
* 0x55 <id> <cmdbyte> <datalen> <data...>
*
* id is incremented for each transaction
*/
#define CMD_DSP_DOWNLOAD 0x13
/* out data: <byte>[28]
* last block must be empty */
#define CMD_DSP_BOOT 0x14
/* out data: nothing */
#define CMD_POWER 0x15
/* out data: <on=1/off=0> */
#define CMD_LNB 0x16
/* out data: <power=1> <18V=0,13V=1> <tone> <??=1> <??=1> */
#define CMD_GET_VERSION 0x17
/* in data: <version_byte>[5] */
#define CMD_DISEQC 0x18
/* out data: <master=0xff/burst=??> <cmdlen> <cmdbytes>[cmdlen] */
#define CMD_PID_ENABLE 0x22
/* out data: <index> <type: ts=1/sec=2> <pid msb> <pid lsb> */
#define CMD_PID_DISABLE 0x23
/* out data: <index> */
#define CMD_FILTER_ENABLE 0x24
/* out data: <index> <pid_idx> <filter>[12] <mask>[12] */
#define CMD_FILTER_DISABLE 0x25
/* out data: <index> */
#define CMD_GET_DSP_VERSION 0x26
/* in data: <version_byte>[28] */
#define CMD_I2C_XFER 0x31
/* out data: <addr << 1> <sndlen> <rcvlen> <data>[sndlen]
* in data: <addr << 1> <sndlen> <rcvlen> <data>[rcvlen] */
#define CMD_I2C_BITRATE 0x32
/* out data: <default=0> */
#endif

View File

@@ -0,0 +1,167 @@
/* DVB USB framework compliant Linux driver for the HanfTek UMT-010 USB2.0
* DVB-T receiver.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "dibusb.h"
#include "mt352.h"
static int umt_mt352_demod_init(struct dvb_frontend *fe)
{
static u8 mt352_clock_config[] = { 0x89, 0xb8, 0x2d };
static u8 mt352_reset[] = { 0x50, 0x80 };
static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 };
static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg[] = { 0x67, 0x10, 0xa0 };
static u8 mt352_sec_agc_cfg1[] = { 0x6a, 0xff };
static u8 mt352_sec_agc_cfg2[] = { 0x6d, 0xff };
static u8 mt352_sec_agc_cfg3[] = { 0x70, 0x40 };
static u8 mt352_sec_agc_cfg4[] = { 0x7b, 0x03 };
static u8 mt352_sec_agc_cfg5[] = { 0x7d, 0x0f };
static u8 mt352_acq_ctl[] = { 0x53, 0x50 };
static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x06 };
mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
udelay(2000);
mt352_write(fe, mt352_reset, sizeof(mt352_reset));
mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio));
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
mt352_write(fe, mt352_sec_agc_cfg1, sizeof(mt352_sec_agc_cfg1));
mt352_write(fe, mt352_sec_agc_cfg2, sizeof(mt352_sec_agc_cfg2));
mt352_write(fe, mt352_sec_agc_cfg3, sizeof(mt352_sec_agc_cfg3));
mt352_write(fe, mt352_sec_agc_cfg4, sizeof(mt352_sec_agc_cfg4));
mt352_write(fe, mt352_sec_agc_cfg5, sizeof(mt352_sec_agc_cfg5));
mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl));
mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1));
return 0;
}
static int umt_mt352_frontend_attach(struct dvb_usb_adapter *adap)
{
struct mt352_config umt_config;
memset(&umt_config,0,sizeof(struct mt352_config));
umt_config.demod_init = umt_mt352_demod_init;
umt_config.demod_address = 0xf;
adap->fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap);
return 0;
}
static int umt_tuner_attach (struct dvb_usb_adapter *adap)
{
adap->pll_addr = 0x61;
adap->pll_desc = &dvb_pll_tua6034;
adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
return 0;
}
/* USB Driver stuff */
static struct dvb_usb_device_properties umt_properties;
static int umt_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
if (dvb_usb_device_init(intf,&umt_properties,THIS_MODULE,NULL) == 0)
return 0;
return -EINVAL;
}
/* do not change the order of the ID table */
static struct usb_device_id umt_table [] = {
/* 00 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) },
/* 01 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, umt_table);
static struct dvb_usb_device_properties umt_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-umt-010-02.fw",
.num_adapters = 1,
.adapter = {
{
.streaming_ctrl = dibusb2_0_streaming_ctrl,
.frontend_attach = umt_mt352_frontend_attach,
.tuner_attach = umt_tuner_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 20,
.endpoint = 0x06,
.u = {
.bulk = {
.buffersize = 512,
}
}
},
.size_of_priv = sizeof(struct dibusb_state),
}
},
.power_ctrl = dibusb_power_ctrl,
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ "Hanftek UMT-010 DVB-T USB2.0",
{ &umt_table[0], NULL },
{ &umt_table[1], NULL },
},
}
};
static struct usb_driver umt_driver = {
.name = "dvb_usb_umt_010",
.probe = umt_probe,
.disconnect = dvb_usb_device_exit,
.id_table = umt_table,
};
/* module stuff */
static int __init umt_module_init(void)
{
int result;
if ((result = usb_register(&umt_driver))) {
err("usb_register failed. Error number %d",result);
return result;
}
return 0;
}
static void __exit umt_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&umt_driver);
}
module_init (umt_module_init);
module_exit (umt_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for HanfTek UMT 010 USB2.0 DVB-T device");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,243 @@
/* usb-urb.c is part of the DVB USB library.
*
* Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
* see dvb-usb-init.c for copyright information.
*
* This file keeps functions for initializing and handling the
* BULK and ISOC USB data transfers in a generic way.
* Can be used for DVB-only and also, that's the plan, for
* Hybrid USB devices (analog and DVB).
*/
#include "dvb-usb-common.h"
/* URB stuff for streaming */
static void usb_urb_complete(struct urb *urb)
{
struct usb_data_stream *stream = urb->context;
int ptype = usb_pipetype(urb->pipe);
int i;
u8 *b;
deb_uxfer("'%s' urb completed. status: %d, length: %d/%d, pack_num: %d, errors: %d\n",
ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk",
urb->status,urb->actual_length,urb->transfer_buffer_length,
urb->number_of_packets,urb->error_count);
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
deb_ts("urb completition error %d.\n", urb->status);
break;
}
b = (u8 *) urb->transfer_buffer;
switch (ptype) {
case PIPE_ISOCHRONOUS:
for (i = 0; i < urb->number_of_packets; i++) {
if (urb->iso_frame_desc[i].status != 0)
deb_ts("iso frame descriptor has an error: %d\n",urb->iso_frame_desc[i].status);
else if (urb->iso_frame_desc[i].actual_length > 0)
stream->complete(stream, b + urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length);
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
debug_dump(b,20,deb_uxfer);
break;
case PIPE_BULK:
if (urb->actual_length > 0)
stream->complete(stream, b, urb->actual_length);
break;
default:
err("unkown endpoint type in completition handler.");
return;
}
usb_submit_urb(urb,GFP_ATOMIC);
}
int usb_urb_kill(struct usb_data_stream *stream)
{
int i;
for (i = 0; i < stream->urbs_submitted; i++) {
deb_ts("killing URB no. %d.\n",i);
/* stop the URB */
usb_kill_urb(stream->urb_list[i]);
}
stream->urbs_submitted = 0;
return 0;
}
int usb_urb_submit(struct usb_data_stream *stream)
{
int i,ret;
for (i = 0; i < stream->urbs_initialized; i++) {
deb_ts("submitting URB no. %d\n",i);
if ((ret = usb_submit_urb(stream->urb_list[i],GFP_ATOMIC))) {
err("could not submit URB no. %d - get them all back",i);
usb_urb_kill(stream);
return ret;
}
stream->urbs_submitted++;
}
return 0;
}
static int usb_free_stream_buffers(struct usb_data_stream *stream)
{
if (stream->state & USB_STATE_URB_BUF) {
while (stream->buf_num) {
stream->buf_num--;
deb_mem("freeing buffer %d\n",stream->buf_num);
usb_buffer_free(stream->udev, stream->buf_size,
stream->buf_list[stream->buf_num], stream->dma_addr[stream->buf_num]);
}
}
stream->state &= ~USB_STATE_URB_BUF;
return 0;
}
static int usb_allocate_stream_buffers(struct usb_data_stream *stream, int num, unsigned long size)
{
stream->buf_num = 0;
stream->buf_size = size;
deb_mem("all in all I will use %lu bytes for streaming\n",num*size);
for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
deb_mem("allocating buffer %d\n",stream->buf_num);
if (( stream->buf_list[stream->buf_num] =
usb_buffer_alloc(stream->udev, size, GFP_ATOMIC,
&stream->dma_addr[stream->buf_num]) ) == NULL) {
deb_mem("not enough memory for urb-buffer allocation.\n");
usb_free_stream_buffers(stream);
return -ENOMEM;
}
deb_mem("buffer %d: %p (dma: %Lu)\n",
stream->buf_num,
stream->buf_list[stream->buf_num], (long long)stream->dma_addr[stream->buf_num]);
memset(stream->buf_list[stream->buf_num],0,size);
stream->state |= USB_STATE_URB_BUF;
}
deb_mem("allocation successful\n");
return 0;
}
static int usb_bulk_urb_init(struct usb_data_stream *stream)
{
int i;
if ((i = usb_allocate_stream_buffers(stream,stream->props.count,
stream->props.u.bulk.buffersize)) < 0)
return i;
/* allocate the URBs */
for (i = 0; i < stream->props.count; i++) {
if ((stream->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC)) == NULL)
return -ENOMEM;
usb_fill_bulk_urb( stream->urb_list[i], stream->udev,
usb_rcvbulkpipe(stream->udev,stream->props.endpoint),
stream->buf_list[i],
stream->props.u.bulk.buffersize,
usb_urb_complete, stream);
stream->urb_list[i]->transfer_flags = 0;
stream->urbs_initialized++;
}
return 0;
}
static int usb_isoc_urb_init(struct usb_data_stream *stream)
{
int i,j;
if ((i = usb_allocate_stream_buffers(stream,stream->props.count,
stream->props.u.isoc.framesize*stream->props.u.isoc.framesperurb)) < 0)
return i;
/* allocate the URBs */
for (i = 0; i < stream->props.count; i++) {
struct urb *urb;
int frame_offset = 0;
if ((stream->urb_list[i] =
usb_alloc_urb(stream->props.u.isoc.framesperurb,GFP_ATOMIC)) == NULL)
return -ENOMEM;
urb = stream->urb_list[i];
urb->dev = stream->udev;
urb->context = stream;
urb->complete = usb_urb_complete;
urb->pipe = usb_rcvisocpipe(stream->udev,stream->props.endpoint);
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->interval = stream->props.u.isoc.interval;
urb->number_of_packets = stream->props.u.isoc.framesperurb;
urb->transfer_buffer_length = stream->buf_size;
urb->transfer_buffer = stream->buf_list[i];
urb->transfer_dma = stream->dma_addr[i];
for (j = 0; j < stream->props.u.isoc.framesperurb; j++) {
urb->iso_frame_desc[j].offset = frame_offset;
urb->iso_frame_desc[j].length = stream->props.u.isoc.framesize;
frame_offset += stream->props.u.isoc.framesize;
}
stream->urbs_initialized++;
}
return 0;
}
int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properties *props)
{
if (stream == NULL || props == NULL)
return -EINVAL;
memcpy(&stream->props, props, sizeof(*props));
usb_clear_halt(stream->udev,usb_rcvbulkpipe(stream->udev,stream->props.endpoint));
if (stream->complete == NULL) {
err("there is no data callback - this doesn't make sense.");
return -EINVAL;
}
switch (stream->props.type) {
case USB_BULK:
return usb_bulk_urb_init(stream);
case USB_ISOC:
return usb_isoc_urb_init(stream);
default:
err("unkown URB-type for data transfer.");
return -EINVAL;
}
}
int usb_urb_exit(struct usb_data_stream *stream)
{
int i;
usb_urb_kill(stream);
for (i = 0; i < stream->urbs_initialized; i++) {
if (stream->urb_list[i] != NULL) {
deb_mem("freeing URB no. %d.\n",i);
/* free the URBs */
usb_free_urb(stream->urb_list[i]);
}
}
stream->urbs_initialized = 0;
usb_free_stream_buffers(stream);
return 0;
}

View File

@@ -0,0 +1,349 @@
/* DVB frontend part of the Linux driver for the TwinhanDTV StarBox USB2.0
* DVB-S receiver.
*
* Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de>
* Metzler Brothers Systementwicklung GbR
*
* Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
*
* Thanks to Twinhan who kindly provided hardware and information.
*
* This file can be removed soon, after the DST-driver is rewritten to provice
* the frontend-controlling separately.
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*
*/
#include "vp702x.h"
struct vp702x_fe_state {
struct dvb_frontend fe;
struct dvb_usb_device *d;
struct dvb_frontend_ops ops;
fe_sec_voltage_t voltage;
fe_sec_tone_mode_t tone_mode;
u8 lnb_buf[8];
u8 lock;
u8 sig;
u8 snr;
unsigned long next_status_check;
unsigned long status_check_interval;
};
static int vp702x_fe_refresh_state(struct vp702x_fe_state *st)
{
u8 buf[10];
if (time_after(jiffies,st->next_status_check)) {
vp702x_usb_in_op(st->d,READ_STATUS,0,0,buf,10);
st->lock = buf[4];
vp702x_usb_in_op(st->d,READ_TUNER_REG_REQ,0x11,0,&st->snr,1);
vp702x_usb_in_op(st->d,READ_TUNER_REG_REQ,0x15,0,&st->sig,1);
st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000;
}
return 0;
}
static u8 vp702x_chksum(u8 *buf,int f, int count)
{
u8 s = 0;
int i;
for (i = f; i < f+count; i++)
s += buf[i];
return ~s+1;
}
static int vp702x_fe_read_status(struct dvb_frontend* fe, fe_status_t *status)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
vp702x_fe_refresh_state(st);
deb_fe("%s\n",__FUNCTION__);
if (st->lock == 0)
*status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER;
else
*status = 0;
if (*status & FE_HAS_LOCK)
st->status_check_interval = 1000;
else
st->status_check_interval = 250;
return 0;
}
/* not supported by this Frontend */
static int vp702x_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
vp702x_fe_refresh_state(st);
*ber = 0;
return 0;
}
/* not supported by this Frontend */
static int vp702x_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
vp702x_fe_refresh_state(st);
*unc = 0;
return 0;
}
static int vp702x_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
vp702x_fe_refresh_state(st);
*strength = (st->sig << 8) | st->sig;
return 0;
}
static int vp702x_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
{
u8 _snr;
struct vp702x_fe_state *st = fe->demodulator_priv;
vp702x_fe_refresh_state(st);
_snr = (st->snr & 0x1f) * 0xff / 0x1f;
*snr = (_snr << 8) | _snr;
return 0;
}
static int vp702x_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
{
deb_fe("%s\n",__FUNCTION__);
tune->min_delay_ms = 2000;
return 0;
}
static int vp702x_fe_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
u32 freq = fep->frequency/1000;
/*CalFrequency*/
/* u16 frequencyRef[16] = { 2, 4, 8, 16, 32, 64, 128, 256, 24, 5, 10, 20, 40, 80, 160, 320 }; */
u64 sr;
u8 cmd[8] = { 0 },ibuf[10];
cmd[0] = (freq >> 8) & 0x7f;
cmd[1] = freq & 0xff;
cmd[2] = 1; /* divrate == 4 -> frequencyRef[1] -> 1 here */
sr = (u64) (fep->u.qpsk.symbol_rate/1000) << 20;
do_div(sr,88000);
cmd[3] = (sr >> 12) & 0xff;
cmd[4] = (sr >> 4) & 0xff;
cmd[5] = (sr << 4) & 0xf0;
deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %lu (%lx)\n",
fep->frequency,freq,freq, fep->u.qpsk.symbol_rate,
(unsigned long) sr, (unsigned long) sr);
/* if (fep->inversion == INVERSION_ON)
cmd[6] |= 0x80; */
if (st->voltage == SEC_VOLTAGE_18)
cmd[6] |= 0x40;
/* if (fep->u.qpsk.symbol_rate > 8000000)
cmd[6] |= 0x20;
if (fep->frequency < 1531000)
cmd[6] |= 0x04;
if (st->tone_mode == SEC_TONE_ON)
cmd[6] |= 0x01;*/
cmd[7] = vp702x_chksum(cmd,0,7);
st->status_check_interval = 250;
st->next_status_check = jiffies;
vp702x_usb_inout_op(st->d,cmd,8,ibuf,10,100);
if (ibuf[2] == 0 && ibuf[3] == 0)
deb_fe("tuning failed.\n");
else
deb_fe("tuning succeeded.\n");
return 0;
}
static int vp702x_fe_init(struct dvb_frontend *fe)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
deb_fe("%s\n",__FUNCTION__);
vp702x_usb_in_op(st->d, RESET_TUNER, 0, 0, NULL, 0);
return 0;
}
static int vp702x_fe_sleep(struct dvb_frontend *fe)
{
deb_fe("%s\n",__FUNCTION__);
return 0;
}
static int vp702x_fe_get_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
deb_fe("%s\n",__FUNCTION__);
return 0;
}
static int vp702x_fe_send_diseqc_msg (struct dvb_frontend* fe,
struct dvb_diseqc_master_cmd *m)
{
//struct vp702x_fe_state *st = fe->demodulator_priv;
u8 cmd[8];//,ibuf[10];
memset(cmd,0,8);
deb_fe("%s\n",__FUNCTION__);
if (m->msg_len > 4)
return -EINVAL;
cmd[1] = SET_DISEQC_CMD;
cmd[2] = m->msg_len;
memcpy(&cmd[3], m->msg, m->msg_len);
cmd[7] = vp702x_chksum(cmd,0,7);
// vp702x_usb_inout_op(st->d,cmd,8,ibuf,10,100);
// if (ibuf[2] == 0 && ibuf[3] == 0)
// deb_fe("diseqc cmd failed.\n");
// else
// deb_fe("diseqc cmd succeeded.\n");
return 0;
}
static int vp702x_fe_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
{
deb_fe("%s\n",__FUNCTION__);
return 0;
}
static int vp702x_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
u8 ibuf[10];
deb_fe("%s\n",__FUNCTION__);
st->tone_mode = tone;
if (tone == SEC_TONE_ON)
st->lnb_buf[2] = 0x02;
else
st->lnb_buf[2] = 0x00;
st->lnb_buf[7] = vp702x_chksum(st->lnb_buf,0,7);
vp702x_usb_inout_op(st->d,st->lnb_buf,8,ibuf,10,100);
if (ibuf[2] == 0 && ibuf[3] == 0)
deb_fe("set_tone cmd failed.\n");
else
deb_fe("set_tone cmd succeeded.\n");
return 0;
}
static int vp702x_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t
voltage)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
u8 ibuf[10];
deb_fe("%s\n",__FUNCTION__);
st->voltage = voltage;
if (voltage != SEC_VOLTAGE_OFF)
st->lnb_buf[4] = 0x01;
else
st->lnb_buf[4] = 0x00;
st->lnb_buf[7] = vp702x_chksum(st->lnb_buf,0,7);
vp702x_usb_inout_op(st->d,st->lnb_buf,8,ibuf,10,100);
if (ibuf[2] == 0 && ibuf[3] == 0)
deb_fe("set_voltage cmd failed.\n");
else
deb_fe("set_voltage cmd succeeded.\n");
return 0;
}
static void vp702x_fe_release(struct dvb_frontend* fe)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
kfree(st);
}
static struct dvb_frontend_ops vp702x_fe_ops;
struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d)
{
struct vp702x_fe_state *s = kzalloc(sizeof(struct vp702x_fe_state), GFP_KERNEL);
if (s == NULL)
goto error;
s->d = d;
memcpy(&s->fe.ops,&vp702x_fe_ops,sizeof(struct dvb_frontend_ops));
s->fe.demodulator_priv = s;
s->lnb_buf[1] = SET_LNB_POWER;
s->lnb_buf[3] = 0xff; /* 0=tone burst, 2=data burst, ff=off */
return &s->fe;
error:
return NULL;
}
static struct dvb_frontend_ops vp702x_fe_ops = {
.info = {
.name = "Twinhan DST-like frontend (VP7021/VP7020) DVB-S",
.type = FE_QPSK,
.frequency_min = 950000,
.frequency_max = 2150000,
.frequency_stepsize = 1000, /* kHz for QPSK frontends */
.frequency_tolerance = 0,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
.symbol_rate_tolerance = 500, /* ppm */
.caps = 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_QPSK |
FE_CAN_FEC_AUTO
},
.release = vp702x_fe_release,
.init = vp702x_fe_init,
.sleep = vp702x_fe_sleep,
.set_frontend = vp702x_fe_set_frontend,
.get_frontend = vp702x_fe_get_frontend,
.get_tune_settings = vp702x_fe_get_tune_settings,
.read_status = vp702x_fe_read_status,
.read_ber = vp702x_fe_read_ber,
.read_signal_strength = vp702x_fe_read_signal_strength,
.read_snr = vp702x_fe_read_snr,
.read_ucblocks = vp702x_fe_read_unc_blocks,
.diseqc_send_master_cmd = vp702x_fe_send_diseqc_msg,
.diseqc_send_burst = vp702x_fe_send_diseqc_burst,
.set_tone = vp702x_fe_set_tone,
.set_voltage = vp702x_fe_set_voltage,
};

View File

@@ -0,0 +1,347 @@
/* DVB USB compliant Linux driver for the TwinhanDTV StarBox USB2.0 DVB-S
* receiver.
*
* Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de>
* Metzler Brothers Systementwicklung GbR
*
* Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
*
* Thanks to Twinhan who kindly provided hardware and information.
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "vp702x.h"
/* debug */
int dvb_usb_vp702x_debug;
module_param_named(debug,dvb_usb_vp702x_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
struct vp702x_state {
int pid_filter_count;
int pid_filter_can_bypass;
u8 pid_filter_state;
};
struct vp702x_device_state {
u8 power_state;
};
/* check for mutex FIXME */
int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
{
int ret = -1;
ret = usb_control_msg(d->udev,
usb_rcvctrlpipe(d->udev,0),
req,
USB_TYPE_VENDOR | USB_DIR_IN,
value,index,b,blen,
2000);
if (ret < 0) {
warn("usb in operation failed. (%d)", ret);
ret = -EIO;
} else
ret = 0;
deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index);
debug_dump(b,blen,deb_xfer);
return ret;
}
int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index);
debug_dump(b,blen,deb_xfer);
if ((ret = usb_control_msg(d->udev,
usb_sndctrlpipe(d->udev,0),
req,
USB_TYPE_VENDOR | USB_DIR_OUT,
value,index,b,blen,
2000)) != blen) {
warn("usb out operation failed. (%d)",ret);
return -EIO;
} else
return 0;
}
int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec)
{
int ret;
if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
return ret;
ret = vp702x_usb_out_op(d,REQUEST_OUT,0,0,o,olen);
msleep(msec);
ret = vp702x_usb_in_op(d,REQUEST_IN,0,0,i,ilen);
mutex_unlock(&d->usb_mutex);
return ret;
}
static int vp702x_usb_inout_cmd(struct dvb_usb_device *d, u8 cmd, u8 *o,
int olen, u8 *i, int ilen, int msec)
{
u8 bout[olen+2];
u8 bin[ilen+1];
int ret = 0;
bout[0] = 0x00;
bout[1] = cmd;
memcpy(&bout[2],o,olen);
ret = vp702x_usb_inout_op(d, bout, olen+2, bin, ilen+1,msec);
if (ret == 0)
memcpy(i,&bin[1],ilen);
return ret;
}
static int vp702x_set_pld_mode(struct dvb_usb_adapter *adap, u8 bypass)
{
u8 buf[16] = { 0 };
return vp702x_usb_in_op(adap->dev, 0xe0, (bypass << 8) | 0x0e, 0, buf, 16);
}
static int vp702x_set_pld_state(struct dvb_usb_adapter *adap, u8 state)
{
u8 buf[16] = { 0 };
return vp702x_usb_in_op(adap->dev, 0xe0, (state << 8) | 0x0f, 0, buf, 16);
}
static int vp702x_set_pid(struct dvb_usb_adapter *adap, u16 pid, u8 id, int onoff)
{
struct vp702x_state *st = adap->priv;
u8 buf[16] = { 0 };
if (onoff)
st->pid_filter_state |= (1 << id);
else {
st->pid_filter_state &= ~(1 << id);
pid = 0xffff;
}
id = 0x10 + id*2;
vp702x_set_pld_state(adap, st->pid_filter_state);
vp702x_usb_in_op(adap->dev, 0xe0, (((pid >> 8) & 0xff) << 8) | (id), 0, buf, 16);
vp702x_usb_in_op(adap->dev, 0xe0, (((pid ) & 0xff) << 8) | (id+1), 0, buf, 16);
return 0;
}
static int vp702x_init_pid_filter(struct dvb_usb_adapter *adap)
{
struct vp702x_state *st = adap->priv;
int i;
u8 b[10] = { 0 };
st->pid_filter_count = 8;
st->pid_filter_can_bypass = 1;
st->pid_filter_state = 0x00;
vp702x_set_pld_mode(adap, 1); // bypass
for (i = 0; i < st->pid_filter_count; i++)
vp702x_set_pid(adap, 0xffff, i, 1);
vp702x_usb_in_op(adap->dev, 0xb5, 3, 0, b, 10);
vp702x_usb_in_op(adap->dev, 0xb5, 0, 0, b, 10);
vp702x_usb_in_op(adap->dev, 0xb5, 1, 0, b, 10);
//vp702x_set_pld_mode(d, 0); // filter
return 0;
}
static int vp702x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
return 0;
}
/* keys for the enclosed remote control */
static struct dvb_usb_rc_key vp702x_rc_keys[] = {
{ 0x00, 0x01, KEY_1 },
{ 0x00, 0x02, KEY_2 },
};
/* remote control stuff (does not work with my box) */
static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
u8 key[10];
int i;
/* remove the following return to enabled remote querying */
return 0;
vp702x_usb_in_op(d,READ_REMOTE_REQ,0,0,key,10);
deb_rc("remote query key: %x %d\n",key[1],key[1]);
if (key[1] == 0x44) {
*state = REMOTE_NO_KEY_PRESSED;
return 0;
}
for (i = 0; i < ARRAY_SIZE(vp702x_rc_keys); i++)
if (vp702x_rc_keys[i].custom == key[1]) {
*state = REMOTE_KEY_PRESSED;
*event = vp702x_rc_keys[i].event;
break;
}
return 0;
}
int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff)
{
struct vp702x_device_state *st = d->priv;
if (st->power_state == 0 && onoff)
vp702x_usb_out_op(d, SET_TUNER_POWER_REQ, 1, 7, NULL, 0);
else if (st->power_state == 1 && onoff == 0)
vp702x_usb_out_op(d, SET_TUNER_POWER_REQ, 0, 7, NULL, 0);
st->power_state = onoff;
return 0;
}
static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
{
u8 i;
for (i = 6; i < 12; i++)
vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1, &mac[i - 6], 1);
return 0;
}
static int vp702x_frontend_attach(struct dvb_usb_adapter *adap)
{
u8 buf[10] = { 0 };
vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 0, 7, NULL, 0);
if (vp702x_usb_inout_cmd(adap->dev, GET_SYSTEM_STRING, NULL, 0, buf, 10, 10))
return -EIO;
buf[9] = '\0';
info("system string: %s",&buf[1]);
vp702x_init_pid_filter(adap);
adap->fe = vp702x_fe_attach(adap->dev);
vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 1, 7, NULL, 0);
return 0;
}
static struct dvb_usb_device_properties vp702x_properties;
static int vp702x_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return dvb_usb_device_init(intf,&vp702x_properties,THIS_MODULE,NULL);
}
static struct usb_device_id vp702x_usb_table [] = {
{ USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_COLD) },
// { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_COLD) },
// { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_WARM) },
{ 0 },
};
MODULE_DEVICE_TABLE(usb, vp702x_usb_table);
static struct dvb_usb_device_properties vp702x_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-vp702x-02.fw",
.no_reconnect = 1,
.size_of_priv = sizeof(struct vp702x_device_state),
.num_adapters = 1,
.adapter = {
{
.caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
.streaming_ctrl = vp702x_streaming_ctrl,
.frontend_attach = vp702x_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 10,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
.size_of_priv = sizeof(struct vp702x_state),
}
},
.read_mac_address = vp702x_read_mac_addr,
.rc_key_map = vp702x_rc_keys,
.rc_key_map_size = ARRAY_SIZE(vp702x_rc_keys),
.rc_interval = 400,
.rc_query = vp702x_rc_query,
.num_device_descs = 1,
.devices = {
{ .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7021)",
.cold_ids = { &vp702x_usb_table[0], NULL },
.warm_ids = { NULL },
},
/* { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7020)",
.cold_ids = { &vp702x_usb_table[2], NULL },
.warm_ids = { &vp702x_usb_table[3], NULL },
},
*/ { NULL },
}
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver vp702x_usb_driver = {
.name = "dvb_usb_vp702x",
.probe = vp702x_usb_probe,
.disconnect = dvb_usb_device_exit,
.id_table = vp702x_usb_table,
};
/* module stuff */
static int __init vp702x_usb_module_init(void)
{
int result;
if ((result = usb_register(&vp702x_usb_driver))) {
err("usb_register failed. (%d)",result);
return result;
}
return 0;
}
static void __exit vp702x_usb_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&vp702x_usb_driver);
}
module_init(vp702x_usb_module_init);
module_exit(vp702x_usb_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for Twinhan StarBox DVB-S USB2.0 and clones");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,108 @@
#ifndef _DVB_USB_VP7021_H_
#define _DVB_USB_VP7021_H_
#define DVB_USB_LOG_PREFIX "vp702x"
#include "dvb-usb.h"
extern int dvb_usb_vp702x_debug;
#define deb_info(args...) dprintk(dvb_usb_vp702x_debug,0x01,args)
#define deb_xfer(args...) dprintk(dvb_usb_vp702x_debug,0x02,args)
#define deb_rc(args...) dprintk(dvb_usb_vp702x_debug,0x04,args)
#define deb_fe(args...) dprintk(dvb_usb_vp702x_debug,0x08,args)
/* commands are read and written with USB control messages */
/* consecutive read/write operation */
#define REQUEST_OUT 0xB2
#define REQUEST_IN 0xB3
/* the out-buffer of these consecutive operations contain sub-commands when b[0] = 0
* request: 0xB2; i: 0; v: 0; b[0] = 0, b[1] = subcmd, additional buffer
* the returning buffer looks as follows
* request: 0xB3; i: 0; v: 0; b[0] = 0xB3, additional buffer */
#define GET_TUNER_STATUS 0x05
/* additional in buffer:
* 0 1 2 3 4 5 6 7 8
* N/A N/A 0x05 signal-quality N/A N/A signal-strength lock==0 N/A */
#define GET_SYSTEM_STRING 0x06
/* additional in buffer:
* 0 1 2 3 4 5 6 7 8
* N/A 'U' 'S' 'B' '7' '0' '2' 'X' N/A */
#define SET_DISEQC_CMD 0x08
/* additional out buffer:
* 0 1 2 3 4
* len X1 X2 X3 X4
* additional in buffer:
* 0 1 2
* N/A 0 0 b[1] == b[2] == 0 -> success, failure otherwise */
#define SET_LNB_POWER 0x09
/* additional out buffer:
* 0 1 2
* 0x00 0xff 1 = on, 0 = off
* additional in buffer:
* 0 1 2
* N/A 0 0 b[1] == b[2] == 0 -> success failure otherwise */
#define GET_MAC_ADDRESS 0x0A
/* #define GET_MAC_ADDRESS 0x0B */
/* additional in buffer:
* 0 1 2 3 4 5 6 7 8
* N/A N/A 0x0A or 0x0B MAC0 MAC1 MAC2 MAC3 MAC4 MAC5 */
#define SET_PID_FILTER 0x11
/* additional in buffer:
* 0 1 ... 14 15 16
* PID0_MSB PID0_LSB ... PID7_MSB PID7_LSB PID_active (bits) */
/* request: 0xB2; i: 0; v: 0;
* b[0] != 0 -> tune and lock a channel
* 0 1 2 3 4 5 6 7
* freq0 freq1 divstep srate0 srate1 srate2 flag chksum
*/
/* one direction requests */
#define READ_REMOTE_REQ 0xB4
/* IN i: 0; v: 0; b[0] == request, b[1] == key */
#define READ_PID_NUMBER_REQ 0xB5
/* IN i: 0; v: 0; b[0] == request, b[1] == 0, b[2] = pid number */
#define WRITE_EEPROM_REQ 0xB6
/* OUT i: offset; v: value to write; no extra buffer */
#define READ_EEPROM_REQ 0xB7
/* IN i: bufferlen; v: offset; buffer with bufferlen bytes */
#define READ_STATUS 0xB8
/* IN i: 0; v: 0; bufferlen 10 */
#define READ_TUNER_REG_REQ 0xB9
/* IN i: 0; v: register; b[0] = value */
#define READ_FX2_REG_REQ 0xBA
/* IN i: offset; v: 0; b[0] = value */
#define WRITE_FX2_REG_REQ 0xBB
/* OUT i: offset; v: value to write; 1 byte extra buffer */
#define SET_TUNER_POWER_REQ 0xBC
/* IN i: 0 = power off, 1 = power on */
#define WRITE_TUNER_REG_REQ 0xBD
/* IN i: register, v: value to write, no extra buffer */
#define RESET_TUNER 0xBE
/* IN i: 0, v: 0, no extra buffer */
extern struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d);
extern int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec);
extern int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
extern int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
extern int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff);
#endif

View File

@@ -0,0 +1,192 @@
/* DVB frontend part of the Linux driver for TwinhanDTV Alpha/MagicBoxII USB2.0
* DVB-T receiver.
*
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
* Thanks to Twinhan who kindly provided hardware and information.
*
* 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, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*
*/
#include "vp7045.h"
/* It is a Zarlink MT352 within a Samsung Tuner (DNOS404ZH102A) - 040929 - AAT
*
* Programming is hidden inside the firmware, so set_frontend is very easy.
* Even though there is a Firmware command that one can use to access the demod
* via its registers. This is used for status information.
*/
struct vp7045_fe_state {
struct dvb_frontend fe;
struct dvb_usb_device *d;
};
static int vp7045_fe_read_status(struct dvb_frontend* fe, fe_status_t *status)
{
struct vp7045_fe_state *state = fe->demodulator_priv;
u8 s0 = vp7045_read_reg(state->d,0x00),
s1 = vp7045_read_reg(state->d,0x01),
s3 = vp7045_read_reg(state->d,0x03);
*status = 0;
if (s0 & (1 << 4))
*status |= FE_HAS_CARRIER;
if (s0 & (1 << 1))
*status |= FE_HAS_VITERBI;
if (s0 & (1 << 5))
*status |= FE_HAS_LOCK;
if (s1 & (1 << 1))
*status |= FE_HAS_SYNC;
if (s3 & (1 << 6))
*status |= FE_HAS_SIGNAL;
if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
(FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
*status &= ~FE_HAS_LOCK;
return 0;
}
static int vp7045_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
{
struct vp7045_fe_state *state = fe->demodulator_priv;
*ber = (vp7045_read_reg(state->d, 0x0D) << 16) |
(vp7045_read_reg(state->d, 0x0E) << 8) |
vp7045_read_reg(state->d, 0x0F);
return 0;
}
static int vp7045_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
{
struct vp7045_fe_state *state = fe->demodulator_priv;
*unc = (vp7045_read_reg(state->d, 0x10) << 8) |
vp7045_read_reg(state->d, 0x11);
return 0;
}
static int vp7045_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
{
struct vp7045_fe_state *state = fe->demodulator_priv;
u16 signal = (vp7045_read_reg(state->d, 0x14) << 8) |
vp7045_read_reg(state->d, 0x15);
*strength = ~signal;
return 0;
}
static int vp7045_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
{
struct vp7045_fe_state *state = fe->demodulator_priv;
u8 _snr = vp7045_read_reg(state->d, 0x09);
*snr = (_snr << 8) | _snr;
return 0;
}
static int vp7045_fe_init(struct dvb_frontend* fe)
{
return 0;
}
static int vp7045_fe_sleep(struct dvb_frontend* fe)
{
return 0;
}
static int vp7045_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
{
tune->min_delay_ms = 800;
return 0;
}
static int vp7045_fe_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct vp7045_fe_state *state = fe->demodulator_priv;
u8 buf[5];
u32 freq = fep->frequency / 1000;
buf[0] = (freq >> 16) & 0xff;
buf[1] = (freq >> 8) & 0xff;
buf[2] = freq & 0xff;
buf[3] = 0;
switch (fep->u.ofdm.bandwidth) {
case BANDWIDTH_8_MHZ: buf[4] = 8; break;
case BANDWIDTH_7_MHZ: buf[4] = 7; break;
case BANDWIDTH_6_MHZ: buf[4] = 6; break;
case BANDWIDTH_AUTO: return -EOPNOTSUPP;
default:
return -EINVAL;
}
vp7045_usb_op(state->d,LOCK_TUNER_COMMAND,buf,5,NULL,0,200);
return 0;
}
static int vp7045_fe_get_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
return 0;
}
static void vp7045_fe_release(struct dvb_frontend* fe)
{
struct vp7045_fe_state *state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops vp7045_fe_ops;
struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d)
{
struct vp7045_fe_state *s = kzalloc(sizeof(struct vp7045_fe_state), GFP_KERNEL);
if (s == NULL)
goto error;
s->d = d;
memcpy(&s->fe.ops, &vp7045_fe_ops, sizeof(struct dvb_frontend_ops));
s->fe.demodulator_priv = s;
return &s->fe;
error:
return NULL;
}
static struct dvb_frontend_ops vp7045_fe_ops = {
.info = {
.name = "Twinhan VP7045/46 USB DVB-T",
.type = FE_OFDM,
.frequency_min = 44250000,
.frequency_max = 867250000,
.frequency_stepsize = 1000,
.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 = vp7045_fe_release,
.init = vp7045_fe_init,
.sleep = vp7045_fe_sleep,
.set_frontend = vp7045_fe_set_frontend,
.get_frontend = vp7045_fe_get_frontend,
.get_tune_settings = vp7045_fe_get_tune_settings,
.read_status = vp7045_fe_read_status,
.read_ber = vp7045_fe_read_ber,
.read_signal_strength = vp7045_fe_read_signal_strength,
.read_snr = vp7045_fe_read_snr,
.read_ucblocks = vp7045_fe_read_unc_blocks,
};

Some files were not shown because too many files have changed in this diff Show More