Fixed MTP to work with TWRP

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

View file

@ -0,0 +1,322 @@
config DVB_USB
tristate "Support for various USB DVB devices"
depends on DVB_CORE && USB && I2C && RC_CORE
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_PLL if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
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_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_DIB3000MB
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
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 MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
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 if MEDIA_SUBDRV_AUTOSELECT
select DVB_DIB7000M if MEDIA_SUBDRV_AUTOSELECT
select DVB_DIB8000 if MEDIA_SUBDRV_AUTOSELECT
select DVB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT
select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TUNER_DIB0090 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2266 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
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 LinuxTV 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_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_DIB3000MC
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
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_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT
select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT
select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT
select DVB_ATBM8830 if MEDIA_SUBDRV_AUTOSELECT
select DVB_LGS8GXX if MEDIA_SUBDRV_AUTOSELECT
select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MAX2165 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
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
TechnoTrend TVStick CT2-4400 and CT2-4650 CI devices
config DVB_USB_M920X
tristate "Uli m920x DVB-T USB2.0 support"
depends on DVB_USB
select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
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_DIGITV
tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support"
depends on DVB_USB
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT
select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
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_PLL if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
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 MEDIA_SUBDRV_AUTOSELECT
select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver and
the TechnoTrend CT-3650 CI DVB-C/T 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.
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.
config DVB_USB_OPERA1
tristate "Opera1 DVB-S USB2.0 receiver"
depends on DVB_USB
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Opera DVB-S USB2.0 receiver.
config DVB_USB_AF9005
tristate "Afatech AF9005 DVB-T USB1.1 support"
depends on DVB_USB
select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
and the TerraTec Cinergy T USB XE (Rev.1)
config DVB_USB_AF9005_REMOTE
tristate "Afatech AF9005 default remote control support"
depends on DVB_USB_AF9005
help
Say Y here to support the default remote control decoding for the
Afatech AF9005 based receiver.
config DVB_USB_PCTV452E
tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600"
depends on DVB_USB
select TTPCI_EEPROM
select DVB_LNBP22 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
help
Support for external USB adapter designed by Pinnacle,
shipped under the brand name 'PCTV HDTV Pro USB'.
Also supports TT Connect S2-3600/3650 cards.
Say Y if you own such a device and want to use it.
config DVB_USB_DW2102
tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support"
depends on DVB_USB
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT
select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT
select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT
select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0
receivers.
config DVB_USB_CINERGY_T2
tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
depends on DVB_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_USB_DTV5100
tristate "AME DTV-5100 USB2.0 DVB-T support"
depends on DVB_USB
select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
config DVB_USB_FRIIO
tristate "Friio ISDB-T USB2.0 Receiver support"
depends on DVB_USB
help
Say Y here to support the Japanese DTV receiver Friio.
config DVB_USB_AZ6027
tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support"
depends on DVB_USB
select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the AZ6027 device
config DVB_USB_TECHNISAT_USB2
tristate "Technisat DVB-S/S2 USB2.0 support"
depends on DVB_USB
select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the Technisat USB2 DVB-S/S2 device

View file

@ -0,0 +1,83 @@
dvb-usb-objs += dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o
dvb-usb-objs += 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-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
dvb-usb-opera-objs := opera1.o
obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o
dvb-usb-af9005-objs := af9005.o af9005-fe.o
obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o
dvb-usb-af9005-remote-objs := af9005-remote.o
obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o
dvb-usb-pctv452e-objs := pctv452e.o
obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o
dvb-usb-dw2102-objs := dw2102.o
obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
dvb-usb-dtv5100-objs := dtv5100.o
obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o
dvb-usb-cinergyT2-objs := cinergyT2-core.o cinergyT2-fe.o
obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o
dvb-usb-friio-objs := friio.o friio-fe.o
obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
dvb-usb-az6027-objs := az6027.o
obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o
dvb-usb-technisat-usb2-objs := technisat-usb2.o
obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o
ccflags-y += -I$(srctree)/drivers/media/dvb-core
ccflags-y += -I$(srctree)/drivers/media/dvb-frontends/
# due to tuner-xc3028
ccflags-y += -I$(srctree)/drivers/media/tuners
ccflags-y += -I$(srctree)/drivers/media/pci/ttpci

View file

@ -0,0 +1,191 @@
/* 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);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#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 rc_map_table rc_map_a800_table[] = {
{ 0x0201, KEY_MODE }, /* SOURCE */
{ 0x0200, KEY_POWER2 }, /* POWER */
{ 0x0205, KEY_1 }, /* 1 */
{ 0x0206, KEY_2 }, /* 2 */
{ 0x0207, KEY_3 }, /* 3 */
{ 0x0209, KEY_4 }, /* 4 */
{ 0x020a, KEY_5 }, /* 5 */
{ 0x020b, KEY_6 }, /* 6 */
{ 0x020d, KEY_7 }, /* 7 */
{ 0x020e, KEY_8 }, /* 8 */
{ 0x020f, KEY_9 }, /* 9 */
{ 0x0212, KEY_LEFT }, /* L / DISPLAY */
{ 0x0211, KEY_0 }, /* 0 */
{ 0x0213, KEY_RIGHT }, /* R / CH RTN */
{ 0x0217, KEY_CAMERA }, /* SNAP SHOT */
{ 0x0210, KEY_LAST }, /* 16-CH PREV */
{ 0x021e, KEY_VOLUMEDOWN }, /* VOL DOWN */
{ 0x020c, KEY_ZOOM }, /* FULL SCREEN */
{ 0x021f, KEY_VOLUMEUP }, /* VOL UP */
{ 0x0214, KEY_MUTE }, /* MUTE */
{ 0x0208, KEY_AUDIO }, /* AUDIO */
{ 0x0219, KEY_RECORD }, /* RECORD */
{ 0x0218, KEY_PLAY }, /* PLAY */
{ 0x021b, KEY_STOP }, /* STOP */
{ 0x021a, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */
{ 0x021d, KEY_BACK }, /* << / RED */
{ 0x021c, KEY_FORWARD }, /* >> / YELLOW */
{ 0x0203, KEY_TEXT }, /* TELETEXT */
{ 0x0204, KEY_EPG }, /* EPG */
{ 0x0215, KEY_MENU }, /* MENU */
{ 0x0303, KEY_CHANNELUP }, /* CH UP */
{ 0x0302, KEY_CHANNELDOWN }, /* CH DOWN */
{ 0x0301, KEY_FIRST }, /* |<< / GREEN */
{ 0x0300, KEY_LAST }, /* >>| / BLUE */
};
static int a800_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
int ret;
u8 *key = kmalloc(5, GFP_KERNEL);
if (!key)
return -ENOMEM;
if (usb_control_msg(d->udev,usb_rcvctrlpipe(d->udev,0),
0x04, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, key, 5,
2000) != 5) {
ret = -ENODEV;
goto out;
}
/* 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: %*ph\n", 5, key);
ret = 0;
out:
kfree(key);
return ret;
}
/* 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, adapter_nr);
}
/* 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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_map_table = rc_map_a800_table,
.rc_map_size = ARRAY_SIZE(rc_map_a800_table),
.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_usb_driver(a800_driver);
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");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,157 @@
/* DVB USB compliant Linux driver for the Afatech 9005
* USB1.1 DVB-T receiver.
*
* Standard remote decode function
*
* Copyright (C) 2007 Luca Olivetti (luca@ventoso.org)
*
* Thanks to Afatech who kindly provided 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; 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.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include "af9005.h"
/* debug */
static int dvb_usb_af9005_remote_debug;
module_param_named(debug, dvb_usb_af9005_remote_debug, int, 0644);
MODULE_PARM_DESC(debug,
"enable (1) or disable (0) debug messages."
DVB_USB_DEBUG_STATUS);
#define deb_decode(args...) dprintk(dvb_usb_af9005_remote_debug,0x01,args)
struct rc_map_table rc_map_af9005_table[] = {
{0x01b7, KEY_POWER},
{0x01a7, KEY_VOLUMEUP},
{0x0187, KEY_CHANNELUP},
{0x017f, KEY_MUTE},
{0x01bf, KEY_VOLUMEDOWN},
{0x013f, KEY_CHANNELDOWN},
{0x01df, KEY_1},
{0x015f, KEY_2},
{0x019f, KEY_3},
{0x011f, KEY_4},
{0x01ef, KEY_5},
{0x016f, KEY_6},
{0x01af, KEY_7},
{0x0127, KEY_8},
{0x0107, KEY_9},
{0x01cf, KEY_ZOOM},
{0x014f, KEY_0},
{0x018f, KEY_GOTO}, /* marked jump on the remote */
{0x00bd, KEY_POWER},
{0x007d, KEY_VOLUMEUP},
{0x00fd, KEY_CHANNELUP},
{0x009d, KEY_MUTE},
{0x005d, KEY_VOLUMEDOWN},
{0x00dd, KEY_CHANNELDOWN},
{0x00ad, KEY_1},
{0x006d, KEY_2},
{0x00ed, KEY_3},
{0x008d, KEY_4},
{0x004d, KEY_5},
{0x00cd, KEY_6},
{0x00b5, KEY_7},
{0x0075, KEY_8},
{0x00f5, KEY_9},
{0x0095, KEY_ZOOM},
{0x0055, KEY_0},
{0x00d5, KEY_GOTO}, /* marked jump on the remote */
};
int rc_map_af9005_table_size = ARRAY_SIZE(rc_map_af9005_table);
static int repeatable_keys[] = {
KEY_VOLUMEUP,
KEY_VOLUMEDOWN,
KEY_CHANNELUP,
KEY_CHANNELDOWN
};
int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event,
int *state)
{
u16 mark, space;
u32 result;
u8 cust, dat, invdat;
int i;
if (len >= 6) {
mark = (u16) (data[0] << 8) + data[1];
space = (u16) (data[2] << 8) + data[3];
if (space * 3 < mark) {
for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) {
if (d->last_event == repeatable_keys[i]) {
*state = REMOTE_KEY_REPEAT;
*event = d->last_event;
deb_decode("repeat key, event %x\n",
*event);
return 0;
}
}
deb_decode("repeated key ignored (non repeatable)\n");
return 0;
} else if (len >= 33 * 4) { /*32 bits + start code */
result = 0;
for (i = 4; i < 4 + 32 * 4; i += 4) {
result <<= 1;
mark = (u16) (data[i] << 8) + data[i + 1];
mark >>= 1;
space = (u16) (data[i + 2] << 8) + data[i + 3];
space >>= 1;
if (mark * 2 > space)
result += 1;
}
deb_decode("key pressed, raw value %x\n", result);
if ((result & 0xff000000) != 0xfe000000) {
deb_decode
("doesn't start with 0xfe, ignored\n");
return 0;
}
cust = (result >> 16) & 0xff;
dat = (result >> 8) & 0xff;
invdat = (~result) & 0xff;
if (dat != invdat) {
deb_decode("code != inverted code\n");
return 0;
}
for (i = 0; i < rc_map_af9005_table_size; i++) {
if (rc5_custom(&rc_map_af9005_table[i]) == cust
&& rc5_data(&rc_map_af9005_table[i]) == dat) {
*event = rc_map_af9005_table[i].keycode;
*state = REMOTE_KEY_PRESSED;
deb_decode
("key pressed, event %x\n", *event);
return 0;
}
}
deb_decode("not found in table\n");
}
}
return 0;
}
EXPORT_SYMBOL(rc_map_af9005_table);
EXPORT_SYMBOL(rc_map_af9005_table_size);
EXPORT_SYMBOL(af9005_rc_decode);
MODULE_AUTHOR("Luca Olivetti <luca@ventoso.org>");
MODULE_DESCRIPTION
("Standard remote control decoder for Afatech 9005 DVB-T USB1.1 stick");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,203 @@
/*
File automatically generated by createinit.py using data
extracted from AF05BDA.sys (windows driver):
dd if=AF05BDA.sys of=initsequence bs=1 skip=88316 count=1110
python createinit.py > af9005-script.h
*/
typedef struct {
u16 reg;
u8 pos;
u8 len;
u8 val;
} RegDesc;
static RegDesc script[] = {
{0xa180, 0x0, 0x8, 0xa},
{0xa181, 0x0, 0x8, 0xd7},
{0xa182, 0x0, 0x8, 0xa3},
{0xa0a0, 0x0, 0x8, 0x0},
{0xa0a1, 0x0, 0x5, 0x0},
{0xa0a1, 0x5, 0x1, 0x1},
{0xa0c0, 0x0, 0x4, 0x1},
{0xa20e, 0x4, 0x4, 0xa},
{0xa20f, 0x0, 0x8, 0x40},
{0xa210, 0x0, 0x8, 0x8},
{0xa32a, 0x0, 0x4, 0xa},
{0xa32c, 0x0, 0x8, 0x20},
{0xa32b, 0x0, 0x8, 0x15},
{0xa1a0, 0x1, 0x1, 0x1},
{0xa000, 0x0, 0x1, 0x1},
{0xa000, 0x1, 0x1, 0x0},
{0xa001, 0x1, 0x1, 0x1},
{0xa001, 0x0, 0x1, 0x0},
{0xa001, 0x5, 0x1, 0x0},
{0xa00e, 0x0, 0x5, 0x10},
{0xa00f, 0x0, 0x3, 0x4},
{0xa00f, 0x3, 0x3, 0x5},
{0xa010, 0x0, 0x3, 0x4},
{0xa010, 0x3, 0x3, 0x5},
{0xa016, 0x4, 0x4, 0x3},
{0xa01f, 0x0, 0x6, 0xa},
{0xa020, 0x0, 0x6, 0xa},
{0xa2bc, 0x0, 0x1, 0x1},
{0xa2bc, 0x5, 0x1, 0x1},
{0xa015, 0x0, 0x8, 0x50},
{0xa016, 0x0, 0x1, 0x0},
{0xa02a, 0x0, 0x8, 0x50},
{0xa029, 0x0, 0x8, 0x4b},
{0xa614, 0x0, 0x8, 0x46},
{0xa002, 0x0, 0x5, 0x19},
{0xa003, 0x0, 0x5, 0x1a},
{0xa004, 0x0, 0x5, 0x19},
{0xa005, 0x0, 0x5, 0x1a},
{0xa008, 0x0, 0x8, 0x69},
{0xa009, 0x0, 0x2, 0x2},
{0xae1b, 0x0, 0x8, 0x69},
{0xae1c, 0x0, 0x8, 0x2},
{0xae1d, 0x0, 0x8, 0x2a},
{0xa022, 0x0, 0x8, 0xaa},
{0xa006, 0x0, 0x8, 0xc8},
{0xa007, 0x0, 0x2, 0x0},
{0xa00c, 0x0, 0x8, 0xba},
{0xa00d, 0x0, 0x2, 0x2},
{0xa608, 0x0, 0x8, 0xba},
{0xa60e, 0x0, 0x2, 0x2},
{0xa609, 0x0, 0x8, 0x80},
{0xa60e, 0x2, 0x2, 0x3},
{0xa00a, 0x0, 0x8, 0xb6},
{0xa00b, 0x0, 0x2, 0x0},
{0xa011, 0x0, 0x8, 0xb9},
{0xa012, 0x0, 0x2, 0x0},
{0xa013, 0x0, 0x8, 0xbd},
{0xa014, 0x0, 0x2, 0x2},
{0xa366, 0x0, 0x1, 0x1},
{0xa2bc, 0x3, 0x1, 0x0},
{0xa2bd, 0x0, 0x8, 0xa},
{0xa2be, 0x0, 0x8, 0x14},
{0xa2bf, 0x0, 0x8, 0x8},
{0xa60a, 0x0, 0x8, 0xbd},
{0xa60e, 0x4, 0x2, 0x2},
{0xa60b, 0x0, 0x8, 0x86},
{0xa60e, 0x6, 0x2, 0x3},
{0xa001, 0x2, 0x2, 0x1},
{0xa1c7, 0x0, 0x8, 0xf5},
{0xa03d, 0x0, 0x8, 0xb1},
{0xa616, 0x0, 0x8, 0xff},
{0xa617, 0x0, 0x8, 0xad},
{0xa618, 0x0, 0x8, 0xad},
{0xa61e, 0x3, 0x1, 0x1},
{0xae1a, 0x0, 0x8, 0x0},
{0xae19, 0x0, 0x8, 0xc8},
{0xae18, 0x0, 0x8, 0x61},
{0xa140, 0x0, 0x8, 0x0},
{0xa141, 0x0, 0x8, 0xc8},
{0xa142, 0x0, 0x7, 0x61},
{0xa023, 0x0, 0x8, 0xff},
{0xa021, 0x0, 0x8, 0xad},
{0xa026, 0x0, 0x1, 0x0},
{0xa024, 0x0, 0x8, 0xff},
{0xa025, 0x0, 0x8, 0xff},
{0xa1c8, 0x0, 0x8, 0xf},
{0xa2bc, 0x1, 0x1, 0x0},
{0xa60c, 0x0, 0x4, 0x5},
{0xa60c, 0x4, 0x4, 0x6},
{0xa60d, 0x0, 0x8, 0xa},
{0xa371, 0x0, 0x1, 0x1},
{0xa366, 0x1, 0x3, 0x7},
{0xa338, 0x0, 0x8, 0x10},
{0xa339, 0x0, 0x6, 0x7},
{0xa33a, 0x0, 0x6, 0x1f},
{0xa33b, 0x0, 0x8, 0xf6},
{0xa33c, 0x3, 0x5, 0x4},
{0xa33d, 0x4, 0x4, 0x0},
{0xa33d, 0x1, 0x1, 0x1},
{0xa33d, 0x2, 0x1, 0x1},
{0xa33d, 0x3, 0x1, 0x1},
{0xa16d, 0x0, 0x4, 0xf},
{0xa161, 0x0, 0x5, 0x5},
{0xa162, 0x0, 0x4, 0x5},
{0xa165, 0x0, 0x8, 0xff},
{0xa166, 0x0, 0x8, 0x9c},
{0xa2c3, 0x0, 0x4, 0x5},
{0xa61a, 0x0, 0x6, 0xf},
{0xb200, 0x0, 0x8, 0xa1},
{0xb201, 0x0, 0x8, 0x7},
{0xa093, 0x0, 0x1, 0x0},
{0xa093, 0x1, 0x5, 0xf},
{0xa094, 0x0, 0x8, 0xff},
{0xa095, 0x0, 0x8, 0xf},
{0xa080, 0x2, 0x5, 0x3},
{0xa081, 0x0, 0x4, 0x0},
{0xa081, 0x4, 0x4, 0x9},
{0xa082, 0x0, 0x5, 0x1f},
{0xa08d, 0x0, 0x8, 0x1},
{0xa083, 0x0, 0x8, 0x32},
{0xa084, 0x0, 0x1, 0x0},
{0xa08e, 0x0, 0x8, 0x3},
{0xa085, 0x0, 0x8, 0x32},
{0xa086, 0x0, 0x3, 0x0},
{0xa087, 0x0, 0x8, 0x6e},
{0xa088, 0x0, 0x5, 0x15},
{0xa089, 0x0, 0x8, 0x0},
{0xa08a, 0x0, 0x5, 0x19},
{0xa08b, 0x0, 0x8, 0x92},
{0xa08c, 0x0, 0x5, 0x1c},
{0xa120, 0x0, 0x8, 0x0},
{0xa121, 0x0, 0x5, 0x10},
{0xa122, 0x0, 0x8, 0x0},
{0xa123, 0x0, 0x7, 0x40},
{0xa123, 0x7, 0x1, 0x0},
{0xa124, 0x0, 0x8, 0x13},
{0xa125, 0x0, 0x7, 0x10},
{0xa1c0, 0x0, 0x8, 0x0},
{0xa1c1, 0x0, 0x5, 0x4},
{0xa1c2, 0x0, 0x8, 0x0},
{0xa1c3, 0x0, 0x5, 0x10},
{0xa1c3, 0x5, 0x3, 0x0},
{0xa1c4, 0x0, 0x6, 0x0},
{0xa1c5, 0x0, 0x7, 0x10},
{0xa100, 0x0, 0x8, 0x0},
{0xa101, 0x0, 0x5, 0x10},
{0xa102, 0x0, 0x8, 0x0},
{0xa103, 0x0, 0x7, 0x40},
{0xa103, 0x7, 0x1, 0x0},
{0xa104, 0x0, 0x8, 0x18},
{0xa105, 0x0, 0x7, 0xa},
{0xa106, 0x0, 0x8, 0x20},
{0xa107, 0x0, 0x8, 0x40},
{0xa108, 0x0, 0x4, 0x0},
{0xa38c, 0x0, 0x8, 0xfc},
{0xa38d, 0x0, 0x8, 0x0},
{0xa38e, 0x0, 0x8, 0x7e},
{0xa38f, 0x0, 0x8, 0x0},
{0xa390, 0x0, 0x8, 0x2f},
{0xa60f, 0x5, 0x1, 0x1},
{0xa170, 0x0, 0x8, 0xdc},
{0xa171, 0x0, 0x2, 0x0},
{0xa2ae, 0x0, 0x1, 0x1},
{0xa2ae, 0x1, 0x1, 0x1},
{0xa392, 0x0, 0x1, 0x1},
{0xa391, 0x2, 0x1, 0x0},
{0xabc1, 0x0, 0x8, 0xff},
{0xabc2, 0x0, 0x8, 0x0},
{0xabc8, 0x0, 0x8, 0x8},
{0xabca, 0x0, 0x8, 0x10},
{0xabcb, 0x0, 0x1, 0x0},
{0xabc3, 0x5, 0x3, 0x7},
{0xabc0, 0x6, 0x1, 0x0},
{0xabc0, 0x4, 0x2, 0x0},
{0xa344, 0x4, 0x4, 0x1},
{0xabc0, 0x7, 0x1, 0x1},
{0xabc0, 0x2, 0x1, 0x1},
{0xa345, 0x0, 0x8, 0x66},
{0xa346, 0x0, 0x8, 0x66},
{0xa347, 0x0, 0x4, 0x0},
{0xa343, 0x0, 0x4, 0xa},
{0xa347, 0x4, 0x4, 0x2},
{0xa348, 0x0, 0x4, 0xc},
{0xa348, 0x4, 0x4, 0x7},
{0xa349, 0x0, 0x6, 0x2},
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
#ifndef _DVB_USB_VP6027_H_
#define _DVB_USB_VP6027_H_
#define DVB_USB_LOG_PREFIX "az6027"
#include "dvb-usb.h"
extern int dvb_usb_az6027_debug;
#define deb_info(args...) dprintk(dvb_usb_az6027_debug, 0x01, args)
#define deb_xfer(args...) dprintk(dvb_usb_az6027_debug, 0x02, args)
#define deb_rc(args...) dprintk(dvb_usb_az6027_debug, 0x04, args)
#define deb_fe(args...) dprintk(dvb_usb_az6027_debug, 0x08, args)
#endif

View file

@ -0,0 +1,253 @@
/*
* TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
*
* Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
*
* Based on the dvb-usb-framework code and the
* original Terratec Cinergy T2 driver by:
*
* Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
* Holger Waechtler <holger@qanu.de>
*
* Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
*
* 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 "cinergyT2.h"
/* debug */
int dvb_usb_cinergyt2_debug;
module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 "
"(or-able)).");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct cinergyt2_state {
u8 rc_counter;
};
/* We are missing a release hook with usb_device data */
static struct dvb_usb_device *cinergyt2_usb_device;
static struct dvb_usb_device_properties cinergyt2_properties;
static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable)
{
char buf[] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
char result[64];
return dvb_usb_generic_rw(adap->dev, buf, sizeof(buf), result,
sizeof(result), 0);
}
static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable)
{
char buf[] = { CINERGYT2_EP1_SLEEP_MODE, enable ? 0 : 1 };
char state[3];
return dvb_usb_generic_rw(d, buf, sizeof(buf), state, sizeof(state), 0);
}
static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap)
{
char query[] = { CINERGYT2_EP1_GET_FIRMWARE_VERSION };
char state[3];
int ret;
adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev);
ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state,
sizeof(state), 0);
if (ret < 0) {
deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep "
"state info\n");
}
/* Copy this pointer as we are gonna need it in the release phase */
cinergyt2_usb_device = adap->dev;
return 0;
}
static struct rc_map_table rc_map_cinergyt2_table[] = {
{ 0x0401, KEY_POWER },
{ 0x0402, KEY_1 },
{ 0x0403, KEY_2 },
{ 0x0404, KEY_3 },
{ 0x0405, KEY_4 },
{ 0x0406, KEY_5 },
{ 0x0407, KEY_6 },
{ 0x0408, KEY_7 },
{ 0x0409, KEY_8 },
{ 0x040a, KEY_9 },
{ 0x040c, KEY_0 },
{ 0x040b, KEY_VIDEO },
{ 0x040d, KEY_REFRESH },
{ 0x040e, KEY_SELECT },
{ 0x040f, KEY_EPG },
{ 0x0410, KEY_UP },
{ 0x0414, KEY_DOWN },
{ 0x0411, KEY_LEFT },
{ 0x0413, KEY_RIGHT },
{ 0x0412, KEY_OK },
{ 0x0415, KEY_TEXT },
{ 0x0416, KEY_INFO },
{ 0x0417, KEY_RED },
{ 0x0418, KEY_GREEN },
{ 0x0419, KEY_YELLOW },
{ 0x041a, KEY_BLUE },
{ 0x041c, KEY_VOLUMEUP },
{ 0x041e, KEY_VOLUMEDOWN },
{ 0x041d, KEY_MUTE },
{ 0x041b, KEY_CHANNELUP },
{ 0x041f, KEY_CHANNELDOWN },
{ 0x0440, KEY_PAUSE },
{ 0x044c, KEY_PLAY },
{ 0x0458, KEY_RECORD },
{ 0x0454, KEY_PREVIOUS },
{ 0x0448, KEY_STOP },
{ 0x045c, KEY_NEXT }
};
/* Number of keypresses to ignore before detect repeating */
#define RC_REPEAT_DELAY 3
static int repeatable_keys[] = {
KEY_UP,
KEY_DOWN,
KEY_LEFT,
KEY_RIGHT,
KEY_VOLUMEUP,
KEY_VOLUMEDOWN,
KEY_CHANNELUP,
KEY_CHANNELDOWN
};
static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
struct cinergyt2_state *st = d->priv;
u8 key[5] = {0, 0, 0, 0, 0}, cmd = CINERGYT2_EP1_GET_RC_EVENTS;
int i;
*state = REMOTE_NO_KEY_PRESSED;
dvb_usb_generic_rw(d, &cmd, 1, key, sizeof(key), 0);
if (key[4] == 0xff) {
/* key repeat */
st->rc_counter++;
if (st->rc_counter > RC_REPEAT_DELAY) {
for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) {
if (d->last_event == repeatable_keys[i]) {
*state = REMOTE_KEY_REPEAT;
*event = d->last_event;
deb_rc("repeat key, event %x\n",
*event);
return 0;
}
}
deb_rc("repeated key (non repeatable)\n");
}
return 0;
}
/* hack to pass checksum on the custom field */
key[2] = ~key[1];
dvb_usb_nec_rc_key_to_event(d, key, event, state);
if (key[0] != 0) {
if (*event != d->last_event)
st->rc_counter = 0;
deb_rc("key: %*ph\n", 5, key);
}
return 0;
}
static int cinergyt2_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return dvb_usb_device_init(intf, &cinergyt2_properties,
THIS_MODULE, NULL, adapter_nr);
}
static struct usb_device_id cinergyt2_usb_table[] = {
{ USB_DEVICE(USB_VID_TERRATEC, 0x0038) },
{ 0 }
};
MODULE_DEVICE_TABLE(usb, cinergyt2_usb_table);
static struct dvb_usb_device_properties cinergyt2_properties = {
.size_of_priv = sizeof(struct cinergyt2_state),
.num_adapters = 1,
.adapter = {
{
.num_frontends = 1,
.fe = {{
.streaming_ctrl = cinergyt2_streaming_ctrl,
.frontend_attach = cinergyt2_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 5,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 512,
}
}
},
}},
}
},
.power_ctrl = cinergyt2_power_ctrl,
.rc.legacy = {
.rc_interval = 50,
.rc_map_table = rc_map_cinergyt2_table,
.rc_map_size = ARRAY_SIZE(rc_map_cinergyt2_table),
.rc_query = cinergyt2_rc_query,
},
.generic_bulk_ctrl_endpoint = 1,
.num_device_descs = 1,
.devices = {
{ .name = "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver",
.cold_ids = {NULL},
.warm_ids = { &cinergyt2_usb_table[0], NULL },
},
{ NULL },
}
};
static struct usb_driver cinergyt2_driver = {
.name = "cinergyT2",
.probe = cinergyt2_usb_probe,
.disconnect = dvb_usb_device_exit,
.id_table = cinergyt2_usb_table
};
module_usb_driver(cinergyt2_driver);
MODULE_DESCRIPTION("Terratec Cinergy T2 DVB-T driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tomi Orava");

View file

@ -0,0 +1,355 @@
/*
* TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
*
* Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
*
* Based on the dvb-usb-framework code and the
* original Terratec Cinergy T2 driver by:
*
* Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
* Holger Waechtler <holger@qanu.de>
*
* Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
*
* 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 "cinergyT2.h"
/**
* convert linux-dvb frontend parameter set into TPS.
* See ETSI ETS-300744, section 4.6.2, table 9 for details.
*
* This function is probably reusable and may better get placed in a support
* library.
*
* We replace errornous fields by default TPS fields (the ones with value 0).
*/
static uint16_t compute_tps(struct dtv_frontend_properties *op)
{
uint16_t tps = 0;
switch (op->code_rate_HP) {
case FEC_2_3:
tps |= (1 << 7);
break;
case FEC_3_4:
tps |= (2 << 7);
break;
case FEC_5_6:
tps |= (3 << 7);
break;
case FEC_7_8:
tps |= (4 << 7);
break;
case FEC_1_2:
case FEC_AUTO:
default:
/* tps |= (0 << 7) */;
}
switch (op->code_rate_LP) {
case FEC_2_3:
tps |= (1 << 4);
break;
case FEC_3_4:
tps |= (2 << 4);
break;
case FEC_5_6:
tps |= (3 << 4);
break;
case FEC_7_8:
tps |= (4 << 4);
break;
case FEC_1_2:
case FEC_AUTO:
default:
/* tps |= (0 << 4) */;
}
switch (op->modulation) {
case QAM_16:
tps |= (1 << 13);
break;
case QAM_64:
tps |= (2 << 13);
break;
case QPSK:
default:
/* tps |= (0 << 13) */;
}
switch (op->transmission_mode) {
case TRANSMISSION_MODE_8K:
tps |= (1 << 0);
break;
case TRANSMISSION_MODE_2K:
default:
/* tps |= (0 << 0) */;
}
switch (op->guard_interval) {
case GUARD_INTERVAL_1_16:
tps |= (1 << 2);
break;
case GUARD_INTERVAL_1_8:
tps |= (2 << 2);
break;
case GUARD_INTERVAL_1_4:
tps |= (3 << 2);
break;
case GUARD_INTERVAL_1_32:
default:
/* tps |= (0 << 2) */;
}
switch (op->hierarchy) {
case HIERARCHY_1:
tps |= (1 << 10);
break;
case HIERARCHY_2:
tps |= (2 << 10);
break;
case HIERARCHY_4:
tps |= (3 << 10);
break;
case HIERARCHY_NONE:
default:
/* tps |= (0 << 10) */;
}
return tps;
}
struct cinergyt2_fe_state {
struct dvb_frontend fe;
struct dvb_usb_device *d;
};
static int cinergyt2_fe_read_status(struct dvb_frontend *fe,
fe_status_t *status)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
struct dvbt_get_status_msg result;
u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
int ret;
ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&result,
sizeof(result), 0);
if (ret < 0)
return ret;
*status = 0;
if (0xffff - le16_to_cpu(result.gain) > 30)
*status |= FE_HAS_SIGNAL;
if (result.lock_bits & (1 << 6))
*status |= FE_HAS_LOCK;
if (result.lock_bits & (1 << 5))
*status |= FE_HAS_SYNC;
if (result.lock_bits & (1 << 4))
*status |= FE_HAS_CARRIER;
if (result.lock_bits & (1 << 1))
*status |= FE_HAS_VITERBI;
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 cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
struct dvbt_get_status_msg status;
char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
int ret;
ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
sizeof(status), 0);
if (ret < 0)
return ret;
*ber = le32_to_cpu(status.viterbi_error_rate);
return 0;
}
static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
struct dvbt_get_status_msg status;
u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
int ret;
ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&status,
sizeof(status), 0);
if (ret < 0) {
err("cinergyt2_fe_read_unc_blocks() Failed! (Error=%d)\n",
ret);
return ret;
}
*unc = le32_to_cpu(status.uncorrected_block_count);
return 0;
}
static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe,
u16 *strength)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
struct dvbt_get_status_msg status;
char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
int ret;
ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
sizeof(status), 0);
if (ret < 0) {
err("cinergyt2_fe_read_signal_strength() Failed!"
" (Error=%d)\n", ret);
return ret;
}
*strength = (0xffff - le16_to_cpu(status.gain));
return 0;
}
static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
struct dvbt_get_status_msg status;
char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
int ret;
ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
sizeof(status), 0);
if (ret < 0) {
err("cinergyt2_fe_read_snr() Failed! (Error=%d)\n", ret);
return ret;
}
*snr = (status.snr << 8) | status.snr;
return 0;
}
static int cinergyt2_fe_init(struct dvb_frontend *fe)
{
return 0;
}
static int cinergyt2_fe_sleep(struct dvb_frontend *fe)
{
deb_info("cinergyt2_fe_sleep() Called\n");
return 0;
}
static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *tune)
{
tune->min_delay_ms = 800;
return 0;
}
static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct cinergyt2_fe_state *state = fe->demodulator_priv;
struct dvbt_set_parameters_msg param;
char result[2];
int err;
param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
param.tps = cpu_to_le16(compute_tps(fep));
param.freq = cpu_to_le32(fep->frequency / 1000);
param.flags = 0;
switch (fep->bandwidth_hz) {
default:
case 8000000:
param.bandwidth = 8;
break;
case 7000000:
param.bandwidth = 7;
break;
case 6000000:
param.bandwidth = 6;
break;
}
err = dvb_usb_generic_rw(state->d,
(char *)&param, sizeof(param),
result, sizeof(result), 0);
if (err < 0)
err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err);
return (err < 0) ? err : 0;
}
static void cinergyt2_fe_release(struct dvb_frontend *fe)
{
struct cinergyt2_fe_state *state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops cinergyt2_fe_ops;
struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d)
{
struct cinergyt2_fe_state *s = kzalloc(sizeof(
struct cinergyt2_fe_state), GFP_KERNEL);
if (s == NULL)
return NULL;
s->d = d;
memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops));
s->fe.demodulator_priv = s;
return &s->fe;
}
static struct dvb_frontend_ops cinergyt2_fe_ops = {
.delsys = { SYS_DVBT },
.info = {
.name = DRIVER_NAME,
.frequency_min = 174000000,
.frequency_max = 862000000,
.frequency_stepsize = 166667,
.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_HIERARCHY_AUTO
| FE_CAN_RECOVER
| FE_CAN_MUTE_TS
},
.release = cinergyt2_fe_release,
.init = cinergyt2_fe_init,
.sleep = cinergyt2_fe_sleep,
.set_frontend = cinergyt2_fe_set_frontend,
.get_tune_settings = cinergyt2_fe_get_tune_settings,
.read_status = cinergyt2_fe_read_status,
.read_ber = cinergyt2_fe_read_ber,
.read_signal_strength = cinergyt2_fe_read_signal_strength,
.read_snr = cinergyt2_fe_read_snr,
.read_ucblocks = cinergyt2_fe_read_unc_blocks,
};

View file

@ -0,0 +1,95 @@
/*
* TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
*
* Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
*
* Based on the dvb-usb-framework code and the
* original Terratec Cinergy T2 driver by:
*
* Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
* Holger Waechtler <holger@qanu.de>
*
* Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
*
* 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_USB_CINERGYT2_H_
#define _DVB_USB_CINERGYT2_H_
#include <linux/usb/input.h>
#define DVB_USB_LOG_PREFIX "cinergyT2"
#include "dvb-usb.h"
#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"
extern int dvb_usb_cinergyt2_debug;
#define deb_info(args...) dprintk(dvb_usb_cinergyt2_debug, 0x001, args)
#define deb_xfer(args...) dprintk(dvb_usb_cinergyt2_debug, 0x002, args)
#define deb_pll(args...) dprintk(dvb_usb_cinergyt2_debug, 0x004, args)
#define deb_ts(args...) dprintk(dvb_usb_cinergyt2_debug, 0x008, args)
#define deb_err(args...) dprintk(dvb_usb_cinergyt2_debug, 0x010, args)
#define deb_rc(args...) dprintk(dvb_usb_cinergyt2_debug, 0x020, args)
#define deb_fw(args...) dprintk(dvb_usb_cinergyt2_debug, 0x040, args)
#define deb_mem(args...) dprintk(dvb_usb_cinergyt2_debug, 0x080, args)
#define deb_uxfer(args...) dprintk(dvb_usb_cinergyt2_debug, 0x100, args)
enum cinergyt2_ep1_cmd {
CINERGYT2_EP1_PID_TABLE_RESET = 0x01,
CINERGYT2_EP1_PID_SETUP = 0x02,
CINERGYT2_EP1_CONTROL_STREAM_TRANSFER = 0x03,
CINERGYT2_EP1_SET_TUNER_PARAMETERS = 0x04,
CINERGYT2_EP1_GET_TUNER_STATUS = 0x05,
CINERGYT2_EP1_START_SCAN = 0x06,
CINERGYT2_EP1_CONTINUE_SCAN = 0x07,
CINERGYT2_EP1_GET_RC_EVENTS = 0x08,
CINERGYT2_EP1_SLEEP_MODE = 0x09,
CINERGYT2_EP1_GET_FIRMWARE_VERSION = 0x0A
};
struct dvbt_get_status_msg {
uint32_t freq;
uint8_t bandwidth;
uint16_t tps;
uint8_t flags;
__le16 gain;
uint8_t snr;
__le32 viterbi_error_rate;
uint32_t rs_error_rate;
__le32 uncorrected_block_count;
uint8_t lock_bits;
uint8_t prev_lock_bits;
} __attribute__((packed));
struct dvbt_set_parameters_msg {
uint8_t cmd;
__le32 freq;
uint8_t bandwidth;
__le16 tps;
uint8_t flags;
} __attribute__((packed));
extern struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d);
#endif /* _DVB_USB_CINERGYT2_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,41 @@
#ifndef _DVB_USB_CXUSB_H_
#define _DVB_USB_CXUSB_H_
#define DVB_USB_LOG_PREFIX "cxusb"
#include "dvb-usb.h"
/* usb commands - some of it are guesses, don't have a reference yet */
#define CMD_BLUEBIRD_GPIO_RW 0x05
#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_AVER_STREAM_ON 0x18
#define CMD_AVER_STREAM_OFF 0x19
#define CMD_GET_IR_CODE 0x47
#define CMD_ANALOG 0x50
#define CMD_DIGITAL 0x51
#define CMD_SP2_CI_WRITE 0x70
#define CMD_SP2_CI_READ 0x71
struct cxusb_state {
u8 gpio_write_state[3];
struct i2c_client *i2c_client_demod;
struct i2c_client *i2c_client_tuner;
struct i2c_client *i2c_client_ci;
};
#endif

View file

@ -0,0 +1,75 @@
/* 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_SET_USB_XFER_LEN 0x0 /* valid only for firmware version */
/* higher than 1.21 */
#define REQUEST_I2C_READ 0x2
#define REQUEST_I2C_WRITE 0x3
#define REQUEST_POLL_RC 0x4 /* deprecated in firmware v1.20 */
#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_SET_I2C_PARAM 0x10
#define REQUEST_SET_RC 0x11
#define REQUEST_NEW_I2C_READ 0x12
#define REQUEST_NEW_I2C_WRITE 0x13
#define REQUEST_GET_VERSION 0x15
struct dib0700_state {
u8 channel_state;
u16 mt2060_if1[2];
u8 rc_toggle;
u8 rc_counter;
u8 is_dib7000pc;
u8 fw_use_new_i2c_api;
u8 disable_streaming_master_mode;
u32 fw_version;
u32 nb_packet_buffer_size;
int (*read_status)(struct dvb_frontend *, fe_status_t *);
int (*sleep)(struct dvb_frontend* fe);
u8 buf[255];
};
extern int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
u32 *romversion, u32 *ramversion, u32 *fwtype);
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_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen);
extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw);
extern int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf);
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_change_protocol(struct rc_dev *dev, u64 *rc_type);
extern int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz);
extern int dib0700_device_count;
extern int dvb_usb_dib0700_ir_proto;
extern struct dvb_usb_device_properties dib0700_devices[];
extern struct usb_device_id dib0700_usb_id_table[];
#endif

View file

@ -0,0 +1,880 @@
/* 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);
static int nb_packet_buffer_size = 21;
module_param(nb_packet_buffer_size, int, 0644);
MODULE_PARM_DESC(nb_packet_buffer_size,
"Set the dib0700 driver data buffer size. This parameter "
"corresponds to the number of TS packets. The actual size of "
"the data buffer corresponds to this parameter "
"multiplied by 188 (default: 21)");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
u32 *romversion, u32 *ramversion, u32 *fwtype)
{
struct dib0700_state *st = d->priv;
int ret;
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
return -EINTR;
}
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
REQUEST_GET_VERSION,
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
st->buf, 16, USB_CTRL_GET_TIMEOUT);
if (hwversion != NULL)
*hwversion = (st->buf[0] << 24) | (st->buf[1] << 16) |
(st->buf[2] << 8) | st->buf[3];
if (romversion != NULL)
*romversion = (st->buf[4] << 24) | (st->buf[5] << 16) |
(st->buf[6] << 8) | st->buf[7];
if (ramversion != NULL)
*ramversion = (st->buf[8] << 24) | (st->buf[9] << 16) |
(st->buf[10] << 8) | st->buf[11];
if (fwtype != NULL)
*fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) |
(st->buf[14] << 8) | st->buf[15];
mutex_unlock(&d->usb_mutex);
return ret;
}
/* 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) */
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];
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)
{
struct dib0700_state *st = d->priv;
int ret;
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
return -EINTR;
}
st->buf[0] = REQUEST_SET_GPIO;
st->buf[1] = gpio;
st->buf[2] = ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6);
ret = dib0700_ctrl_wr(d, st->buf, 3);
mutex_unlock(&d->usb_mutex);
return ret;
}
static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
{
struct dib0700_state *st = d->priv;
int ret;
if (st->fw_version >= 0x10201) {
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
return -EINTR;
}
st->buf[0] = REQUEST_SET_USB_XFER_LEN;
st->buf[1] = (nb_ts_packets >> 8) & 0xff;
st->buf[2] = nb_ts_packets & 0xff;
deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets);
ret = dib0700_ctrl_wr(d, st->buf, 3);
mutex_unlock(&d->usb_mutex);
} else {
deb_info("this firmware does not allow to change the USB xfer len\n");
ret = -EIO;
}
return ret;
}
/*
* I2C master xfer function (supported in 1.20 firmware)
*/
static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
int num)
{
/* The new i2c firmware messages are more reliable and in particular
properly support i2c read calls not preceded by a write */
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct dib0700_state *st = d->priv;
uint8_t bus_mode = 1; /* 0=eeprom bus, 1=frontend bus */
uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
uint8_t en_start = 0;
uint8_t en_stop = 0;
int result, i;
/* Ensure nobody else hits the i2c bus while we're sending our
sequence of messages, (such as the remote control thread) */
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EINTR;
for (i = 0; i < num; i++) {
if (i == 0) {
/* First message in the transaction */
en_start = 1;
} else if (!(msg[i].flags & I2C_M_NOSTART)) {
/* Device supports repeated-start */
en_start = 1;
} else {
/* Not the first packet and device doesn't support
repeated start */
en_start = 0;
}
if (i == (num - 1)) {
/* Last message in the transaction */
en_stop = 1;
}
if (msg[i].flags & I2C_M_RD) {
/* Read request */
u16 index, value;
uint8_t i2c_dest;
i2c_dest = (msg[i].addr << 1);
value = ((en_start << 7) | (en_stop << 6) |
(msg[i].len & 0x3F)) << 8 | i2c_dest;
/* I2C ctrl + FE bus; */
index = ((gen_mode << 6) & 0xC0) |
((bus_mode << 4) & 0x30);
result = usb_control_msg(d->udev,
usb_rcvctrlpipe(d->udev, 0),
REQUEST_NEW_I2C_READ,
USB_TYPE_VENDOR | USB_DIR_IN,
value, index, msg[i].buf,
msg[i].len,
USB_CTRL_GET_TIMEOUT);
if (result < 0) {
deb_info("i2c read error (status = %d)\n", result);
break;
}
deb_data("<<< ");
debug_dump(msg[i].buf, msg[i].len, deb_data);
} else {
/* Write request */
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
mutex_unlock(&d->i2c_mutex);
return -EINTR;
}
st->buf[0] = REQUEST_NEW_I2C_WRITE;
st->buf[1] = msg[i].addr << 1;
st->buf[2] = (en_start << 7) | (en_stop << 6) |
(msg[i].len & 0x3F);
/* I2C ctrl + FE bus; */
st->buf[3] = ((gen_mode << 6) & 0xC0) |
((bus_mode << 4) & 0x30);
/* The Actual i2c payload */
memcpy(&st->buf[4], msg[i].buf, msg[i].len);
deb_data(">>> ");
debug_dump(st->buf, msg[i].len + 4, deb_data);
result = usb_control_msg(d->udev,
usb_sndctrlpipe(d->udev, 0),
REQUEST_NEW_I2C_WRITE,
USB_TYPE_VENDOR | USB_DIR_OUT,
0, 0, st->buf, msg[i].len + 4,
USB_CTRL_GET_TIMEOUT);
mutex_unlock(&d->usb_mutex);
if (result < 0) {
deb_info("i2c write error (status = %d)\n", result);
break;
}
}
}
mutex_unlock(&d->i2c_mutex);
return i;
}
/*
* I2C master xfer function (pre-1.20 firmware)
*/
static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
struct i2c_msg *msg, int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct dib0700_state *st = d->priv;
int i,len;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EINTR;
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
mutex_unlock(&d->i2c_mutex);
return -EINTR;
}
for (i = 0; i < num; i++) {
/* fill in the address */
st->buf[1] = msg[i].addr << 1;
/* fill the buffer */
memcpy(&st->buf[2], msg[i].buf, msg[i].len);
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
st->buf[0] = REQUEST_I2C_READ;
st->buf[1] |= 1;
/* special thing in the current firmware: when length is zero the read-failed */
len = dib0700_ctrl_rd(d, st->buf, msg[i].len + 2,
msg[i+1].buf, msg[i+1].len);
if (len <= 0) {
deb_info("I2C read failed on address 0x%02x\n",
msg[i].addr);
break;
}
msg[i+1].len = len;
i++;
} else {
st->buf[0] = REQUEST_I2C_WRITE;
if (dib0700_ctrl_wr(d, st->buf, msg[i].len + 2) < 0)
break;
}
}
mutex_unlock(&d->usb_mutex);
mutex_unlock(&d->i2c_mutex);
return i;
}
static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct dib0700_state *st = d->priv;
if (st->fw_use_new_i2c_api == 1) {
/* User running at least fw 1.20 */
return dib0700_i2c_xfer_new(adap, msg, num);
} else {
/* Use legacy calls */
return dib0700_i2c_xfer_legacy(adap, msg, num);
}
}
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)
{
s16 ret;
u8 *b;
b = kmalloc(16, GFP_KERNEL);
if (!b)
return -ENOMEM;
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);
kfree(b);
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)
{
struct dib0700_state *st = d->priv;
int ret;
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
return -EINTR;
}
st->buf[0] = REQUEST_SET_CLOCK;
st->buf[1] = (en_pll << 7) | (pll_src << 6) |
(pll_range << 5) | (clock_gpio3 << 4);
st->buf[2] = (pll_prediv >> 8) & 0xff; /* MSB */
st->buf[3] = pll_prediv & 0xff; /* LSB */
st->buf[4] = (pll_loopdiv >> 8) & 0xff; /* MSB */
st->buf[5] = pll_loopdiv & 0xff; /* LSB */
st->buf[6] = (free_div >> 8) & 0xff; /* MSB */
st->buf[7] = free_div & 0xff; /* LSB */
st->buf[8] = (dsuScaler >> 8) & 0xff; /* MSB */
st->buf[9] = dsuScaler & 0xff; /* LSB */
ret = dib0700_ctrl_wr(d, st->buf, 10);
mutex_unlock(&d->usb_mutex);
return ret;
}
int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
{
struct dib0700_state *st = d->priv;
u16 divider;
int ret;
if (scl_kHz == 0)
return -EINVAL;
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
return -EINTR;
}
st->buf[0] = REQUEST_SET_I2C_PARAM;
divider = (u16) (30000 / scl_kHz);
st->buf[1] = 0;
st->buf[2] = (u8) (divider >> 8);
st->buf[3] = (u8) (divider & 0xff);
divider = (u16) (72000 / scl_kHz);
st->buf[4] = (u8) (divider >> 8);
st->buf[5] = (u8) (divider & 0xff);
divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */
st->buf[6] = (u8) (divider >> 8);
st->buf[7] = (u8) (divider & 0xff);
deb_info("setting I2C speed: %04x %04x %04x (%d kHz).",
(st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) |
st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz);
ret = dib0700_ctrl_wr(d, st->buf, 8);
mutex_unlock(&d->usb_mutex);
return ret;
}
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 = 0, actlen;
u8 *buf;
buf = kmalloc(8, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = REQUEST_JUMPRAM;
buf[1] = 0;
buf[2] = 0;
buf[3] = 0;
buf[4] = (address >> 24) & 0xff;
buf[5] = (address >> 16) & 0xff;
buf[6] = (address >> 8) & 0xff;
buf[7] = 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);
goto out;
}
if (actlen != 8) {
deb_fw("jumpram to 0x%x failed\n",address);
ret = -EIO;
goto out;
}
out:
kfree(buf);
return ret;
}
int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw)
{
struct hexline hx;
int pos = 0, ret, act_len, i, adap_num;
u8 *buf;
u32 fw_version;
buf = kmalloc(260, GFP_KERNEL);
if (!buf)
return -ENOMEM;
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);
goto out;
}
}
if (ret == 0) {
/* start the firmware */
if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
info("firmware started successfully.");
msleep(500);
}
} else
ret = -EIO;
/* the number of ts packet has to be at least 1 */
if (nb_packet_buffer_size < 1)
nb_packet_buffer_size = 1;
/* get the fimware version */
usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
REQUEST_GET_VERSION,
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
buf, 16, USB_CTRL_GET_TIMEOUT);
fw_version = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
/* set the buffer size - DVB-USB is allocating URB buffers
* only after the firwmare download was successful */
for (i = 0; i < dib0700_device_count; i++) {
for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
adap_num++) {
if (fw_version >= 0x10201) {
dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
} else {
/* for fw version older than 1.20.1,
* the buffersize has to be n times 512 */
dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512)
dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512;
}
}
}
out:
kfree(buf);
return ret;
}
int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct dib0700_state *st = adap->dev->priv;
int ret;
if ((onoff != 0) && (st->fw_version >= 0x10201)) {
/* for firmware later than 1.20.1,
* the USB xfer length can be set */
ret = dib0700_set_usb_xfer_len(adap->dev,
st->nb_packet_buffer_size);
if (ret < 0) {
deb_info("can not set the USB xfer len\n");
return ret;
}
}
mutex_lock(&adap->dev->usb_mutex);
st->buf[0] = REQUEST_ENABLE_VIDEO;
/* this bit gives a kind of command,
* rather than enabling something or not */
st->buf[1] = (onoff << 4) | 0x00;
if (st->disable_streaming_master_mode == 1)
st->buf[2] = 0x00;
else
st->buf[2] = 0x01 << 4; /* Master mode */
st->buf[3] = 0x00;
deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
st->channel_state &= ~0x3;
if ((adap->fe_adap[0].stream.props.endpoint != 2)
&& (adap->fe_adap[0].stream.props.endpoint != 3)) {
deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].stream.props.endpoint);
if (onoff)
st->channel_state |= 1 << (adap->id);
else
st->channel_state |= 1 << ~(adap->id);
} else {
if (onoff)
st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2);
else
st->channel_state |= 1 << (3-adap->fe_adap[0].stream.props.endpoint);
}
st->buf[2] |= st->channel_state;
deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]);
ret = dib0700_ctrl_wr(adap->dev, st->buf, 4);
mutex_unlock(&adap->dev->usb_mutex);
return ret;
}
int dib0700_change_protocol(struct rc_dev *rc, u64 *rc_type)
{
struct dvb_usb_device *d = rc->priv;
struct dib0700_state *st = d->priv;
int new_proto, ret;
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
return -EINTR;
}
st->buf[0] = REQUEST_SET_RC;
st->buf[1] = 0;
st->buf[2] = 0;
/* Set the IR mode */
if (*rc_type & RC_BIT_RC5) {
new_proto = 1;
*rc_type = RC_BIT_RC5;
} else if (*rc_type & RC_BIT_NEC) {
new_proto = 0;
*rc_type = RC_BIT_NEC;
} else if (*rc_type & RC_BIT_RC6_MCE) {
if (st->fw_version < 0x10200) {
ret = -EINVAL;
goto out;
}
new_proto = 2;
*rc_type = RC_BIT_RC6_MCE;
} else {
ret = -EINVAL;
goto out;
}
st->buf[1] = new_proto;
ret = dib0700_ctrl_wr(d, st->buf, 3);
if (ret < 0) {
err("ir protocol setup failed");
goto out;
}
d->props.rc.core.protocol = *rc_type;
out:
mutex_unlock(&d->usb_mutex);
return ret;
}
/* Number of keypresses to ignore before start repeating */
#define RC_REPEAT_DELAY_V1_20 10
/* This is the structure of the RC response packet starting in firmware 1.20 */
struct dib0700_rc_response {
u8 report_id;
u8 data_state;
u8 system;
u8 not_system;
u8 data;
u8 not_data;
};
#define RC_MSG_SIZE_V1_20 6
static void dib0700_rc_urb_completion(struct urb *purb)
{
struct dvb_usb_device *d = purb->context;
struct dib0700_rc_response *poll_reply;
enum rc_type protocol;
u32 uninitialized_var(keycode);
u8 toggle;
deb_info("%s()\n", __func__);
if (d->rc_dev == NULL) {
/* This will occur if disable_rc_polling=1 */
kfree(purb->transfer_buffer);
usb_free_urb(purb);
return;
}
poll_reply = purb->transfer_buffer;
if (purb->status < 0) {
deb_info("discontinuing polling\n");
kfree(purb->transfer_buffer);
usb_free_urb(purb);
return;
}
if (purb->actual_length != RC_MSG_SIZE_V1_20) {
deb_info("malformed rc msg size=%d\n", purb->actual_length);
goto resubmit;
}
deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n",
poll_reply->report_id, poll_reply->data_state,
poll_reply->system, poll_reply->not_system,
poll_reply->data, poll_reply->not_data,
purb->actual_length);
switch (d->props.rc.core.protocol) {
case RC_BIT_NEC:
protocol = RC_TYPE_NEC;
toggle = 0;
/* NEC protocol sends repeat code as 0 0 0 FF */
if (poll_reply->system == 0x00 &&
poll_reply->not_system == 0x00 &&
poll_reply->data == 0x00 &&
poll_reply->not_data == 0xff) {
poll_reply->data_state = 2;
break;
}
if ((poll_reply->data ^ poll_reply->not_data) != 0xff) {
deb_data("NEC32 protocol\n");
keycode = RC_SCANCODE_NEC32(poll_reply->system << 24 |
poll_reply->not_system << 16 |
poll_reply->data << 8 |
poll_reply->not_data);
} else if ((poll_reply->system ^ poll_reply->not_system) != 0xff) {
deb_data("NEC extended protocol\n");
keycode = RC_SCANCODE_NECX(poll_reply->system << 8 |
poll_reply->not_system,
poll_reply->data);
} else {
deb_data("NEC normal protocol\n");
keycode = RC_SCANCODE_NEC(poll_reply->system,
poll_reply->data);
}
break;
default:
deb_data("RC5 protocol\n");
protocol = RC_TYPE_RC5;
toggle = poll_reply->report_id;
keycode = RC_SCANCODE_RC5(poll_reply->system, poll_reply->data);
break;
}
if ((poll_reply->data + poll_reply->not_data) != 0xff) {
/* Key failed integrity check */
err("key failed integrity check: %02x %02x %02x %02x",
poll_reply->system, poll_reply->not_system,
poll_reply->data, poll_reply->not_data);
goto resubmit;
}
rc_keydown(d->rc_dev, protocol, keycode, toggle);
resubmit:
/* Clean the buffer before we requeue */
memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20);
/* Requeue URB */
usb_submit_urb(purb, GFP_ATOMIC);
}
int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf)
{
struct dib0700_state *st = d->priv;
struct urb *purb;
const struct usb_endpoint_descriptor *e;
int ret, rc_ep = 1;
unsigned int pipe = 0;
/* Poll-based. Don't initialize bulk mode */
if (st->fw_version < 0x10200 || !intf)
return 0;
/* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
purb = usb_alloc_urb(0, GFP_KERNEL);
if (purb == NULL) {
err("rc usb alloc urb failed");
return -ENOMEM;
}
purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL);
if (purb->transfer_buffer == NULL) {
err("rc kzalloc failed");
usb_free_urb(purb);
return -ENOMEM;
}
purb->status = -EINPROGRESS;
/*
* Some devices like the Hauppauge NovaTD model 52009 use an interrupt
* endpoint, while others use a bulk one.
*/
e = &intf->altsetting[0].endpoint[rc_ep].desc;
if (usb_endpoint_dir_in(e)) {
if (usb_endpoint_xfer_bulk(e)) {
pipe = usb_rcvbulkpipe(d->udev, rc_ep);
usb_fill_bulk_urb(purb, d->udev, pipe,
purb->transfer_buffer,
RC_MSG_SIZE_V1_20,
dib0700_rc_urb_completion, d);
} else if (usb_endpoint_xfer_int(e)) {
pipe = usb_rcvintpipe(d->udev, rc_ep);
usb_fill_int_urb(purb, d->udev, pipe,
purb->transfer_buffer,
RC_MSG_SIZE_V1_20,
dib0700_rc_urb_completion, d, 1);
}
}
if (!pipe) {
err("There's no endpoint for remote controller");
kfree(purb->transfer_buffer);
usb_free_urb(purb);
return 0;
}
ret = usb_submit_urb(purb, GFP_ATOMIC);
if (ret) {
err("rc submit urb failed");
kfree(purb->transfer_buffer);
usb_free_urb(purb);
}
return ret;
}
static int dib0700_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int i;
struct dvb_usb_device *dev;
for (i = 0; i < dib0700_device_count; i++)
if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE,
&dev, adapter_nr) == 0) {
struct dib0700_state *st = dev->priv;
u32 hwversion, romversion, fw_version, fwtype;
dib0700_get_version(dev, &hwversion, &romversion,
&fw_version, &fwtype);
deb_info("Firmware version: %x, %d, 0x%x, %d\n",
hwversion, romversion, fw_version, fwtype);
st->fw_version = fw_version;
st->nb_packet_buffer_size = (u32)nb_packet_buffer_size;
/* Disable polling mode on newer firmwares */
if (st->fw_version >= 0x10200)
dev->props.rc.core.bulk_mode = true;
else
dev->props.rc.core.bulk_mode = false;
dib0700_rc_setup(dev, intf);
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_usb_driver(dib0700_driver);
MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw");
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");

File diff suppressed because it is too large Load diff

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,488 @@
/* 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 <linux/kconfig.h>
#include "dibusb.h"
/* Max transfer size done by I2C transfer functions */
#define MAX_XFER_SIZE 64
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_adap[0].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_adap[0].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_adap[0].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[MAX_XFER_SIZE]; /* 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);
if (4 + wlen > sizeof(sndbuf)) {
warn("i2c wr: len=%d is too big!\n", wlen);
return -EOPNOTSUPP;
}
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].flags & I2C_M_RD) == 0
&& (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 ((msg[i].flags & I2C_M_RD) == 0) {
if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0)
break;
} else if (msg[i].addr != 0x50) {
/* 0x50 is the address of the eeprom - we need to protect it
* from dibusb's bad i2c implementation: reads without
* writing the offset before are forbidden */
if (dibusb_i2c_msg(d, msg[i].addr, NULL, 0, msg[i].buf, msg[i].len) < 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,
};
#if IS_ENABLED(CONFIG_DVB_DIB3000MC)
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 (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON &&
le16_to_cpu(adap->dev->udev->descriptor.idProduct) ==
USB_PID_LITEON_DVB_T_WARM) {
msleep(1000);
}
adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach,
&adap->dev->i2c_adap,
DEFAULT_DIB3000P_I2C_ADDRESS,
&mod3000p_dib3000p_config);
if ((adap->fe_adap[0].fe) == NULL)
adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach,
&adap->dev->i2c_adap,
DEFAULT_DIB3000MC_I2C_ADDRESS,
&mod3000p_dib3000p_config);
if ((adap->fe_adap[0].fe) != 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 (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_LITEON &&
le16_to_cpu(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 (le16_to_cpu(adap->dev->udev->descriptor.idVendor) == USB_VID_DIBCOM &&
le16_to_cpu(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_adap[0].fe, 1);
if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) {
/* not found - use panasonic pll parameters */
if (dvb_attach(dvb_pll_attach, adap->fe_adap[0].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_adap[0].fe, &stk3000p_dib3000p_config);
}
return 0;
}
EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach);
#endif
/*
* common remote control stuff
*/
struct rc_map_table rc_map_dibusb_table[] = {
/* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */
{ 0x0016, KEY_POWER },
{ 0x0010, KEY_MUTE },
{ 0x0003, KEY_1 },
{ 0x0001, KEY_2 },
{ 0x0006, KEY_3 },
{ 0x0009, KEY_4 },
{ 0x001d, KEY_5 },
{ 0x001f, KEY_6 },
{ 0x000d, KEY_7 },
{ 0x0019, KEY_8 },
{ 0x001b, KEY_9 },
{ 0x0015, KEY_0 },
{ 0x0005, KEY_CHANNELUP },
{ 0x0002, KEY_CHANNELDOWN },
{ 0x001e, KEY_VOLUMEUP },
{ 0x000a, KEY_VOLUMEDOWN },
{ 0x0011, KEY_RECORD },
{ 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */
{ 0x0014, KEY_PLAY },
{ 0x001a, KEY_STOP },
{ 0x0040, KEY_REWIND },
{ 0x0012, KEY_FASTFORWARD },
{ 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */
{ 0x004c, KEY_PAUSE },
{ 0x004d, KEY_SCREEN }, /* Full screen mode. */
{ 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
/* additional keys TwinHan VisionPlus, the Artec seemingly not have */
{ 0x000c, KEY_CANCEL }, /* Cancel */
{ 0x001c, KEY_EPG }, /* EPG */
{ 0x0000, KEY_TAB }, /* Tab */
{ 0x0048, KEY_INFO }, /* Preview */
{ 0x0004, KEY_LIST }, /* RecordList */
{ 0x000f, KEY_TEXT }, /* Teletext */
/* Key codes for the KWorld/ADSTech/JetWay remote. */
{ 0x8612, KEY_POWER },
{ 0x860f, KEY_SELECT }, /* source */
{ 0x860c, KEY_UNKNOWN }, /* scan */
{ 0x860b, KEY_EPG },
{ 0x8610, KEY_MUTE },
{ 0x8601, KEY_1 },
{ 0x8602, KEY_2 },
{ 0x8603, KEY_3 },
{ 0x8604, KEY_4 },
{ 0x8605, KEY_5 },
{ 0x8606, KEY_6 },
{ 0x8607, KEY_7 },
{ 0x8608, KEY_8 },
{ 0x8609, KEY_9 },
{ 0x860a, KEY_0 },
{ 0x8618, KEY_ZOOM },
{ 0x861c, KEY_UNKNOWN }, /* preview */
{ 0x8613, KEY_UNKNOWN }, /* snap */
{ 0x8600, KEY_UNDO },
{ 0x861d, KEY_RECORD },
{ 0x860d, KEY_STOP },
{ 0x860e, KEY_PAUSE },
{ 0x8616, KEY_PLAY },
{ 0x8611, KEY_BACK },
{ 0x8619, KEY_FORWARD },
{ 0x8614, KEY_UNKNOWN }, /* pip */
{ 0x8615, KEY_ESC },
{ 0x861a, KEY_UP },
{ 0x861e, KEY_DOWN },
{ 0x861f, KEY_LEFT },
{ 0x861b, KEY_RIGHT },
/* Key codes for the DiBcom MOD3000 remote. */
{ 0x8000, KEY_MUTE },
{ 0x8001, KEY_TEXT },
{ 0x8002, KEY_HOME },
{ 0x8003, KEY_POWER },
{ 0x8004, KEY_RED },
{ 0x8005, KEY_GREEN },
{ 0x8006, KEY_YELLOW },
{ 0x8007, KEY_BLUE },
{ 0x8008, KEY_DVD },
{ 0x8009, KEY_AUDIO },
{ 0x800a, KEY_IMAGES }, /* Pictures */
{ 0x800b, KEY_VIDEO },
{ 0x800c, KEY_BACK },
{ 0x800d, KEY_UP },
{ 0x800e, KEY_RADIO },
{ 0x800f, KEY_EPG },
{ 0x8010, KEY_LEFT },
{ 0x8011, KEY_OK },
{ 0x8012, KEY_RIGHT },
{ 0x8013, KEY_UNKNOWN }, /* SAP */
{ 0x8014, KEY_TV },
{ 0x8015, KEY_DOWN },
{ 0x8016, KEY_MENU }, /* DVD Menu */
{ 0x8017, KEY_LAST },
{ 0x8018, KEY_RECORD },
{ 0x8019, KEY_STOP },
{ 0x801a, KEY_PAUSE },
{ 0x801b, KEY_PLAY },
{ 0x801c, KEY_PREVIOUS },
{ 0x801d, KEY_REWIND },
{ 0x801e, KEY_FASTFORWARD },
{ 0x801f, KEY_NEXT},
{ 0x8040, KEY_1 },
{ 0x8041, KEY_2 },
{ 0x8042, KEY_3 },
{ 0x8043, KEY_CHANNELUP },
{ 0x8044, KEY_4 },
{ 0x8045, KEY_5 },
{ 0x8046, KEY_6 },
{ 0x8047, KEY_CHANNELDOWN },
{ 0x8048, KEY_7 },
{ 0x8049, KEY_8 },
{ 0x804a, KEY_9 },
{ 0x804b, KEY_VOLUMEUP },
{ 0x804c, KEY_CLEAR },
{ 0x804d, KEY_0 },
{ 0x804e, KEY_ENTER },
{ 0x804f, KEY_VOLUMEDOWN },
};
EXPORT_SYMBOL(rc_map_dibusb_table);
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: %*ph\n", 5, key);
return 0;
}
EXPORT_SYMBOL(dibusb_rc_query);

View file

@ -0,0 +1,471 @@
/* 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"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int dib3000mb_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct dibusb_state *st = adap->priv;
return st->ops.tuner_pass_ctrl(fe, enable, st->tuner_addr);
}
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;
adap->fe_adap[0].fe = dvb_attach(dib3000mb_attach, &demod_cfg,
&adap->dev->i2c_adap, &st->ops);
if ((adap->fe_adap[0].fe) == NULL)
return -ENODEV;
adap->fe_adap[0].fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl;
return 0;
}
static int dibusb_thomson_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dibusb_state *st = adap->priv;
st->tuner_addr = 0x61;
dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap,
DVB_PLL_TUA6010XS);
return 0;
}
static int dibusb_panasonic_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dibusb_state *st = adap->priv;
st->tuner_addr = 0x60;
dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap,
DVB_PLL_TDA665X);
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 },
};
struct dibusb_state *st = adap->priv;
/* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */
msg[0].addr = msg[1].addr = st->tuner_addr = 0x60;
if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1);
if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) {
err("tuner i2c write failed.");
ret = -EREMOTEIO;
}
if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0);
if (b2[0] == 0xfe) {
info("This device has the Thomson Cable onboard. Which is default.");
ret = dibusb_thomson_tuner_attach(adap);
} else {
info("This device has the Panasonic ENV77H11D5 onboard.");
ret = dibusb_panasonic_tuner_attach(adap);
}
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 (0 == dvb_usb_device_init(intf, &dibusb1_1_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &dibusb1_1_an2235_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &dibusb2_0b_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &artec_t1_usb2_properties,
THIS_MODULE, NULL, adapter_nr))
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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_map_table = rc_map_dibusb_table,
.rc_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 },
},
{ "Unknown 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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_map_table = rc_map_dibusb_table,
.rc_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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_map_table = rc_map_dibusb_table,
.rc_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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_map_table = rc_map_dibusb_table,
.rc_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_usb_driver(dibusb_driver);
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,149 @@
/* 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"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
/* 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, adapter_nr);
}
/* 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) },
/* 14 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD) },
/* 15 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_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 = {
{
.num_frontends = 1,
.fe = {{
.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 = 8,
.endpoint = 0x06,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}},
.size_of_priv = sizeof(struct dibusb_state),
}
},
.power_ctrl = dibusb2_0_power_ctrl,
.rc.legacy = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_map_table = rc_map_dibusb_table,
.rc_map_size = 111, /* FIXME */
.rc_query = dibusb_rc_query,
},
.i2c_algo = &dibusb_i2c_algo,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 8,
.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 },
},
{ "Humax/Coex DVB-T USB Stick 2.0 High Speed",
{ &dibusb_dib3000mc_table[14], NULL },
{ &dibusb_dib3000mc_table[15], 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_usb_driver(dibusb_mc_driver);
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,131 @@
/* 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;
u8 tuner_addr;
};
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 rc_map_table rc_map_dibusb_table[];
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,354 @@
/* 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 */
static 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);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args)
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_usb_adapter *adap = fe->dvb->priv;
u8 b[5];
fe->ops.tuner_ops.calc_regs(fe, b, sizeof(b));
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)
{
struct digitv_state *st = adap->dev->priv;
adap->fe_adap[0].fe = dvb_attach(mt352_attach, &digitv_mt352_config,
&adap->dev->i2c_adap);
if ((adap->fe_adap[0].fe) != NULL) {
st->is_nxt6000 = 0;
return 0;
}
adap->fe_adap[0].fe = dvb_attach(nxt6000_attach,
&digitv_nxt6000_config,
&adap->dev->i2c_adap);
if ((adap->fe_adap[0].fe) != NULL) {
st->is_nxt6000 = 1;
return 0;
}
return -EIO;
}
static int digitv_tuner_attach(struct dvb_usb_adapter *adap)
{
struct digitv_state *st = adap->dev->priv;
if (!dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, NULL, DVB_PLL_TDED4))
return -ENODEV;
if (st->is_nxt6000)
adap->fe_adap[0].fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params;
return 0;
}
static struct rc_map_table rc_map_digitv_table[] = {
{ 0x5f55, KEY_0 },
{ 0x6f55, KEY_1 },
{ 0x9f55, KEY_2 },
{ 0xaf55, KEY_3 },
{ 0x5f56, KEY_4 },
{ 0x6f56, KEY_5 },
{ 0x9f56, KEY_6 },
{ 0xaf56, KEY_7 },
{ 0x5f59, KEY_8 },
{ 0x6f59, KEY_9 },
{ 0x9f59, KEY_TV },
{ 0xaf59, KEY_AUX },
{ 0x5f5a, KEY_DVD },
{ 0x6f5a, KEY_POWER },
{ 0x9f5a, KEY_CAMERA }, /* labelled 'Picture' */
{ 0xaf5a, KEY_AUDIO },
{ 0x5f65, KEY_INFO },
{ 0x6f65, KEY_F13 }, /* 16:9 */
{ 0x9f65, KEY_F14 }, /* 14:9 */
{ 0xaf65, KEY_EPG },
{ 0x5f66, KEY_EXIT },
{ 0x6f66, KEY_MENU },
{ 0x9f66, KEY_UP },
{ 0xaf66, KEY_DOWN },
{ 0x5f69, KEY_LEFT },
{ 0x6f69, KEY_RIGHT },
{ 0x9f69, KEY_ENTER },
{ 0xaf69, KEY_CHANNELUP },
{ 0x5f6a, KEY_CHANNELDOWN },
{ 0x6f6a, KEY_VOLUMEUP },
{ 0x9f6a, KEY_VOLUMEDOWN },
{ 0xaf6a, KEY_RED },
{ 0x5f95, KEY_GREEN },
{ 0x6f95, KEY_YELLOW },
{ 0x9f95, KEY_BLUE },
{ 0xaf95, KEY_SUBTITLE },
{ 0x5f96, KEY_F15 }, /* AD */
{ 0x6f96, KEY_TEXT },
{ 0x9f96, KEY_MUTE },
{ 0xaf96, KEY_REWIND },
{ 0x5f99, KEY_STOP },
{ 0x6f99, KEY_PLAY },
{ 0x9f99, KEY_FASTFORWARD },
{ 0xaf99, KEY_F16 }, /* chapter */
{ 0x5f9a, KEY_PAUSE },
{ 0x6f9a, KEY_PLAY },
{ 0x9f9a, KEY_RECORD },
{ 0xaf9a, KEY_F17 }, /* picture in picture */
{ 0x5fa5, KEY_KPPLUS }, /* zoom in */
{ 0x6fa5, KEY_KPMINUS }, /* zoom out */
{ 0x9fa5, KEY_F18 }, /* capture */
{ 0xafa5, KEY_F19 }, /* web */
{ 0x5fa6, KEY_EMAIL },
{ 0x6fa6, KEY_PHONE },
{ 0x9fa6, 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.legacy.rc_map_size; i++) {
if (rc5_custom(&d->props.rc.legacy.rc_map_table[i]) == key[1] &&
rc5_data(&d->props.rc.legacy.rc_map_table[i]) == key[2]) {
*event = d->props.rc.legacy.rc_map_table[i].keycode;
*state = REMOTE_KEY_PRESSED;
return 0;
}
}
}
if (key[0] != 0)
deb_rc("key: %*ph\n", 5, key);
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 = dvb_usb_device_init(intf, &digitv_properties, THIS_MODULE, &d,
adapter_nr);
if (ret == 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",
.size_of_priv = sizeof(struct digitv_state),
.num_adapters = 1,
.adapter = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = 1000,
.rc_map_table = rc_map_digitv_table,
.rc_map_size = ARRAY_SIZE(rc_map_digitv_table),
.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_usb_driver(digitv_driver);
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,66 @@
#ifndef _DVB_USB_DIGITV_H_
#define _DVB_USB_DIGITV_H_
#define DVB_USB_LOG_PREFIX "digitv"
#include "dvb-usb.h"
struct digitv_state {
int is_nxt6000;
};
/* 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,210 @@
/* 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 dtv_frontend_properties 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 dtv_frontend_properties *fep = &fe->dtv_property_cache;
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->bandwidth_hz) {
case 8000000:
bwbuf[1] = 8;
break;
case 7000000:
bwbuf[1] = 7;
break;
case 6000000:
bwbuf[1] = 6;
break;
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 dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct dtt200u_fe_state *state = fe->demodulator_priv;
memcpy(fep, &state->fep, sizeof(struct dtv_frontend_properties));
return 0;
}
static void dtt200u_fe_release(struct dvb_frontend* fe)
{
struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
kfree(state);
}
static 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 = {
.delsys = { SYS_DVBT },
.info = {
.name = "WideView USB DVB-T",
.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,368 @@
/* DVB USB library compliant Linux driver for the WideView/ Yakumo/ Hama/
* Typhoon/ Yuan/ Miglia 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);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
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 rc_map_table rc_map_dtt200u_table[] = {
{ 0x8001, KEY_MUTE },
{ 0x8002, KEY_CHANNELDOWN },
{ 0x8003, KEY_VOLUMEDOWN },
{ 0x8004, KEY_1 },
{ 0x8005, KEY_2 },
{ 0x8006, KEY_3 },
{ 0x8007, KEY_4 },
{ 0x8008, KEY_5 },
{ 0x8009, KEY_6 },
{ 0x800a, KEY_7 },
{ 0x800c, KEY_ZOOM },
{ 0x800d, KEY_0 },
{ 0x800e, KEY_SELECT },
{ 0x8012, KEY_POWER },
{ 0x801a, KEY_CHANNELUP },
{ 0x801b, KEY_8 },
{ 0x801e, KEY_VOLUMEUP },
{ 0x801f, 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: %*ph\n", 5, key);
return 0;
}
static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap)
{
adap->fe_adap[0].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 struct dvb_usb_device_properties wt220u_miglia_properties;
static int dtt200u_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
if (0 == dvb_usb_device_init(intf, &dtt200u_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &wt220u_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &wt220u_fc_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &wt220u_zl0353_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &wt220u_miglia_properties,
THIS_MODULE, NULL, adapter_nr))
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) },
{ USB_DEVICE(USB_VID_MIGLIA, 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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = 300,
.rc_map_table = rc_map_dtt200u_table,
.rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table),
.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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = 300,
.rc_map_table = rc_map_dtt200u_table,
.rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table),
.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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = 300,
.rc_map_table = rc_map_dtt200u_table,
.rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table),
.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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = 300,
.rc_map_table = rc_map_dtt200u_table,
.rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table),
.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 },
}
};
static struct dvb_usb_device_properties wt220u_miglia_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-wt220u-miglia-01.fw",
.num_adapters = 1,
.generic_bulk_ctrl_endpoint = 0x01,
.num_device_descs = 1,
.devices = {
{ .name = "WideView WT-220U PenType Receiver (Miglia)",
.cold_ids = { &dtt200u_usb_table[9], NULL },
/* This device turns into WT220U_ZL0353_WARM when fw
has been uploaded */
.warm_ids = { 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_usb_driver(dtt200u_usb_driver);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia 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,224 @@
/*
* DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T
*
* Copyright (C) 2008 Antoine Jacquet <royale@zerezo.com>
* http://royale.zerezo.com/dtv5100/
*
* Inspired by gl861.c and au6610.c drivers
*
* 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
*/
#include "dtv5100.h"
#include "zl10353.h"
#include "qt1010.h"
/* debug */
static int dvb_usb_dtv5100_debug;
module_param_named(debug, dvb_usb_dtv5100_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u8 request;
u8 type;
u16 value;
u16 index;
switch (wlen) {
case 1:
/* write { reg }, read { value } */
request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ :
DTV5100_TUNER_READ);
type = USB_TYPE_VENDOR | USB_DIR_IN;
value = 0;
break;
case 2:
/* write { reg, value } */
request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE :
DTV5100_TUNER_WRITE);
type = USB_TYPE_VENDOR | USB_DIR_OUT;
value = wbuf[1];
break;
default:
warn("wlen = %x, aborting.", wlen);
return -EINVAL;
}
index = (addr << 8) + wbuf[0];
msleep(1); /* avoid I2C errors */
return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), request,
type, value, index, rbuf, rlen,
DTV5100_USB_TIMEOUT);
}
/* I2C */
static int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (num > 2)
return -EINVAL;
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 (dtv5100_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 (dtv5100_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 dtv5100_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm dtv5100_i2c_algo = {
.master_xfer = dtv5100_i2c_xfer,
.functionality = dtv5100_i2c_func,
};
/* Callbacks for DVB USB */
static struct zl10353_config dtv5100_zl10353_config = {
.demod_address = DTV5100_DEMOD_ADDR,
.no_tuner = 1,
.parallel_ts = 1,
};
static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap)
{
adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
&adap->dev->i2c_adap);
if (adap->fe_adap[0].fe == NULL)
return -EIO;
/* disable i2c gate, or it won't work... is this safe? */
adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL;
return 0;
}
static struct qt1010_config dtv5100_qt1010_config = {
.i2c_address = DTV5100_TUNER_ADDR
};
static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap)
{
return dvb_attach(qt1010_attach,
adap->fe_adap[0].fe, &adap->dev->i2c_adap,
&dtv5100_qt1010_config) == NULL ? -ENODEV : 0;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties dtv5100_properties;
static int dtv5100_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int i, ret;
struct usb_device *udev = interface_to_usbdev(intf);
/* initialize non qt1010/zl10353 part? */
for (i = 0; dtv5100_init[i].request; i++) {
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
dtv5100_init[i].request,
USB_TYPE_VENDOR | USB_DIR_OUT,
dtv5100_init[i].value,
dtv5100_init[i].index, NULL, 0,
DTV5100_USB_TIMEOUT);
if (ret)
return ret;
}
ret = dvb_usb_device_init(intf, &dtv5100_properties,
THIS_MODULE, NULL, adapter_nr);
if (ret)
return ret;
return 0;
}
static struct usb_device_id dtv5100_table[] = {
{ USB_DEVICE(0x06be, 0xa232) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dtv5100_table);
static struct dvb_usb_device_properties dtv5100_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.size_of_priv = 0,
.num_adapters = 1,
.adapter = {{
.num_frontends = 1,
.fe = {{
.frontend_attach = dtv5100_frontend_attach,
.tuner_attach = dtv5100_tuner_attach,
.stream = {
.type = USB_BULK,
.count = 8,
.endpoint = 0x82,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}},
} },
.i2c_algo = &dtv5100_i2c_algo,
.num_device_descs = 1,
.devices = {
{
.name = "AME DTV-5100 USB2.0 DVB-T",
.cold_ids = { NULL },
.warm_ids = { &dtv5100_table[0], NULL },
},
}
};
static struct usb_driver dtv5100_driver = {
.name = "dvb_usb_dtv5100",
.probe = dtv5100_probe,
.disconnect = dvb_usb_device_exit,
.id_table = dtv5100_table,
};
module_usb_driver(dtv5100_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,51 @@
/*
* DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T
*
* Copyright (C) 2008 Antoine Jacquet <royale@zerezo.com>
* http://royale.zerezo.com/dtv5100/
*
* 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
*/
#ifndef _DVB_USB_DTV5100_H_
#define _DVB_USB_DTV5100_H_
#define DVB_USB_LOG_PREFIX "dtv5100"
#include "dvb-usb.h"
#define DTV5100_USB_TIMEOUT 500
#define DTV5100_DEMOD_ADDR 0x00
#define DTV5100_DEMOD_WRITE 0xc0
#define DTV5100_DEMOD_READ 0xc1
#define DTV5100_TUNER_ADDR 0xc4
#define DTV5100_TUNER_WRITE 0xc7
#define DTV5100_TUNER_READ 0xc8
#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
#define DRIVER_DESC "AME DTV-5100 USB2.0 DVB-T"
static struct {
u8 request;
u8 value;
u16 index;
} dtv5100_init[] = {
{ 0x000000c5, 0x00000000, 0x00000001 },
{ 0x000000c5, 0x00000001, 0x00000001 },
{ } /* Terminating entry */
};
#endif

View file

@ -0,0 +1,52 @@
/* 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,
short *adapter_nums);
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,288 @@
/* 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;
if ((adap->active_fe < 0) ||
(adap->active_fe >= adap->num_frontends_initialized)) {
return -EINVAL;
}
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->fe_adap[adap->active_fe].stream);
if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 0);
if (ret < 0) {
err("error while stopping stream.");
return ret;
}
}
}
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->fe_adap[adap->active_fe].pid_filtering ?
"yes" : "no", dvbdmxfeed->pid, dvbdmxfeed->pid,
dvbdmxfeed->index, onoff ? "on" : "off");
if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
adap->fe_adap[adap->active_fe].pid_filtering &&
adap->props.fe[adap->active_fe].pid_filter != NULL)
adap->props.fe[adap->active_fe].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->fe_adap[adap->active_fe].stream);
deb_ts("controlling pid parser\n");
if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
adap->props.fe[adap->active_fe].caps &
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF &&
adap->props.fe[adap->active_fe].pid_filter_ctrl != NULL) {
ret = adap->props.fe[adap->active_fe].pid_filter_ctrl(adap,
adap->fe_adap[adap->active_fe].pid_filtering);
if (ret < 0) {
err("could not handle pid_parser");
return ret;
}
}
deb_ts("start feeding\n");
if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 1);
if (ret < 0) {
err("error while enabling fifo.");
return ret;
}
}
}
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, short *adapter_nums)
{
int i;
int ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name,
adap->dev->owner, &adap->dev->udev->dev,
adapter_nums);
if (ret < 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: %pM",adap->dvb_adap.proposed_mac);
else
err("MAC address reading failed.");
}
adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
adap->demux.priv = adap;
adap->demux.filternum = 0;
for (i = 0; i < adap->props.num_frontends; i++) {
if (adap->demux.filternum < adap->fe_adap[i].max_feed_count)
adap->demux.filternum = adap->fe_adap[i].max_feed_count;
}
adap->demux.feednum = adap->demux.filternum;
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;
}
if ((ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net,
&adap->demux.dmx)) < 0) {
err("dvb_net_init failed: error %d",ret);
goto err_net_init;
}
adap->state |= DVB_USB_ADAP_STATE_DVB;
return 0;
err_net_init:
dvb_dmxdev_release(&adap->dmxdev);
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_set_active_fe(struct dvb_frontend *fe, int onoff)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
int ret = (adap->props.frontend_ctrl) ?
adap->props.frontend_ctrl(fe, onoff) : 0;
if (ret < 0) {
err("frontend_ctrl request failed");
return ret;
}
if (onoff)
adap->active_fe = fe->id;
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);
dvb_usb_set_active_fe(fe, 1);
if (adap->fe_adap[fe->id].fe_init)
adap->fe_adap[fe->id].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_adap[fe->id].fe_sleep)
adap->fe_adap[fe->id].fe_sleep(fe);
dvb_usb_set_active_fe(fe, 0);
return dvb_usb_device_power_ctrl(adap->dev, 0);
}
int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
{
int ret, i;
/* register all given adapter frontends */
for (i = 0; i < adap->props.num_frontends; i++) {
if (adap->props.fe[i].frontend_attach == NULL) {
err("strange: '%s' #%d,%d "
"doesn't want to attach a frontend.",
adap->dev->desc->name, adap->id, i);
return 0;
}
ret = adap->props.fe[i].frontend_attach(adap);
if (ret || adap->fe_adap[i].fe == NULL) {
/* only print error when there is no FE at all */
if (i == 0)
err("no frontend was attached by '%s'",
adap->dev->desc->name);
return 0;
}
adap->fe_adap[i].fe->id = i;
/* re-assign sleep and wakeup functions */
adap->fe_adap[i].fe_init = adap->fe_adap[i].fe->ops.init;
adap->fe_adap[i].fe->ops.init = dvb_usb_fe_wakeup;
adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep;
adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep;
if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) {
err("Frontend %d registration failed.", i);
dvb_frontend_detach(adap->fe_adap[i].fe);
adap->fe_adap[i].fe = NULL;
/* In error case, do not try register more FEs,
* still leaving already registered FEs alive. */
if (i == 0)
return -ENODEV;
else
return 0;
}
/* only attach the tuner if the demod is there */
if (adap->props.fe[i].tuner_attach != NULL)
adap->props.fe[i].tuner_attach(adap);
adap->num_frontends_initialized++;
}
return 0;
}
int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap)
{
int i = adap->num_frontends_initialized - 1;
/* unregister all given adapter frontends */
for (; i >= 0; i--) {
if (adap->fe_adap[i].fe != NULL) {
dvb_unregister_frontend(adap->fe_adap[i].fe);
dvb_frontend_detach(adap->fe_adap[i].fe);
}
}
adap->num_frontends_initialized = 0;
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 = b[1] | (b[2] << 8);
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,43 @@
/* 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;
}
strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
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;
}

View file

@ -0,0 +1,304 @@
/*
* 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(force_pid_filter_usage, "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, short *adapter_nrs)
{
struct dvb_usb_adapter *adap;
int ret, n, o;
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));
for (o = 0; o < adap->props.num_frontends; o++) {
struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o];
/* speed - when running at FULL speed we need a HW PID filter */
if (d->udev->speed == USB_SPEED_FULL && !(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 && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) ||
(props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) {
info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count);
adap->fe_adap[o].pid_filtering = 1;
adap->fe_adap[o].max_feed_count = props->pid_filter_count;
} else {
info("will pass the complete MPEG2 transport stream to the software demuxer.");
adap->fe_adap[o].pid_filtering = 0;
adap->fe_adap[o].max_feed_count = 255;
}
if (!adap->fe_adap[o].pid_filtering &&
dvb_usb_force_pid_filter_usage &&
props->caps & DVB_USB_ADAP_HAS_PID_FILTER) {
info("pid filter enabled by module option.");
adap->fe_adap[o].pid_filtering = 1;
adap->fe_adap[o].max_feed_count = props->pid_filter_count;
}
if (props->size_of_priv > 0) {
adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL);
if (adap->fe_adap[o].priv == NULL) {
err("no memory for priv for adapter %d fe %d.", n, o);
return -ENOMEM;
}
}
}
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, adapter_nrs)) ||
(ret = dvb_usb_adapter_frontend_init(adap))) {
return ret;
}
/* use exclusive FE lock if there is multiple shared FEs */
if (adap->fe_adap[1].fe)
adap->dvb_adap.mfe_shared = 1;
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, short *adapter_nums)
{
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, adapter_nums))) {
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,
short *adapter_nums)
{
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 -ENOMEM;
}
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, adapter_nums);
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,391 @@
/* 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 input-device and for handling remote-control-queries.
*/
#include "dvb-usb-common.h"
#include <linux/usb/input.h>
static unsigned int
legacy_dvb_usb_get_keymap_index(const struct input_keymap_entry *ke,
struct rc_map_table *keymap,
unsigned int keymap_size)
{
unsigned int index;
unsigned int scancode;
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
index = ke->index;
} else {
if (input_scancode_to_scalar(ke, &scancode))
return keymap_size;
/* See if we can match the raw key code. */
for (index = 0; index < keymap_size; index++)
if (keymap[index].scancode == scancode)
break;
/* See if there is an unused hole in the map */
if (index >= keymap_size) {
for (index = 0; index < keymap_size; index++) {
if (keymap[index].keycode == KEY_RESERVED ||
keymap[index].keycode == KEY_UNKNOWN) {
break;
}
}
}
}
return index;
}
static int legacy_dvb_usb_getkeycode(struct input_dev *dev,
struct input_keymap_entry *ke)
{
struct dvb_usb_device *d = input_get_drvdata(dev);
struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table;
unsigned int keymap_size = d->props.rc.legacy.rc_map_size;
unsigned int index;
index = legacy_dvb_usb_get_keymap_index(ke, keymap, keymap_size);
if (index >= keymap_size)
return -EINVAL;
ke->keycode = keymap[index].keycode;
if (ke->keycode == KEY_UNKNOWN)
ke->keycode = KEY_RESERVED;
ke->len = sizeof(keymap[index].scancode);
memcpy(&ke->scancode, &keymap[index].scancode, ke->len);
ke->index = index;
return 0;
}
static int legacy_dvb_usb_setkeycode(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode)
{
struct dvb_usb_device *d = input_get_drvdata(dev);
struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table;
unsigned int keymap_size = d->props.rc.legacy.rc_map_size;
unsigned int index;
index = legacy_dvb_usb_get_keymap_index(ke, keymap, keymap_size);
/*
* FIXME: Currently, it is not possible to increase the size of
* scancode table. For it to happen, one possibility
* would be to allocate a table with key_map_size + 1,
* copying data, appending the new key on it, and freeing
* the old one - or maybe just allocating some spare space
*/
if (index >= keymap_size)
return -EINVAL;
*old_keycode = keymap[index].keycode;
keymap->keycode = ke->keycode;
__set_bit(ke->keycode, dev->keybit);
if (*old_keycode != KEY_RESERVED) {
__clear_bit(*old_keycode, dev->keybit);
for (index = 0; index < keymap_size; index++) {
if (keymap[index].keycode == *old_keycode) {
__set_bit(*old_keycode, dev->keybit);
break;
}
}
}
return 0;
}
/* 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 legacy_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.legacy.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->input_dev, EV_KEY, event, 1);
input_sync(d->input_dev);
input_event(d->input_dev, EV_KEY, d->last_event, 0);
input_sync(d->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.legacy.rc_interval));
}
static int legacy_dvb_usb_remote_init(struct dvb_usb_device *d)
{
int i, err, rc_interval;
struct input_dev *input_dev;
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
input_dev->evbit[0] = BIT_MASK(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->dev.parent = &d->udev->dev;
d->input_dev = input_dev;
d->rc_dev = NULL;
input_dev->getkeycode = legacy_dvb_usb_getkeycode;
input_dev->setkeycode = legacy_dvb_usb_setkeycode;
/* set the bits for the keys */
deb_rc("key map size: %d\n", d->props.rc.legacy.rc_map_size);
for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) {
deb_rc("setting bit for event %d item %d\n",
d->props.rc.legacy.rc_map_table[i].keycode, i);
set_bit(d->props.rc.legacy.rc_map_table[i].keycode, input_dev->keybit);
}
/* setting these two values to non-zero, we have to manage key repeats */
input_dev->rep[REP_PERIOD] = d->props.rc.legacy.rc_interval;
input_dev->rep[REP_DELAY] = d->props.rc.legacy.rc_interval + 150;
input_set_drvdata(input_dev, d);
err = input_register_device(input_dev);
if (err)
input_free_device(input_dev);
rc_interval = d->props.rc.legacy.rc_interval;
INIT_DELAYED_WORK(&d->rc_query_work, legacy_dvb_usb_read_remote_control);
info("schedule remote query interval to %d msecs.", rc_interval);
schedule_delayed_work(&d->rc_query_work,
msecs_to_jiffies(rc_interval));
d->state |= DVB_USB_STATE_REMOTE;
return err;
}
/* 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);
int err;
/* 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, or when bulk mode is enabled after IR init
*/
if (dvb_usb_disable_rc_polling || d->props.rc.core.bulk_mode)
return;
err = d->props.rc.core.rc_query(d);
if (err)
err("error %d while querying for an remote control event.", err);
schedule_delayed_work(&d->rc_query_work,
msecs_to_jiffies(d->props.rc.core.rc_interval));
}
static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d)
{
int err, rc_interval;
struct rc_dev *dev;
dev = rc_allocate_device();
if (!dev)
return -ENOMEM;
dev->driver_name = d->props.rc.core.module_name;
dev->map_name = d->props.rc.core.rc_codes;
dev->change_protocol = d->props.rc.core.change_protocol;
dev->allowed_protocols = d->props.rc.core.allowed_protos;
dev->driver_type = d->props.rc.core.driver_type;
usb_to_input_id(d->udev, &dev->input_id);
dev->input_name = "IR-receiver inside an USB DVB receiver";
dev->input_phys = d->rc_phys;
dev->dev.parent = &d->udev->dev;
dev->priv = d;
err = rc_register_device(dev);
if (err < 0) {
rc_free_device(dev);
return err;
}
d->input_dev = NULL;
d->rc_dev = dev;
if (!d->props.rc.core.rc_query || d->props.rc.core.bulk_mode)
return 0;
/* Polling mode - initialize a work queue for handling it */
INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control);
rc_interval = d->props.rc.core.rc_interval;
info("schedule remote query interval to %d msecs.", rc_interval);
schedule_delayed_work(&d->rc_query_work,
msecs_to_jiffies(rc_interval));
return 0;
}
int dvb_usb_remote_init(struct dvb_usb_device *d)
{
int err;
if (dvb_usb_disable_rc_polling)
return 0;
if (d->props.rc.legacy.rc_map_table && d->props.rc.legacy.rc_query)
d->props.rc.mode = DVB_RC_LEGACY;
else if (d->props.rc.core.rc_codes)
d->props.rc.mode = DVB_RC_CORE;
else
return 0;
usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
/* Start the remote-control polling. */
if (d->props.rc.legacy.rc_interval < 40)
d->props.rc.legacy.rc_interval = 100; /* default */
if (d->props.rc.mode == DVB_RC_LEGACY)
err = legacy_dvb_usb_remote_init(d);
else
err = rc_core_dvb_usb_remote_init(d);
if (err)
return err;
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_delayed_work_sync(&d->rc_query_work);
if (d->props.rc.mode == DVB_RC_LEGACY)
input_unregister_device(d->input_dev);
else
rc_unregister_device(d->rc_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 rc_map_table *keymap = d->props.rc.legacy.rc_map_table;
*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.legacy.rc_map_size; i++)
if (rc5_custom(&keymap[i]) == keybuf[1] &&
rc5_data(&keymap[i]) == keybuf[3]) {
*event = keymap[i].keycode;
*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("unknown 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,121 @@
/* 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 || wbuf == NULL || wlen == 0)
return -EINVAL;
if (d->props.generic_bulk_ctrl_endpoint == 0) {
err("endpoint for generic control not specified.");
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_response ?
d->props.generic_bulk_ctrl_endpoint_response :
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);
}
static void dvb_usb_data_complete_raw(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_raw(&adap->demux, buffer, length);
}
int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap)
{
int i, ret = 0;
for (i = 0; i < adap->props.num_frontends; i++) {
adap->fe_adap[i].stream.udev = adap->dev->udev;
if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS)
adap->fe_adap[i].stream.complete =
dvb_usb_data_complete_204;
else
if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD)
adap->fe_adap[i].stream.complete =
dvb_usb_data_complete_raw;
else
adap->fe_adap[i].stream.complete = dvb_usb_data_complete;
adap->fe_adap[i].stream.user_priv = adap;
ret = usb_urb_init(&adap->fe_adap[i].stream,
&adap->props.fe[i].stream);
if (ret < 0)
break;
}
return ret;
}
int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap)
{
int i;
for (i = 0; i < adap->props.num_frontends; i++)
usb_urb_exit(&adap->fe_adap[i].stream);
return 0;
}

View file

@ -0,0 +1,483 @@
/* 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 <media/rc-core.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];
};
static inline u8 rc5_custom(struct rc_map_table *key)
{
return (key->scancode >> 8) & 0xff;
}
static inline u8 rc5_data(struct rc_map_table *key)
{
return key->scancode & 0xff;
}
static inline u16 rc5_scan(struct rc_map_table *key)
{
return key->scancode & 0xffff;
}
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.
* @num_frontends: number of frontends of the DVB USB adapter.
* @frontend_ctrl: called to power on/off active frontend.
* @streaming_ctrl: 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_fe_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
#define DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD 0x10
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;
};
#define MAX_NO_OF_FE_PER_ADAP 3
struct dvb_usb_adapter_properties {
int size_of_priv;
int (*frontend_ctrl) (struct dvb_frontend *, int);
int num_frontends;
struct dvb_usb_adapter_fe_properties fe[MAX_NO_OF_FE_PER_ADAP];
};
/**
* struct dvb_rc_legacy - old properties of remote controller
* @rc_map_table: a hard-wired array of struct rc_map_table (NULL to disable
* remote control handling).
* @rc_map_size: number of items in @rc_map_table.
* @rc_query: called to query an event event.
* @rc_interval: time in ms between two queries.
*/
struct dvb_rc_legacy {
/* remote control properties */
#define REMOTE_NO_KEY_PRESSED 0x00
#define REMOTE_KEY_PRESSED 0x01
#define REMOTE_KEY_REPEAT 0x02
struct rc_map_table *rc_map_table;
int rc_map_size;
int (*rc_query) (struct dvb_usb_device *, u32 *, int *);
int rc_interval;
};
/**
* struct dvb_rc properties of remote controller, using rc-core
* @rc_codes: name of rc codes table
* @protocol: type of protocol(s) currently used by the driver
* @allowed_protos: protocol(s) supported by the driver
* @driver_type: Used to point if a device supports raw mode
* @change_protocol: callback to change protocol
* @rc_query: called to query an event event.
* @rc_interval: time in ms between two queries.
* @bulk_mode: device supports bulk mode for RC (disable polling mode)
*/
struct dvb_rc {
char *rc_codes;
u64 protocol;
u64 allowed_protos;
enum rc_driver_type driver_type;
int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
char *module_name;
int (*rc_query) (struct dvb_usb_device *d);
int rc_interval;
bool bulk_mode; /* uses bulk mode */
};
/**
* enum dvb_usb_mode - Specifies if it is using a legacy driver or a new one
* based on rc-core
* This is initialized/used only inside dvb-usb-remote.c.
* It shouldn't be set by the drivers.
*/
enum dvb_usb_mode {
DVB_RC_LEGACY,
DVB_RC_CORE,
};
/**
* 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: remote controller properties
*
* @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.
*
* @generic_bulk_ctrl_endpoint_response: some DVB USB devices use a separate
* endpoint for responses to control messages sent with bulk transfers via
* the generic_bulk_ctrl_endpoint. When this is non-zero, this will be used
* instead of the generic_bulk_ctrl_endpoint when reading usb responses in
* the dvb_usb_generic_rw helper function.
*
* @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;
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 *);
struct {
enum dvb_usb_mode mode; /* Drivers shouldn't touch on it */
struct dvb_rc_legacy legacy;
struct dvb_rc core;
} rc;
struct i2c_algorithm *i2c_algo;
int generic_bulk_ctrl_endpoint;
int generic_bulk_ctrl_endpoint_response;
int num_device_descs;
struct dvb_usb_device_description devices[12];
};
/**
* 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_fe_adapter {
struct dvb_frontend *fe;
int (*fe_init) (struct dvb_frontend *);
int (*fe_sleep) (struct dvb_frontend *);
struct usb_data_stream stream;
int pid_filtering;
int max_feed_count;
void *priv;
};
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;
/* dvb */
struct dvb_adapter dvb_adap;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dvb_net dvb_net;
struct dvb_usb_fe_adapter fe_adap[MAX_NO_OF_FE_PER_ADAP];
int active_fe;
int num_frontends_initialized;
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_dev: rc device for the remote control (rc-core mode)
* @input_dev: input device for the remote control (legacy mode)
* @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 rc_dev *rc_dev;
struct input_dev *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 **,
short *adapter_nums);
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 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
#ifndef _DW2102_H_
#define _DW2102_H_
#define DVB_USB_LOG_PREFIX "dw2102"
#include "dvb-usb.h"
#define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args)
#define deb_rc(args...) dprintk(dvb_usb_dw2102_debug, 0x04, args)
#endif

View file

@ -0,0 +1,472 @@
/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver.
*
* Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp>
*
* This module is based off the the gl861 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 <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
#include "friio.h"
struct jdvbt90502_state {
struct i2c_adapter *i2c;
struct dvb_frontend frontend;
struct jdvbt90502_config config;
};
/* NOTE: TC90502 has 16bit register-address? */
/* register 0x0100 is used for reading PLL status, so reg is u16 here */
static int jdvbt90502_reg_read(struct jdvbt90502_state *state,
const u16 reg, u8 *buf, const size_t count)
{
int ret;
u8 wbuf[3];
struct i2c_msg msg[2];
wbuf[0] = reg & 0xFF;
wbuf[1] = 0;
wbuf[2] = reg >> 8;
msg[0].addr = state->config.demod_address;
msg[0].flags = 0;
msg[0].buf = wbuf;
msg[0].len = sizeof(wbuf);
msg[1].addr = msg[0].addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = count;
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2) {
deb_fe(" reg read failed.\n");
return -EREMOTEIO;
}
return 0;
}
/* currently 16bit register-address is not used, so reg is u8 here */
static int jdvbt90502_single_reg_write(struct jdvbt90502_state *state,
const u8 reg, const u8 val)
{
struct i2c_msg msg;
u8 wbuf[2];
wbuf[0] = reg;
wbuf[1] = val;
msg.addr = state->config.demod_address;
msg.flags = 0;
msg.buf = wbuf;
msg.len = sizeof(wbuf);
if (i2c_transfer(state->i2c, &msg, 1) != 1) {
deb_fe(" reg write failed.");
return -EREMOTEIO;
}
return 0;
}
static int _jdvbt90502_write(struct dvb_frontend *fe, const u8 buf[], int len)
{
struct jdvbt90502_state *state = fe->demodulator_priv;
int err, i;
for (i = 0; i < len - 1; i++) {
err = jdvbt90502_single_reg_write(state,
buf[0] + i, buf[i + 1]);
if (err)
return err;
}
return 0;
}
/* read pll status byte via the demodulator's I2C register */
/* note: Win box reads it by 8B block at the I2C addr 0x30 from reg:0x80 */
static int jdvbt90502_pll_read(struct jdvbt90502_state *state, u8 *result)
{
int ret;
/* +1 for reading */
u8 pll_addr_byte = (state->config.pll_address << 1) + 1;
*result = 0;
ret = jdvbt90502_single_reg_write(state, JDVBT90502_2ND_I2C_REG,
pll_addr_byte);
if (ret)
goto error;
ret = jdvbt90502_reg_read(state, 0x0100, result, 1);
if (ret)
goto error;
deb_fe("PLL read val:%02x\n", *result);
return 0;
error:
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
/* set pll frequency via the demodulator's I2C register */
static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq)
{
int ret;
int retry;
u8 res1;
u8 res2[9];
u8 pll_freq_cmd[PLL_CMD_LEN];
u8 pll_agc_cmd[PLL_CMD_LEN];
struct i2c_msg msg[2];
u32 f;
deb_fe("%s: freq=%d, step=%d\n", __func__, freq,
state->frontend.ops.info.frequency_stepsize);
/* freq -> oscilator frequency conversion. */
/* freq: 473,000,000 + n*6,000,000 [+ 142857 (center freq. shift)] */
f = freq / state->frontend.ops.info.frequency_stepsize;
/* add 399[1/7 MHZ] = 57MHz for the IF */
f += 399;
/* add center frequency shift if necessary */
if (f % 7 == 0)
f++;
pll_freq_cmd[DEMOD_REDIRECT_REG] = JDVBT90502_2ND_I2C_REG; /* 0xFE */
pll_freq_cmd[ADDRESS_BYTE] = state->config.pll_address << 1;
pll_freq_cmd[DIVIDER_BYTE1] = (f >> 8) & 0x7F;
pll_freq_cmd[DIVIDER_BYTE2] = f & 0xFF;
pll_freq_cmd[CONTROL_BYTE] = 0xB2; /* ref.divider:28, 4MHz/28=1/7MHz */
pll_freq_cmd[BANDSWITCH_BYTE] = 0x08; /* UHF band */
msg[0].addr = state->config.demod_address;
msg[0].flags = 0;
msg[0].buf = pll_freq_cmd;
msg[0].len = sizeof(pll_freq_cmd);
ret = i2c_transfer(state->i2c, &msg[0], 1);
if (ret != 1)
goto error;
udelay(50);
pll_agc_cmd[DEMOD_REDIRECT_REG] = pll_freq_cmd[DEMOD_REDIRECT_REG];
pll_agc_cmd[ADDRESS_BYTE] = pll_freq_cmd[ADDRESS_BYTE];
pll_agc_cmd[DIVIDER_BYTE1] = pll_freq_cmd[DIVIDER_BYTE1];
pll_agc_cmd[DIVIDER_BYTE2] = pll_freq_cmd[DIVIDER_BYTE2];
pll_agc_cmd[CONTROL_BYTE] = 0x9A; /* AGC_CTRL instead of BANDSWITCH */
pll_agc_cmd[AGC_CTRL_BYTE] = 0x50;
/* AGC Time Constant 2s, AGC take-over point:103dBuV(lowest) */
msg[1].addr = msg[0].addr;
msg[1].flags = 0;
msg[1].buf = pll_agc_cmd;
msg[1].len = sizeof(pll_agc_cmd);
ret = i2c_transfer(state->i2c, &msg[1], 1);
if (ret != 1)
goto error;
/* I don't know what these cmds are for, */
/* but the USB log on a windows box contains them */
ret = jdvbt90502_single_reg_write(state, 0x01, 0x40);
ret |= jdvbt90502_single_reg_write(state, 0x01, 0x00);
if (ret)
goto error;
udelay(100);
/* wait for the demod to be ready? */
#define RETRY_COUNT 5
for (retry = 0; retry < RETRY_COUNT; retry++) {
ret = jdvbt90502_reg_read(state, 0x0096, &res1, 1);
if (ret)
goto error;
/* if (res1 != 0x00) goto error; */
ret = jdvbt90502_reg_read(state, 0x00B0, res2, sizeof(res2));
if (ret)
goto error;
if (res2[0] >= 0xA7)
break;
msleep(100);
}
if (retry >= RETRY_COUNT) {
deb_fe("%s: FE does not get ready after freq setting.\n",
__func__);
return -EREMOTEIO;
}
return 0;
error:
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state)
{
u8 result;
int ret;
*state = FE_HAS_SIGNAL;
ret = jdvbt90502_pll_read(fe->demodulator_priv, &result);
if (ret) {
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
*state = FE_HAS_SIGNAL
| FE_HAS_CARRIER
| FE_HAS_VITERBI
| FE_HAS_SYNC;
if (result & PLL_STATUS_LOCKED)
*state |= FE_HAS_LOCK;
return 0;
}
static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe,
u16 *strength)
{
int ret;
u8 rbuf[37];
*strength = 0;
/* status register (incl. signal strength) : 0x89 */
/* TODO: read just the necessary registers [0x8B..0x8D]? */
ret = jdvbt90502_reg_read(fe->demodulator_priv, 0x0089,
rbuf, sizeof(rbuf));
if (ret) {
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
/* signal_strength: rbuf[2-4] (24bit BE), use lower 16bit for now. */
*strength = (rbuf[3] << 8) + rbuf[4];
if (rbuf[2])
*strength = 0xffff;
return 0;
}
/* filter out un-supported properties to notify users */
static int jdvbt90502_set_property(struct dvb_frontend *fe,
struct dtv_property *tvp)
{
int r = 0;
switch (tvp->cmd) {
case DTV_DELIVERY_SYSTEM:
if (tvp->u.data != SYS_ISDBT)
r = -EINVAL;
break;
case DTV_CLEAR:
case DTV_TUNE:
case DTV_FREQUENCY:
break;
default:
r = -EINVAL;
}
return r;
}
static int jdvbt90502_get_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
p->inversion = INVERSION_AUTO;
p->bandwidth_hz = 6000000;
p->code_rate_HP = FEC_AUTO;
p->code_rate_LP = FEC_AUTO;
p->modulation = QAM_64;
p->transmission_mode = TRANSMISSION_MODE_AUTO;
p->guard_interval = GUARD_INTERVAL_AUTO;
p->hierarchy = HIERARCHY_AUTO;
return 0;
}
static int jdvbt90502_set_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
/**
* NOTE: ignore all the parameters except frequency.
* others should be fixed to the proper value for ISDB-T,
* but don't check here.
*/
struct jdvbt90502_state *state = fe->demodulator_priv;
int ret;
deb_fe("%s: Freq:%d\n", __func__, p->frequency);
/* for recovery from DTV_CLEAN */
fe->dtv_property_cache.delivery_system = SYS_ISDBT;
ret = jdvbt90502_pll_set_freq(state, p->frequency);
if (ret) {
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
return 0;
}
/**
* (reg, val) commad list to initialize this module.
* captured on a Windows box.
*/
static u8 init_code[][2] = {
{0x01, 0x40},
{0x04, 0x38},
{0x05, 0x40},
{0x07, 0x40},
{0x0F, 0x4F},
{0x11, 0x21},
{0x12, 0x0B},
{0x13, 0x2F},
{0x14, 0x31},
{0x16, 0x02},
{0x21, 0xC4},
{0x22, 0x20},
{0x2C, 0x79},
{0x2D, 0x34},
{0x2F, 0x00},
{0x30, 0x28},
{0x31, 0x31},
{0x32, 0xDF},
{0x38, 0x01},
{0x39, 0x78},
{0x3B, 0x33},
{0x3C, 0x33},
{0x48, 0x90},
{0x51, 0x68},
{0x5E, 0x38},
{0x71, 0x00},
{0x72, 0x08},
{0x77, 0x00},
{0xC0, 0x21},
{0xC1, 0x10},
{0xE4, 0x1A},
{0xEA, 0x1F},
{0x77, 0x00},
{0x71, 0x00},
{0x71, 0x00},
{0x76, 0x0C},
};
static const int init_code_len = sizeof(init_code) / sizeof(u8[2]);
static int jdvbt90502_init(struct dvb_frontend *fe)
{
int i = -1;
int ret;
struct i2c_msg msg;
struct jdvbt90502_state *state = fe->demodulator_priv;
deb_fe("%s called.\n", __func__);
msg.addr = state->config.demod_address;
msg.flags = 0;
msg.len = 2;
for (i = 0; i < init_code_len; i++) {
msg.buf = init_code[i];
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
goto error;
}
fe->dtv_property_cache.delivery_system = SYS_ISDBT;
msleep(100);
return 0;
error:
deb_fe("%s: init_code[%d] failed. ret==%d\n", __func__, i, ret);
return -EREMOTEIO;
}
static void jdvbt90502_release(struct dvb_frontend *fe)
{
struct jdvbt90502_state *state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops jdvbt90502_ops;
struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d)
{
struct jdvbt90502_state *state = NULL;
deb_info("%s called.\n", __func__);
/* allocate memory for the internal state */
state = kzalloc(sizeof(struct jdvbt90502_state), GFP_KERNEL);
if (state == NULL)
goto error;
/* setup the state */
state->i2c = &d->i2c_adap;
state->config = friio_fe_config;
/* create dvb_frontend */
state->frontend.ops = jdvbt90502_ops;
state->frontend.demodulator_priv = state;
if (jdvbt90502_init(&state->frontend) < 0)
goto error;
return &state->frontend;
error:
kfree(state);
return NULL;
}
static struct dvb_frontend_ops jdvbt90502_ops = {
.delsys = { SYS_ISDBT },
.info = {
.name = "Comtech JDVBT90502 ISDB-T",
.frequency_min = 473000000, /* UHF 13ch, center */
.frequency_max = 767142857, /* UHF 62ch, center */
.frequency_stepsize = JDVBT90502_PLL_CLK / JDVBT90502_PLL_DIVIDER,
.frequency_tolerance = 0,
/* NOTE: this driver ignores all parameters but frequency. */
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO,
},
.release = jdvbt90502_release,
.init = jdvbt90502_init,
.write = _jdvbt90502_write,
.set_property = jdvbt90502_set_property,
.set_frontend = jdvbt90502_set_frontend,
.get_frontend = jdvbt90502_get_frontend,
.read_status = jdvbt90502_read_status,
.read_signal_strength = jdvbt90502_read_signal_strength,
};

View file

@ -0,0 +1,522 @@
/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver.
*
* Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp>
*
* This module is based off the the gl861 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 "friio.h"
/* debug */
int dvb_usb_friio_debug;
module_param_named(debug, dvb_usb_friio_debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debugging level (1=info,2=xfer,4=rc,8=fe (or-able))."
DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
/**
* Indirect I2C access to the PLL via FE.
* whole I2C protocol data to the PLL is sent via the FE's I2C register.
* This is done by a control msg to the FE with the I2C data accompanied, and
* a specific USB request number is assigned for that purpose.
*
* this func sends wbuf[1..] to the I2C register wbuf[0] at addr (= at FE).
* TODO: refoctored, smarter i2c functions.
*/
static int gl861_i2c_ctrlmsg_data(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u16 index = wbuf[0]; /* must be JDVBT90502_2ND_I2C_REG(=0xFE) */
u16 value = addr << (8 + 1);
int wo = (rbuf == NULL || rlen == 0); /* write only */
u8 req, type;
deb_xfer("write to PLL:0x%02x via FE reg:0x%02x, len:%d\n",
wbuf[1], wbuf[0], wlen - 1);
if (wo && wlen >= 2) {
req = GL861_REQ_I2C_DATA_CTRL_WRITE;
type = GL861_WRITE;
udelay(20);
return usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
req, type, value, index,
&wbuf[1], wlen - 1, 2000);
}
deb_xfer("not supported ctrl-msg, aborting.");
return -EINVAL;
}
/* normal I2C access (without extra data arguments).
* write to the register wbuf[0] at I2C address addr with the value wbuf[1],
* or read from the register wbuf[0].
* register address can be 16bit (wbuf[2]<<8 | wbuf[0]) if wlen==3
*/
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 + 1);
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 req, type;
unsigned int pipe;
/* special case for the indirect I2C access to the PLL via FE, */
if (addr == friio_fe_config.demod_address &&
wbuf[0] == JDVBT90502_2ND_I2C_REG)
return gl861_i2c_ctrlmsg_data(d, addr, wbuf, wlen, rbuf, rlen);
if (wo) {
req = GL861_REQ_I2C_WRITE;
type = GL861_WRITE;
pipe = usb_sndctrlpipe(d->udev, 0);
} else { /* rw */
req = GL861_REQ_I2C_READ;
type = GL861_READ;
pipe = usb_rcvctrlpipe(d->udev, 0);
}
switch (wlen) {
case 1:
index = wbuf[0];
break;
case 2:
index = wbuf[0];
value = value + wbuf[1];
break;
case 3:
/* special case for 16bit register-address */
index = (wbuf[2] << 8) | wbuf[0];
value = value + wbuf[1];
break;
default:
deb_xfer("wlen = %x, aborting.", wlen);
return -EINVAL;
}
msleep(1);
return usb_control_msg(d->udev, pipe, 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 (num > 2)
return -EINVAL;
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 (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 int friio_ext_ctl(struct dvb_usb_adapter *adap,
u32 sat_color, int lnb_on)
{
int i;
int ret;
struct i2c_msg msg;
u8 *buf;
u32 mask;
u8 lnb = (lnb_on) ? FRIIO_CTL_LNB : 0;
buf = kmalloc(2, GFP_KERNEL);
if (!buf)
return -ENOMEM;
msg.addr = 0x00;
msg.flags = 0;
msg.len = 2;
msg.buf = buf;
buf[0] = 0x00;
/* send 2bit header (&B10) */
buf[1] = lnb | FRIIO_CTL_LED | FRIIO_CTL_STROBE;
ret = gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] |= FRIIO_CTL_CLK;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] = lnb | FRIIO_CTL_STROBE;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] |= FRIIO_CTL_CLK;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
/* send 32bit(satur, R, G, B) data in serial */
mask = 1 << 31;
for (i = 0; i < 32; i++) {
buf[1] = lnb | FRIIO_CTL_STROBE;
if (sat_color & mask)
buf[1] |= FRIIO_CTL_LED;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] |= FRIIO_CTL_CLK;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
mask >>= 1;
}
/* set the strobe off */
buf[1] = lnb;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] |= FRIIO_CTL_CLK;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
kfree(buf);
return (ret == 70);
}
static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
/* TODO: move these init cmds to the FE's init routine? */
static u8 streaming_init_cmds[][2] = {
{0x33, 0x08},
{0x37, 0x40},
{0x3A, 0x1F},
{0x3B, 0xFF},
{0x3C, 0x1F},
{0x3D, 0xFF},
{0x38, 0x00},
{0x35, 0x00},
{0x39, 0x00},
{0x36, 0x00},
};
static int cmdlen = sizeof(streaming_init_cmds) / 2;
/*
* Command sequence in this init function is a replay
* of the captured USB commands from the Windows proprietary driver.
*/
static int friio_initialize(struct dvb_usb_device *d)
{
int ret;
int i;
int retry = 0;
u8 *rbuf, *wbuf;
deb_info("%s called.\n", __func__);
wbuf = kmalloc(3, GFP_KERNEL);
if (!wbuf)
return -ENOMEM;
rbuf = kmalloc(2, GFP_KERNEL);
if (!rbuf) {
kfree(wbuf);
return -ENOMEM;
}
/* use gl861_i2c_msg instead of gl861_i2c_xfer(), */
/* because the i2c device is not set up yet. */
wbuf[0] = 0x11;
wbuf[1] = 0x02;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
msleep(2);
wbuf[0] = 0x11;
wbuf[1] = 0x00;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
msleep(1);
/* following msgs should be in the FE's init code? */
/* cmd sequence to identify the device type? (friio black/white) */
wbuf[0] = 0x03;
wbuf[1] = 0x80;
/* can't use gl861_i2c_cmd, as the register-addr is 16bit(0x0100) */
ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE,
0x1200, 0x0100, wbuf, 2, 2000);
if (ret < 0)
goto error;
msleep(2);
wbuf[0] = 0x00;
wbuf[2] = 0x01; /* reg.0x0100 */
wbuf[1] = 0x00;
ret = gl861_i2c_msg(d, 0x12 >> 1, wbuf, 3, rbuf, 2);
/* my Friio White returns 0xffff. */
if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff)
goto error;
msleep(2);
wbuf[0] = 0x03;
wbuf[1] = 0x80;
ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE,
0x9000, 0x0100, wbuf, 2, 2000);
if (ret < 0)
goto error;
msleep(2);
wbuf[0] = 0x00;
wbuf[2] = 0x01; /* reg.0x0100 */
wbuf[1] = 0x00;
ret = gl861_i2c_msg(d, 0x90 >> 1, wbuf, 3, rbuf, 2);
/* my Friio White returns 0xffff again. */
if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff)
goto error;
msleep(1);
restart:
/* ============ start DEMOD init cmds ================== */
/* read PLL status to clear the POR bit */
wbuf[0] = JDVBT90502_2ND_I2C_REG;
wbuf[1] = (FRIIO_PLL_ADDR << 1) + 1; /* +1 for reading */
ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
msleep(5);
/* note: DEMODULATOR has 16bit register-address. */
wbuf[0] = 0x00;
wbuf[2] = 0x01; /* reg addr: 0x0100 */
wbuf[1] = 0x00; /* val: not used */
ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 3, rbuf, 1);
if (ret < 0)
goto error;
/*
msleep(1);
wbuf[0] = 0x80;
wbuf[1] = 0x00;
ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, rbuf, 1);
if (ret < 0)
goto error;
*/
if (rbuf[0] & 0x80) { /* still in PowerOnReset state? */
if (++retry > 3) {
deb_info("failed to get the correct"
" FE demod status:0x%02x\n", rbuf[0]);
goto error;
}
msleep(100);
goto restart;
}
/* TODO: check return value in rbuf */
/* =========== end DEMOD init cmds ===================== */
msleep(1);
wbuf[0] = 0x30;
wbuf[1] = 0x04;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
msleep(2);
/* following 2 cmds unnecessary? */
wbuf[0] = 0x00;
wbuf[1] = 0x01;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
wbuf[0] = 0x06;
wbuf[1] = 0x0F;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
/* some streaming ctl cmds (maybe) */
msleep(10);
for (i = 0; i < cmdlen; i++) {
ret = gl861_i2c_msg(d, 0x00, streaming_init_cmds[i], 2,
NULL, 0);
if (ret < 0)
goto error;
msleep(1);
}
msleep(20);
/* change the LED color etc. */
ret = friio_streaming_ctrl(&d->adapter[0], 0);
if (ret < 0)
goto error;
return 0;
error:
kfree(wbuf);
kfree(rbuf);
deb_info("%s:ret == %d\n", __func__, ret);
return -EIO;
}
/* Callbacks for DVB USB */
static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
int ret;
deb_info("%s called.(%d)\n", __func__, onoff);
/* set the LED color and saturation (and LNB on) */
if (onoff)
ret = friio_ext_ctl(adap, 0x6400ff64, 1);
else
ret = friio_ext_ctl(adap, 0x96ff00ff, 1);
if (ret != 1) {
deb_info("%s failed to send cmdx. ret==%d\n", __func__, ret);
return -EREMOTEIO;
}
return 0;
}
static int friio_frontend_attach(struct dvb_usb_adapter *adap)
{
if (friio_initialize(adap->dev) < 0)
return -EIO;
adap->fe_adap[0].fe = jdvbt90502_attach(adap->dev);
if (adap->fe_adap[0].fe == NULL)
return -EIO;
return 0;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties friio_properties;
static int friio_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 < GL861_ALTSETTING_COUNT)
return -ENODEV;
alt = usb_altnum_to_altsetting(intf, FRIIO_BULK_ALTSETTING);
if (alt == NULL) {
deb_rc("not alt found!\n");
return -ENODEV;
}
ret = usb_set_interface(interface_to_usbdev(intf),
alt->desc.bInterfaceNumber,
alt->desc.bAlternateSetting);
if (ret != 0) {
deb_rc("failed to set alt-setting!\n");
return ret;
}
ret = dvb_usb_device_init(intf, &friio_properties,
THIS_MODULE, &d, adapter_nr);
if (ret == 0)
friio_streaming_ctrl(&d->adapter[0], 1);
return ret;
}
struct jdvbt90502_config friio_fe_config = {
.demod_address = FRIIO_DEMOD_ADDR,
.pll_address = FRIIO_PLL_ADDR,
};
static struct i2c_algorithm gl861_i2c_algo = {
.master_xfer = gl861_i2c_xfer,
.functionality = gl861_i2c_func,
};
static struct usb_device_id friio_table[] = {
{ USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, friio_table);
static struct dvb_usb_device_properties friio_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.size_of_priv = 0,
.num_adapters = 1,
.adapter = {
/* caps:0 => no pid filter, 188B TS packet */
/* GL861 has a HW pid filter, but no info available. */
{
.num_frontends = 1,
.fe = {{
.caps = 0,
.frontend_attach = friio_frontend_attach,
.streaming_ctrl = friio_streaming_ctrl,
.stream = {
.type = USB_BULK,
/* count <= MAX_NO_URBS_FOR_DATA_STREAM(10) */
.count = 8,
.endpoint = 0x01,
.u = {
/* GL861 has 6KB buf inside */
.bulk = {
.buffersize = 16384,
}
}
},
}},
}
},
.i2c_algo = &gl861_i2c_algo,
.num_device_descs = 1,
.devices = {
{
.name = "774 Friio ISDB-T USB2.0",
.cold_ids = { NULL },
.warm_ids = { &friio_table[0], NULL },
},
}
};
static struct usb_driver friio_driver = {
.name = "dvb_usb_friio",
.probe = friio_probe,
.disconnect = dvb_usb_device_exit,
.id_table = friio_table,
};
module_usb_driver(friio_driver);
MODULE_AUTHOR("Akihiro Tsukada <tskd2@yahoo.co.jp>");
MODULE_DESCRIPTION("Driver for Friio ISDB-T USB2.0 Receiver");
MODULE_VERSION("0.2");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,99 @@
/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver.
*
* Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp>
*
* This module is based off the the gl861 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_FRIIO_H_
#define _DVB_USB_FRIIO_H_
/**
* Friio Components
* USB hub: AU4254
* USB controller(+ TS dmx & streaming): GL861
* Frontend: comtech JDVBT-90502
* (tuner PLL: tua6034, I2C addr:(0xC0 >> 1))
* (OFDM demodulator: TC90502, I2C addr:(0x30 >> 1))
* LED x3 (+LNB) control: PIC 16F676
* EEPROM: 24C08
*
* (USB smart card reader: AU9522)
*
*/
#define DVB_USB_LOG_PREFIX "friio"
#include "dvb-usb.h"
extern int dvb_usb_friio_debug;
#define deb_info(args...) dprintk(dvb_usb_friio_debug, 0x01, args)
#define deb_xfer(args...) dprintk(dvb_usb_friio_debug, 0x02, args)
#define deb_rc(args...) dprintk(dvb_usb_friio_debug, 0x04, args)
#define deb_fe(args...) dprintk(dvb_usb_friio_debug, 0x08, args)
/* Vendor requests */
#define GL861_WRITE 0x40
#define GL861_READ 0xc0
/* command bytes */
#define GL861_REQ_I2C_WRITE 0x01
#define GL861_REQ_I2C_READ 0x02
/* For control msg with data argument */
/* Used for accessing the PLL on the secondary I2C bus of FE via GL861 */
#define GL861_REQ_I2C_DATA_CTRL_WRITE 0x03
#define GL861_ALTSETTING_COUNT 2
#define FRIIO_BULK_ALTSETTING 0
#define FRIIO_ISOC_ALTSETTING 1
/* LED & LNB control via PIC. */
/* basically, it's serial control with clock and strobe. */
/* write the below 4bit control data to the reg 0x00 at the I2C addr 0x00 */
/* when controlling the LEDs, 32bit(saturation, R, G, B) is sent on the bit3*/
#define FRIIO_CTL_LNB (1 << 0)
#define FRIIO_CTL_STROBE (1 << 1)
#define FRIIO_CTL_CLK (1 << 2)
#define FRIIO_CTL_LED (1 << 3)
/* Front End related */
#define FRIIO_DEMOD_ADDR (0x30 >> 1)
#define FRIIO_PLL_ADDR (0xC0 >> 1)
#define JDVBT90502_PLL_CLK 4000000
#define JDVBT90502_PLL_DIVIDER 28
#define JDVBT90502_2ND_I2C_REG 0xFE
/* byte index for pll i2c command data structure*/
/* see datasheet for tua6034 */
#define DEMOD_REDIRECT_REG 0
#define ADDRESS_BYTE 1
#define DIVIDER_BYTE1 2
#define DIVIDER_BYTE2 3
#define CONTROL_BYTE 4
#define BANDSWITCH_BYTE 5
#define AGC_CTRL_BYTE 5
#define PLL_CMD_LEN 6
/* bit masks for PLL STATUS response */
#define PLL_STATUS_POR_MODE 0x80 /* 1: Power on Reset (test) Mode */
#define PLL_STATUS_LOCKED 0x40 /* 1: locked */
#define PLL_STATUS_AGC_ACTIVE 0x08 /* 1:active */
#define PLL_STATUS_TESTMODE 0x07 /* digital output level (5 level) */
/* 0.15Vcc step 0x00: < 0.15Vcc, ..., 0x04: >= 0.6Vcc (<= 1Vcc) */
struct jdvbt90502_config {
u8 demod_address; /* i2c addr for demodulator IC */
u8 pll_address; /* PLL addr on the secondary i2c*/
};
extern struct jdvbt90502_config friio_fe_config;
extern struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d);
#endif

View file

@ -0,0 +1,369 @@
/* DVB USB compliant Linux driver for the
* - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module
*
* Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com)
* Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.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;
u8 lock;
u16 snr;
unsigned long next_status_check;
unsigned long status_check_interval;
};
static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe)
{
struct gp8psk_fe_state *st = fe->demodulator_priv;
u8 status;
gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1);
return status & bmDCtuned;
}
static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode)
{
struct gp8psk_fe_state *state = fe->demodulator_priv;
return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0);
}
static int gp8psk_fe_update_status(struct gp8psk_fe_state *st)
{
u8 buf[6];
if (time_after(jiffies,st->next_status_check)) {
gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0,0,&st->lock,1);
gp8psk_usb_in_op(st->d, GET_SIGNAL_STRENGTH, 0,0,buf,6);
st->snr = (buf[1]) << 8 | buf[0];
st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000;
}
return 0;
}
static int gp8psk_fe_read_status(struct dvb_frontend* fe, fe_status_t *status)
{
struct gp8psk_fe_state *st = fe->demodulator_priv;
gp8psk_fe_update_status(st);
if (st->lock)
*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 = 100;
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;
gp8psk_fe_update_status(st);
/* snr is reported in dBu*256 */
*snr = st->snr;
return 0;
}
static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
{
struct gp8psk_fe_state *st = fe->demodulator_priv;
gp8psk_fe_update_status(st);
/* snr is reported in dBu*256 */
/* snr / 38.4 ~= 100% strength */
/* snr * 17 returns 100% strength as 65535 */
if (st->snr > 0xf00)
*strength = 0xffff;
else
*strength = (st->snr << 4) + st->snr; /* snr*17 */
return 0;
}
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 gp8psk_fe_state *state = fe->demodulator_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
u8 cmd[10];
u32 freq = c->frequency * 1000;
int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct);
deb_fe("%s()\n", __func__);
cmd[4] = freq & 0xff;
cmd[5] = (freq >> 8) & 0xff;
cmd[6] = (freq >> 16) & 0xff;
cmd[7] = (freq >> 24) & 0xff;
/* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */
if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8)
c->delivery_system = SYS_TURBO;
switch (c->delivery_system) {
case SYS_DVBS:
if (c->modulation != QPSK) {
deb_fe("%s: unsupported modulation selected (%d)\n",
__func__, c->modulation);
return -EOPNOTSUPP;
}
c->fec_inner = FEC_AUTO;
break;
case SYS_DVBS2: /* kept for backwards compatibility */
deb_fe("%s: DVB-S2 delivery system selected\n", __func__);
break;
case SYS_TURBO:
deb_fe("%s: Turbo-FEC delivery system selected\n", __func__);
break;
default:
deb_fe("%s: unsupported delivery system selected (%d)\n",
__func__, c->delivery_system);
return -EOPNOTSUPP;
}
cmd[0] = c->symbol_rate & 0xff;
cmd[1] = (c->symbol_rate >> 8) & 0xff;
cmd[2] = (c->symbol_rate >> 16) & 0xff;
cmd[3] = (c->symbol_rate >> 24) & 0xff;
switch (c->modulation) {
case QPSK:
if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
if (gp8psk_tuned_to_DCII(fe))
gp8psk_bcm4500_reload(state->d);
switch (c->fec_inner) {
case FEC_1_2:
cmd[9] = 0; break;
case FEC_2_3:
cmd[9] = 1; break;
case FEC_3_4:
cmd[9] = 2; break;
case FEC_5_6:
cmd[9] = 3; break;
case FEC_7_8:
cmd[9] = 4; break;
case FEC_AUTO:
cmd[9] = 5; break;
default:
cmd[9] = 5; break;
}
if (c->delivery_system == SYS_TURBO)
cmd[8] = ADV_MOD_TURBO_QPSK;
else
cmd[8] = ADV_MOD_DVB_QPSK;
break;
case PSK_8: /* PSK_8 is for compatibility with DN */
cmd[8] = ADV_MOD_TURBO_8PSK;
switch (c->fec_inner) {
case FEC_2_3:
cmd[9] = 0; break;
case FEC_3_4:
cmd[9] = 1; break;
case FEC_3_5:
cmd[9] = 2; break;
case FEC_5_6:
cmd[9] = 3; break;
case FEC_8_9:
cmd[9] = 4; break;
default:
cmd[9] = 0; break;
}
break;
case QAM_16: /* QAM_16 is for compatibility with DN */
cmd[8] = ADV_MOD_TURBO_16QAM;
cmd[9] = 0;
break;
default: /* Unknown modulation */
deb_fe("%s: unsupported modulation selected (%d)\n",
__func__, c->modulation);
return -EOPNOTSUPP;
}
if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
gp8psk_set_tuner_mode(fe, 0);
gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10);
state->lock = 0;
state->next_status_check = jiffies;
state->status_check_interval = 200;
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",__func__);
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",__func__);
/* 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_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff)
{
struct gp8psk_fe_state* state = fe->demodulator_priv;
return gp8psk_usb_out_op(state->d, USE_EXTRA_VOLT, onoff, 0,NULL,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 = {
.delsys = { SYS_DVBS },
.info = {
.name = "Genpix DVB-S",
.frequency_min = 800000,
.frequency_max = 2250000,
.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_QAM_16 is for compatibility
* (Myth incorrectly detects Turbo-QPSK as plain QAM-16)
*/
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC
},
.release = gp8psk_fe_release,
.init = NULL,
.sleep = NULL,
.set_frontend = gp8psk_fe_set_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,
.enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage
};

View file

@ -0,0 +1,328 @@
/* DVB USB compliant Linux driver for the
* - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module
*
* Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com)
* Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.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);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int gp8psk_get_fw_version(struct dvb_usb_device *d, u8 *fw_vers)
{
return (gp8psk_usb_in_op(d, GET_FW_VERS, 0, 0, fw_vers, 6));
}
static int gp8psk_get_fpga_version(struct dvb_usb_device *d, u8 *fpga_vers)
{
return (gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, fpga_vers, 1));
}
static void gp8psk_info(struct dvb_usb_device *d)
{
u8 fpga_vers, fw_vers[6];
if (!gp8psk_get_fw_version(d, fw_vers))
info("FW Version = %i.%02i.%i (0x%x) Build %4i/%02i/%02i",
fw_vers[2], fw_vers[1], fw_vers[0], GP8PSK_FW_VERS(fw_vers),
2000 + fw_vers[5], fw_vers[4], fw_vers[3]);
else
info("failed to get FW version");
if (!gp8psk_get_fpga_version(d, &fpga_vers))
info("FPGA Version = %i", fpga_vers);
else
info("failed to get FPGA version");
}
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 %d operation failed.", req);
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;
const u8 *ptr;
u8 *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("downloading bcm4500 firmware from file '%s'",bcm4500_firmware);
ptr = fw->data;
buf = kmalloc(64, GFP_KERNEL | GFP_DMA);
if (!buf) {
ret = -ENOMEM;
goto out_rel_fw;
}
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;
int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct);
if (onoff) {
gp8psk_usb_in_op(d, GET_8PSK_CONFIG,0,0,&status,1);
if (! (status & bm8pskStarted)) { /* started */
if(gp_product_id == USB_PID_GENPIX_SKYWALKER_CW3K)
gp8psk_usb_out_op(d, CW3K_INIT, 1, 0, NULL, 0);
if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1))
return -EINVAL;
gp8psk_info(d);
}
if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
if (! (status & bm8pskFW_Loaded)) /* BCM4500 firmware loaded */
if(gp8psk_load_bcm4500fw(d))
return -EINVAL;
if (! (status & bmIntersilOn)) /* LNB Power */
if (gp8psk_usb_in_op(d, START_INTERSIL, 1, 0,
&buf, 1))
return -EINVAL;
/* Set DVB mode to 1 */
if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
if (gp8psk_usb_out_op(d, SET_DVB_MODE, 1, 0, NULL, 0))
return -EINVAL;
/* Abort possible TS (if previous tune crashed) */
if (gp8psk_usb_out_op(d, ARM_TRANSFER, 0, 0, NULL, 0))
return -EINVAL;
} 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;
if(gp_product_id == USB_PID_GENPIX_SKYWALKER_CW3K)
gp8psk_usb_out_op(d, CW3K_INIT, 0, 0, NULL, 0);
}
return 0;
}
int gp8psk_bcm4500_reload(struct dvb_usb_device *d)
{
u8 buf;
int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct);
/* Turn off 8psk power */
if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1))
return -EINVAL;
/* Turn On 8psk power */
if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1))
return -EINVAL;
/* load BCM4500 firmware */
if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
if (gp8psk_load_bcm4500fw(d))
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_adap[0].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)
{
int ret;
struct usb_device *udev = interface_to_usbdev(intf);
ret = dvb_usb_device_init(intf, &gp8psk_properties,
THIS_MODULE, NULL, adapter_nr);
if (ret == 0) {
info("found Genpix USB device pID = %x (hex)",
le16_to_cpu(udev->descriptor.idProduct));
}
return ret;
}
static struct usb_device_id gp8psk_usb_table [] = {
{ USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_1_COLD) },
{ USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_1_WARM) },
{ USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_2) },
{ USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_1) },
{ USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_2) },
/* { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_CW3K) }, */
{ 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 = {
{
.num_frontends = 1,
.fe = {{
.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 = 4,
.devices = {
{ .name = "Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver",
.cold_ids = { &gp8psk_usb_table[0], NULL },
.warm_ids = { &gp8psk_usb_table[1], NULL },
},
{ .name = "Genpix 8PSK-to-USB2 Rev.2 DVB-S receiver",
.cold_ids = { NULL },
.warm_ids = { &gp8psk_usb_table[2], NULL },
},
{ .name = "Genpix SkyWalker-1 DVB-S receiver",
.cold_ids = { NULL },
.warm_ids = { &gp8psk_usb_table[3], NULL },
},
{ .name = "Genpix SkyWalker-2 DVB-S receiver",
.cold_ids = { NULL },
.warm_ids = { &gp8psk_usb_table[4], 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_usb_driver(gp8psk_usb_driver);
MODULE_AUTHOR("Alan Nisota <alannisota@gamil.com>");
MODULE_DESCRIPTION("Driver for Genpix DVB-S");
MODULE_VERSION("1.1");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,100 @@
/* DVB USB compliant Linux driver for the
* - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module
*
* Copyright (C) 2006 Alan Nisota (alannisota@gmail.com)
* Copyright (C) 2006,2007 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)
/* Twinhan Vendor requests */
#define TH_COMMAND_IN 0xC0
#define TH_COMMAND_OUT 0xC1
/* gp8psk commands */
#define GET_8PSK_CONFIG 0x80 /* in */
#define SET_8PSK_CONFIG 0x81
#define I2C_WRITE 0x83
#define I2C_READ 0x84
#define ARM_TRANSFER 0x85
#define TUNE_8PSK 0x86
#define GET_SIGNAL_STRENGTH 0x87 /* in */
#define LOAD_BCM4500 0x88
#define BOOT_8PSK 0x89 /* in */
#define START_INTERSIL 0x8A /* in */
#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 /* in */
#define GET_FW_VERS 0x92
#define GET_SERIAL_NUMBER 0x93 /* in */
#define USE_EXTRA_VOLT 0x94
#define GET_FPGA_VERS 0x95
#define CW3K_INIT 0x9d
/* PSK_configuration bits */
#define bm8pskStarted 0x01
#define bm8pskFW_Loaded 0x02
#define bmIntersilOn 0x04
#define bmDVBmode 0x08
#define bm22kHz 0x10
#define bmSEL18V 0x20
#define bmDCtuned 0x40
#define bmArmed 0x80
/* 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 RESET_FX2 0x13
#define FW_VERSION_READ 0x0B
#define VENDOR_STRING_READ 0x0C
#define PRODUCT_STRING_READ 0x0D
#define FW_BCD_VERSION_READ 0x14
/* firmware revision id's */
#define GP8PSK_FW_REV1 0x020604
#define GP8PSK_FW_REV2 0x020704
#define GP8PSK_FW_VERS(_fw_vers) ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0])
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);
extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,77 @@
#ifndef _DVB_USB_M920X_H_
#define _DVB_USB_M920X_H_
#define DVB_USB_LOG_PREFIX "m920x"
#include "dvb-usb.h"
#define deb(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_MAX_ADAPTERS 4
/*
sequences found in logs:
[index value]
0x80 write addr
(0x00 out byte)*
0x40 out byte
0x80 write addr
(0x00 out byte)*
0x80 read addr
(0x21 in byte)*
0x60 in byte
this sequence works:
0x80 read addr
(0x21 in byte)*
0x60 in byte
Guess at API of the I2C function:
I2C operation is done one byte at a time with USB control messages. The
index the messages is sent to is made up of a set of flags that control
the I2C bus state:
0x80: Send START condition. After a START condition, one would normally
always send the 7-bit slave I2C address as the 7 MSB, followed by
the read/write bit as the LSB.
0x40: Send STOP condition. This should be set on the last byte of an
I2C transaction.
0x20: Read a byte from the slave. As opposed to writing a byte to the
slave. The slave will normally not produce any data unless you
set the R/W bit to 1 when sending the slave's address after the
START condition.
0x01: Respond with ACK, as opposed to a NACK. For a multi-byte read,
the master should send an ACK, that is pull SDA low during the 9th
clock cycle, after every byte but the last. This flags only makes
sense when bit 0x20 is set, indicating a read.
What any other bits might mean, or how to get the slave's ACK/NACK
response to a write, is unknown.
*/
struct m920x_state {
u16 filters[M9206_MAX_ADAPTERS][M9206_MAX_FILTERS];
int filtering_enabled[M9206_MAX_ADAPTERS];
int rep_count;
};
/* Initialisation data for the m920x
*/
struct m920x_inits {
u16 address;
u8 data;
};
#endif

View file

@ -0,0 +1,233 @@
/* 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);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define deb_rc(args...) dprintk(debug,0x01,args)
#define deb_ee(args...) dprintk(debug,0x02,args)
/* Hauppauge NOVA-T USB2 keys */
static struct rc_map_table rc_map_haupp_table[] = {
{ 0x1e00, KEY_0 },
{ 0x1e01, KEY_1 },
{ 0x1e02, KEY_2 },
{ 0x1e03, KEY_3 },
{ 0x1e04, KEY_4 },
{ 0x1e05, KEY_5 },
{ 0x1e06, KEY_6 },
{ 0x1e07, KEY_7 },
{ 0x1e08, KEY_8 },
{ 0x1e09, KEY_9 },
{ 0x1e0a, KEY_KPASTERISK },
{ 0x1e0b, KEY_RED },
{ 0x1e0c, KEY_RADIO },
{ 0x1e0d, KEY_MENU },
{ 0x1e0e, KEY_GRAVE }, /* # */
{ 0x1e0f, KEY_MUTE },
{ 0x1e10, KEY_VOLUMEUP },
{ 0x1e11, KEY_VOLUMEDOWN },
{ 0x1e12, KEY_CHANNEL },
{ 0x1e14, KEY_UP },
{ 0x1e15, KEY_DOWN },
{ 0x1e16, KEY_LEFT },
{ 0x1e17, KEY_RIGHT },
{ 0x1e18, KEY_VIDEO },
{ 0x1e19, KEY_AUDIO },
{ 0x1e1a, KEY_IMAGES },
{ 0x1e1b, KEY_EPG },
{ 0x1e1c, KEY_TV },
{ 0x1e1e, KEY_NEXT },
{ 0x1e1f, KEY_BACK },
{ 0x1e20, KEY_CHANNELUP },
{ 0x1e21, KEY_CHANNELDOWN },
{ 0x1e24, KEY_LAST }, /* Skip backwards */
{ 0x1e25, KEY_OK },
{ 0x1e29, KEY_BLUE},
{ 0x1e2e, KEY_GREEN },
{ 0x1e30, KEY_PAUSE },
{ 0x1e32, KEY_REWIND },
{ 0x1e34, KEY_FASTFORWARD },
{ 0x1e35, KEY_PLAY },
{ 0x1e36, KEY_STOP },
{ 0x1e37, KEY_RECORD },
{ 0x1e38, KEY_YELLOW },
{ 0x1e3b, KEY_GOTO },
{ 0x1e3d, 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(rc_map_haupp_table); i++) {
if (rc5_data(&rc_map_haupp_table[i]) == data &&
rc5_custom(&rc_map_haupp_table[i]) == custom) {
deb_rc("c: %x, d: %x\n", rc5_data(&rc_map_haupp_table[i]),
rc5_custom(&rc_map_haupp_table[i]));
*event = rc_map_haupp_table[i].keycode;
*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, adapter_nr);
}
/* 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 = {
{
.num_frontends = 1,
.fe = {{
.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.legacy = {
.rc_interval = 100,
.rc_map_table = rc_map_haupp_table,
.rc_map_size = ARRAY_SIZE(rc_map_haupp_table),
.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_usb_driver(nova_t_driver);
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,583 @@
/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card
*
* Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org)
* Copyright (C) 2006 Marco Gittler (g.marco@freenet.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
*/
#define DVB_USB_LOG_PREFIX "opera"
#include "dvb-usb.h"
#include "stv0299.h"
#define OPERA_READ_MSG 0
#define OPERA_WRITE_MSG 1
#define OPERA_I2C_TUNER 0xd1
#define READ_FX2_REG_REQ 0xba
#define READ_MAC_ADDR 0x08
#define OPERA_WRITE_FX2 0xbb
#define OPERA_TUNER_REQ 0xb1
#define REG_1F_SYMBOLRATE_BYTE0 0x1f
#define REG_20_SYMBOLRATE_BYTE1 0x20
#define REG_21_SYMBOLRATE_BYTE2 0x21
#define ADDR_B600_VOLTAGE_13V (0x02)
#define ADDR_B601_VOLTAGE_18V (0x03)
#define ADDR_B1A6_STREAM_CTRL (0x04)
#define ADDR_B880_READ_REMOTE (0x05)
struct opera1_state {
u32 last_key_pressed;
};
struct rc_map_opera_table {
u32 keycode;
u32 event;
};
static int dvb_usb_opera1_debug;
module_param_named(debug, dvb_usb_opera1_debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))."
DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value,
u8 * data, u16 len, int flags)
{
int ret;
u8 tmp;
u8 *buf;
unsigned int pipe = (flags == OPERA_READ_MSG) ?
usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0);
u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (flags == OPERA_WRITE_MSG)
memcpy(buf, data, len);
ret = usb_control_msg(dev, pipe, request,
request_type | USB_TYPE_VENDOR, value, 0x0,
buf, len, 2000);
if (request == OPERA_TUNER_REQ) {
tmp = buf[0];
if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR,
0x01, 0x0, buf, 1, 2000) < 1 || buf[0] != 0x08) {
ret = 0;
goto out;
}
buf[0] = tmp;
}
if (flags == OPERA_READ_MSG)
memcpy(data, buf, len);
out:
kfree(buf);
return ret;
}
/* I2C */
static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr,
u8 * buf, u16 len)
{
int ret = 0;
u8 request;
u16 value;
if (!dev) {
info("no usb_device");
return -EINVAL;
}
if (mutex_lock_interruptible(&dev->usb_mutex) < 0)
return -EAGAIN;
switch (addr>>1){
case ADDR_B600_VOLTAGE_13V:
request=0xb6;
value=0x00;
break;
case ADDR_B601_VOLTAGE_18V:
request=0xb6;
value=0x01;
break;
case ADDR_B1A6_STREAM_CTRL:
request=0xb1;
value=0xa6;
break;
case ADDR_B880_READ_REMOTE:
request=0xb8;
value=0x80;
break;
default:
request=0xb1;
value=addr;
}
ret = opera1_xilinx_rw(dev->udev, request,
value, buf, len,
addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG);
mutex_unlock(&dev->usb_mutex);
return ret;
}
static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i = 0, tmp = 0;
if (!d)
return -ENODEV;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
if ((tmp = opera1_usb_i2c_msgxfer(d,
(msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0),
msg[i].buf,
msg[i].len
)) != msg[i].len) {
break;
}
if (dvb_usb_opera1_debug & 0x10)
info("sending i2c message %d %d", tmp, msg[i].len);
}
mutex_unlock(&d->i2c_mutex);
return num;
}
static u32 opera1_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm opera1_i2c_algo = {
.master_xfer = opera1_i2c_xfer,
.functionality = opera1_i2c_func,
};
static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
static u8 command_13v[1]={0x00};
static u8 command_18v[1]={0x01};
struct i2c_msg msg[] = {
{.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1},
};
struct dvb_usb_adapter *udev_adap =
(struct dvb_usb_adapter *)(fe->dvb->priv);
if (voltage == SEC_VOLTAGE_18) {
msg[0].addr = ADDR_B601_VOLTAGE_18V;
msg[0].buf = command_18v;
}
i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1);
return 0;
}
static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate,
u32 ratio)
{
stv0299_writereg(fe, 0x13, 0x98);
stv0299_writereg(fe, 0x14, 0x95);
stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff);
stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff);
stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0);
return 0;
}
static u8 opera1_inittab[] = {
0x00, 0xa1,
0x01, 0x15,
0x02, 0x30,
0x03, 0x00,
0x04, 0x7d,
0x05, 0x05,
0x06, 0x02,
0x07, 0x00,
0x0b, 0x00,
0x0c, 0x01,
0x0d, 0x81,
0x0e, 0x44,
0x0f, 0x19,
0x10, 0x3f,
0x11, 0x84,
0x12, 0xda,
0x13, 0x98,
0x14, 0x95,
0x15, 0xc9,
0x16, 0xeb,
0x17, 0x00,
0x18, 0x19,
0x19, 0x8b,
0x1a, 0x00,
0x1b, 0x82,
0x1c, 0x7f,
0x1d, 0x00,
0x1e, 0x00,
REG_1F_SYMBOLRATE_BYTE0, 0x06,
REG_20_SYMBOLRATE_BYTE1, 0x50,
REG_21_SYMBOLRATE_BYTE2, 0x10,
0x22, 0x00,
0x23, 0x00,
0x24, 0x37,
0x25, 0xbc,
0x26, 0x00,
0x27, 0x00,
0x28, 0x00,
0x29, 0x1e,
0x2a, 0x14,
0x2b, 0x1f,
0x2c, 0x09,
0x2d, 0x0a,
0x2e, 0x00,
0x2f, 0x00,
0x30, 0x00,
0x31, 0x1f,
0x32, 0x19,
0x33, 0xfc,
0x34, 0x13,
0xff, 0xff,
};
static struct stv0299_config opera1_stv0299_config = {
.demod_address = 0xd0>>1,
.min_delay_ms = 100,
.mclk = 88000000UL,
.invert = 1,
.skip_reinit = 0,
.lock_output = STV0299_LOCKOUTPUT_0,
.volt13_op0_op1 = STV0299_VOLT13_OP0,
.inittab = opera1_inittab,
.set_symbol_rate = opera1_stv0299_set_symbol_rate,
};
static int opera1_frontend_attach(struct dvb_usb_adapter *d)
{
d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config,
&d->dev->i2c_adap);
if ((d->fe_adap[0].fe) != NULL) {
d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage;
return 0;
}
info("not attached stv0299");
return -EIO;
}
static int opera1_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(
dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1,
&adap->dev->i2c_adap, DVB_PLL_OPERA1
);
return 0;
}
static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff)
{
u8 val = onoff ? 0x01 : 0x00;
if (dvb_usb_opera1_debug)
info("power %s", onoff ? "on" : "off");
return opera1_xilinx_rw(d->udev, 0xb7, val,
&val, 1, OPERA_WRITE_MSG);
}
static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
static u8 buf_start[2] = { 0xff, 0x03 };
static u8 buf_stop[2] = { 0xff, 0x00 };
struct i2c_msg start_tuner[] = {
{.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2},
};
if (dvb_usb_opera1_debug)
info("streaming %s", onoff ? "on" : "off");
i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1);
return 0;
}
static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
int onoff)
{
u8 b_pid[3];
struct i2c_msg msg[] = {
{.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3},
};
if (dvb_usb_opera1_debug)
info("pidfilter index: %d pid: %d %s", index, pid,
onoff ? "on" : "off");
b_pid[0] = (2 * index) + 4;
b_pid[1] = onoff ? (pid & 0xff) : (0x00);
b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00);
i2c_transfer(&adap->dev->i2c_adap, msg, 1);
return 0;
}
static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff)
{
int u = 0x04;
u8 b_pid[3];
struct i2c_msg msg[] = {
{.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3},
};
if (dvb_usb_opera1_debug)
info("%s hw-pidfilter", onoff ? "enable" : "disable");
for (; u < 0x7e; u += 2) {
b_pid[0] = u;
b_pid[1] = 0;
b_pid[2] = 0x80;
i2c_transfer(&adap->dev->i2c_adap, msg, 1);
}
return 0;
}
static struct rc_map_table rc_map_opera1_table[] = {
{0x5fa0, KEY_1},
{0x51af, KEY_2},
{0x5da2, KEY_3},
{0x41be, KEY_4},
{0x0bf5, KEY_5},
{0x43bd, KEY_6},
{0x47b8, KEY_7},
{0x49b6, KEY_8},
{0x05fa, KEY_9},
{0x45ba, KEY_0},
{0x09f6, KEY_CHANNELUP}, /*chanup */
{0x1be5, KEY_CHANNELDOWN}, /*chandown */
{0x5da3, KEY_VOLUMEDOWN}, /*voldown */
{0x5fa1, KEY_VOLUMEUP}, /*volup */
{0x07f8, KEY_SPACE}, /*tab */
{0x1fe1, KEY_OK}, /*play ok */
{0x1be4, KEY_ZOOM}, /*zoom */
{0x59a6, KEY_MUTE}, /*mute */
{0x5ba5, KEY_RADIO}, /*tv/f */
{0x19e7, KEY_RECORD}, /*rec */
{0x01fe, KEY_STOP}, /*Stop */
{0x03fd, KEY_PAUSE}, /*pause */
{0x03fc, KEY_SCREEN}, /*<- -> */
{0x07f9, KEY_CAMERA}, /*capture */
{0x47b9, KEY_ESC}, /*exit */
{0x43bc, KEY_POWER2}, /*power */
};
static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state)
{
struct opera1_state *opst = dev->priv;
u8 rcbuffer[32];
const u16 startmarker1 = 0x10ed;
const u16 startmarker2 = 0x11ec;
struct i2c_msg read_remote[] = {
{.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32},
};
int i = 0;
u32 send_key = 0;
if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) {
for (i = 0; i < 32; i++) {
if (rcbuffer[i])
send_key |= 1;
if (i < 31)
send_key = send_key << 1;
}
if (send_key & 0x8000)
send_key = (send_key << 1) | (send_key >> 15 & 0x01);
if (send_key == 0xffff && opst->last_key_pressed != 0) {
*state = REMOTE_KEY_REPEAT;
*event = opst->last_key_pressed;
return 0;
}
for (; send_key != 0;) {
if (send_key >> 16 == startmarker2) {
break;
} else if (send_key >> 16 == startmarker1) {
send_key =
(send_key & 0xfffeffff) | (startmarker1 << 16);
break;
} else
send_key >>= 1;
}
if (send_key == 0)
return 0;
send_key = (send_key & 0xffff) | 0x0100;
for (i = 0; i < ARRAY_SIZE(rc_map_opera1_table); i++) {
if (rc5_scan(&rc_map_opera1_table[i]) == (send_key & 0xffff)) {
*state = REMOTE_KEY_PRESSED;
*event = rc_map_opera1_table[i].keycode;
opst->last_key_pressed =
rc_map_opera1_table[i].keycode;
break;
}
opst->last_key_pressed = 0;
}
} else
*state = REMOTE_NO_KEY_PRESSED;
return 0;
}
static struct usb_device_id opera1_table[] = {
{USB_DEVICE(USB_VID_CYPRESS, USB_PID_OPERA1_COLD)},
{USB_DEVICE(USB_VID_OPERA1, USB_PID_OPERA1_WARM)},
{}
};
MODULE_DEVICE_TABLE(usb, opera1_table);
static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
{
u8 command[] = { READ_MAC_ADDR };
opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG);
opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG);
return 0;
}
static int opera1_xilinx_load_firmware(struct usb_device *dev,
const char *filename)
{
const struct firmware *fw = NULL;
u8 *b, *p;
int ret = 0, i,fpgasize=40;
u8 testval;
info("start downloading fpga firmware %s",filename);
if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) {
err("did not find the firmware file. (%s) "
"Please see linux/Documentation/dvb/ for more details on firmware-problems.",
filename);
return ret;
} else {
p = kmalloc(fw->size, GFP_KERNEL);
opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG);
if (p != NULL && testval != 0x67) {
u8 reset = 0, fpga_command = 0;
memcpy(p, fw->data, fw->size);
/* clear fpga ? */
opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1,
OPERA_WRITE_MSG);
for (i = 0; i < fw->size;) {
if ( (fw->size - i) <fpgasize){
fpgasize=fw->size-i;
}
b = (u8 *) p + i;
if (opera1_xilinx_rw
(dev, OPERA_WRITE_FX2, 0x0, b , fpgasize,
OPERA_WRITE_MSG) != fpgasize
) {
err("error while transferring firmware");
ret = -EINVAL;
break;
}
i = i + fpgasize;
}
/* restart the CPU */
if (ret || opera1_xilinx_rw
(dev, 0xa0, 0xe600, &reset, 1,
OPERA_WRITE_MSG) != 1) {
err("could not restart the USB controller CPU.");
ret = -EINVAL;
}
}
}
kfree(p);
release_firmware(fw);
return ret;
}
static struct dvb_usb_device_properties opera1_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-opera-01.fw",
.size_of_priv = sizeof(struct opera1_state),
.power_ctrl = opera1_power_ctrl,
.i2c_algo = &opera1_i2c_algo,
.rc.legacy = {
.rc_map_table = rc_map_opera1_table,
.rc_map_size = ARRAY_SIZE(rc_map_opera1_table),
.rc_interval = 200,
.rc_query = opera1_rc_query,
},
.read_mac_address = opera1_read_mac_address,
.generic_bulk_ctrl_endpoint = 0x00,
/* parameter for the MPEG2-data transfer */
.num_adapters = 1,
.adapter = {
{
.num_frontends = 1,
.fe = {{
.frontend_attach = opera1_frontend_attach,
.streaming_ctrl = opera1_streaming_ctrl,
.tuner_attach = opera1_tuner_attach,
.caps =
DVB_USB_ADAP_HAS_PID_FILTER |
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.pid_filter = opera1_pid_filter,
.pid_filter_ctrl = opera1_pid_filter_control,
.pid_filter_count = 252,
.stream = {
.type = USB_BULK,
.count = 10,
.endpoint = 0x82,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}},
}
},
.num_device_descs = 1,
.devices = {
{"Opera1 DVB-S USB2.0",
{&opera1_table[0], NULL},
{&opera1_table[1], NULL},
},
}
};
static int opera1_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
if (le16_to_cpu(udev->descriptor.idProduct) == USB_PID_OPERA1_WARM &&
le16_to_cpu(udev->descriptor.idVendor) == USB_VID_OPERA1 &&
opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga-01.fw") != 0
) {
return -EINVAL;
}
if (0 != dvb_usb_device_init(intf, &opera1_properties,
THIS_MODULE, NULL, adapter_nr))
return -EINVAL;
return 0;
}
static struct usb_driver opera1_driver = {
.name = "opera1",
.probe = opera1_probe,
.disconnect = dvb_usb_device_exit,
.id_table = opera1_table,
};
module_usb_driver(opera1_driver);
MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org");
MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de");
MODULE_DESCRIPTION("Driver for Opera1 DVB-S device");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,790 @@
/*
* Linux driver for Technisat DVB-S/S2 USB 2.0 device
*
* Copyright (C) 2010 Patrick Boettcher,
* Kernel Labs Inc. PO Box 745, St James, NY 11780
*
* Development was sponsored by Technisat Digital UK Limited, whose
* registered office is Witan Gate House 500 - 600 Witan Gate West,
* Milton Keynes, MK9 1SH
*
* 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.
*
*
* 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.
*
* THIS PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND
* TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COPYRIGHT HOLDER
* NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL,
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the
* GNU General Public License for more details.
*/
#define DVB_USB_LOG_PREFIX "technisat-usb2"
#include "dvb-usb.h"
#include "stv6110x.h"
#include "stv090x.h"
/* module parameters */
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debugging level (bit-mask: 1=info,2=eeprom,4=i2c,8=rc)." \
DVB_USB_DEBUG_STATUS);
/* disables all LED control command and
* also does not start the signal polling thread */
static int disable_led_control;
module_param(disable_led_control, int, 0444);
MODULE_PARM_DESC(disable_led_control,
"disable LED control of the device "
"(default: 0 - LED control is active).");
/* device private data */
struct technisat_usb2_state {
struct dvb_usb_device *dev;
struct delayed_work green_led_work;
u8 power_state;
u16 last_scan_code;
};
/* debug print helpers */
#define deb_info(args...) dprintk(debug, 0x01, args)
#define deb_eeprom(args...) dprintk(debug, 0x02, args)
#define deb_i2c(args...) dprintk(debug, 0x04, args)
#define deb_rc(args...) dprintk(debug, 0x08, args)
/* vendor requests */
#define SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST 0xB3
#define SET_FRONT_END_RESET_VENDOR_REQUEST 0xB4
#define GET_VERSION_INFO_VENDOR_REQUEST 0xB5
#define SET_GREEN_LED_VENDOR_REQUEST 0xB6
#define SET_RED_LED_VENDOR_REQUEST 0xB7
#define GET_IR_DATA_VENDOR_REQUEST 0xB8
#define SET_LED_TIMER_DIVIDER_VENDOR_REQUEST 0xB9
#define SET_USB_REENUMERATION 0xBA
/* i2c-access methods */
#define I2C_SPEED_100KHZ_BIT 0x40
#define I2C_STATUS_NAK 7
#define I2C_STATUS_OK 8
static int technisat_usb2_i2c_access(struct usb_device *udev,
u8 device_addr, u8 *tx, u8 txlen, u8 *rx, u8 rxlen)
{
u8 b[64];
int ret, actual_length;
deb_i2c("i2c-access: %02x, tx: ", device_addr);
debug_dump(tx, txlen, deb_i2c);
deb_i2c(" ");
if (txlen > 62) {
err("i2c TX buffer can't exceed 62 bytes (dev 0x%02x)",
device_addr);
txlen = 62;
}
if (rxlen > 62) {
err("i2c RX buffer can't exceed 62 bytes (dev 0x%02x)",
device_addr);
rxlen = 62;
}
b[0] = I2C_SPEED_100KHZ_BIT;
b[1] = device_addr << 1;
if (rx != NULL) {
b[0] |= rxlen;
b[1] |= 1;
}
memcpy(&b[2], tx, txlen);
ret = usb_bulk_msg(udev,
usb_sndbulkpipe(udev, 0x01),
b, 2 + txlen,
NULL, 1000);
if (ret < 0) {
err("i2c-error: out failed %02x = %d", device_addr, ret);
return -ENODEV;
}
ret = usb_bulk_msg(udev,
usb_rcvbulkpipe(udev, 0x01),
b, 64, &actual_length, 1000);
if (ret < 0) {
err("i2c-error: in failed %02x = %d", device_addr, ret);
return -ENODEV;
}
if (b[0] != I2C_STATUS_OK) {
err("i2c-error: %02x = %d", device_addr, b[0]);
/* handle tuner-i2c-nak */
if (!(b[0] == I2C_STATUS_NAK &&
device_addr == 0x60
/* && device_is_technisat_usb2 */))
return -ENODEV;
}
deb_i2c("status: %d, ", b[0]);
if (rx != NULL) {
memcpy(rx, &b[2], rxlen);
deb_i2c("rx (%d): ", rxlen);
debug_dump(rx, rxlen, deb_i2c);
}
deb_i2c("\n");
return 0;
}
static int technisat_usb2_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
int num)
{
int ret = 0, i;
struct dvb_usb_device *d = i2c_get_adapdata(adap);
/* Ensure nobody else hits the i2c bus while we're sending our
sequence of messages, (such as the remote control thread) */
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
if (i+1 < num && msg[i+1].flags & I2C_M_RD) {
ret = technisat_usb2_i2c_access(d->udev, msg[i+1].addr,
msg[i].buf, msg[i].len,
msg[i+1].buf, msg[i+1].len);
if (ret != 0)
break;
i++;
} else {
ret = technisat_usb2_i2c_access(d->udev, msg[i].addr,
msg[i].buf, msg[i].len,
NULL, 0);
if (ret != 0)
break;
}
}
if (ret == 0)
ret = i;
mutex_unlock(&d->i2c_mutex);
return ret;
}
static u32 technisat_usb2_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm technisat_usb2_i2c_algo = {
.master_xfer = technisat_usb2_i2c_xfer,
.functionality = technisat_usb2_i2c_func,
};
#if 0
static void technisat_usb2_frontend_reset(struct usb_device *udev)
{
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
SET_FRONT_END_RESET_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_OUT,
10, 0,
NULL, 0, 500);
}
#endif
/* LED control */
enum technisat_usb2_led_state {
TECH_LED_OFF,
TECH_LED_BLINK,
TECH_LED_ON,
TECH_LED_UNDEFINED
};
static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state)
{
int ret;
u8 led[8] = {
red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST,
0
};
if (disable_led_control && state != TECH_LED_OFF)
return 0;
switch (state) {
case TECH_LED_ON:
led[1] = 0x82;
break;
case TECH_LED_BLINK:
led[1] = 0x82;
if (red) {
led[2] = 0x02;
led[3] = 10;
led[4] = 10;
} else {
led[2] = 0xff;
led[3] = 50;
led[4] = 50;
}
led[5] = 1;
break;
default:
case TECH_LED_OFF:
led[1] = 0x80;
break;
}
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_OUT,
0, 0,
led, sizeof(led), 500);
mutex_unlock(&d->i2c_mutex);
return ret;
}
static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 green)
{
int ret;
u8 b = 0;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
SET_LED_TIMER_DIVIDER_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_OUT,
(red << 8) | green, 0,
&b, 1, 500);
mutex_unlock(&d->i2c_mutex);
return ret;
}
static void technisat_usb2_green_led_control(struct work_struct *work)
{
struct technisat_usb2_state *state =
container_of(work, struct technisat_usb2_state, green_led_work.work);
struct dvb_frontend *fe = state->dev->adapter[0].fe_adap[0].fe;
if (state->power_state == 0)
goto schedule;
if (fe != NULL) {
enum fe_status status;
if (fe->ops.read_status(fe, &status) != 0)
goto schedule;
if (status & FE_HAS_LOCK) {
u32 ber;
if (fe->ops.read_ber(fe, &ber) != 0)
goto schedule;
if (ber > 1000)
technisat_usb2_set_led(state->dev, 0, TECH_LED_BLINK);
else
technisat_usb2_set_led(state->dev, 0, TECH_LED_ON);
} else
technisat_usb2_set_led(state->dev, 0, TECH_LED_OFF);
}
schedule:
schedule_delayed_work(&state->green_led_work,
msecs_to_jiffies(500));
}
/* method to find out whether the firmware has to be downloaded or not */
static int technisat_usb2_identify_state(struct usb_device *udev,
struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc, int *cold)
{
int ret;
u8 version[3];
/* first select the interface */
if (usb_set_interface(udev, 0, 1) != 0)
err("could not set alternate setting to 0");
else
info("set alternate setting");
*cold = 0; /* by default do not download a firmware - just in case something is wrong */
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
GET_VERSION_INFO_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_IN,
0, 0,
version, sizeof(version), 500);
if (ret < 0)
*cold = 1;
else {
info("firmware version: %d.%d", version[1], version[2]);
*cold = 0;
}
return 0;
}
/* power control */
static int technisat_usb2_power_ctrl(struct dvb_usb_device *d, int level)
{
struct technisat_usb2_state *state = d->priv;
state->power_state = level;
if (disable_led_control)
return 0;
/* green led is turned off in any case - will be turned on when tuning */
technisat_usb2_set_led(d, 0, TECH_LED_OFF);
/* red led is turned on all the time */
technisat_usb2_set_led(d, 1, TECH_LED_ON);
return 0;
}
/* mac address reading - from the eeprom */
#if 0
static void technisat_usb2_eeprom_dump(struct dvb_usb_device *d)
{
u8 reg;
u8 b[16];
int i, j;
/* full EEPROM dump */
for (j = 0; j < 256 * 4; j += 16) {
reg = j;
if (technisat_usb2_i2c_access(d->udev, 0x50 + j / 256, &reg, 1, b, 16) != 0)
break;
deb_eeprom("EEPROM: %01x%02x: ", j / 256, reg);
for (i = 0; i < 16; i++)
deb_eeprom("%02x ", b[i]);
deb_eeprom("\n");
}
}
#endif
static u8 technisat_usb2_calc_lrc(const u8 *b, u16 length)
{
u8 lrc = 0;
while (--length)
lrc ^= *b++;
return lrc;
}
static int technisat_usb2_eeprom_lrc_read(struct dvb_usb_device *d,
u16 offset, u8 *b, u16 length, u8 tries)
{
u8 bo = offset & 0xff;
struct i2c_msg msg[] = {
{
.addr = 0x50 | ((offset >> 8) & 0x3),
.buf = &bo,
.len = 1
}, {
.addr = 0x50 | ((offset >> 8) & 0x3),
.flags = I2C_M_RD,
.buf = b,
.len = length
}
};
while (tries--) {
int status;
if (i2c_transfer(&d->i2c_adap, msg, 2) != 2)
break;
status =
technisat_usb2_calc_lrc(b, length - 1) == b[length - 1];
if (status)
return 0;
}
return -EREMOTEIO;
}
#define EEPROM_MAC_START 0x3f8
#define EEPROM_MAC_TOTAL 8
static int technisat_usb2_read_mac_address(struct dvb_usb_device *d,
u8 mac[])
{
u8 buf[EEPROM_MAC_TOTAL];
if (technisat_usb2_eeprom_lrc_read(d, EEPROM_MAC_START,
buf, EEPROM_MAC_TOTAL, 4) != 0)
return -ENODEV;
memcpy(mac, buf, 6);
return 0;
}
/* frontend attach */
static int technisat_usb2_set_voltage(struct dvb_frontend *fe,
fe_sec_voltage_t voltage)
{
int i;
u8 gpio[3] = { 0 }; /* 0 = 2, 1 = 3, 2 = 4 */
gpio[2] = 1; /* high - voltage ? */
switch (voltage) {
case SEC_VOLTAGE_13:
gpio[0] = 1;
break;
case SEC_VOLTAGE_18:
gpio[0] = 1;
gpio[1] = 1;
break;
default:
case SEC_VOLTAGE_OFF:
break;
}
for (i = 0; i < 3; i++)
if (stv090x_set_gpio(fe, i+2, 0, gpio[i], 0) != 0)
return -EREMOTEIO;
return 0;
}
static struct stv090x_config technisat_usb2_stv090x_config = {
.device = STV0903,
.demod_mode = STV090x_SINGLE,
.clk_mode = STV090x_CLK_EXT,
.xtal = 8000000,
.address = 0x68,
.ts1_mode = STV090x_TSMODE_DVBCI,
.ts1_clk = 13400000,
.ts1_tei = 1,
.repeater_level = STV090x_RPTLEVEL_64,
.tuner_bbgain = 6,
};
static struct stv6110x_config technisat_usb2_stv6110x_config = {
.addr = 0x60,
.refclk = 16000000,
.clk_div = 2,
};
static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a)
{
struct usb_device *udev = a->dev->udev;
int ret;
a->fe_adap[0].fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config,
&a->dev->i2c_adap, STV090x_DEMODULATOR_0);
if (a->fe_adap[0].fe) {
struct stv6110x_devctl *ctl;
ctl = dvb_attach(stv6110x_attach,
a->fe_adap[0].fe,
&technisat_usb2_stv6110x_config,
&a->dev->i2c_adap);
if (ctl) {
technisat_usb2_stv090x_config.tuner_init = ctl->tuner_init;
technisat_usb2_stv090x_config.tuner_sleep = ctl->tuner_sleep;
technisat_usb2_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
technisat_usb2_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
technisat_usb2_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
technisat_usb2_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
technisat_usb2_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
technisat_usb2_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
technisat_usb2_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
technisat_usb2_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
technisat_usb2_stv090x_config.tuner_get_status = ctl->tuner_get_status;
/* call the init function once to initialize
tuner's clock output divider and demod's
master clock */
if (a->fe_adap[0].fe->ops.init)
a->fe_adap[0].fe->ops.init(a->fe_adap[0].fe);
if (mutex_lock_interruptible(&a->dev->i2c_mutex) < 0)
return -EAGAIN;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_OUT,
0, 0,
NULL, 0, 500);
mutex_unlock(&a->dev->i2c_mutex);
if (ret != 0)
err("could not set IF_CLK to external");
a->fe_adap[0].fe->ops.set_voltage = technisat_usb2_set_voltage;
/* if everything was successful assign a nice name to the frontend */
strlcpy(a->fe_adap[0].fe->ops.info.name, a->dev->desc->name,
sizeof(a->fe_adap[0].fe->ops.info.name));
} else {
dvb_frontend_detach(a->fe_adap[0].fe);
a->fe_adap[0].fe = NULL;
}
}
technisat_usb2_set_led_timer(a->dev, 1, 1);
return a->fe_adap[0].fe == NULL ? -ENODEV : 0;
}
/* Remote control */
/* the device is giving providing raw IR-signals to the host mapping
* it only to one remote control is just the default implementation
*/
#define NOMINAL_IR_BIT_TRANSITION_TIME_US 889
#define NOMINAL_IR_BIT_TIME_US (2 * NOMINAL_IR_BIT_TRANSITION_TIME_US)
#define FIRMWARE_CLOCK_TICK 83333
#define FIRMWARE_CLOCK_DIVISOR 256
#define IR_PERCENT_TOLERANCE 15
#define NOMINAL_IR_BIT_TRANSITION_TICKS ((NOMINAL_IR_BIT_TRANSITION_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK)
#define NOMINAL_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICKS / FIRMWARE_CLOCK_DIVISOR)
#define NOMINAL_IR_BIT_TIME_TICKS ((NOMINAL_IR_BIT_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK)
#define NOMINAL_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICKS / FIRMWARE_CLOCK_DIVISOR)
#define MINIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT - ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100))
#define MAXIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT + ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100))
#define MINIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT - ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100))
#define MAXIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT + ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100))
static int technisat_usb2_get_ir(struct dvb_usb_device *d)
{
u8 buf[62], *b;
int ret;
struct ir_raw_event ev;
buf[0] = GET_IR_DATA_VENDOR_REQUEST;
buf[1] = 0x08;
buf[2] = 0x8f;
buf[3] = MINIMUM_IR_BIT_TRANSITION_TICK_COUNT;
buf[4] = MAXIMUM_IR_BIT_TIME_TICK_COUNT;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
GET_IR_DATA_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_OUT,
0, 0,
buf, 5, 500);
if (ret < 0)
goto unlock;
buf[1] = 0;
buf[2] = 0;
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
GET_IR_DATA_VENDOR_REQUEST,
USB_TYPE_VENDOR | USB_DIR_IN,
0x8080, 0,
buf, sizeof(buf), 500);
unlock:
mutex_unlock(&d->i2c_mutex);
if (ret < 0)
return ret;
if (ret == 1)
return 0; /* no key pressed */
/* decoding */
b = buf+1;
#if 0
deb_rc("RC: %d ", ret);
debug_dump(b, ret, deb_rc);
#endif
ev.pulse = 0;
while (1) {
ev.pulse = !ev.pulse;
ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000;
ir_raw_event_store(d->rc_dev, &ev);
b++;
if (*b == 0xff) {
ev.pulse = 0;
ev.duration = 888888*2;
ir_raw_event_store(d->rc_dev, &ev);
break;
}
}
ir_raw_event_handle(d->rc_dev);
return 1;
}
static int technisat_usb2_rc_query(struct dvb_usb_device *d)
{
int ret = technisat_usb2_get_ir(d);
if (ret < 0)
return ret;
if (ret == 0)
return 0;
if (!disable_led_control)
technisat_usb2_set_led(d, 1, TECH_LED_BLINK);
return 0;
}
/* DVB-USB and USB stuff follows */
static struct usb_device_id technisat_usb2_id_table[] = {
{ USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_DVB_S2) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, technisat_usb2_id_table);
/* device description */
static struct dvb_usb_device_properties technisat_usb2_devices = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.identify_state = technisat_usb2_identify_state,
.firmware = "dvb-usb-SkyStar_USB_HD_FW_v17_63.HEX.fw",
.size_of_priv = sizeof(struct technisat_usb2_state),
.i2c_algo = &technisat_usb2_i2c_algo,
.power_ctrl = technisat_usb2_power_ctrl,
.read_mac_address = technisat_usb2_read_mac_address,
.num_adapters = 1,
.adapter = {
{
.num_frontends = 1,
.fe = {{
.frontend_attach = technisat_usb2_frontend_attach,
.stream = {
.type = USB_ISOC,
.count = 8,
.endpoint = 0x2,
.u = {
.isoc = {
.framesperurb = 32,
.framesize = 2048,
.interval = 1,
}
}
},
}},
.size_of_priv = 0,
},
},
.num_device_descs = 1,
.devices = {
{ "Technisat SkyStar USB HD (DVB-S/S2)",
{ &technisat_usb2_id_table[0], NULL },
{ NULL },
},
},
.rc.core = {
.rc_interval = 100,
.rc_codes = RC_MAP_TECHNISAT_USB2,
.module_name = "technisat-usb2",
.rc_query = technisat_usb2_rc_query,
.allowed_protos = RC_BIT_ALL,
.driver_type = RC_DRIVER_IR_RAW,
}
};
static int technisat_usb2_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dvb_usb_device *dev;
if (dvb_usb_device_init(intf, &technisat_usb2_devices, THIS_MODULE,
&dev, adapter_nr) != 0)
return -ENODEV;
if (dev) {
struct technisat_usb2_state *state = dev->priv;
state->dev = dev;
if (!disable_led_control) {
INIT_DELAYED_WORK(&state->green_led_work,
technisat_usb2_green_led_control);
schedule_delayed_work(&state->green_led_work,
msecs_to_jiffies(500));
}
}
return 0;
}
static void technisat_usb2_disconnect(struct usb_interface *intf)
{
struct dvb_usb_device *dev = usb_get_intfdata(intf);
/* work and stuff was only created when the device is is hot-state */
if (dev != NULL) {
struct technisat_usb2_state *state = dev->priv;
if (state != NULL)
cancel_delayed_work_sync(&state->green_led_work);
}
dvb_usb_device_exit(intf);
}
static struct usb_driver technisat_usb2_driver = {
.name = "dvb_usb_technisat_usb2",
.probe = technisat_usb2_probe,
.disconnect = technisat_usb2_disconnect,
.id_table = technisat_usb2_id_table,
};
module_usb_driver(technisat_usb2_driver);
MODULE_AUTHOR("Patrick Boettcher <pboettcher@kernellabs.com>");
MODULE_DESCRIPTION("Driver for Technisat DVB-S/S2 USB 2.0 device");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,826 @@
/* 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 "tda1002x.h"
#include "tda10048.h"
#include "tda827x.h"
#include "lnbp21.h"
/* CA */
#include "dvb_ca_en50221.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);
static int dvb_usb_ttusb2_debug_ci;
module_param_named(debug_ci,dvb_usb_ttusb2_debug_ci, int, 0644);
MODULE_PARM_DESC(debug_ci, "set debugging ci." DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define ci_dbg(format, arg...) \
do { \
if (dvb_usb_ttusb2_debug_ci) \
printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
": %s " format "\n" , __func__, ## arg); \
} while (0)
enum {
TT3650_CMD_CI_TEST = 0x40,
TT3650_CMD_CI_RD_CTRL,
TT3650_CMD_CI_WR_CTRL,
TT3650_CMD_CI_RD_ATTR,
TT3650_CMD_CI_WR_ATTR,
TT3650_CMD_CI_RESET,
TT3650_CMD_CI_SET_VIDEO_PORT
};
struct ttusb2_state {
struct dvb_ca_en50221 ca;
struct mutex ca_mutex;
u8 id;
u16 last_rc_key;
};
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, *r = NULL;
int ret = 0;
s = kzalloc(wlen+4, GFP_KERNEL);
if (!s)
return -ENOMEM;
r = kzalloc(64, GFP_KERNEL);
if (!r) {
kfree(s);
return -ENOMEM;
}
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]);
kfree(s);
kfree(r);
return -EIO;
}
if (rlen > 0)
memcpy(rbuf, &r[4], rlen);
kfree(s);
kfree(r);
return 0;
}
/* ci */
static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
{
int ret;
u8 rx[60];/* (64 -4) */
ret = ttusb2_msg(d, cmd, data, write_len, rx, read_len);
if (!ret)
memcpy(data, rx, read_len);
return ret;
}
static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
{
struct dvb_usb_device *d = ca->data;
struct ttusb2_state *state = d->priv;
int ret;
mutex_lock(&state->ca_mutex);
ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
mutex_unlock(&state->ca_mutex);
return ret;
}
static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
{
u8 buf[3];
int ret = 0;
if (slot)
return -EINVAL;
buf[0] = (address >> 8) & 0x0F;
buf[1] = address;
ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
ci_dbg("%04x -> %d 0x%02x", address, ret, buf[2]);
if (ret < 0)
return ret;
return buf[2];
}
static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
{
u8 buf[3];
ci_dbg("%d 0x%04x 0x%02x", slot, address, value);
if (slot)
return -EINVAL;
buf[0] = (address >> 8) & 0x0F;
buf[1] = address;
buf[2] = value;
return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
}
static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
{
u8 buf[2];
int ret;
if (slot)
return -EINVAL;
buf[0] = address & 3;
ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
ci_dbg("0x%02x -> %d 0x%02x", address, ret, buf[1]);
if (ret < 0)
return ret;
return buf[1];
}
static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
{
u8 buf[2];
ci_dbg("%d 0x%02x 0x%02x", slot, address, value);
if (slot)
return -EINVAL;
buf[0] = address;
buf[1] = value;
return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
}
static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, int slot, int enable)
{
u8 buf[1];
int ret;
ci_dbg("%d %d", slot, enable);
if (slot)
return -EINVAL;
buf[0] = enable;
ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
if (ret < 0)
return ret;
if (enable != buf[0]) {
err("CI not %sabled.", enable ? "en" : "dis");
return -EIO;
}
return 0;
}
static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
{
return tt3650_ci_set_video_port(ca, slot, 0);
}
static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
{
return tt3650_ci_set_video_port(ca, slot, 1);
}
static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
{
struct dvb_usb_device *d = ca->data;
struct ttusb2_state *state = d->priv;
u8 buf[1];
int ret;
ci_dbg("%d", slot);
if (slot)
return -EINVAL;
buf[0] = 0;
mutex_lock(&state->ca_mutex);
ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
if (ret)
goto failed;
msleep(500);
buf[0] = 1;
ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
if (ret)
goto failed;
msleep(500);
buf[0] = 0; /* FTA */
ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
msleep(1100);
failed:
mutex_unlock(&state->ca_mutex);
return ret;
}
static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
{
u8 buf[1];
int ret;
if (slot)
return -EINVAL;
ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
if (ret)
return ret;
if (1 == buf[0]) {
return DVB_CA_EN50221_POLL_CAM_PRESENT |
DVB_CA_EN50221_POLL_CAM_READY;
}
return 0;
}
static void tt3650_ci_uninit(struct dvb_usb_device *d)
{
struct ttusb2_state *state;
ci_dbg("");
if (NULL == d)
return;
state = d->priv;
if (NULL == state)
return;
if (NULL == state->ca.data)
return;
dvb_ca_en50221_release(&state->ca);
memset(&state->ca, 0, sizeof(state->ca));
}
static int tt3650_ci_init(struct dvb_usb_adapter *a)
{
struct dvb_usb_device *d = a->dev;
struct ttusb2_state *state = d->priv;
int ret;
ci_dbg("");
mutex_init(&state->ca_mutex);
state->ca.owner = THIS_MODULE;
state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
state->ca.read_cam_control = tt3650_ci_read_cam_control;
state->ca.write_cam_control = tt3650_ci_write_cam_control;
state->ca.slot_reset = tt3650_ci_slot_reset;
state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
state->ca.data = d;
ret = dvb_ca_en50221_init(&a->dvb_adap,
&state->ca,
/* flags */ 0,
/* n_slots */ 1);
if (ret) {
err("Cannot initialize CI: Error %d.", ret);
memset(&state->ca, 0, sizeof(state->ca));
return ret;
}
info("CI initialized.");
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, write_read, 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++) {
write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
read = msg[i].flags & I2C_M_RD;
obuf[0] = (msg[i].addr << 1) | (write_read | read);
if (read)
obuf[1] = 0;
else
obuf[1] = msg[i].len;
/* read request */
if (write_read)
obuf[2] = msg[i+1].len;
else if (read)
obuf[2] = msg[i].len;
else
obuf[2] = 0;
memcpy(&obuf[3], msg[i].buf, msg[i].len);
if (ttusb2_msg(d, CMD_I2C_XFER, obuf, obuf[1]+3, ibuf, obuf[2] + 3) < 0) {
err("i2c transfer failed.");
break;
}
if (write_read) {
memcpy(msg[i+1].buf, &ibuf[3], msg[i+1].len);
i++;
} else if (read)
memcpy(msg[i].buf, &ibuf[3], msg[i].len);
}
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,
};
/* command to poll IR receiver (copied from pctv452e.c) */
#define CMD_GET_IR_CODE 0x1b
/* IR */
static int tt3650_rc_query(struct dvb_usb_device *d)
{
int ret;
u8 rx[9]; /* A CMD_GET_IR_CODE reply is 9 bytes long */
struct ttusb2_state *st = d->priv;
ret = ttusb2_msg(d, CMD_GET_IR_CODE, NULL, 0, rx, sizeof(rx));
if (ret != 0)
return ret;
if (rx[8] & 0x01) {
/* got a "press" event */
st->last_rc_key = RC_SCANCODE_RC5(rx[3], rx[2]);
deb_info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[2], rx[3]);
rc_keydown(d->rc_dev, RC_TYPE_RC5, st->last_rc_key, rx[1]);
} else if (st->last_rc_key) {
rc_keyup(d->rc_dev);
st->last_rc_key = 0;
}
return 0;
}
/* 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,
.diseqc_tone = 1,
.xtal_freq = TDA10086_XTAL_16M,
};
static struct tda10023_config tda10023_config = {
.demod_address = 0x0c,
.invert = 0,
.xtal = 16000000,
.pll_m = 11,
.pll_p = 3,
.pll_n = 1,
.deltaf = 0xa511,
};
static struct tda10048_config tda10048_config = {
.demod_address = 0x10 >> 1,
.output_mode = TDA10048_PARALLEL_OUTPUT,
.inversion = TDA10048_INVERSION_ON,
.dtv6_if_freq_khz = TDA10048_IF_4000,
.dtv7_if_freq_khz = TDA10048_IF_4500,
.dtv8_if_freq_khz = TDA10048_IF_5000,
.clk_freq_khz = TDA10048_CLK_16000,
.no_firmware = 1,
.set_pll = true ,
.pll_m = 5,
.pll_n = 3,
.pll_p = 0,
};
static struct tda827x_config tda827x_config = {
.config = 0,
};
static int ttusb2_frontend_tda10086_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_adap[0].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_ct3650_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
return adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, enable);
}
static int ttusb2_frontend_tda10023_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_adap[0].fe == NULL) {
/* FE 0 DVB-C */
adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
&tda10023_config, &adap->dev->i2c_adap, 0x48);
if (adap->fe_adap[0].fe == NULL) {
deb_info("TDA10023 attach failed\n");
return -ENODEV;
}
tt3650_ci_init(adap);
} else {
adap->fe_adap[1].fe = dvb_attach(tda10048_attach,
&tda10048_config, &adap->dev->i2c_adap);
if (adap->fe_adap[1].fe == NULL) {
deb_info("TDA10048 attach failed\n");
return -ENODEV;
}
/* tuner is behind TDA10023 I2C-gate */
adap->fe_adap[1].fe->ops.i2c_gate_ctrl = ttusb2_ct3650_i2c_gate_ctrl;
}
return 0;
}
static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap)
{
struct dvb_frontend *fe;
/* MFE: select correct FE to attach tuner since that's called twice */
if (adap->fe_adap[1].fe == NULL)
fe = adap->fe_adap[0].fe;
else
fe = adap->fe_adap[1].fe;
/* attach tuner */
if (dvb_attach(tda827x_attach, fe, 0x61, &adap->dev->i2c_adap, &tda827x_config) == NULL) {
printk(KERN_ERR "%s: No tda827x found!\n", __func__);
return -ENODEV;
}
return 0;
}
static int ttusb2_tuner_tda826x_attach(struct dvb_usb_adapter *adap)
{
if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
deb_info("TDA8263 attach failed\n");
return -ENODEV;
}
if (dvb_attach(lnbp21_attach, adap->fe_adap[0].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 struct dvb_usb_device_properties ttusb2_properties_s2400;
static struct dvb_usb_device_properties ttusb2_properties_ct3650;
static void ttusb2_usb_disconnect(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
tt3650_ci_uninit(d);
dvb_usb_device_exit(intf);
}
static int ttusb2_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
if (0 == dvb_usb_device_init(intf, &ttusb2_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &ttusb2_properties_s2400,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &ttusb2_properties_ct3650,
THIS_MODULE, NULL, adapter_nr))
return 0;
return -ENODEV;
}
static struct usb_device_id ttusb2_table [] = {
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) },
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) },
{ USB_DEVICE(USB_VID_TECHNOTREND,
USB_PID_TECHNOTREND_CONNECT_S2400) },
{ USB_DEVICE(USB_VID_TECHNOTREND,
USB_PID_TECHNOTREND_CONNECT_CT3650) },
{ USB_DEVICE(USB_VID_TECHNOTREND,
USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM) },
{} /* 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 = {
{
.num_frontends = 1,
.fe = {{
.streaming_ctrl = NULL, // ttusb2_streaming_ctrl,
.frontend_attach = ttusb2_frontend_tda10086_attach,
.tuner_attach = ttusb2_tuner_tda826x_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 = 2,
.devices = {
{ "Pinnacle 400e DVB-S USB2.0",
{ &ttusb2_table[0], NULL },
{ NULL },
},
{ "Pinnacle 450e DVB-S USB2.0",
{ &ttusb2_table[1], NULL },
{ NULL },
},
}
};
static struct dvb_usb_device_properties ttusb2_properties_s2400 = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-tt-s2400-01.fw",
.size_of_priv = sizeof(struct ttusb2_state),
.num_adapters = 1,
.adapter = {
{
.num_frontends = 1,
.fe = {{
.streaming_ctrl = NULL,
.frontend_attach = ttusb2_frontend_tda10086_attach,
.tuner_attach = ttusb2_tuner_tda826x_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 = 2,
.devices = {
{ "Technotrend TT-connect S-2400",
{ &ttusb2_table[2], NULL },
{ NULL },
},
{ "Technotrend TT-connect S-2400 (8kB EEPROM)",
{ &ttusb2_table[4], NULL },
{ NULL },
},
}
};
static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
.size_of_priv = sizeof(struct ttusb2_state),
.rc.core = {
.rc_interval = 150, /* Less than IR_KEYPRESS_TIMEOUT */
.rc_codes = RC_MAP_TT_1500,
.rc_query = tt3650_rc_query,
.allowed_protos = RC_BIT_RC5,
},
.num_adapters = 1,
.adapter = {
{
.num_frontends = 2,
.fe = {{
.streaming_ctrl = NULL,
.frontend_attach = ttusb2_frontend_tda10023_attach,
.tuner_attach = ttusb2_tuner_tda827x_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_ISOC,
.count = 5,
.endpoint = 0x02,
.u = {
.isoc = {
.framesperurb = 4,
.framesize = 940,
.interval = 1,
}
}
}
}, {
.streaming_ctrl = NULL,
.frontend_attach = ttusb2_frontend_tda10023_attach,
.tuner_attach = ttusb2_tuner_tda827x_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 = {
{ "Technotrend TT-connect CT-3650",
.warm_ids = { &ttusb2_table[3], NULL },
},
}
};
static struct usb_driver ttusb2_driver = {
.name = "dvb_usb_ttusb2",
.probe = ttusb2_probe,
.disconnect = ttusb2_usb_disconnect,
.id_table = ttusb2_table,
};
module_usb_driver(ttusb2_driver);
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,151 @@
/* 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"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
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_adap[0].fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap);
return 0;
}
static int umt_tuner_attach (struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_TUA6034);
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 (0 == dvb_usb_device_init(intf, &umt_properties,
THIS_MODULE, NULL, adapter_nr))
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 = {
{
.num_frontends = 1,
.fe = {{
.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 = MAX_NO_URBS_FOR_DATA_STREAM,
.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_usb_driver(umt_driver);
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,254 @@
/* 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("unknown 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_free_coherent(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_alloc_coherent(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, j;
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++) {
stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
if (!stream->urb_list[i]) {
deb_mem("not enough memory for urb_alloc_urb!.\n");
for (j = 0; j < i; j++)
usb_free_urb(stream->urb_list[j]);
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 = URB_NO_TRANSFER_DMA_MAP;
stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
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;
stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_ATOMIC);
if (!stream->urb_list[i]) {
deb_mem("not enough memory for urb_alloc_urb!\n");
for (j = 0; j < i; j++)
usb_free_urb(stream->urb_list[j]);
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("unknown 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,379 @@
/* 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)
{
struct vp702x_device_state *dst = st->d->priv;
u8 *buf;
if (time_after(jiffies, st->next_status_check)) {
mutex_lock(&dst->buf_mutex);
buf = dst->buf;
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, buf, 1);
st->snr = buf[0];
vp702x_usb_in_op(st->d, READ_TUNER_REG_REQ, 0x15, 0, buf, 1);
st->sig = buf[0];
mutex_unlock(&dst->buf_mutex);
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",__func__);
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",__func__);
tune->min_delay_ms = 2000;
return 0;
}
static int vp702x_fe_set_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
struct vp702x_fe_state *st = fe->demodulator_priv;
struct vp702x_device_state *dst = st->d->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;
mutex_lock(&dst->buf_mutex);
cmd = dst->buf;
memset(cmd, 0, 10);
cmd[0] = (freq >> 8) & 0x7f;
cmd[1] = freq & 0xff;
cmd[2] = 1; /* divrate == 4 -> frequencyRef[1] -> 1 here */
sr = (u64) (fep->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->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->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, cmd, 10, 100);
if (cmd[2] == 0 && cmd[3] == 0)
deb_fe("tuning failed.\n");
else
deb_fe("tuning succeeded.\n");
mutex_unlock(&dst->buf_mutex);
return 0;
}
static int vp702x_fe_init(struct dvb_frontend *fe)
{
struct vp702x_fe_state *st = fe->demodulator_priv;
deb_fe("%s\n",__func__);
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",__func__);
return 0;
}
static int vp702x_fe_send_diseqc_msg (struct dvb_frontend* fe,
struct dvb_diseqc_master_cmd *m)
{
u8 *cmd;
struct vp702x_fe_state *st = fe->demodulator_priv;
struct vp702x_device_state *dst = st->d->priv;
deb_fe("%s\n",__func__);
if (m->msg_len > 4)
return -EINVAL;
mutex_lock(&dst->buf_mutex);
cmd = dst->buf;
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, cmd, 10, 100);
if (cmd[2] == 0 && cmd[3] == 0)
deb_fe("diseqc cmd failed.\n");
else
deb_fe("diseqc cmd succeeded.\n");
mutex_unlock(&dst->buf_mutex);
return 0;
}
static int vp702x_fe_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
{
deb_fe("%s\n",__func__);
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;
struct vp702x_device_state *dst = st->d->priv;
u8 *buf;
deb_fe("%s\n",__func__);
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);
mutex_lock(&dst->buf_mutex);
buf = dst->buf;
memcpy(buf, st->lnb_buf, 8);
vp702x_usb_inout_op(st->d, buf, 8, buf, 10, 100);
if (buf[2] == 0 && buf[3] == 0)
deb_fe("set_tone cmd failed.\n");
else
deb_fe("set_tone cmd succeeded.\n");
mutex_unlock(&dst->buf_mutex);
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;
struct vp702x_device_state *dst = st->d->priv;
u8 *buf;
deb_fe("%s\n",__func__);
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);
mutex_lock(&dst->buf_mutex);
buf = dst->buf;
memcpy(buf, st->lnb_buf, 8);
vp702x_usb_inout_op(st->d, buf, 8, buf, 10, 100);
if (buf[2] == 0 && buf[3] == 0)
deb_fe("set_voltage cmd failed.\n");
else
deb_fe("set_voltage cmd succeeded.\n");
mutex_unlock(&dst->buf_mutex);
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 = {
.delsys = { SYS_DVBS },
.info = {
.name = "Twinhan DST-like frontend (VP7021/VP7020) DVB-S",
.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_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,444 @@
/* 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"
#include <linux/mutex.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);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct vp702x_adapter_state {
int pid_filter_count;
int pid_filter_can_bypass;
u8 pid_filter_state;
};
static int vp702x_usb_in_op_unlocked(struct dvb_usb_device *d, u8 req,
u16 value, u16 index, u8 *b, int blen)
{
int ret;
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_in_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
mutex_lock(&d->usb_mutex);
ret = vp702x_usb_in_op_unlocked(d, req, value, index, b, blen);
mutex_unlock(&d->usb_mutex);
return ret;
}
static int vp702x_usb_out_op_unlocked(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;
}
static int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
mutex_lock(&d->usb_mutex);
ret = vp702x_usb_out_op_unlocked(d, req, value, index, b, blen);
mutex_unlock(&d->usb_mutex);
return ret;
}
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_unlocked(d, REQUEST_OUT, 0, 0, o, olen);
msleep(msec);
ret = vp702x_usb_in_op_unlocked(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)
{
struct vp702x_device_state *st = d->priv;
int ret = 0;
u8 *buf;
int buflen = max(olen + 2, ilen + 1);
ret = mutex_lock_interruptible(&st->buf_mutex);
if (ret < 0)
return ret;
if (buflen > st->buf_len) {
buf = kmalloc(buflen, GFP_KERNEL);
if (!buf) {
mutex_unlock(&st->buf_mutex);
return -ENOMEM;
}
info("successfully reallocated a bigger buffer");
kfree(st->buf);
st->buf = buf;
st->buf_len = buflen;
} else {
buf = st->buf;
}
buf[0] = 0x00;
buf[1] = cmd;
memcpy(&buf[2], o, olen);
ret = vp702x_usb_inout_op(d, buf, olen+2, buf, ilen+1, msec);
if (ret == 0)
memcpy(i, &buf[1], ilen);
mutex_unlock(&st->buf_mutex);
return ret;
}
static int vp702x_set_pld_mode(struct dvb_usb_adapter *adap, u8 bypass)
{
int ret;
struct vp702x_device_state *st = adap->dev->priv;
u8 *buf;
mutex_lock(&st->buf_mutex);
buf = st->buf;
memset(buf, 0, 16);
ret = vp702x_usb_in_op(adap->dev, 0xe0, (bypass << 8) | 0x0e,
0, buf, 16);
mutex_unlock(&st->buf_mutex);
return ret;
}
static int vp702x_set_pld_state(struct dvb_usb_adapter *adap, u8 state)
{
int ret;
struct vp702x_device_state *st = adap->dev->priv;
u8 *buf;
mutex_lock(&st->buf_mutex);
buf = st->buf;
memset(buf, 0, 16);
ret = vp702x_usb_in_op(adap->dev, 0xe0, (state << 8) | 0x0f,
0, buf, 16);
mutex_unlock(&st->buf_mutex);
return ret;
}
static int vp702x_set_pid(struct dvb_usb_adapter *adap, u16 pid, u8 id, int onoff)
{
struct vp702x_adapter_state *st = adap->priv;
struct vp702x_device_state *dst = adap->dev->priv;
u8 *buf;
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);
mutex_lock(&dst->buf_mutex);
buf = dst->buf;
memset(buf, 0, 16);
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);
mutex_unlock(&dst->buf_mutex);
return 0;
}
static int vp702x_init_pid_filter(struct dvb_usb_adapter *adap)
{
struct vp702x_adapter_state *st = adap->priv;
struct vp702x_device_state *dst = adap->dev->priv;
int i;
u8 *b;
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);
mutex_lock(&dst->buf_mutex);
b = dst->buf;
memset(b, 0, 10);
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);
mutex_unlock(&dst->buf_mutex);
/*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 rc_map_table rc_map_vp702x_table[] = {
{ 0x0001, KEY_1 },
{ 0x0002, 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;
int i;
/* remove the following return to enabled remote querying */
return 0;
key = kmalloc(10, GFP_KERNEL);
if (!key)
return -ENOMEM;
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;
kfree(key);
return 0;
}
for (i = 0; i < ARRAY_SIZE(rc_map_vp702x_table); i++)
if (rc5_custom(&rc_map_vp702x_table[i]) == key[1]) {
*state = REMOTE_KEY_PRESSED;
*event = rc_map_vp702x_table[i].keycode;
break;
}
kfree(key);
return 0;
}
static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
{
u8 i, *buf;
struct vp702x_device_state *st = d->priv;
mutex_lock(&st->buf_mutex);
buf = st->buf;
for (i = 6; i < 12; i++)
vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1, &buf[i - 6], 1);
memcpy(mac, buf, 6);
mutex_unlock(&st->buf_mutex);
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_adap[0].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)
{
struct dvb_usb_device *d;
struct vp702x_device_state *st;
int ret;
ret = dvb_usb_device_init(intf, &vp702x_properties,
THIS_MODULE, &d, adapter_nr);
if (ret)
goto out;
st = d->priv;
st->buf_len = 16;
st->buf = kmalloc(st->buf_len, GFP_KERNEL);
if (!st->buf) {
ret = -ENOMEM;
dvb_usb_device_exit(intf);
goto out;
}
mutex_init(&st->buf_mutex);
out:
return ret;
}
static void vp702x_usb_disconnect(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
struct vp702x_device_state *st = d->priv;
mutex_lock(&st->buf_mutex);
kfree(st->buf);
mutex_unlock(&st->buf_mutex);
dvb_usb_device_exit(intf);
}
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 = {
{
.num_frontends = 1,
.fe = {{
.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_adapter_state),
}
},
.read_mac_address = vp702x_read_mac_addr,
.rc.legacy = {
.rc_map_table = rc_map_vp702x_table,
.rc_map_size = ARRAY_SIZE(rc_map_vp702x_table),
.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 = vp702x_usb_disconnect,
.id_table = vp702x_usb_table,
};
module_usb_driver(vp702x_usb_driver);
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,113 @@
#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 */
struct vp702x_device_state {
struct mutex buf_mutex;
int buf_len;
u8 *buf;
};
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);
#endif

View file

@ -0,0 +1,190 @@
/* 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 dtv_frontend_properties *fep = &fe->dtv_property_cache;
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->bandwidth_hz) {
case 8000000:
buf[4] = 8;
break;
case 7000000:
buf[4] = 7;
break;
case 6000000:
buf[4] = 6;
break;
default:
return -EINVAL;
}
vp7045_usb_op(state->d,LOCK_TUNER_COMMAND,buf,5,NULL,0,200);
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 = {
.delsys = { SYS_DVBT },
.info = {
.name = "Twinhan VP7045/46 USB DVB-T",
.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_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,
};

View file

@ -0,0 +1,302 @@
/* DVB USB compliant Linux driver for the
* - TwinhanDTV Alpha/MagicBoxII USB2.0 DVB-T receiver
* - DigitalNow TinyUSB2 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"
/* debug */
static int dvb_usb_vp7045_debug;
module_param_named(debug,dvb_usb_vp7045_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args)
#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args)
#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args)
int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec)
{
int ret = 0;
u8 *buf = d->priv;
buf[0] = cmd;
if (outlen > 19)
outlen = 19;
if (inlen > 11)
inlen = 11;
ret = mutex_lock_interruptible(&d->usb_mutex);
if (ret)
return ret;
if (out != NULL && outlen > 0)
memcpy(&buf[1], out, outlen);
deb_xfer("out buffer: ");
debug_dump(buf, outlen+1, deb_xfer);
if (usb_control_msg(d->udev,
usb_sndctrlpipe(d->udev,0),
TH_COMMAND_OUT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0,
buf, 20, 2000) != 20) {
err("USB control message 'out' went wrong.");
ret = -EIO;
goto unlock;
}
msleep(msec);
if (usb_control_msg(d->udev,
usb_rcvctrlpipe(d->udev,0),
TH_COMMAND_IN, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
buf, 12, 2000) != 12) {
err("USB control message 'in' went wrong.");
ret = -EIO;
goto unlock;
}
deb_xfer("in buffer: ");
debug_dump(buf, 12, deb_xfer);
if (in != NULL && inlen > 0)
memcpy(in, &buf[1], inlen);
unlock:
mutex_unlock(&d->usb_mutex);
return ret;
}
u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg)
{
u8 obuf[2] = { 0 },v;
obuf[1] = reg;
vp7045_usb_op(d,TUNER_REG_READ,obuf,2,&v,1,30);
return v;
}
static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff)
{
u8 v = onoff;
return vp7045_usb_op(d,SET_TUNER_POWER,&v,1,NULL,0,150);
}
/* remote control stuff */
/* The keymapping struct. Somehow this should be loaded to the driver, but
* currently it is hardcoded. */
static struct rc_map_table rc_map_vp7045_table[] = {
{ 0x0016, KEY_POWER },
{ 0x0010, KEY_MUTE },
{ 0x0003, KEY_1 },
{ 0x0001, KEY_2 },
{ 0x0006, KEY_3 },
{ 0x0009, KEY_4 },
{ 0x001d, KEY_5 },
{ 0x001f, KEY_6 },
{ 0x000d, KEY_7 },
{ 0x0019, KEY_8 },
{ 0x001b, KEY_9 },
{ 0x0015, KEY_0 },
{ 0x0005, KEY_CHANNELUP },
{ 0x0002, KEY_CHANNELDOWN },
{ 0x001e, KEY_VOLUMEUP },
{ 0x000a, KEY_VOLUMEDOWN },
{ 0x0011, KEY_RECORD },
{ 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */
{ 0x0014, KEY_PLAY },
{ 0x001a, KEY_STOP },
{ 0x0040, KEY_REWIND },
{ 0x0012, KEY_FASTFORWARD },
{ 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */
{ 0x004c, KEY_PAUSE },
{ 0x004d, KEY_SCREEN }, /* Full screen mode. */
{ 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
{ 0x000c, KEY_CANCEL }, /* Cancel */
{ 0x001c, KEY_EPG }, /* EPG */
{ 0x0000, KEY_TAB }, /* Tab */
{ 0x0048, KEY_INFO }, /* Preview */
{ 0x0004, KEY_LIST }, /* RecordList */
{ 0x000f, KEY_TEXT }, /* Teletext */
{ 0x0041, KEY_PREVIOUSSONG },
{ 0x0042, KEY_NEXTSONG },
{ 0x004b, KEY_UP },
{ 0x0051, KEY_DOWN },
{ 0x004e, KEY_LEFT },
{ 0x0052, KEY_RIGHT },
{ 0x004f, KEY_ENTER },
{ 0x0013, KEY_CANCEL },
{ 0x004a, KEY_CLEAR },
{ 0x0054, KEY_PRINT }, /* Capture */
{ 0x0043, KEY_SUBTITLE }, /* Subtitle/CC */
{ 0x0008, KEY_VIDEO }, /* A/V */
{ 0x0007, KEY_SLEEP }, /* Hibernate */
{ 0x0045, KEY_ZOOM }, /* Zoom+ */
{ 0x0018, KEY_RED},
{ 0x0053, KEY_GREEN},
{ 0x005e, KEY_YELLOW},
{ 0x005f, KEY_BLUE}
};
static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
u8 key;
int i;
vp7045_usb_op(d,RC_VAL_READ,NULL,0,&key,1,20);
deb_rc("remote query key: %x %d\n",key,key);
if (key == 0x44) {
*state = REMOTE_NO_KEY_PRESSED;
return 0;
}
for (i = 0; i < ARRAY_SIZE(rc_map_vp7045_table); i++)
if (rc5_data(&rc_map_vp7045_table[i]) == key) {
*state = REMOTE_KEY_PRESSED;
*event = rc_map_vp7045_table[i].keycode;
break;
}
return 0;
}
static int vp7045_read_eeprom(struct dvb_usb_device *d,u8 *buf, int len, int offset)
{
int i = 0;
u8 v,br[2];
for (i=0; i < len; i++) {
v = offset + i;
vp7045_usb_op(d,GET_EE_VALUE,&v,1,br,2,5);
buf[i] = br[1];
}
deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ",offset, i);
debug_dump(buf,i,deb_info);
return 0;
}
static int vp7045_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
{
return vp7045_read_eeprom(d,mac, 6, MAC_0_ADDR);
}
static int vp7045_frontend_attach(struct dvb_usb_adapter *adap)
{
u8 buf[255] = { 0 };
vp7045_usb_op(adap->dev,VENDOR_STRING_READ,NULL,0,buf,20,0);
buf[10] = '\0';
deb_info("firmware says: %s ",buf);
vp7045_usb_op(adap->dev,PRODUCT_STRING_READ,NULL,0,buf,20,0);
buf[10] = '\0';
deb_info("%s ",buf);
vp7045_usb_op(adap->dev,FW_VERSION_READ,NULL,0,buf,20,0);
buf[10] = '\0';
deb_info("v%s\n",buf);
/* Dump the EEPROM */
/* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */
adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev);
return 0;
}
static struct dvb_usb_device_properties vp7045_properties;
static int vp7045_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return dvb_usb_device_init(intf, &vp7045_properties,
THIS_MODULE, NULL, adapter_nr);
}
static struct usb_device_id vp7045_usb_table [] = {
{ USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_COLD) },
{ USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_WARM) },
{ USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_COLD) },
{ USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_WARM) },
{ 0 },
};
MODULE_DEVICE_TABLE(usb, vp7045_usb_table);
static struct dvb_usb_device_properties vp7045_properties = {
.usb_ctrl = CYPRESS_FX2,
.firmware = "dvb-usb-vp7045-01.fw",
.size_of_priv = 20,
.num_adapters = 1,
.adapter = {
{
.num_frontends = 1,
.fe = {{
.frontend_attach = vp7045_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
.count = 7,
.endpoint = 0x02,
.u = {
.bulk = {
.buffersize = 4096,
}
}
},
}},
}
},
.power_ctrl = vp7045_power_ctrl,
.read_mac_address = vp7045_read_mac_addr,
.rc.legacy = {
.rc_interval = 400,
.rc_map_table = rc_map_vp7045_table,
.rc_map_size = ARRAY_SIZE(rc_map_vp7045_table),
.rc_query = vp7045_rc_query,
},
.num_device_descs = 2,
.devices = {
{ .name = "Twinhan USB2.0 DVB-T receiver (TwinhanDTV Alpha/MagicBox II)",
.cold_ids = { &vp7045_usb_table[0], NULL },
.warm_ids = { &vp7045_usb_table[1], NULL },
},
{ .name = "DigitalNow TinyUSB 2 DVB-t Receiver",
.cold_ids = { &vp7045_usb_table[2], NULL },
.warm_ids = { &vp7045_usb_table[3], NULL },
},
{ NULL },
}
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver vp7045_usb_driver = {
.name = "dvb_usb_vp7045",
.probe = vp7045_usb_probe,
.disconnect = dvb_usb_device_exit,
.id_table = vp7045_usb_table,
};
module_usb_driver(vp7045_usb_driver);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
MODULE_DESCRIPTION("Driver for Twinhan MagicBox/Alpha and DNTV tinyUSB2 DVB-T USB2.0");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,70 @@
/* Common header-file of the Linux driver for the 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
*/
#ifndef _DVB_USB_VP7045_H_
#define _DVB_USB_VP7045_H_
#define DVB_USB_LOG_PREFIX "vp7045"
#include "dvb-usb.h"
/* vp7045 commands */
/* Twinhan Vendor requests */
#define TH_COMMAND_IN 0xC0
#define TH_COMMAND_OUT 0xC1
/* command bytes */
#define TUNER_REG_READ 0x03
#define TUNER_REG_WRITE 0x04
#define RC_VAL_READ 0x05
#define RC_NO_KEY 0x44
#define SET_TUNER_POWER 0x06
#define CHECK_TUNER_POWER 0x12
#define Tuner_Power_ON 1
#define Tuner_Power_OFF 0
#define GET_USB_SPEED 0x07
#define LOCK_TUNER_COMMAND 0x09
#define TUNER_SIGNAL_READ 0x0A
/* FX2 eeprom */
#define SET_EE_VALUE 0x10
#define GET_EE_VALUE 0x11
#define FX2_ID_ADDR 0x00
#define VID_MSB_ADDR 0x02
#define VID_LSB_ADDR 0x01
#define PID_MSB_ADDR 0x04
#define PID_LSB_ADDR 0x03
#define MAC_0_ADDR 0x07
#define MAC_1_ADDR 0x08
#define MAC_2_ADDR 0x09
#define MAC_3_ADDR 0x0a
#define MAC_4_ADDR 0x0b
#define MAC_5_ADDR 0x0c
#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 * vp7045_fe_attach(struct dvb_usb_device *d);
extern int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen,int msec);
extern u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg);
#endif