From 986bd1e58b18c09b753f797df19251804bfe3e84 Mon Sep 17 00:00:00 2001 From: Tomi Orava Date: Fri, 19 Sep 2008 00:48:31 -0300 Subject: V4L/DVB (9107): Alternative version of Terratec Cinergy T2 driver Alternative version of the Terratec Cinergy T2 driver that uses the dvb framework. Signed-off-by: Tomi Orava Signed-off-by: Thierry MERLE [mchehab@redhat.com: fix dvb Makefile] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/Kconfig | 1 - drivers/media/dvb/Makefile | 2 +- drivers/media/dvb/cinergyT2/Kconfig | 85 --- drivers/media/dvb/cinergyT2/Makefile | 3 - drivers/media/dvb/cinergyT2/cinergyT2.c | 1105 ---------------------------- drivers/media/dvb/dvb-usb/Kconfig | 8 + drivers/media/dvb/dvb-usb/Makefile | 4 + drivers/media/dvb/dvb-usb/cinergyT2-core.c | 230 ++++++ drivers/media/dvb/dvb-usb/cinergyT2-fe.c | 351 +++++++++ drivers/media/dvb/dvb-usb/cinergyT2.h | 95 +++ 10 files changed, 689 insertions(+), 1195 deletions(-) delete mode 100644 drivers/media/dvb/cinergyT2/Kconfig delete mode 100644 drivers/media/dvb/cinergyT2/Makefile delete mode 100644 drivers/media/dvb/cinergyT2/cinergyT2.c create mode 100644 drivers/media/dvb/dvb-usb/cinergyT2-core.c create mode 100644 drivers/media/dvb/dvb-usb/cinergyT2-fe.c create mode 100644 drivers/media/dvb/dvb-usb/cinergyT2.h diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 14488f64479..0bcd852576d 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -20,7 +20,6 @@ comment "Supported USB Adapters" source "drivers/media/dvb/dvb-usb/Kconfig" source "drivers/media/dvb/ttusb-budget/Kconfig" source "drivers/media/dvb/ttusb-dec/Kconfig" -source "drivers/media/dvb/cinergyT2/Kconfig" source "drivers/media/dvb/siano/Kconfig" comment "Supported FlexCopII (B2C2) Adapters" diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index ea953a03e24..f91e9eb15e5 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -2,4 +2,4 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/ dm1105/ +obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ diff --git a/drivers/media/dvb/cinergyT2/Kconfig b/drivers/media/dvb/cinergyT2/Kconfig deleted file mode 100644 index c03513b2cca..00000000000 --- a/drivers/media/dvb/cinergyT2/Kconfig +++ /dev/null @@ -1,85 +0,0 @@ -config DVB_CINERGYT2 - tristate "Terratec CinergyT2/qanu USB2 DVB-T receiver" - depends on DVB_CORE && USB && INPUT - help - Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers - - Say Y if you own such a device and want to use it. - - -config DVB_CINERGYT2_TUNING - bool "sophisticated fine-tuning for CinergyT2 cards" - depends on DVB_CINERGYT2 - help - Here you can fine-tune some parameters of the CinergyT2 driver. - - Normally you don't need to touch this, but in exotic setups you - may fine-tune your setup and adjust e.g. DMA buffer sizes for - a particular application. - - -config DVB_CINERGYT2_STREAM_URB_COUNT - int "Number of queued USB Request Blocks for Highspeed Stream Transfers" - depends on DVB_CINERGYT2_TUNING - default "32" - help - USB Request Blocks for Highspeed Stream transfers are scheduled in - a queue for the Host Controller. - - Usually the default value is a safe choice. - - You may increase this number if you are using this device in a - Server Environment with many high-traffic USB Highspeed devices - sharing the same USB bus. - - -config DVB_CINERGYT2_STREAM_BUF_SIZE - int "Size of URB Stream Buffers for Highspeed Transfers" - depends on DVB_CINERGYT2_TUNING - default "512" - help - Should be a multiple of native buffer size of 512 bytes. - Default value is a safe choice. - - You may increase this number if you are using this device in a - Server Environment with many high-traffic USB Highspeed devices - sharing the same USB bus. - - -config DVB_CINERGYT2_QUERY_INTERVAL - int "Status update interval [milliseconds]" - depends on DVB_CINERGYT2_TUNING - default "250" - help - This is the interval for status readouts from the demodulator. - You may try lower values if you need more responsive signal quality - measurements. - - Please keep in mind that these updates cause traffic on the tuner - control bus and thus may or may not affect reception sensitivity. - - The default value should be a safe choice for common applications. - - -config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE - bool "Register the onboard IR Remote Control Receiver as Input Device" - depends on DVB_CINERGYT2_TUNING - default y - help - Enable this option if you want to use the onboard Infrared Remote - Control Receiver as Linux-Input device. - - Right now only the keycode table for the default Remote Control - delivered with the device is supported, please see the driver - source code to find out how to add support for other controls. - - -config DVB_CINERGYT2_RC_QUERY_INTERVAL - int "Infrared Remote Controller update interval [milliseconds]" - depends on DVB_CINERGYT2_TUNING && DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE - default "50" - help - If you have a very fast-repeating remote control you can try lower - values, for normal consumer receivers the default value should be - a safe choice. - diff --git a/drivers/media/dvb/cinergyT2/Makefile b/drivers/media/dvb/cinergyT2/Makefile deleted file mode 100644 index d762d8cb0cf..00000000000 --- a/drivers/media/dvb/cinergyT2/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_DVB_CINERGYT2) += cinergyT2.o - -EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c deleted file mode 100644 index a824f3719f8..00000000000 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * TerraTec Cinergy T²/qanu USB2 DVB-T adapter. - * - * Copyright (C) 2004 Daniel Mack and - * Holger Waechtler - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dmxdev.h" -#include "dvb_demux.h" -#include "dvb_net.h" - -#ifdef CONFIG_DVB_CINERGYT2_TUNING - #define STREAM_URB_COUNT (CONFIG_DVB_CINERGYT2_STREAM_URB_COUNT) - #define STREAM_BUF_SIZE (CONFIG_DVB_CINERGYT2_STREAM_BUF_SIZE) - #define QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_QUERY_INTERVAL) - #ifdef CONFIG_DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE - #define RC_QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_RC_QUERY_INTERVAL) - #define ENABLE_RC (1) - #endif -#else - #define STREAM_URB_COUNT (32) - #define STREAM_BUF_SIZE (512) /* bytes */ - #define ENABLE_RC (1) - #define RC_QUERY_INTERVAL (50) /* milliseconds */ - #define QUERY_INTERVAL (333) /* milliseconds */ -#endif - -#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver" - -static int debug; -module_param_named(debug, debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define dprintk(level, args...) \ -do { \ - if ((debug & level)) { \ - printk("%s: %s(): ", KBUILD_MODNAME, \ - __func__); \ - printk(args); } \ -} while (0) - -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 -}; - -struct dvbt_set_parameters_msg { - uint8_t cmd; - __le32 freq; - uint8_t bandwidth; - __le16 tps; - uint8_t flags; -} __attribute__((packed)); - -struct dvbt_get_status_msg { - __le32 freq; - uint8_t bandwidth; - __le16 tps; - uint8_t flags; - __le16 gain; - uint8_t snr; - __le32 viterbi_error_rate; - __le32 rs_error_rate; - __le32 uncorrected_block_count; - uint8_t lock_bits; - uint8_t prev_lock_bits; -} __attribute__((packed)); - -static struct dvb_frontend_info cinergyt2_fe_info = { - .name = DRIVER_NAME, - .type = FE_OFDM, - .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 -}; - -struct cinergyt2 { - struct dvb_demux demux; - struct usb_device *udev; - struct mutex sem; - struct mutex wq_sem; - struct dvb_adapter adapter; - struct dvb_device *fedev; - struct dmxdev dmxdev; - struct dvb_net dvbnet; - - int streaming; - int sleeping; - - struct dvbt_set_parameters_msg param; - struct dvbt_get_status_msg status; - struct delayed_work query_work; - - wait_queue_head_t poll_wq; - int pending_fe_events; - int disconnect_pending; - unsigned int uncorrected_block_count; - atomic_t inuse; - - void *streambuf; - dma_addr_t streambuf_dmahandle; - struct urb *stream_urb [STREAM_URB_COUNT]; - -#ifdef ENABLE_RC - struct input_dev *rc_input_dev; - char phys[64]; - struct delayed_work rc_query_work; - int rc_input_event; - __le32 rc_last_code; - unsigned long last_event_jiffies; -#endif -}; - -enum { - CINERGYT2_RC_EVENT_TYPE_NONE = 0x00, - CINERGYT2_RC_EVENT_TYPE_NEC = 0x01, - CINERGYT2_RC_EVENT_TYPE_RC5 = 0x02 -}; - -struct cinergyt2_rc_event { - char type; - __le32 value; -} __attribute__((packed)); - -static const uint32_t rc_keys[] = { - CINERGYT2_RC_EVENT_TYPE_NEC, 0xfe01eb04, KEY_POWER, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xfd02eb04, KEY_1, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xfc03eb04, KEY_2, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xfb04eb04, KEY_3, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xfa05eb04, KEY_4, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf906eb04, KEY_5, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf807eb04, KEY_6, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf708eb04, KEY_7, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf609eb04, KEY_8, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf50aeb04, KEY_9, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf30ceb04, KEY_0, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf40beb04, KEY_VIDEO, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf20deb04, KEY_REFRESH, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf10eeb04, KEY_SELECT, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xf00feb04, KEY_EPG, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xef10eb04, KEY_UP, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xeb14eb04, KEY_DOWN, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xee11eb04, KEY_LEFT, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xec13eb04, KEY_RIGHT, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xed12eb04, KEY_OK, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xea15eb04, KEY_TEXT, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe916eb04, KEY_INFO, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe817eb04, KEY_RED, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe718eb04, KEY_GREEN, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe619eb04, KEY_YELLOW, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe51aeb04, KEY_BLUE, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe31ceb04, KEY_VOLUMEUP, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe11eeb04, KEY_VOLUMEDOWN, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe21deb04, KEY_MUTE, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe41beb04, KEY_CHANNELUP, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xe01feb04, KEY_CHANNELDOWN, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xbf40eb04, KEY_PAUSE, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xb34ceb04, KEY_PLAY, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xa758eb04, KEY_RECORD, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xab54eb04, KEY_PREVIOUS, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xb748eb04, KEY_STOP, - CINERGYT2_RC_EVENT_TYPE_NEC, 0xa35ceb04, KEY_NEXT -}; - -static int cinergyt2_command (struct cinergyt2 *cinergyt2, - char *send_buf, int send_buf_len, - char *recv_buf, int recv_buf_len) -{ - int actual_len; - char dummy; - int ret; - - ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1), - send_buf, send_buf_len, &actual_len, 1000); - - if (ret) - dprintk(1, "usb_bulk_msg (send) failed, err %i\n", ret); - - if (!recv_buf) - recv_buf = &dummy; - - ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1), - recv_buf, recv_buf_len, &actual_len, 1000); - - if (ret) - dprintk(1, "usb_bulk_msg (read) failed, err %i\n", ret); - - return ret ? ret : actual_len; -} - -static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable) -{ - char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 }; - cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); -} - -static void cinergyt2_sleep (struct cinergyt2 *cinergyt2, int sleep) -{ - char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 }; - cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0); - cinergyt2->sleeping = sleep; -} - -static void cinergyt2_stream_irq (struct urb *urb); - -static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb *urb) -{ - int err; - - usb_fill_bulk_urb(urb, - cinergyt2->udev, - usb_rcvbulkpipe(cinergyt2->udev, 0x2), - urb->transfer_buffer, - STREAM_BUF_SIZE, - cinergyt2_stream_irq, - cinergyt2); - - if ((err = usb_submit_urb(urb, GFP_ATOMIC))) - dprintk(1, "urb submission failed (err = %i)!\n", err); - - return err; -} - -static void cinergyt2_stream_irq (struct urb *urb) -{ - struct cinergyt2 *cinergyt2 = urb->context; - - if (urb->actual_length > 0) - dvb_dmx_swfilter(&cinergyt2->demux, - urb->transfer_buffer, urb->actual_length); - - if (cinergyt2->streaming) - cinergyt2_submit_stream_urb(cinergyt2, urb); -} - -static void cinergyt2_free_stream_urbs (struct cinergyt2 *cinergyt2) -{ - int i; - - for (i=0; istream_urb[i]); - - usb_buffer_free(cinergyt2->udev, STREAM_URB_COUNT*STREAM_BUF_SIZE, - cinergyt2->streambuf, cinergyt2->streambuf_dmahandle); -} - -static int cinergyt2_alloc_stream_urbs (struct cinergyt2 *cinergyt2) -{ - int i; - - cinergyt2->streambuf = usb_buffer_alloc(cinergyt2->udev, STREAM_URB_COUNT*STREAM_BUF_SIZE, - GFP_KERNEL, &cinergyt2->streambuf_dmahandle); - if (!cinergyt2->streambuf) { - dprintk(1, "failed to alloc consistent stream memory area, bailing out!\n"); - return -ENOMEM; - } - - memset(cinergyt2->streambuf, 0, STREAM_URB_COUNT*STREAM_BUF_SIZE); - - for (i=0; itransfer_buffer = cinergyt2->streambuf + i * STREAM_BUF_SIZE; - urb->transfer_buffer_length = STREAM_BUF_SIZE; - - cinergyt2->stream_urb[i] = urb; - } - - return 0; -} - -static void cinergyt2_stop_stream_xfer (struct cinergyt2 *cinergyt2) -{ - int i; - - cinergyt2_control_stream_transfer(cinergyt2, 0); - - for (i=0; istream_urb[i]); -} - -static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2) -{ - int i, err; - - for (i=0; istream_urb[i]))) { - cinergyt2_stop_stream_xfer(cinergyt2); - dprintk(1, "failed urb submission (%i: err = %i)!\n", i, err); - return err; - } - } - - cinergyt2_control_stream_transfer(cinergyt2, 1); - return 0; -} - -static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *demux = dvbdmxfeed->demux; - struct cinergyt2 *cinergyt2 = demux->priv; - - if (cinergyt2->disconnect_pending) - return -EAGAIN; - if (mutex_lock_interruptible(&cinergyt2->sem)) - return -ERESTARTSYS; - - if (cinergyt2->streaming == 0) - cinergyt2_start_stream_xfer(cinergyt2); - - cinergyt2->streaming++; - mutex_unlock(&cinergyt2->sem); - return 0; -} - -static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *demux = dvbdmxfeed->demux; - struct cinergyt2 *cinergyt2 = demux->priv; - - if (cinergyt2->disconnect_pending) - return -EAGAIN; - if (mutex_lock_interruptible(&cinergyt2->sem)) - return -ERESTARTSYS; - - if (--cinergyt2->streaming == 0) - cinergyt2_stop_stream_xfer(cinergyt2); - - mutex_unlock(&cinergyt2->sem); - return 0; -} - -/** - * 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 dvb_frontend_parameters *p) -{ - struct dvb_ofdm_parameters *op = &p->u.ofdm; - 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->constellation) { - 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_information) { - 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; -} - -static int cinergyt2_open (struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct cinergyt2 *cinergyt2 = dvbdev->priv; - int err = -EAGAIN; - - if (cinergyt2->disconnect_pending) - goto out; - err = mutex_lock_interruptible(&cinergyt2->wq_sem); - if (err) - goto out; - - err = mutex_lock_interruptible(&cinergyt2->sem); - if (err) - goto out_unlock1; - - if ((err = dvb_generic_open(inode, file))) - goto out_unlock2; - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - cinergyt2_sleep(cinergyt2, 0); - schedule_delayed_work(&cinergyt2->query_work, HZ/2); - } - - atomic_inc(&cinergyt2->inuse); - -out_unlock2: - mutex_unlock(&cinergyt2->sem); -out_unlock1: - mutex_unlock(&cinergyt2->wq_sem); -out: - return err; -} - -static void cinergyt2_unregister(struct cinergyt2 *cinergyt2) -{ - dvb_net_release(&cinergyt2->dvbnet); - dvb_dmxdev_release(&cinergyt2->dmxdev); - dvb_dmx_release(&cinergyt2->demux); - dvb_unregister_device(cinergyt2->fedev); - dvb_unregister_adapter(&cinergyt2->adapter); - - cinergyt2_free_stream_urbs(cinergyt2); - kfree(cinergyt2); -} - -static int cinergyt2_release (struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct cinergyt2 *cinergyt2 = dvbdev->priv; - - mutex_lock(&cinergyt2->wq_sem); - - if (!cinergyt2->disconnect_pending && (file->f_flags & O_ACCMODE) != O_RDONLY) { - cancel_rearming_delayed_work(&cinergyt2->query_work); - - mutex_lock(&cinergyt2->sem); - cinergyt2_sleep(cinergyt2, 1); - mutex_unlock(&cinergyt2->sem); - } - - mutex_unlock(&cinergyt2->wq_sem); - - if (atomic_dec_and_test(&cinergyt2->inuse) && cinergyt2->disconnect_pending) { - warn("delayed unregister in release"); - cinergyt2_unregister(cinergyt2); - } - - return dvb_generic_release(inode, file); -} - -static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct *wait) -{ - struct dvb_device *dvbdev = file->private_data; - struct cinergyt2 *cinergyt2 = dvbdev->priv; - unsigned int mask = 0; - - if (cinergyt2->disconnect_pending) - return -EAGAIN; - if (mutex_lock_interruptible(&cinergyt2->sem)) - return -ERESTARTSYS; - - poll_wait(file, &cinergyt2->poll_wq, wait); - - if (cinergyt2->pending_fe_events != 0) - mask |= (POLLIN | POLLRDNORM | POLLPRI); - - mutex_unlock(&cinergyt2->sem); - - return mask; -} - - -static int cinergyt2_ioctl (struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) -{ - struct dvb_device *dvbdev = file->private_data; - struct cinergyt2 *cinergyt2 = dvbdev->priv; - struct dvbt_get_status_msg *stat = &cinergyt2->status; - fe_status_t status = 0; - - switch (cmd) { - case FE_GET_INFO: - return copy_to_user((void __user*) arg, &cinergyt2_fe_info, - sizeof(struct dvb_frontend_info)); - - case FE_READ_STATUS: - if (0xffff - le16_to_cpu(stat->gain) > 30) - status |= FE_HAS_SIGNAL; - if (stat->lock_bits & (1 << 6)) - status |= FE_HAS_LOCK; - if (stat->lock_bits & (1 << 5)) - status |= FE_HAS_SYNC; - if (stat->lock_bits & (1 << 4)) - status |= FE_HAS_CARRIER; - if (stat->lock_bits & (1 << 1)) - status |= FE_HAS_VITERBI; - - return copy_to_user((void __user*) arg, &status, sizeof(status)); - - case FE_READ_BER: - return put_user(le32_to_cpu(stat->viterbi_error_rate), - (__u32 __user *) arg); - - case FE_READ_SIGNAL_STRENGTH: - return put_user(0xffff - le16_to_cpu(stat->gain), - (__u16 __user *) arg); - - case FE_READ_SNR: - return put_user((stat->snr << 8) | stat->snr, - (__u16 __user *) arg); - - case FE_READ_UNCORRECTED_BLOCKS: - { - uint32_t unc_count; - - if (mutex_lock_interruptible(&cinergyt2->sem)) - return -ERESTARTSYS; - unc_count = cinergyt2->uncorrected_block_count; - cinergyt2->uncorrected_block_count = 0; - mutex_unlock(&cinergyt2->sem); - - /* UNC are already converted to host byte order... */ - return put_user(unc_count,(__u32 __user *) arg); - } - case FE_SET_FRONTEND: - { - struct dvbt_set_parameters_msg *param = &cinergyt2->param; - struct dvb_frontend_parameters p; - int err; - - if ((file->f_flags & O_ACCMODE) == O_RDONLY) - return -EPERM; - - if (copy_from_user(&p, (void __user*) arg, sizeof(p))) - return -EFAULT; - - if (cinergyt2->disconnect_pending) - return -EAGAIN; - if (mutex_lock_interruptible(&cinergyt2->sem)) - return -ERESTARTSYS; - - param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; - param->tps = cpu_to_le16(compute_tps(&p)); - param->freq = cpu_to_le32(p.frequency / 1000); - param->bandwidth = 8 - p.u.ofdm.bandwidth - BANDWIDTH_8_MHZ; - - stat->lock_bits = 0; - cinergyt2->pending_fe_events++; - wake_up_interruptible(&cinergyt2->poll_wq); - - err = cinergyt2_command(cinergyt2, - (char *) param, sizeof(*param), - NULL, 0); - - mutex_unlock(&cinergyt2->sem); - - return (err < 0) ? err : 0; - } - - case FE_GET_FRONTEND: - /** - * trivial to implement (see struct dvbt_get_status_msg). - * equivalent to FE_READ ioctls, but needs - * TPS -> linux-dvb parameter set conversion. Feel free - * to implement this and send us a patch if you need this - * functionality. - */ - break; - - case FE_GET_EVENT: - { - /** - * for now we only fill the status field. the parameters - * are trivial to fill as soon FE_GET_FRONTEND is done. - */ - struct dvb_frontend_event __user *e = (void __user *) arg; - if (cinergyt2->pending_fe_events == 0) { - if (file->f_flags & O_NONBLOCK) - return -EWOULDBLOCK; - wait_event_interruptible(cinergyt2->poll_wq, - cinergyt2->pending_fe_events > 0); - } - cinergyt2->pending_fe_events = 0; - return cinergyt2_ioctl(inode, file, FE_READ_STATUS, - (unsigned long) &e->status); - } - - default: - ; - } - - return -EINVAL; -} - -static int cinergyt2_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct dvb_device *dvbdev = file->private_data; - struct cinergyt2 *cinergyt2 = dvbdev->priv; - int ret = 0; - - lock_kernel(); - - if (vma->vm_flags & (VM_WRITE | VM_EXEC)) { - ret = -EPERM; - goto bailout; - } - - if (vma->vm_end > vma->vm_start + STREAM_URB_COUNT * STREAM_BUF_SIZE) { - ret = -EINVAL; - goto bailout; - } - - vma->vm_flags |= (VM_IO | VM_DONTCOPY); - vma->vm_file = file; - - ret = remap_pfn_range(vma, vma->vm_start, - virt_to_phys(cinergyt2->streambuf) >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot) ? -EAGAIN : 0; -bailout: - unlock_kernel(); - return ret; -} - -static struct file_operations cinergyt2_fops = { - .owner = THIS_MODULE, - .ioctl = cinergyt2_ioctl, - .poll = cinergyt2_poll, - .open = cinergyt2_open, - .release = cinergyt2_release, - .mmap = cinergyt2_mmap -}; - -static struct dvb_device cinergyt2_fe_template = { - .users = ~0, - .writers = 1, - .readers = (~0)-1, - .fops = &cinergyt2_fops -}; - -#ifdef ENABLE_RC - -static void cinergyt2_query_rc (struct work_struct *work) -{ - struct cinergyt2 *cinergyt2 = - container_of(work, struct cinergyt2, rc_query_work.work); - char buf[1] = { CINERGYT2_EP1_GET_RC_EVENTS }; - struct cinergyt2_rc_event rc_events[12]; - int n, len, i; - - if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) - return; - - len = cinergyt2_command(cinergyt2, buf, sizeof(buf), - (char *) rc_events, sizeof(rc_events)); - if (len < 0) - goto out; - if (len == 0) { - if (time_after(jiffies, cinergyt2->last_event_jiffies + - msecs_to_jiffies(150))) { - /* stop key repeat */ - if (cinergyt2->rc_input_event != KEY_MAX) { - dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event); - input_report_key(cinergyt2->rc_input_dev, - cinergyt2->rc_input_event, 0); - input_sync(cinergyt2->rc_input_dev); - cinergyt2->rc_input_event = KEY_MAX; - } - cinergyt2->rc_last_code = cpu_to_le32(~0); - } - goto out; - } - cinergyt2->last_event_jiffies = jiffies; - - for (n = 0; n < (len / sizeof(rc_events[0])); n++) { - dprintk(1, "rc_events[%d].value = %x, type=%x\n", - n, le32_to_cpu(rc_events[n].value), rc_events[n].type); - - if (rc_events[n].type == CINERGYT2_RC_EVENT_TYPE_NEC && - rc_events[n].value == cpu_to_le32(~0)) { - /* keyrepeat bit -> just repeat last rc_input_event */ - } else { - cinergyt2->rc_input_event = KEY_MAX; - for (i = 0; i < ARRAY_SIZE(rc_keys); i += 3) { - if (rc_keys[i + 0] == rc_events[n].type && - rc_keys[i + 1] == le32_to_cpu(rc_events[n].value)) { - cinergyt2->rc_input_event = rc_keys[i + 2]; - break; - } - } - } - - if (cinergyt2->rc_input_event != KEY_MAX) { - if (rc_events[n].value == cinergyt2->rc_last_code && - cinergyt2->rc_last_code != cpu_to_le32(~0)) { - /* emit a key-up so the double event is recognized */ - dprintk(1, "rc_input_event=%d UP\n", cinergyt2->rc_input_event); - input_report_key(cinergyt2->rc_input_dev, - cinergyt2->rc_input_event, 0); - } - dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event); - input_report_key(cinergyt2->rc_input_dev, - cinergyt2->rc_input_event, 1); - input_sync(cinergyt2->rc_input_dev); - cinergyt2->rc_last_code = rc_events[n].value; - } - } - -out: - schedule_delayed_work(&cinergyt2->rc_query_work, - msecs_to_jiffies(RC_QUERY_INTERVAL)); - - mutex_unlock(&cinergyt2->sem); -} - -static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) -{ - struct input_dev *input_dev; - int i; - int err; - - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; - - usb_make_path(cinergyt2->udev, cinergyt2->phys, sizeof(cinergyt2->phys)); - strlcat(cinergyt2->phys, "/input0", sizeof(cinergyt2->phys)); - cinergyt2->rc_input_event = KEY_MAX; - cinergyt2->rc_last_code = cpu_to_le32(~0); - INIT_DELAYED_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc); - - input_dev->name = DRIVER_NAME " remote control"; - input_dev->phys = cinergyt2->phys; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - for (i = 0; i < ARRAY_SIZE(rc_keys); i += 3) - set_bit(rc_keys[i + 2], input_dev->keybit); - input_dev->keycodesize = 0; - input_dev->keycodemax = 0; - input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = le16_to_cpu(cinergyt2->udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(cinergyt2->udev->descriptor.idProduct); - input_dev->id.version = 1; - input_dev->dev.parent = &cinergyt2->udev->dev; - - err = input_register_device(input_dev); - if (err) { - input_free_device(input_dev); - return err; - } - - cinergyt2->rc_input_dev = input_dev; - schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2); - - return 0; -} - -static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2) -{ - cancel_rearming_delayed_work(&cinergyt2->rc_query_work); - input_unregister_device(cinergyt2->rc_input_dev); -} - -static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2) -{ - cancel_rearming_delayed_work(&cinergyt2->rc_query_work); -} - -static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2) -{ - schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2); -} - -#else - -static inline int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) { return 0; } -static inline void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2) { } -static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2) { } -static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2) { } - -#endif /* ENABLE_RC */ - -static void cinergyt2_query (struct work_struct *work) -{ - struct cinergyt2 *cinergyt2 = - container_of(work, struct cinergyt2, query_work.work); - char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS }; - struct dvbt_get_status_msg *s = &cinergyt2->status; - uint8_t lock_bits; - - if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) - return; - - lock_bits = s->lock_bits; - - cinergyt2_command(cinergyt2, cmd, sizeof(cmd), (char *) s, sizeof(*s)); - - cinergyt2->uncorrected_block_count += - le32_to_cpu(s->uncorrected_block_count); - - if (lock_bits != s->lock_bits) { - wake_up_interruptible(&cinergyt2->poll_wq); - cinergyt2->pending_fe_events++; - } - - schedule_delayed_work(&cinergyt2->query_work, - msecs_to_jiffies(QUERY_INTERVAL)); - - mutex_unlock(&cinergyt2->sem); -} - -static int cinergyt2_probe (struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct cinergyt2 *cinergyt2; - int err; - - if (!(cinergyt2 = kzalloc (sizeof(struct cinergyt2), GFP_KERNEL))) { - dprintk(1, "out of memory?!?\n"); - return -ENOMEM; - } - - usb_set_intfdata (intf, (void *) cinergyt2); - - mutex_init(&cinergyt2->sem); - mutex_init(&cinergyt2->wq_sem); - init_waitqueue_head (&cinergyt2->poll_wq); - INIT_DELAYED_WORK(&cinergyt2->query_work, cinergyt2_query); - - cinergyt2->udev = interface_to_usbdev(intf); - cinergyt2->param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; - - if (cinergyt2_alloc_stream_urbs (cinergyt2) < 0) { - dprintk(1, "unable to allocate stream urbs\n"); - kfree(cinergyt2); - return -ENOMEM; - } - - err = dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, - THIS_MODULE, &cinergyt2->udev->dev, - adapter_nr); - if (err < 0) { - kfree(cinergyt2); - return err; - } - - cinergyt2->demux.priv = cinergyt2; - cinergyt2->demux.filternum = 256; - cinergyt2->demux.feednum = 256; - cinergyt2->demux.start_feed = cinergyt2_start_feed; - cinergyt2->demux.stop_feed = cinergyt2_stop_feed; - cinergyt2->demux.dmx.capabilities = DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - - if ((err = dvb_dmx_init(&cinergyt2->demux)) < 0) { - dprintk(1, "dvb_dmx_init() failed (err = %d)\n", err); - goto bailout; - } - - cinergyt2->dmxdev.filternum = cinergyt2->demux.filternum; - cinergyt2->dmxdev.demux = &cinergyt2->demux.dmx; - cinergyt2->dmxdev.capabilities = 0; - - if ((err = dvb_dmxdev_init(&cinergyt2->dmxdev, &cinergyt2->adapter)) < 0) { - dprintk(1, "dvb_dmxdev_init() failed (err = %d)\n", err); - goto bailout; - } - - if (dvb_net_init(&cinergyt2->adapter, &cinergyt2->dvbnet, &cinergyt2->demux.dmx)) - dprintk(1, "dvb_net_init() failed!\n"); - - dvb_register_device(&cinergyt2->adapter, &cinergyt2->fedev, - &cinergyt2_fe_template, cinergyt2, - DVB_DEVICE_FRONTEND); - - err = cinergyt2_register_rc(cinergyt2); - if (err) - goto bailout; - - return 0; - -bailout: - dvb_net_release(&cinergyt2->dvbnet); - dvb_dmxdev_release(&cinergyt2->dmxdev); - dvb_dmx_release(&cinergyt2->demux); - dvb_unregister_adapter(&cinergyt2->adapter); - cinergyt2_free_stream_urbs(cinergyt2); - kfree(cinergyt2); - return -ENOMEM; -} - -static void cinergyt2_disconnect (struct usb_interface *intf) -{ - struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); - - cinergyt2_unregister_rc(cinergyt2); - cancel_rearming_delayed_work(&cinergyt2->query_work); - wake_up_interruptible(&cinergyt2->poll_wq); - - cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx); - cinergyt2->disconnect_pending = 1; - - if (!atomic_read(&cinergyt2->inuse)) - cinergyt2_unregister(cinergyt2); -} - -static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state) -{ - struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); - - if (cinergyt2->disconnect_pending) - return -EAGAIN; - if (mutex_lock_interruptible(&cinergyt2->wq_sem)) - return -ERESTARTSYS; - - cinergyt2_suspend_rc(cinergyt2); - cancel_rearming_delayed_work(&cinergyt2->query_work); - - mutex_lock(&cinergyt2->sem); - if (cinergyt2->streaming) - cinergyt2_stop_stream_xfer(cinergyt2); - cinergyt2_sleep(cinergyt2, 1); - mutex_unlock(&cinergyt2->sem); - - mutex_unlock(&cinergyt2->wq_sem); - - return 0; -} - -static int cinergyt2_resume (struct usb_interface *intf) -{ - struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); - struct dvbt_set_parameters_msg *param = &cinergyt2->param; - int err = -EAGAIN; - - if (cinergyt2->disconnect_pending) - goto out; - err = mutex_lock_interruptible(&cinergyt2->wq_sem); - if (err) - goto out; - - err = mutex_lock_interruptible(&cinergyt2->sem); - if (err) - goto out_unlock1; - - if (!cinergyt2->sleeping) { - cinergyt2_sleep(cinergyt2, 0); - cinergyt2_command(cinergyt2, (char *) param, sizeof(*param), NULL, 0); - if (cinergyt2->streaming) - cinergyt2_start_stream_xfer(cinergyt2); - schedule_delayed_work(&cinergyt2->query_work, HZ/2); - } - - cinergyt2_resume_rc(cinergyt2); - - mutex_unlock(&cinergyt2->sem); -out_unlock1: - mutex_unlock(&cinergyt2->wq_sem); -out: - return err; -} - -static const struct usb_device_id cinergyt2_table [] __devinitdata = { - { USB_DEVICE(0x0ccd, 0x0038) }, - { 0 } -}; - -MODULE_DEVICE_TABLE(usb, cinergyt2_table); - -static struct usb_driver cinergyt2_driver = { - .name = "cinergyT2", - .probe = cinergyt2_probe, - .disconnect = cinergyt2_disconnect, - .suspend = cinergyt2_suspend, - .resume = cinergyt2_resume, - .id_table = cinergyt2_table -}; - -static int __init cinergyt2_init (void) -{ - int err; - - if ((err = usb_register(&cinergyt2_driver)) < 0) - dprintk(1, "usb_register() failed! (err %i)\n", err); - - return err; -} - -static void __exit cinergyt2_exit (void) -{ - usb_deregister(&cinergyt2_driver); -} - -module_init (cinergyt2_init); -module_exit (cinergyt2_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Holger Waechtler, Daniel Mack"); diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 241c9ab54b1..57bb470bd06 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -259,6 +259,14 @@ config DVB_USB_DW2102 Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers and the TeVii S650. +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_ANYSEE tristate "Anysee DVB-T/C USB2.0 support" depends on DVB_USB diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index ece8c9dfed1..3122b7cc2c2 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -73,6 +73,10 @@ obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o dvb-usb-af9015-objs = af9015.o obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o +dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o +obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o + + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c new file mode 100644 index 00000000000..25863033c48 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c @@ -0,0 +1,230 @@ +/* + * 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 and + * Holger Waechtler + * + * 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; +int disable_remote; + +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); + + +/* We are missing a release hook with usb_device data */ +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 = 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 dvb_usb_rc_key cinergyt2_rc_keys[] = { + { 0x04, 0x01, KEY_POWER }, + { 0x04, 0x02, KEY_1 }, + { 0x04, 0x03, KEY_2 }, + { 0x04, 0x04, KEY_3 }, + { 0x04, 0x05, KEY_4 }, + { 0x04, 0x06, KEY_5 }, + { 0x04, 0x07, KEY_6 }, + { 0x04, 0x08, KEY_7 }, + { 0x04, 0x09, KEY_8 }, + { 0x04, 0x0a, KEY_9 }, + { 0x04, 0x0c, KEY_0 }, + { 0x04, 0x0b, KEY_VIDEO }, + { 0x04, 0x0d, KEY_REFRESH }, + { 0x04, 0x0e, KEY_SELECT }, + { 0x04, 0x0f, KEY_EPG }, + { 0x04, 0x10, KEY_UP }, + { 0x04, 0x14, KEY_DOWN }, + { 0x04, 0x11, KEY_LEFT }, + { 0x04, 0x13, KEY_RIGHT }, + { 0x04, 0x12, KEY_OK }, + { 0x04, 0x15, KEY_TEXT }, + { 0x04, 0x16, KEY_INFO }, + { 0x04, 0x17, KEY_RED }, + { 0x04, 0x18, KEY_GREEN }, + { 0x04, 0x19, KEY_YELLOW }, + { 0x04, 0x1a, KEY_BLUE }, + { 0x04, 0x1c, KEY_VOLUMEUP }, + { 0x04, 0x1e, KEY_VOLUMEDOWN }, + { 0x04, 0x1d, KEY_MUTE }, + { 0x04, 0x1b, KEY_CHANNELUP }, + { 0x04, 0x1f, KEY_CHANNELDOWN }, + { 0x04, 0x40, KEY_PAUSE }, + { 0x04, 0x4c, KEY_PLAY }, + { 0x04, 0x58, KEY_RECORD }, + { 0x04, 0x54, KEY_PREVIOUS }, + { 0x04, 0x48, KEY_STOP }, + { 0x04, 0x5c, KEY_NEXT } +}; + +static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 key[5] = {0, 0, 0, 0, 0}, cmd = CINERGYT2_EP1_GET_RC_EVENTS; + *state = REMOTE_NO_KEY_PRESSED; + + dvb_usb_generic_rw(d, &cmd, 1, key, sizeof(key), 0); + if (key[4] == 0xff) + return 0; + + /* hack to pass checksum on the custom field (is set to 0xeb) */ + key[2] = ~0x04; + dvb_usb_nec_rc_key_to_event(d, key, event, state); + if (key[0] != 0) + deb_info("key: %x %x %x %x %x\n", + key[0], key[1], key[2], key[3], key[4]); + + return 0; +} + +static int 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 = { + + .num_adapters = 1, + .adapter = { + { + .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_interval = 50, + .rc_key_map = cinergyt2_rc_keys, + .rc_key_map_size = ARRAY_SIZE(cinergyt2_rc_keys), + .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 +}; + +static int __init cinergyt2_usb_init(void) +{ + int err; + + err = usb_register(&cinergyt2_driver); + if (err) { + err("usb_register() failed! (err %i)\n", err); + return err; + } + return 0; +} + +static void __exit cinergyt2_usb_exit(void) +{ + usb_deregister(&cinergyt2_driver); +} + +module_init(cinergyt2_usb_init); +module_exit(cinergyt2_usb_exit); + +MODULE_DESCRIPTION("Terratec Cinergy T2 DVB-T driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomi Orava"); diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c new file mode 100644 index 00000000000..649f25cca49 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c @@ -0,0 +1,351 @@ +/* + * 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 and + * Holger Waechtler + * + * 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 dvb_frontend_parameters *p) +{ + struct dvb_ofdm_parameters *op = &p->u.ofdm; + 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->constellation) { + 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_information) { + 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 dvb_frontend_parameters *fep) +{ + 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.bandwidth = 8 - fep->u.ofdm.bandwidth - BANDWIDTH_8_MHZ; + + err = dvb_usb_generic_rw(state->d, + (char *)¶m, 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 int cinergyt2_fe_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + return 0; +} + +static void cinergyt2_fe_release(struct dvb_frontend *fe) +{ + struct cinergyt2_fe_state *state = fe->demodulator_priv; + if (state != NULL) + 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 = { + .info = { + .name = DRIVER_NAME, + .type = FE_OFDM, + .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_frontend = cinergyt2_fe_get_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, +}; diff --git a/drivers/media/dvb/dvb-usb/cinergyT2.h b/drivers/media/dvb/dvb-usb/cinergyT2.h new file mode 100644 index 00000000000..11d79eb384c --- /dev/null +++ b/drivers/media/dvb/dvb-usb/cinergyT2.h @@ -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 and + * Holger Waechtler + * + * 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 + +#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; + uint16_t gain; + uint8_t snr; + uint32_t viterbi_error_rate; + uint32_t rs_error_rate; + uint32_t uncorrected_block_count; + uint8_t lock_bits; + uint8_t prev_lock_bits; +} __attribute__((packed)); + + +struct dvbt_set_parameters_msg { + uint8_t cmd; + uint32_t freq; + uint8_t bandwidth; + uint16_t tps; + uint8_t flags; +} __attribute__((packed)); + + +extern struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d); + +#endif /* _DVB_USB_CINERGYT2_H_ */ + -- cgit v1.2.3