[PATCH] dvb: DST: reorganize Twinhan DST driver to support CI

- reorganize Twinhan DST driver to support CI
- add support for more cards
(Manu Abraham)

Signed-off-by: Johannes Stezenbach <js@linuxtv.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Johannes Stezenbach 2005-05-16 21:54:41 -07:00 committed by Linus Torvalds
parent daeb6aa40b
commit 50b215a058
12 changed files with 1917 additions and 372 deletions

219
Documentation/dvb/ci.txt Normal file
View File

@ -0,0 +1,219 @@
* For the user
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTE: This document describes the usage of the high level CI API as
in accordance to the Linux DVB API. This is a not a documentation for the,
existing low level CI API.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To utilize the High Level CI capabilities,
(1*) This point is valid only for the Twinhan/clones
For the Twinhan/Twinhan clones, the dst_ca module handles the CI
hardware handling.This module is loaded automatically if a CI
(Common Interface, that holds the CAM (Conditional Access Module)
is detected.
(2) one requires a userspace application, ca_zap. This small userland
application is in charge of sending the descrambling related information
to the CAM.
This application requires the following to function properly as of now.
(a) Tune to a valid channel, with szap.
eg: $ szap -c channels.conf -r "TMC" -x
(b) a channels.conf containing a valid PMT PID
eg: TMC:11996:h:0:27500:278:512:650:321
here 278 is a valid PMT PID. the rest of the values are the
same ones that szap uses.
(c) after running a szap, you have to run ca_zap, for the
descrambler to function,
eg: $ ca_zap patched_channels.conf "TMC"
The patched means a patch to apply to scan, such that scan can
generate a channels.conf_with pmt, which has this PMT PID info
(NOTE: szap cannot use this channels.conf with the PMT_PID)
(d) Hopeflly Enjoy your favourite subscribed channel as you do with
a FTA card.
(3) Currently ca_zap, and dst_test, both are meant for demonstration
purposes only, they can become full fledged applications if necessary.
* Cards that fall in this category
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At present the cards that fall in this category are the Twinhan and it's
clones, these cards are available as VVMER, Tomato, Hercules, Orange and
so on.
* CI modules that are supported
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The CI module support is largely dependant upon the firmware on the cards
Some cards do support almost all of the available CI modules. There is
nothing much that can be done in order to make additional CI modules
working with these cards.
Modules that have been tested by this driver at present are
(1) Irdeto 1 and 2 from SCM
(2) Viaccess from SCM
(3) Dragoncam
* The High level CI API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* For the programmer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
With the High Level CI approach any new card with almost any random
architecture can be implemented with this style, the definitions
insidethe switch statement can be easily adapted for any card, thereby
eliminating the need for any additional ioctls.
The disadvantage is that the driver/hardware has to manage the rest. For
the application programmer it would be as simple as sending/receiving an
array to/from the CI ioctls as defined in the Linux DVB API. No changes
have been made in the API to accomodate this feature.
* Why the need for another CI interface ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is one of the most commonly asked question. Well a nice question.
Strictly speaking this is not a new interface.
The CI interface is defined in the DVB API in ca.h as
typedef struct ca_slot_info {
int num; /* slot number */
int type; /* CA interface this slot supports */
#define CA_CI 1 /* CI high level interface */
#define CA_CI_LINK 2 /* CI link layer level interface */
#define CA_CI_PHYS 4 /* CI physical layer level interface */
#define CA_DESCR 8 /* built-in descrambler */
#define CA_SC 128 /* simple smart card interface */
unsigned int flags;
#define CA_CI_MODULE_PRESENT 1 /* module (or card) inserted */
#define CA_CI_MODULE_READY 2
} ca_slot_info_t;
This CI interface follows the CI high level interface, which is not
implemented by most applications. Hence this area is revisited.
This CI interface is quite different in the case that it tries to
accomodate all other CI based devices, that fall into the other categories
This means that this CI interface handles the EN50221 style tags in the
Application layer only and no session management is taken care of by the
application. The driver/hardware will take care of all that.
This interface is purely an EN50221 interface exchanging APDU's. This
means that no session management, link layer or a transport layer do
exist in this case in the application to driver communication. It is
as simple as that. The driver/hardware has to take care of that.
With this High Level CI interface, the interface can be defined with the
regular ioctls.
All these ioctls are also valid for the High level CI interface
#define CA_RESET _IO('o', 128)
#define CA_GET_CAP _IOR('o', 129, ca_caps_t)
#define CA_GET_SLOT_INFO _IOR('o', 130, ca_slot_info_t)
#define CA_GET_DESCR_INFO _IOR('o', 131, ca_descr_info_t)
#define CA_GET_MSG _IOR('o', 132, ca_msg_t)
#define CA_SEND_MSG _IOW('o', 133, ca_msg_t)
#define CA_SET_DESCR _IOW('o', 134, ca_descr_t)
#define CA_SET_PID _IOW('o', 135, ca_pid_t)
On querying the device, the device yields information thus
CA_GET_SLOT_INFO
----------------------------
Command = [info]
APP: Number=[1]
APP: Type=[1]
APP: flags=[1]
APP: CI High level interface
APP: CA/CI Module Present
CA_GET_CAP
----------------------------
Command = [caps]
APP: Slots=[1]
APP: Type=[1]
APP: Descrambler keys=[16]
APP: Type=[1]
CA_SEND_MSG
----------------------------
Descriptors(Program Level)=[ 09 06 06 04 05 50 ff f1]
Found CA descriptor @ program level
(20) ES type=[2] ES pid=[201] ES length =[0 (0x0)]
(25) ES type=[4] ES pid=[301] ES length =[0 (0x0)]
ca_message length is 25 (0x19) bytes
EN50221 CA MSG=[ 9f 80 32 19 03 01 2d d1 f0 08 01 09 06 06 04 05 50 ff f1 02 e0 c9 00 00 04 e1 2d 00 00]
Not all ioctl's are implemented in the driver from the API, the other
features of the hardware that cannot be implemented by the API are achieved
using the CA_GET_MSG and CA_SEND_MSG ioctls. An EN50221 style wrapper is
used to exchange the data to maintain compatibility with other hardware.
/* a message to/from a CI-CAM */
typedef struct ca_msg {
unsigned int index;
unsigned int type;
unsigned int length;
unsigned char msg[256];
} ca_msg_t;
The flow of data can be described thus,
App (User)
-----
parse
|
|
v
en50221 APDU (package)
--------------------------------------
| | | High Level CI driver
| | |
| v |
| en50221 APDU (unpackage) |
| | |
| | |
| v |
| sanity checks |
| | |
| | |
| v |
| do (H/W dep) |
--------------------------------------
| Hardware
|
v
The High Level CI interface uses the EN50221 DVB standard, following a
standard ensures futureproofness.

View File

@ -11,9 +11,8 @@ config DVB_BT8XX
the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards and
pcHDTV HD2000 cards.
Since these cards have no MPEG decoder onboard, they transmit
Since these cards have no MPEG decoder onboard, they transmit
only compressed MPEG data over the PCI bus, so you need
an external software decoder to watch TV on your computer.
Say Y if you own such a device and want to use it.

View File

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

View File

@ -4,8 +4,8 @@
* Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de>
*
* large parts based on the bttv driver
* Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
* & Marcus Metzler (mocm@thp.uni-koeln.de)
* Copyright (C) 1996,97,98 Ralph Metzler (rjkm@metzlerbros.de)
* & Marcus Metzler (mocm@metzlerbros.de)
* (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
*
* This program is free software; you can redistribute it and/or
@ -461,9 +461,9 @@ static int __devinit bt878_probe(struct pci_dev *dev,
pci_set_drvdata(dev, bt);
/* if(init_bt878(btv) < 0) {
bt878_remove(dev);
return -EIO;
}
bt878_remove(dev);
return -EIO;
}
*/
if ((result = bt878_mem_alloc(bt))) {
@ -536,10 +536,10 @@ static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
static struct pci_driver bt878_pci_driver = {
.name = "bt878",
.name = "bt878",
.id_table = bt878_pci_tbl,
.probe = bt878_probe,
.remove = bt878_remove,
.probe = bt878_probe,
.remove = bt878_remove,
};
static int bt878_pci_driver_registered = 0;
@ -558,7 +558,7 @@ static int bt878_init_module(void)
(BT878_VERSION_CODE >> 8) & 0xff,
BT878_VERSION_CODE & 0xff);
/*
bt878_check_chipset();
bt878_check_chipset();
*/
/* later we register inside of bt878_find_audio_dma()
* because we may want to ignore certain cards */

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +0,0 @@
/*
Frontend-driver for TwinHan DST Frontend
Copyright (C) 2003 Jamie Honan
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DST_H
#define DST_H
#include <linux/dvb/frontend.h>
#include <linux/device.h>
#include "bt878.h"
struct dst_config
{
/* the demodulator's i2c address */
u8 demod_address;
};
extern struct dvb_frontend* dst_attach(const struct dst_config* config,
struct i2c_adapter* i2c,
struct bt878 *bt);
#endif // DST_H

View File

@ -0,0 +1,868 @@
/*
CA-driver for TwinHan DST Frontend/Card
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/dvb/ca.h>
#include "dvbdev.h"
#include "dvb_frontend.h"
#include "dst_ca.h"
#include "dst_common.h"
static unsigned int verbose = 1;
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
static unsigned int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(dst_ca_debug, "debug messages, default is 0 (yes)");
static unsigned int session;
module_param(session, int, 0644);
MODULE_PARM_DESC(session, "Support for hardware that has multiple sessions, default 0");
static unsigned int new_ca;
module_param(new_ca, int, 0644);
MODULE_PARM_DESC(new_ca, "Support for the new CA interface firmware, default 0");
#define dprintk if (debug) printk
static int ca_set_slot_descr(void)
{
/* We could make this more graceful ? */
return -EOPNOTSUPP;
}
static int ca_set_pid(void)
{
/* We could make this more graceful ? */
return -EOPNOTSUPP;
}
static int put_checksum(u8 *check_string, int length)
{
u8 i = 0, checksum = 0;
if (verbose > 3) {
dprintk("%s: ========================= Checksum calculation ===========================\n", __FUNCTION__);
dprintk("%s: String Length=[0x%02x]\n", __FUNCTION__, length);
dprintk("%s: String=[", __FUNCTION__);
}
while (i < length) {
if (verbose > 3)
dprintk(" %02x", check_string[i]);
checksum += check_string[i];
i++;
}
if (verbose > 3) {
dprintk(" ]\n");
dprintk("%s: Sum=[%02x]\n", __FUNCTION__, checksum);
}
check_string[length] = ~checksum + 1;
if (verbose > 3) {
dprintk("%s: Checksum=[%02x]\n", __FUNCTION__, check_string[length]);
dprintk("%s: ==========================================================================\n", __FUNCTION__);
}
return 0;
}
static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read)
{
u8 reply;
dst_comm_init(state);
msleep(65);
if (write_dst(state, data, len)) {
dprintk("%s: Write not successful, trying to recover\n", __FUNCTION__);
dst_error_recovery(state);
return -1;
}
if ((dst_pio_disable(state)) < 0) {
dprintk("%s: DST PIO disable failed.\n", __FUNCTION__);
return -1;
}
if (read_dst(state, &reply, GET_ACK) < 0) {
dprintk("%s: Read not successful, trying to recover\n", __FUNCTION__);
dst_error_recovery(state);
return -1;
}
if (read) {
if (! dst_wait_dst_ready(state, LONG_DELAY)) {
dprintk("%s: 8820 not ready\n", __FUNCTION__);
return -1;
}
if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */
dprintk("%s: Read not successful, trying to recover\n", __FUNCTION__);
dst_error_recovery(state);
return -1;
}
}
return 0;
}
static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read)
{
u8 dst_ca_comm_err = 0;
while (dst_ca_comm_err < RETRIES) {
dst_comm_init(state);
if (verbose > 2)
dprintk("%s: Put Command\n", __FUNCTION__);
if (dst_ci_command(state, data, ca_string, len, read)) { // If error
dst_error_recovery(state);
dst_ca_comm_err++; // work required here.
}
break;
}
return 0;
}
static int ca_get_app_info(struct dst_state *state)
{
static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff};
put_checksum(&command[0], command[0]);
if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) {
dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
return -1;
}
if (verbose > 1) {
dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
dprintk("%s: ================================ CI Module Application Info ======================================\n", __FUNCTION__);
dprintk("%s: Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]\n",
__FUNCTION__, state->messages[7], (state->messages[8] << 8) | state->messages[9],
(state->messages[10] << 8) | state->messages[11], __FUNCTION__, (char *)(&state->messages[11]));
dprintk("%s: ==================================================================================================\n", __FUNCTION__);
}
return 0;
}
static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void *arg)
{
int i;
u8 slot_cap[256];
static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff};
put_checksum(&slot_command[0], slot_command[0]);
if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) {
dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
/* Will implement the rest soon */
if (verbose > 1) {
dprintk("%s: Slot cap = [%d]\n", __FUNCTION__, slot_cap[7]);
dprintk("===================================\n");
for (i = 0; i < 8; i++)
dprintk(" %d", slot_cap[i]);
dprintk("\n");
}
p_ca_caps->slot_num = 1;
p_ca_caps->slot_type = 1;
p_ca_caps->descr_num = slot_cap[7];
p_ca_caps->descr_type = 1;
if (copy_to_user((struct ca_caps *)arg, p_ca_caps, sizeof (struct ca_caps))) {
return -EFAULT;
}
return 0;
}
static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
{
return -EOPNOTSUPP;
}
static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void *arg)
{
int i;
static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
u8 *slot_info = state->rxbuffer;
put_checksum(&slot_command[0], 7);
if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) {
dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
/* Will implement the rest soon */
if (verbose > 1) {
dprintk("%s: Slot info = [%d]\n", __FUNCTION__, slot_info[3]);
dprintk("===================================\n");
for (i = 0; i < 8; i++)
dprintk(" %d", slot_info[i]);
dprintk("\n");
}
if (slot_info[4] & 0x80) {
p_ca_slot_info->flags = CA_CI_MODULE_PRESENT;
p_ca_slot_info->num = 1;
p_ca_slot_info->type = CA_CI;
}
else if (slot_info[4] & 0x40) {
p_ca_slot_info->flags = CA_CI_MODULE_READY;
p_ca_slot_info->num = 1;
p_ca_slot_info->type = CA_CI;
}
else {
p_ca_slot_info->flags = 0;
}
if (copy_to_user((struct ca_slot_info *)arg, p_ca_slot_info, sizeof (struct ca_slot_info))) {
return -EFAULT;
}
return 0;
}
static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
{
u8 i = 0;
u32 command = 0;
if (copy_from_user(p_ca_message, (void *)arg, sizeof (struct ca_msg)))
return -EFAULT;
if (p_ca_message->msg) {
if (verbose > 3)
dprintk("Message = [%02x %02x %02x]\n", p_ca_message->msg[0], p_ca_message->msg[1], p_ca_message->msg[2]);
for (i = 0; i < 3; i++) {
command = command | p_ca_message->msg[i];
if (i < 2)
command = command << 8;
}
if (verbose > 3)
dprintk("%s:Command=[0x%x]\n", __FUNCTION__, command);
switch (command) {
case CA_APP_INFO:
memcpy(p_ca_message->msg, state->messages, 128);
if (copy_to_user((void *)arg, p_ca_message, sizeof (struct ca_msg)) )
return -EFAULT;
break;
}
}
return 0;
}
static int handle_en50221_tag(struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
{
if (session) {
hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */
hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */
}
else {
hw_buffer->msg[2] = 0x03;
hw_buffer->msg[3] = 0x00;
}
return 0;
}
static int debug_8820_buffer(struct ca_msg *hw_buffer)
{
unsigned int i;
dprintk("%s:Debug=[", __FUNCTION__);
for (i = 0; i < (hw_buffer->msg[0] + 1); i++)
dprintk(" %02x", hw_buffer->msg[i]);
dprintk("]\n");
return 0;
}
static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 reply)
{
if ((dst_put_ci(state, hw_buffer->msg, (hw_buffer->length + 1), hw_buffer->msg, reply)) < 0) {
dprintk("%s: DST-CI Command failed.\n", __FUNCTION__);
dprintk("%s: Resetting DST.\n", __FUNCTION__);
rdc_reset_state(state);
return -1;
}
if (verbose > 2)
dprintk("%s: DST-CI Command succes.\n", __FUNCTION__);
return 0;
}
static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query)
{
u32 hw_offset, buf_offset, i, k;
u32 program_info_length = 0, es_info_length = 0, length = 0, words = 0;
u8 found_prog_ca_desc = 0, found_stream_ca_desc = 0, error_condition = 0, hw_buffer_length = 0;
if (verbose > 3)
dprintk("%s, p_ca_message length %d (0x%x)\n", __FUNCTION__,p_ca_message->length,p_ca_message->length );
handle_en50221_tag(p_ca_message, hw_buffer); /* EN50221 tag */
/* Handle the length field (variable) */
if (!(p_ca_message->msg[3] & 0x80)) { /* Length = 1 */
length = p_ca_message->msg[3] & 0x7f;
words = 0; /* domi's suggestion */
}
else { /* Length = words */
words = p_ca_message->msg[3] & 0x7f;
for (i = 0; i < words; i++) {
length = length << 8;
length = length | p_ca_message->msg[4 + i];
}
}
if (verbose > 4) {
dprintk("%s:Length=[%d (0x%x)], Words=[%d]\n", __FUNCTION__, length,length, words);
/* Debug Input string */
for (i = 0; i < length; i++)
dprintk(" %02x", p_ca_message->msg[i]);
dprintk("]\n");
}
hw_offset = 7;
buf_offset = words + 4;
/* Program Header */
if (verbose > 4)
dprintk("\n%s:Program Header=[", __FUNCTION__);
for (i = 0; i < 6; i++) {
hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
if (verbose > 4)
dprintk(" %02x", p_ca_message->msg[buf_offset]);
hw_offset++, buf_offset++, hw_buffer_length++;
}
if (verbose > 4)
dprintk("]\n");
program_info_length = 0;
program_info_length = (((program_info_length | p_ca_message->msg[words + 8]) & 0x0f) << 8) | p_ca_message->msg[words + 9];
if (verbose > 4)
dprintk("%s:Program info Length=[%d][%02x], hw_offset=[%d], buf_offset=[%d] \n",
__FUNCTION__, program_info_length, program_info_length, hw_offset, buf_offset);
if (program_info_length && (program_info_length < 256)) { /* If program_info_length */
hw_buffer->msg[11] = hw_buffer->msg[11] & 0x0f; /* req only 4 bits */
hw_buffer->msg[12] = hw_buffer->msg[12] + 1; /* increment! ASIC bug! */
if (p_ca_message->msg[buf_offset + 1] == 0x09) { /* Check CA descriptor */
found_prog_ca_desc = 1;
if (verbose > 4)
dprintk("%s: Found CA descriptor @ Program level\n", __FUNCTION__);
}
if (found_prog_ca_desc) { /* Command only if CA descriptor */
hw_buffer->msg[13] = p_ca_message->msg[buf_offset]; /* CA PMT command ID */
hw_offset++, buf_offset++, hw_buffer_length++;
}
/* Program descriptors */
if (verbose > 4) {
dprintk("%s:**********>buf_offset=[%d], hw_offset=[%d]\n", __FUNCTION__, buf_offset, hw_offset);
dprintk("%s:Program descriptors=[", __FUNCTION__);
}
while (program_info_length && !error_condition) { /* Copy prog descriptors */
if (program_info_length > p_ca_message->length) { /* Error situation */
dprintk ("%s:\"WARNING\" Length error, line=[%d], prog_info_length=[%d]\n",
__FUNCTION__, __LINE__, program_info_length);
dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
error_condition = 1;
break;
}
hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
dprintk(" %02x", p_ca_message->msg[buf_offset]);
hw_offset++, buf_offset++, hw_buffer_length++, program_info_length--;
}
if (verbose > 4) {
dprintk("]\n");
dprintk("%s:**********>buf_offset=[%d], hw_offset=[%d]\n", __FUNCTION__, buf_offset, hw_offset);
}
if (found_prog_ca_desc) {
if (!reply) {
hw_buffer->msg[13] = 0x01; /* OK descrambling */
if (verbose > 1)
dprintk("CA PMT Command = OK Descrambling\n");
}
else {
hw_buffer->msg[13] = 0x02; /* Ok MMI */
if (verbose > 1)
dprintk("CA PMT Command = Ok MMI\n");
}
if (query) {
hw_buffer->msg[13] = 0x03; /* Query */
if (verbose > 1)
dprintk("CA PMT Command = CA PMT query\n");
}
}
}
else {
hw_buffer->msg[11] = hw_buffer->msg[11] & 0xf0; /* Don't write to ASIC */
hw_buffer->msg[12] = hw_buffer->msg[12] = 0x00;
}
if (verbose > 4)
dprintk("%s:**********>p_ca_message->length=[%d], buf_offset=[%d], hw_offset=[%d]\n",
__FUNCTION__, p_ca_message->length, buf_offset, hw_offset);
while ((buf_offset < p_ca_message->length) && !error_condition) {
/* Bail out in case of an indefinite loop */
if ((es_info_length > p_ca_message->length) || (buf_offset > p_ca_message->length)) {
dprintk("%s:\"WARNING\" Length error, line=[%d], prog_info_length=[%d], buf_offset=[%d]\n",
__FUNCTION__, __LINE__, program_info_length, buf_offset);
dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
error_condition = 1;
break;
}
/* Stream Header */
for (k = 0; k < 5; k++) {
hw_buffer->msg[hw_offset + k] = p_ca_message->msg[buf_offset + k];
}
es_info_length = 0;
es_info_length = (es_info_length | (p_ca_message->msg[buf_offset + 3] & 0x0f)) << 8 | p_ca_message->msg[buf_offset + 4];
if (verbose > 4) {
dprintk("\n%s:----->Stream header=[%02x %02x %02x %02x %02x]\n", __FUNCTION__,
p_ca_message->msg[buf_offset + 0], p_ca_message->msg[buf_offset + 1],
p_ca_message->msg[buf_offset + 2], p_ca_message->msg[buf_offset + 3],
p_ca_message->msg[buf_offset + 4]);
dprintk("%s:----->Stream type=[%02x], es length=[%d (0x%x)], Chars=[%02x] [%02x], buf_offset=[%d]\n", __FUNCTION__,
p_ca_message->msg[buf_offset + 0], es_info_length, es_info_length,
p_ca_message->msg[buf_offset + 3], p_ca_message->msg[buf_offset + 4], buf_offset);
}
hw_buffer->msg[hw_offset + 3] &= 0x0f; /* req only 4 bits */
if (found_prog_ca_desc) {
hw_buffer->msg[hw_offset + 3] = 0x00;
hw_buffer->msg[hw_offset + 4] = 0x00;
}
hw_offset += 5, buf_offset += 5, hw_buffer_length += 5;
/* Check for CA descriptor */
if (p_ca_message->msg[buf_offset + 1] == 0x09) {
if (verbose > 4)
dprintk("%s:Found CA descriptor @ Stream level\n", __FUNCTION__);
found_stream_ca_desc = 1;
}
/* ES descriptors */
if (es_info_length && !error_condition && !found_prog_ca_desc && found_stream_ca_desc) {
// if (!ca_pmt_done) {
hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset]; /* CA PMT cmd(es) */
if (verbose > 4)
printk("%s:----->CA PMT Command ID=[%02x]\n", __FUNCTION__, p_ca_message->msg[buf_offset]);
// hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--, ca_pmt_done = 1;
hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--;
// }
if (verbose > 4)
dprintk("%s:----->ES descriptors=[", __FUNCTION__);
while (es_info_length && !error_condition) { /* ES descriptors */
if ((es_info_length > p_ca_message->length) || (buf_offset > p_ca_message->length)) {
if (verbose > 4) {
dprintk("%s:\"WARNING\" ES Length error, line=[%d], es_info_length=[%d], buf_offset=[%d]\n",
__FUNCTION__, __LINE__, es_info_length, buf_offset);
dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
}
error_condition = 1;
break;
}
hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
if (verbose > 3)
dprintk("%02x ", hw_buffer->msg[hw_offset]);
hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--;
}
found_stream_ca_desc = 0; /* unset for new streams */
dprintk("]\n");
}
}
/* MCU Magic words */
hw_buffer_length += 7;
hw_buffer->msg[0] = hw_buffer_length;
hw_buffer->msg[1] = 64;
hw_buffer->msg[4] = 3;
hw_buffer->msg[5] = hw_buffer->msg[0] - 7;
hw_buffer->msg[6] = 0;
/* Fix length */
hw_buffer->length = hw_buffer->msg[0];
put_checksum(&hw_buffer->msg[0], hw_buffer->msg[0]);
/* Do the actual write */
if (verbose > 4) {
dprintk("%s:======================DEBUGGING================================\n", __FUNCTION__);
dprintk("%s: Actual Length=[%d]\n", __FUNCTION__, hw_buffer_length);
}
/* Only for debugging! */
if (verbose > 2)
debug_8820_buffer(hw_buffer);
if (verbose > 3)
dprintk("%s: Reply = [%d]\n", __FUNCTION__, reply);
write_to_8820(state, hw_buffer, reply);
return 0;
}
/* Board supports CA PMT reply ? */
static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
{
int ca_pmt_reply_test = 0;
/* Do test board */
/* Not there yet but soon */
/* CA PMT Reply capable */
if (ca_pmt_reply_test) {
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) {
dprintk("%s: ca_set_pmt.. failed !\n", __FUNCTION__);
return -1;
}
/* Process CA PMT Reply */
/* will implement soon */
dprintk("%s: Not there yet\n", __FUNCTION__);
}
/* CA PMT Reply not capable */
if (!ca_pmt_reply_test) {
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) {
dprintk("%s: ca_set_pmt.. failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 3)
dprintk("%s: ca_set_pmt.. success !\n", __FUNCTION__);
/* put a dummy message */
}
return 0;
}
static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
{
int i = 0;
unsigned int ca_message_header_len;
u32 command = 0;
struct ca_msg *hw_buffer;
if ((hw_buffer = (struct ca_msg *) kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
printk("%s: Memory allocation failure\n", __FUNCTION__);
return -ENOMEM;
}
if (verbose > 3)
dprintk("%s\n", __FUNCTION__);
if (copy_from_user(p_ca_message, (void *)arg, sizeof (struct ca_msg)))
return -EFAULT;
if (p_ca_message->msg) {
ca_message_header_len = p_ca_message->length; /* Restore it back when you are done */
/* EN50221 tag */
command = 0;
for (i = 0; i < 3; i++) {
command = command | p_ca_message->msg[i];
if (i < 2)
command = command << 8;
}
if (verbose > 3)
dprintk("%s:Command=[0x%x]\n", __FUNCTION__, command);
switch (command) {
case CA_PMT:
if (verbose > 3)
dprintk("Command = SEND_CA_PMT\n");
if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) {
dprintk("%s: -->CA_PMT Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 3)
dprintk("%s: -->CA_PMT Success !\n", __FUNCTION__);
// retval = dummy_set_pmt(state, p_ca_message, hw_buffer, 0, 0);
break;
case CA_PMT_REPLY:
if (verbose > 3)
dprintk("Command = CA_PMT_REPLY\n");
/* Have to handle the 2 basic types of cards here */
if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) {
dprintk("%s: -->CA_PMT_REPLY Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 3)
dprintk("%s: -->CA_PMT_REPLY Success !\n", __FUNCTION__);
/* Certain boards do behave different ? */
// retval = ca_set_pmt(state, p_ca_message, hw_buffer, 1, 1);
case CA_APP_INFO_ENQUIRY: // only for debugging
if (verbose > 3)
dprintk("%s: Getting Cam Application information\n", __FUNCTION__);
if ((ca_get_app_info(state)) < 0) {
dprintk("%s: -->CA_APP_INFO_ENQUIRY Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 3)
printk("%s: -->CA_APP_INFO_ENQUIRY Success !\n", __FUNCTION__);
break;
}
}
return 0;
}
static int dst_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg)
{
struct dvb_device* dvbdev = (struct dvb_device*) file->private_data;
struct dst_state* state = (struct dst_state*) dvbdev->priv;
struct ca_slot_info *p_ca_slot_info;
struct ca_caps *p_ca_caps;
struct ca_msg *p_ca_message;
if ((p_ca_message = (struct ca_msg *) kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
printk("%s: Memory allocation failure\n", __FUNCTION__);
return -ENOMEM;
}
if ((p_ca_slot_info = (struct ca_slot_info *) kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL)) == NULL) {
printk("%s: Memory allocation failure\n", __FUNCTION__);
return -ENOMEM;
}
if ((p_ca_caps = (struct ca_caps *) kmalloc(sizeof (struct ca_caps), GFP_KERNEL)) == NULL) {
printk("%s: Memory allocation failure\n", __FUNCTION__);
return -ENOMEM;
}
/* We have now only the standard ioctl's, the driver is upposed to handle internals. */
switch (cmd) {
case CA_SEND_MSG:
if (verbose > 1)
dprintk("%s: Sending message\n", __FUNCTION__);
if ((ca_send_message(state, p_ca_message, arg)) < 0) {
dprintk("%s: -->CA_SEND_MSG Failed !\n", __FUNCTION__);
return -1;
}
break;
case CA_GET_MSG:
if (verbose > 1)
dprintk("%s: Getting message\n", __FUNCTION__);
if ((ca_get_message(state, p_ca_message, arg)) < 0) {
dprintk("%s: -->CA_GET_MSG Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_GET_MSG Success !\n", __FUNCTION__);
break;
case CA_RESET:
if (verbose > 1)
dprintk("%s: Resetting DST\n", __FUNCTION__);
dst_error_bailout(state);
msleep(4000);
break;
case CA_GET_SLOT_INFO:
if (verbose > 1)
dprintk("%s: Getting Slot info\n", __FUNCTION__);
if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) {
dprintk("%s: -->CA_GET_SLOT_INFO Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_GET_SLOT_INFO Success !\n", __FUNCTION__);
break;
case CA_GET_CAP:
if (verbose > 1)
dprintk("%s: Getting Slot capabilities\n", __FUNCTION__);
if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) {
dprintk("%s: -->CA_GET_CAP Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_GET_CAP Success !\n", __FUNCTION__);
break;
case CA_GET_DESCR_INFO:
if (verbose > 1)
dprintk("%s: Getting descrambler description\n", __FUNCTION__);
if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) {
dprintk("%s: -->CA_GET_DESCR_INFO Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_GET_DESCR_INFO Success !\n", __FUNCTION__);
break;
case CA_SET_DESCR:
if (verbose > 1)
dprintk("%s: Setting descrambler\n", __FUNCTION__);
if ((ca_set_slot_descr()) < 0) {
dprintk("%s: -->CA_SET_DESCR Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_SET_DESCR Success !\n", __FUNCTION__);
break;
case CA_SET_PID:
if (verbose > 1)
dprintk("%s: Setting PID\n", __FUNCTION__);
if ((ca_set_pid()) < 0) {
dprintk("%s: -->CA_SET_PID Failed !\n", __FUNCTION__);
return -1;
}
if (verbose > 1)
dprintk("%s: -->CA_SET_PID Success !\n", __FUNCTION__);
default:
return -EOPNOTSUPP;
};
return 0;
}
static int dst_ca_open(struct inode *inode, struct file *file)
{
if (verbose > 4)
dprintk("%s:Device opened [%p]\n", __FUNCTION__, file);
try_module_get(THIS_MODULE);
return 0;
}
static int dst_ca_release(struct inode *inode, struct file *file)
{
if (verbose > 4)
dprintk("%s:Device closed.\n", __FUNCTION__);
module_put(THIS_MODULE);
return 0;
}
static int dst_ca_read(struct file *file, char __user * buffer, size_t length, loff_t * offset)
{
int bytes_read = 0;
if (verbose > 4)
dprintk("%s:Device read.\n", __FUNCTION__);
return bytes_read;
}
static int dst_ca_write(struct file *file, const char __user * buffer, size_t length, loff_t * offset)
{
if (verbose > 4)
dprintk("%s:Device write.\n", __FUNCTION__);
return 0;
}
static struct file_operations dst_ca_fops = {
.owner = THIS_MODULE,
.ioctl = (void *)dst_ca_ioctl,
.open = dst_ca_open,
.release = dst_ca_release,
.read = dst_ca_read,
.write = dst_ca_write
};
static struct dvb_device dvbdev_ca = {
.priv = NULL,
.users = 1,
.readers = 1,
.writers = 1,
.fops = &dst_ca_fops
};
int dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
{
struct dvb_device *dvbdev;
if (verbose > 4)
dprintk("%s:registering DST-CA device\n", __FUNCTION__);
dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA);
return 0;
}
EXPORT_SYMBOL(dst_ca_attach);
MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver");
MODULE_AUTHOR("Manu Abraham");
MODULE_LICENSE("GPL");

View File

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

View File

@ -0,0 +1,153 @@
/*
Frontend-driver for TwinHan DST Frontend
Copyright (C) 2003 Jamie Honan
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef DST_COMMON_H
#define DST_COMMON_H
#include <linux/dvb/frontend.h>
#include <linux/device.h>
#include "bt878.h"
#include "dst_ca.h"
#define NO_DELAY 0
#define LONG_DELAY 1
#define DEVICE_INIT 2
#define DELAY 1
#define DST_TYPE_IS_SAT 0
#define DST_TYPE_IS_TERR 1
#define DST_TYPE_IS_CABLE 2
#define DST_TYPE_IS_ATSC 3
#define DST_TYPE_HAS_NEWTUNE 1
#define DST_TYPE_HAS_TS204 2
#define DST_TYPE_HAS_SYMDIV 4
#define DST_TYPE_HAS_FW_1 8
#define DST_TYPE_HAS_FW_2 16
#define DST_TYPE_HAS_FW_3 32
/* Card capability list */
#define DST_TYPE_HAS_MAC 1
#define DST_TYPE_HAS_DISEQC3 2
#define DST_TYPE_HAS_DISEQC4 4
#define DST_TYPE_HAS_DISEQC5 8
#define DST_TYPE_HAS_MOTO 16
#define DST_TYPE_HAS_CA 32
#define DST_TYPE_HAS_ANALOG 64 /* Analog inputs */
#define RDC_8820_PIO_0_DISABLE 0
#define RDC_8820_PIO_0_ENABLE 1
#define RDC_8820_INT 2
#define RDC_8820_RESET 4
/* DST Communication */
#define GET_REPLY 1
#define NO_REPLY 0
#define GET_ACK 1
#define FIXED_COMM 8
#define ACK 0xff
struct dst_state {
struct i2c_adapter* i2c;
struct bt878* bt;
struct dvb_frontend_ops ops;
/* configuration settings */
const struct dst_config* config;
struct dvb_frontend frontend;
/* private ASIC data */
u8 tx_tuna[10];
u8 rx_tuna[10];
u8 rxbuffer[10];
u8 diseq_flags;
u8 dst_type;
u32 type_flags;
u32 frequency; /* intermediate frequency in kHz for QPSK */
fe_spectral_inversion_t inversion;
u32 symbol_rate; /* symbol rate in Symbols per second */
fe_code_rate_t fec;
fe_sec_voltage_t voltage;
fe_sec_tone_mode_t tone;
u32 decode_freq;
u8 decode_lock;
u16 decode_strength;
u16 decode_snr;
unsigned long cur_jiff;
u8 k22;
fe_bandwidth_t bandwidth;
u8 dst_hw_cap;
u8 dst_fw_version;
fe_sec_mini_cmd_t minicmd;
u8 messages[256];
};
struct dst_types {
char *device_id;
int offset;
u8 dst_type;
u32 type_flags;
u8 dst_feature;
};
struct dst_config
{
/* the ASIC i2c address */
u8 demod_address;
};
int rdc_reset_state(struct dst_state *state);
int rdc_8820_reset(struct dst_state *state);
int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode);
int dst_pio_enable(struct dst_state *state);
int dst_pio_disable(struct dst_state *state);
int dst_error_recovery(struct dst_state* state);
int dst_error_bailout(struct dst_state *state);
int dst_comm_init(struct dst_state* state);
int write_dst(struct dst_state *state, u8 * data, u8 len);
int read_dst(struct dst_state *state, u8 * ret, u8 len);
u8 dst_check_sum(u8 * buf, u32 len);
struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter);
int dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh, int delay);
int dst_command(struct dst_state* state, u8 * data, u8 len);
#endif // DST_COMMON_H

View File

@ -33,4 +33,3 @@ union dst_gpio_packet {
struct bt878;
int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp);

View File

@ -142,7 +142,7 @@ static int thomson_dtt7579_demod_init(struct dvb_frontend* fe)
mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
return 0;
@ -161,7 +161,7 @@ static int thomson_dtt7579_pll_set(struct dvb_frontend* fe, struct dvb_frontend_
else if (params->frequency < 771000000) cp = 0xbc;
else cp = 0xf4;
if (params->frequency == 0) bs = 0x03;
if (params->frequency == 0) bs = 0x03;
else if (params->frequency < 443250000) bs = 0x02;
else bs = 0x08;
@ -190,44 +190,44 @@ static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_paramete
u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000,
1576000,1718000,1856000,2036000,2150000};
1576000,1718000,1856000,2036000,2150000};
u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000,
0x00102000,0x00104000,0x00108000,0x00110000,
0x00120000,0x00140000};
0x00102000,0x00104000,0x00108000,0x00110000,
0x00120000,0x00140000};
#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */
printk("cx24108 debug: entering SetTunerFreq, freq=%d\n",freq);
printk("cx24108 debug: entering SetTunerFreq, freq=%d\n",freq);
/* This is really the bit driving the tuner chip cx24108 */
/* This is really the bit driving the tuner chip cx24108 */
if(freq<950000) freq=950000; /* kHz */
if(freq>2150000) freq=2150000; /* satellite IF is 950..2150MHz */
if(freq<950000) freq=950000; /* kHz */
if(freq>2150000) freq=2150000; /* satellite IF is 950..2150MHz */
/* decide which VCO to use for the input frequency */
for(i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<freq);i++);
printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq);
band=bandsel[i];
/* the gain values must be set by SetSymbolrate */
/* compute the pll divider needed, from Conexant data sheet,
resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
depending on the divider bit. It is set to /4 on the 2 lowest
bands */
n=((i<=2?2:1)*freq*10L)/(XTAL/100);
a=n%32; n/=32; if(a==0) n--;
pump=(freq<(osci[i-1]+osci[i])/2);
pll=0xf8000000|
((pump?1:2)<<(14+11))|
((n&0x1ff)<<(5+11))|
((a&0x1f)<<11);
/* everything is shifted left 11 bits to left-align the bits in the
32bit word. Output to the tuner goes MSB-aligned, after all */
printk("cx24108 debug: pump=%d, n=%d, a=%d\n",pump,n,a);
cx24110_pll_write(fe,band);
/* set vga and vca to their widest-band settings, as a precaution.
SetSymbolrate might not be called to set this up */
cx24110_pll_write(fe,0x500c0000);
cx24110_pll_write(fe,0x83f1f800);
cx24110_pll_write(fe,pll);
/* decide which VCO to use for the input frequency */
for(i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<freq);i++);
printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq);
band=bandsel[i];
/* the gain values must be set by SetSymbolrate */
/* compute the pll divider needed, from Conexant data sheet,
resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
depending on the divider bit. It is set to /4 on the 2 lowest
bands */
n=((i<=2?2:1)*freq*10L)/(XTAL/100);
a=n%32; n/=32; if(a==0) n--;
pump=(freq<(osci[i-1]+osci[i])/2);
pll=0xf8000000|
((pump?1:2)<<(14+11))|
((n&0x1ff)<<(5+11))|
((a&0x1f)<<11);
/* everything is shifted left 11 bits to left-align the bits in the
32bit word. Output to the tuner goes MSB-aligned, after all */
printk("cx24108 debug: pump=%d, n=%d, a=%d\n",pump,n,a);
cx24110_pll_write(fe,band);
/* set vga and vca to their widest-band settings, as a precaution.
SetSymbolrate might not be called to set this up */
cx24110_pll_write(fe,0x500c0000);
cx24110_pll_write(fe,0x83f1f800);
cx24110_pll_write(fe,pll);
/* writereg(client,0x56,0x7f);*/
return 0;
@ -299,7 +299,7 @@ static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
static u8 mt352_reset [] = { 0x50, 0x80 };
static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
0x00, 0xFF, 0x00, 0x40, 0x40 };
0x00, 0xFF, 0x00, 0x40, 0x40 };
static u8 mt352_av771_extra[] = { 0xB5, 0x7A };
static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
@ -463,6 +463,9 @@ static struct nxt6000_config vp3021_alps_tded4_config = {
static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
{
int ret;
struct dst_state* state = NULL;
switch(type) {
#ifdef BTTV_DVICO_DVBT_LITE
case BTTV_DVICO_DVBT_LITE:
@ -503,7 +506,25 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
break;
case BTTV_TWINHAN_DST:
card->fe = dst_attach(&dst_config, card->i2c_adapter, card->bt);
/* DST is not a frontend driver !!! */
state = (struct dst_state *) kmalloc(sizeof (struct dst_state), GFP_KERNEL);
/* Setup the Card */
state->config = &dst_config;
state->i2c = card->i2c_adapter;
state->bt = card->bt;
/* DST is not a frontend, attaching the ASIC */
if ((dst_attach(state, &card->dvb_adapter)) == NULL) {
printk("%s: Could not find a Twinhan DST.\n", __FUNCTION__);
break;
}
card->fe = &state->frontend;
/* Attach other DST peripherals if any */
/* Conditional Access device */
if (state->dst_hw_cap & DST_TYPE_HAS_CA) {
ret = dst_ca_attach(state, &card->dvb_adapter);
}
if (card->fe != NULL) {
break;
}
@ -648,7 +669,7 @@ static int dvb_bt8xx_probe(struct device *dev)
case BTTV_PINNACLESAT:
card->gpio_mode = 0x0400c060;
/* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR,
BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */
BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */
card->op_sync_orin = 0;
card->irq_err_ignore = 0;
break;

View File

@ -31,7 +31,7 @@
#include "bttv.h"
#include "mt352.h"
#include "sp887x.h"
#include "dst.h"
#include "dst_common.h"
#include "nxt6000.h"
#include "cx24110.h"
#include "or51211.h"