summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2004-04-25 19:01:15 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-04-25 19:01:15 -0700
commit2ce6c0f56af9a2db006129f2fd83d727543ade2a (patch)
tree904b452e9eb13da7b07d86b6c72a7c01463424b2
parentaf9037dd1d87e057f5d4a638231b12ebf90e107c (diff)
parent804ca075d25251a5e8a4f416199f2da0f218e62d (diff)
Merge bk://kernel.bkbits.net/davem/sparc-2.6
into ppc970.osdl.org:/home/torvalds/v2.6/linux
-rw-r--r--Documentation/dvb/cards.txt2
-rw-r--r--Documentation/dvb/contributors.txt8
-rw-r--r--Documentation/dvb/faq.txt5
-rw-r--r--drivers/media/common/saa7146_fops.c12
-rw-r--r--drivers/media/common/saa7146_hlp.c4
-rw-r--r--drivers/media/common/saa7146_i2c.c7
-rw-r--r--drivers/media/common/saa7146_video.c335
-rw-r--r--drivers/media/dvb/Kconfig5
-rw-r--r--drivers/media/dvb/b2c2/skystar2.c4
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.c2
-rw-r--r--drivers/media/dvb/dvb-core/Makefile3
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.c1628
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ca_en50221.h128
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.c3
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c427
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h13
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ksyms.c6
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c616
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.c116
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.h55
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.c12
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h4
-rw-r--r--drivers/media/dvb/frontends/alps_tdlb7.c71
-rw-r--r--drivers/media/dvb/frontends/alps_tdmb7.c12
-rw-r--r--drivers/media/dvb/frontends/at76c651.c15
-rw-r--r--drivers/media/dvb/frontends/cx24110.c8
-rw-r--r--drivers/media/dvb/frontends/dst.c10
-rw-r--r--drivers/media/dvb/frontends/dvb_dummy_fe.c6
-rw-r--r--drivers/media/dvb/frontends/grundig_29504-401.c174
-rw-r--r--drivers/media/dvb/frontends/grundig_29504-491.c8
-rw-r--r--drivers/media/dvb/frontends/mt312.c16
-rw-r--r--drivers/media/dvb/frontends/nxt6000.c10
-rw-r--r--drivers/media/dvb/frontends/sp887x.c38
-rw-r--r--drivers/media/dvb/frontends/stv0299.c322
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c64
-rw-r--r--drivers/media/dvb/frontends/ves1820.c3
-rw-r--r--drivers/media/dvb/frontends/ves1x93.c68
-rw-r--r--drivers/media/dvb/ttpci/av7110.c48
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.c44
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.c54
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.h30
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c53
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c33
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c305
-rw-r--r--drivers/media/dvb/ttpci/budget-core.c120
-rw-r--r--drivers/media/dvb/ttpci/budget.c58
-rw-r--r--drivers/media/dvb/ttpci/budget.h9
-rw-r--r--drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c19
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusb_dec.c69
-rw-r--r--drivers/media/video/dpc7146.c14
-rw-r--r--drivers/media/video/hexium_gemini.c14
-rw-r--r--drivers/media/video/hexium_orion.c14
-rw-r--r--drivers/media/video/mxb.c18
-rw-r--r--include/linux/dvb/frontend.h6
-rw-r--r--include/linux/dvb/net.h14
-rw-r--r--include/media/saa7146.h2
-rw-r--r--include/media/saa7146_vv.h10
57 files changed, 4337 insertions, 817 deletions
diff --git a/Documentation/dvb/cards.txt b/Documentation/dvb/cards.txt
index 5e07f18308b4..695f174d859d 100644
--- a/Documentation/dvb/cards.txt
+++ b/Documentation/dvb/cards.txt
@@ -50,6 +50,8 @@ o Cards based on the Phillips saa7146 multimedia PCI bridge chip:
- Technotrend Budget / Hauppauge WinTV-Nova PCI Cards
- SATELCO Multimedia PCI
- KNC1 DVB-S
+ - Typhoon DVB-S budget
+ - Fujitsu-Siemens Activy DVB-S budget card
o Cards based on the B2C2 Inc. FlexCopII/IIb/III:
- Technisat SkyStar2 PCI DVB card revision 2.3, 2.6B, 2.6C
diff --git a/Documentation/dvb/contributors.txt b/Documentation/dvb/contributors.txt
index 86c9270da146..12026e93397a 100644
--- a/Documentation/dvb/contributors.txt
+++ b/Documentation/dvb/contributors.txt
@@ -62,5 +62,13 @@ Hans-Frieder Vogt <hfvogt@arcor.de>
for his work on calculating and checking the crc's for the
TechnoTrend/Hauppauge DEC driver firmware
+Michael Dreher <michael@5dot1.de>
+Andreas 'randy' Weinberger
+ for the support of the Fujitsu-Siemens Activy budget DVB-S
+
+Kenneth Aafløy <ke-aa@frisurf.no>
+ for adding support for Typhoon DVB-S budget card
+
+
(If you think you should be in this list, but you are not, drop a
line to the DVB mailing list)
diff --git a/Documentation/dvb/faq.txt b/Documentation/dvb/faq.txt
index d3457495b541..79c2ce4ca04a 100644
--- a/Documentation/dvb/faq.txt
+++ b/Documentation/dvb/faq.txt
@@ -94,6 +94,11 @@ Some very frequently asked questions about linuxtv-dvb
MythTV - analog TV PVR, but now with DVB support, too
(with software MPEG decode)
+ http://dvbsnoop.sourceforge.net/
+ DVB sniffer program to monitor, analyze, debug, dump
+ or view dvb/mpeg/dsm-cc/mhp stream information (TS,
+ PES, SECTION)
+
4. Can't get a signal tuned correctly
If you are using a Technotrend/Hauppauge DVB-C card *without* analog
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 40064909e04a..26995e2d5d46 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -10,14 +10,14 @@ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
- if (fh->resources & bit)
+ if (fh->resources & bit) {
+ DEB_D(("already allocated! want: 0x%02x, cur:0x%02x\n",bit,vv->resources));
/* have it already allocated */
return 1;
+ }
/* is it free? */
- DEB_D(("getting lock...\n"));
down(&dev->lock);
- DEB_D(("got lock\n"));
if (vv->resources & bit) {
DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));
/* no, someone else uses it */
@@ -27,7 +27,7 @@ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
/* it's free, grab it */
fh->resources |= bit;
vv->resources |= bit;
- DEB_D(("res: get %d\n",bit));
+ DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources));
up(&dev->lock);
return 1;
}
@@ -51,12 +51,10 @@ void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
if ((fh->resources & bits) != bits)
BUG();
- DEB_D(("getting lock...\n"));
down(&dev->lock);
- DEB_D(("got lock\n"));
fh->resources &= ~bits;
vv->resources &= ~bits;
- DEB_D(("res: put %d\n",bits));
+ DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources));
up(&dev->lock);
}
diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c
index fffe73fd8812..bdae7fab33a4 100644
--- a/drivers/media/common/saa7146_hlp.c
+++ b/drivers/media/common/saa7146_hlp.c
@@ -536,13 +536,13 @@ static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, e
/* set vertical scale */
hps_v_scale = 0; /* all bits get set by the function-call */
hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/
- calculate_v_scale_registers(dev, field, vv->standard->v_calc, height, &hps_v_scale, &hps_v_gain);
+ calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain);
/* set horizontal scale */
hps_ctrl = 0;
hps_h_prescale = 0; /* all bits get set in the function */
hps_h_scale = 0;
- calculate_h_scale_registers(dev, vv->standard->h_calc, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
+ calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale);
/* set hyo and hxo */
calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl);
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
index ff94b635ec1c..18bc5b671b9a 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146_i2c.c
@@ -400,7 +400,7 @@ static struct i2c_algorithm saa7146_algo = {
.functionality = saa7146_i2c_func,
};
-int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate)
+int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, unsigned int class, u32 bitrate)
{
DEB_EE(("bitrate: 0x%08x\n",bitrate));
@@ -417,16 +417,13 @@ int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c
i2c_adapter->data = dev;
#else
i2c_set_adapdata(i2c_adapter,dev);
+ i2c_adapter->class = class;
#endif
i2c_adapter->algo = &saa7146_algo;
i2c_adapter->algo_data = NULL;
i2c_adapter->id = I2C_ALGO_SAA7146;
i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
i2c_adapter->retries = SAA7146_I2C_RETRIES;
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
-#else
- i2c_adapter->class = I2C_ADAP_CLASS_TV_ANALOG;
-#endif
}
return 0;
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index 1dec1678e752..93299758d7ad 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -5,6 +5,12 @@ static int memory = 32;
MODULE_PARM(memory,"i");
MODULE_PARM_DESC(memory, "maximum memory usage for capture buffers (default: 32Mb)");
+#define IS_CAPTURE_ACTIVE(fh) \
+ (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh))
+
+#define IS_OVERLAY_ACTIVE(fh) \
+ (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh))
+
/* format descriptions for capture and preview */
static struct saa7146_format formats[] = {
{
@@ -260,24 +266,31 @@ int saa7146_start_preview(struct saa7146_fh *fh)
return -EAGAIN;
}
- /* check if overlay is running */
- if( 0 != vv->ov_data ) {
- if( fh != vv->ov_data->fh ) {
- DEB_D(("overlay is running in another open.\n"));
- return -EAGAIN;
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D(("streaming capture is active.\n"));
+ return -EBUSY;
}
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh == fh) {
DEB_D(("overlay is already active.\n"));
return 0;
}
+ DEB_D(("overlay is already active in another open.\n"));
+ return -EBUSY;
+ }
- if( 0 != vv->streaming ) {
- DEB_D(("streaming capture is active.\n"));
+ if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
+ DEB_D(("cannot get necessary overlay resources\n"));
return -EBUSY;
}
err = try_win(dev,&fh->ov.win);
if (0 != err) {
- return err;
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return -EBUSY;
}
vv->ov_data = &fh->ov;
@@ -288,11 +301,14 @@ int saa7146_start_preview(struct saa7146_fh *fh)
vv->ov_fmt->name,v4l2_field_names[fh->ov.win.field]));
if (0 != (ret = saa7146_enable_overlay(fh))) {
- vv->ov_data = NULL;
DEB_D(("enabling overlay failed: %d\n",ret));
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
return ret;
}
+ vv->video_status = STATUS_OVERLAY;
+ vv->video_fh = fh;
+
return 0;
}
@@ -303,20 +319,30 @@ int saa7146_stop_preview(struct saa7146_fh *fh)
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
- /* check if overlay is running */
- if( 0 == vv->ov_data ) {
- DEB_D(("overlay is not active.\n"));
- return 0;
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D(("streaming capture is active.\n"));
+ return -EBUSY;
}
- if( fh != vv->ov_data->fh ) {
- DEB_D(("overlay is active, but for another open.\n"));
+ /* check if overlay is running at all */
+ if ((vv->video_status & STATUS_OVERLAY) == 0) {
+ DEB_D(("no active overlay.\n"));
return 0;
}
- vv->ov_data = NULL;
+ if (vv->video_fh != fh) {
+ DEB_D(("overlay is active, but in another open.\n"));
+ return -EBUSY;
+ }
+
+ vv->video_status = 0;
+ vv->video_fh = NULL;
+
saa7146_disable_overlay(fh);
+ saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+
return 0;
}
@@ -325,15 +351,14 @@ static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
- unsigned long flags;
int err;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
- if( fh == vv->streaming ) {
- DEB_EE(("streaming capture is active"));
- return -EAGAIN;
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_EE(("streaming capture is active\n"));
+ return -EBUSY;
}
err = try_fmt(fh,f);
if (0 != err)
@@ -359,16 +384,13 @@ static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
/* fh->ov.fh is used to indicate that we have valid overlay informations, too */
fh->ov.fh = fh;
- /* check if we have an active overlay */
- if( vv->ov_data != NULL ) {
- if( fh == vv->ov_data->fh) {
- spin_lock_irqsave(&dev->slock,flags);
+ up(&dev->lock);
+
+ /* check if our current overlay is active */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
saa7146_stop_preview(fh);
saa7146_start_preview(fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- }
}
- up(&dev->lock);
return 0;
default:
DEB_D(("unknown format type '%d'\n",f->type));
@@ -480,8 +502,6 @@ static int set_control(struct saa7146_fh *fh, struct v4l2_control *c)
struct saa7146_vv *vv = dev->vv_data;
const struct v4l2_queryctrl* ctrl;
- unsigned long flags;
- int restart_overlay = 0;
ctrl = ctrl_by_id(c->id);
if (NULL == ctrl) {
@@ -489,6 +509,8 @@ static int set_control(struct saa7146_fh *fh, struct v4l2_control *c)
return -EINVAL;
}
+ down(&dev->lock);
+
switch (ctrl->type) {
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_MENU:
@@ -528,35 +550,31 @@ static int set_control(struct saa7146_fh *fh, struct v4l2_control *c)
break;
}
case V4L2_CID_HFLIP:
- /* fixme: we can supfhrt changing VFLIP and HFLIP here... */
- if( 0 != vv->streaming ) {
+ /* fixme: we can support changing VFLIP and HFLIP here... */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
+ up(&dev->lock);
return -EINVAL;
}
vv->hflip = c->value;
- restart_overlay = 1;
break;
case V4L2_CID_VFLIP:
- if( 0 != vv->streaming ) {
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
+ up(&dev->lock);
return -EINVAL;
}
vv->vflip = c->value;
- restart_overlay = 1;
break;
default: {
return -EINVAL;
}
}
- if( 0 != restart_overlay ) {
- if( 0 != vv->ov_data ) {
- if( fh == vv->ov_data->fh ) {
- spin_lock_irqsave(&dev->slock,flags);
+ up(&dev->lock);
+
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
saa7146_stop_preview(fh);
saa7146_start_preview(fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- }
- }
}
return 0;
}
@@ -687,21 +705,30 @@ static int video_begin(struct saa7146_fh *fh)
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_format *fmt = NULL;
- unsigned long flags;
unsigned int resource;
- int ret = 0;
+ int ret = 0, err = 0;
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
- if( fh == vv->streaming ) {
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ if (vv->video_fh == fh) {
DEB_S(("already capturing.\n"));
- return -EBUSY;
+ return 0;
}
- if( vv->streaming != 0 ) {
- DEB_S(("already capturing, but in another open.\n"));
+ DEB_S(("already capturing in another open.\n"));
return -EBUSY;
}
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ DEB_S(("warning: suspending overlay video for streaming capture.\n"));
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (0 != err) {
+ DEB_D(("suspending video failed. aborting\n"));
+ return err;
+ }
+ }
+
fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
/* we need to have a valid format set here */
BUG_ON(NULL == fmt);
@@ -715,19 +742,22 @@ static int video_begin(struct saa7146_fh *fh)
ret = saa7146_res_get(fh, resource);
if (0 == ret) {
DEB_S(("cannot get capture resource %d\n",resource));
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
return -EBUSY;
}
- spin_lock_irqsave(&dev->slock,flags);
-
/* clear out beginning of streaming bit (rps register 0)*/
saa7146_write(dev, MC2, MASK_27 );
/* enable rps0 irqs */
IER_ENABLE(dev, MASK_27);
- vv->streaming = fh;
- spin_unlock_irqrestore(&dev->slock,flags);
+ vv->video_fh = fh;
+ vv->video_status = STATUS_CAPTURE;
+
return 0;
}
@@ -741,9 +771,14 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
u32 dmas = 0;
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
- if( vv->streaming != fh ) {
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
DEB_S(("not capturing.\n"));
- return -EINVAL;
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S(("capturing, but in another open.\n"));
+ return -EBUSY;
}
fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
@@ -757,8 +792,6 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
resource = RESOURCE_DMA1_HPS;
dmas = MASK_22;
}
- saa7146_res_free(fh, resource);
-
spin_lock_irqsave(&dev->slock,flags);
/* disable rps0 */
@@ -770,13 +803,20 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
/* shut down all used video dma transfers */
saa7146_write(dev, MC1, dmas);
- vv->streaming = NULL;
-
spin_unlock_irqrestore(&dev->slock, flags);
- return 0;
+ vv->video_fh = NULL;
+ vv->video_status = 0;
+
+ saa7146_res_free(fh, resource);
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
}
+ return 0;
+}
/*
* This function is _not_ called directly, but from
@@ -790,7 +830,6 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
- unsigned long flags;
int err = 0, result = 0, ee = 0;
struct saa7146_use_ops *ops;
@@ -867,17 +906,12 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
{
struct v4l2_framebuffer *fb = arg;
struct saa7146_format *fmt;
- struct saa7146_fh *ov_fh = NULL;
- int restart_overlay = 0;
DEB_EE(("VIDIOC_S_FBUF\n"));
-/*
- if(!capable(CAP_SYS_ADMIN)) { // && !capable(CAP_SYS_RAWIO)) {
- DEB_D(("VIDIOC_S_FBUF: not CAP_SYS_ADMIN or CAP_SYS_RAWIO.\n"));
+ if(!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
return -EPERM;
- }
-*/
/* check args */
fmt = format_by_fourcc(dev,fb->fmt.pixelformat);
@@ -890,13 +924,16 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",(char *)&fmt->pixelformat));
}
- down(&dev->lock);
- if( vv->ov_data != NULL ) {
- ov_fh = vv->ov_data->fh;
- saa7146_stop_preview(ov_fh);
- restart_overlay = 1;
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh != fh) {
+ DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
+ return -EBUSY;
+ }
}
+ down(&dev->lock);
+
/* ok, accept it */
vv->ov_fb = *fb;
vv->ov_fmt = fmt;
@@ -904,10 +941,6 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
vv->ov_fb.fmt.bytesperline =
vv->ov_fb.fmt.width*fmt->depth/8;
- if( 0 != restart_overlay ) {
- saa7146_start_preview(ov_fh);
- }
-
up(&dev->lock);
return 0;
@@ -966,11 +999,13 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
return get_control(fh,arg);
}
case VIDIOC_S_CTRL:
+
+
+
+
{
DEB_EE(("VIDIOC_S_CTRL\n"));
- down(&dev->lock);
err = set_control(fh,arg);
- up(&dev->lock);
return err;
}
case VIDIOC_G_PARM:
@@ -1029,29 +1064,27 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
case VIDIOC_S_STD:
{
v4l2_std_id *id = arg;
- int i;
-
- int restart_overlay = 0;
int found = 0;
-
- struct saa7146_fh *ov_fh = NULL;
+ int i, err;
DEB_EE(("VIDIOC_S_STD\n"));
- if( 0 != vv->streaming ) {
+ if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
+ DEB_D(("cannot change video standard while streaming capture is active\n"));
return -EBUSY;
}
- DEB_D(("before getting lock...\n"));
- down(&dev->lock);
- DEB_D(("got lock\n"));
-
- if( vv->ov_data != NULL ) {
- ov_fh = vv->ov_data->fh;
- saa7146_stop_preview(ov_fh);
- restart_overlay = 1;
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (0 != err) {
+ DEB_D(("suspending video failed. aborting\n"));
+ return err;
+ }
}
+ down(&dev->lock);
+
for(i = 0; i < dev->ext_vv_data->num_stds; i++)
if (*id & dev->ext_vv_data->stds[i].id)
break;
@@ -1062,11 +1095,13 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
found = 1;
}
- if( 0 != restart_overlay ) {
- saa7146_start_preview(ov_fh);
- }
up(&dev->lock);
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
if( 0 == found ) {
DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
return -EINVAL;
@@ -1076,49 +1111,20 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
return 0;
}
case VIDIOC_OVERLAY:
+
+
+
+
{
int on = *(int *)arg;
int err = 0;
- if( NULL == vv->ov_fmt && on != 0 ) {
- DEB_D(("VIDIOC_OVERLAY: no framebuffer informations. call S_FBUF first!\n"));
- return -EAGAIN;
- }
-
DEB_D(("VIDIOC_OVERLAY on:%d\n",on));
- if( 0 != on ) {
- if( vv->ov_data != NULL ) {
- if( fh != vv->ov_data->fh) {
- DEB_D(("overlay already active in another open\n"));
- return -EAGAIN;
- }
- }
-
- if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
- DEB_D(("cannot get overlay resources\n"));
- return -EBUSY;
- }
-
- spin_lock_irqsave(&dev->slock,flags);
+ if (on != 0) {
err = saa7146_start_preview(fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- return err;
- }
-
- if( vv->ov_data != NULL ) {
- if( fh != vv->ov_data->fh) {
- DEB_D(("overlay is active, but in another open\n"));
- return -EAGAIN;
- }
} else {
- DEB_D(("overlay is not active\n"));
- return 0;
- }
- spin_lock_irqsave(&dev->slock,flags);
err = saa7146_stop_preview(fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- /* free resources */
- saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ }
return err;
}
case VIDIOC_REQBUFS: {
@@ -1149,11 +1155,6 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
int *type = arg;
DEB_D(("VIDIOC_STREAMON, type:%d\n",*type));
- if( fh == vv->streaming ) {
- DEB_D(("already capturing.\n"));
- return 0;
- }
-
err = video_begin(fh);
if( 0 != err) {
return err;
@@ -1166,13 +1167,26 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int
DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type));
- if( fh != vv->streaming ) {
- DEB_D(("this open is not capturing.\n"));
- return -EINVAL;
+ /* ugly: we need to copy some checks from video_end(),
+ because videobuf_streamoff() relies on the capture running.
+ check and fix this */
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S(("not capturing.\n"));
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S(("capturing, but in another open.\n"));
+ return -EBUSY;
}
err = videobuf_streamoff(file,q);
+ if (0 != err) {
+ DEB_D(("warning: videobuf_streamoff() failed.\n"));
video_end(fh, file);
+ } else {
+ err = video_end(fh, file);
+ }
return err;
}
case VIDIOCGMBUF:
@@ -1406,20 +1420,16 @@ static void video_close(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
struct saa7146_vv *vv = dev->vv_data;
- unsigned long flags;
+ int err;
- if( 0 != vv->ov_data ) {
- if( fh == vv->ov_data->fh ) {
- spin_lock_irqsave(&dev->slock,flags);
- saa7146_stop_preview(fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
- }
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ err = video_end(fh, file);
+ } else if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ err = saa7146_stop_preview(fh);
}
- if( fh == vv->streaming ) {
- video_end(fh, file);
- }
+ /* hmm, why is this function declared void? */
+ /* return err */
}
@@ -1447,23 +1457,16 @@ static ssize_t video_read(struct file *file, char *data, size_t count, loff_t *p
struct saa7146_vv *vv = dev->vv_data;
ssize_t ret = 0;
- int restart_overlay = 0;
- struct saa7146_fh *ov_fh = NULL;
-
DEB_EE(("called.\n"));
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
/* fixme: should we allow read() captures while streaming capture? */
- if( 0 != vv->streaming ) {
+ if (vv->video_fh == fh) {
DEB_S(("already capturing.\n"));
return -EBUSY;
}
-
- /* stop any active overlay */
- if( vv->ov_data != NULL ) {
- ov_fh = vv->ov_data->fh;
- saa7146_stop_preview(ov_fh);
- saa7146_res_free(ov_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
- restart_overlay = 1;
+ DEB_S(("already capturing in another open.\n"));
+ return -EBUSY;
}
ret = video_begin(fh);
@@ -1472,16 +1475,16 @@ static ssize_t video_read(struct file *file, char *data, size_t count, loff_t *p
}
ret = videobuf_read_one(file,&fh->video_q , data, count, ppos);
+ if (ret != 0) {
video_end(fh, file);
-
+ } else {
+ ret = video_end(fh, file);
+ }
out:
/* restart overlay if it was active before */
- if( 0 != restart_overlay ) {
- if (0 == saa7146_res_get(ov_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
- DEB_D(("cannot get overlay resources again!\n"));
- BUG();
- }
- saa7146_start_preview(ov_fh);
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
}
return ret;
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index ad3e82859f58..f0f1520130c5 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -18,12 +18,11 @@ config DVB
Please report problems regarding this driver to the LinuxDVB
mailing list.
- You might want add the following lines to your /etc/modprobe.conf:
+ You might want add the following lines to your /etc/modules.conf:
alias char-major-250 dvb
alias dvb dvb-ttpci
- install dvb-ttpci /sbin/modprobe --first-time -i dvb-ttpci && \
- /sbin/modprobe -a alps_bsru6 alps_bsrv2 \
+ below dvb-ttpci alps_bsru6 alps_bsrv2 \
grundig_29504-401 grundig_29504-491 \
ves1820
diff --git a/drivers/media/dvb/b2c2/skystar2.c b/drivers/media/dvb/b2c2/skystar2.c
index 39c84aedd770..dc0c687a0e34 100644
--- a/drivers/media/dvb/b2c2/skystar2.c
+++ b/drivers/media/dvb/b2c2/skystar2.c
@@ -2242,7 +2242,7 @@ static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (driver_initialize(pdev) != 0)
return -ENODEV;
- dvb_register_adapter(&dvb_adapter, skystar2_pci_driver.name);
+ dvb_register_adapter(&dvb_adapter, skystar2_pci_driver.name, THIS_MODULE);
if (dvb_adapter == NULL) {
printk("%s: Error registering DVB adapter\n", __FUNCTION__);
@@ -2342,6 +2342,8 @@ static struct pci_device_id skystar2_pci_tbl[] = {
{0,},
};
+MODULE_DEVICE_TABLE(pci, skystar2_pci_tbl);
+
static struct pci_driver skystar2_pci_driver = {
.name = "Technisat SkyStar2 driver",
.id_table = skystar2_pci_tbl,
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index b5006ecafc0a..77a37cfc9914 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -286,7 +286,7 @@ static int __init dvb_bt8xx_load_card( struct dvb_bt8xx_card *card)
}
- if ((result = dvb_register_adapter(&card->dvb_adapter, card->card_name)) < 0) {
+ if ((result = dvb_register_adapter(&card->dvb_adapter, card->card_name, THIS_MODULE)) < 0) {
printk("dvb_bt8xx: dvb_register_adapter failed (errno = %d)\n", result);
diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb/dvb-core/Makefile
index a58e89e8ceaf..7127789146cc 100644
--- a/drivers/media/dvb/dvb-core/Makefile
+++ b/drivers/media/dvb/dvb-core/Makefile
@@ -3,6 +3,7 @@
#
dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
- dvb_functions.o dvb_frontend.o dvb_i2c.o dvb_net.o dvb_ksyms.o dvb_ringbuffer.o
+ dvb_ca_en50221.o dvb_functions.o dvb_frontend.o \
+ dvb_i2c.o dvb_net.o dvb_ksyms.o dvb_ringbuffer.o
obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
new file mode 100644
index 000000000000..88ed3b92c37f
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -0,0 +1,1628 @@
+/*
+ * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * based on code:
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/semaphore.h>
+#include <asm/atomic.h>
+
+#include "dvb_ca_en50221.h"
+#include "dvb_functions.h"
+#include "dvb_ringbuffer.h"
+
+static int dvb_ca_en50221_debug = 0;
+#define dprintk if (dvb_ca_en50221_debug) printk
+
+#define INIT_TIMEOUT_SECS 5
+
+#define HOST_LINK_BUF_SIZE 0x200
+
+#define RX_BUFFER_SIZE 65535
+
+#define MAX_RX_PACKETS_PER_ITERATION 10
+
+#define CTRLIF_DATA 0
+#define CTRLIF_COMMAND 1
+#define CTRLIF_STATUS 1
+#define CTRLIF_SIZE_LOW 2
+#define CTRLIF_SIZE_HIGH 3
+
+#define CMDREG_HC 1 /* Host control */
+#define CMDREG_SW 2 /* Size write */
+#define CMDREG_SR 4 /* Size read */
+#define CMDREG_RS 8 /* Reset interface */
+#define CMDREG_FRIE 0x40 /* Enable FR interrupt */
+#define CMDREG_DAIE 0x80 /* Enable DA interrupt */
+#define IRQEN (CMDREG_DAIE)
+
+#define STATUSREG_RE 1 /* read error */
+#define STATUSREG_WE 2 /* write error */
+#define STATUSREG_FR 0x40 /* module free */
+#define STATUSREG_DA 0x80 /* data available */
+#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE) /* general transfer error */
+
+
+#define DVB_CA_SLOTSTATE_NONE 0
+#define DVB_CA_SLOTSTATE_UNINITIALISED 1
+#define DVB_CA_SLOTSTATE_RUNNING 2
+#define DVB_CA_SLOTSTATE_INVALID 3
+#define DVB_CA_SLOTSTATE_WAITREADY 4
+#define DVB_CA_SLOTSTATE_VALIDATE 5
+#define DVB_CA_SLOTSTATE_WAITFR 6
+#define DVB_CA_SLOTSTATE_LINKINIT 7
+
+
+/* Information on a CA slot */
+struct dvb_ca_slot {
+
+ /* current state of the CAM */
+ int slot_state;
+
+ /* Number of CAMCHANGES that have occurred since last processing */
+ atomic_t camchange_count;
+
+ /* Type of last CAMCHANGE */
+ int camchange_type;
+
+ /* base address of CAM config */
+ u32 config_base;
+
+ /* value to write into Config Control register */
+ u8 config_option;
+
+ /* if 1, the CAM supports DA IRQs */
+ u8 da_irq_supported:1;
+
+ /* size of the buffer to use when talking to the CAM */
+ int link_buf_size;
+
+ /* semaphore for syncing access to slot structure */
+ struct semaphore sem;
+
+ /* buffer for incoming packets */
+ struct dvb_ringbuffer rx_buffer;
+
+ /* timer used during various states of the slot */
+ unsigned long timeout;
+};
+
+/* Private CA-interface information */
+struct dvb_ca_private {
+
+ /* pointer back to the public data structure */
+ struct dvb_ca_en50221* pub;
+
+ /* the DVB device */
+ struct dvb_device *dvbdev;
+
+ /* Flags describing the interface (DVB_CA_FLAG_*) */
+ u32 flags;
+
+ /* number of slots supported by this CA interface */
+ unsigned int slot_count;
+
+ /* information on each slot */
+ struct dvb_ca_slot* slot_info;
+
+ /* wait queues for read() and write() operations */
+ wait_queue_head_t wait_queue;
+
+ /* PID of the monitoring thread */
+ pid_t thread_pid;
+
+ /* Wait queue used when shutting thread down */
+ wait_queue_head_t thread_queue;
+
+ /* Flag indicating when thread should exit */
+ int exit:1;
+
+ /* Flag indicating if the CA device is open */
+ int open:1;
+
+ /* Flag indicating the thread should wake up now */
+ int wakeup:1;
+
+ /* Delay the main thread should use */
+ unsigned long delay;
+
+ /* Slot to start looking for data to read from in the next user-space read operation */
+ int next_read_slot;
+};
+
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private* ca);
+static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebuf, int ecount);
+static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* ebuf, int ecount);
+
+
+/**
+ * Safely find needle in haystack.
+ *
+ * @param haystack Buffer to look in.
+ * @param hlen Number of bytes in haystack.
+ * @param needle Buffer to find.
+ * @param nlen Number of bytes in needle.
+ * @return Pointer into haystack needle was found at, or NULL if not found.
+ */
+static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen)
+{
+ int i;
+
+ if (hlen < nlen) return NULL;
+
+ for(i=0; i<= hlen - nlen; i++) {
+ if (!strncmp(haystack+i, needle, nlen)) return haystack+i;
+ }
+
+ return NULL;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 physical interface functions */
+
+
+/**
+ * Check CAM status.
+ */
+static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot)
+{
+ int slot_status;
+ int status;
+ int cam_present_now;
+ int cam_changed;
+
+ /* IRQ mode */
+ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) {
+ return (atomic_read(&ca->slot_info[slot].camchange_count) != 0);
+ }
+
+ /* poll mode */
+ if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+ slot_status = ca->pub->poll_slot_status(ca->pub, slot);
+ up(&ca->slot_info[slot].sem);
+
+ cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1: 0;
+ cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1: 0;
+ if (!cam_changed) {
+ int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE);
+ cam_changed = (cam_present_now != cam_present_old);
+ }
+
+ if (cam_changed) {
+ if (!cam_present_now) {
+ ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ } else {
+ ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+ }
+ atomic_set(&ca->slot_info[slot].camchange_count, 1);
+ } else {
+ if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
+ (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
+ // move to validate state if reset is completed
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+ }
+ }
+
+ return cam_changed;
+}
+
+
+/**
+ * Wait for flags to become set on the STATUS register on a CAM interface,
+ * checking for errors and timeout.
+ *
+ * @param ca CA instance.
+ * @param slot Slot on interface.
+ * @param waitfor Flags to wait for.
+ * @param timeout_ms Timeout in milliseconds.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private* ca, int slot, u8 waitfor, int timeout_hz)
+{
+ unsigned long timeout;
+ unsigned long start;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* loop until timeout elapsed */
+ start = jiffies;
+ timeout = jiffies + timeout_hz;
+ while(1) {
+ /* read the status and check for error */
+ int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (res < 0) return -EIO;
+
+ /* if we got the flags, it was successful! */
+ if (res & waitfor) {
+ dprintk("%s succeeded timeout:%lu\n", __FUNCTION__, jiffies - start);
+ return 0;
+ }
+
+ /* check for timeout */
+ if (time_after(jiffies, timeout)) {
+ break;
+ }
+
+ /* wait for a bit */
+ dvb_delay(1);
+ }
+
+ dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start);
+
+ /* if we get here, we've timed out */
+ return -ETIMEDOUT;
+}
+
+
+/**
+ * Initialise the link layer connection to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, nonzero on failure.
+ */
+static int dvb_ca_en50221_link_init(struct dvb_ca_private* ca, int slot)
+{
+ int ret;
+ int buf_size;
+ u8 buf[2];
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* we'll be determining these during this function */
+ ca->slot_info[slot].da_irq_supported = 0;
+
+ /* reset the link interface. Note CAM IRQs are disabled */
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, CMDREG_RS)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ/10)) != 0) return ret;
+
+ /* set the host link buffer size temporarily. it will be overwritten with the
+ * real negotiated size later. */
+ ca->slot_info[slot].link_buf_size = 2;
+
+ /* read the buffer size from the CAM */
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ/10)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) return -EIO;
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) return ret;
+
+ /* store it, and choose the minimum of our buffer and the CAM's buffer size */
+ buf_size = (buf[0] << 8) | buf[1];
+ if (buf_size > HOST_LINK_BUF_SIZE) buf_size = HOST_LINK_BUF_SIZE;
+ ca->slot_info[slot].link_buf_size = buf_size;
+ buf[0] = buf_size >> 8;
+ buf[1] = buf_size & 0xff;
+ dprintk("Chosen link buffer size of %i\n", buf_size);
+
+ /* write the buffer size to the CAM */
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ/10)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2) return -EIO;
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) return ret;
+
+ /* success */
+ return 0;
+}
+
+/**
+ * Read a tuple from attribute memory.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ * @param address Address to read from. Updated.
+ * @param tupleType Tuple id byte. Updated.
+ * @param tupleLength Tuple length. Updated.
+ * @param tuple Dest buffer for tuple (must be 256 bytes). Updated.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_read_tuple(struct dvb_ca_private* ca, int slot,
+ int* address, int* tupleType, int* tupleLength, u8* tuple)
+{
+ int i;
+ int _tupleType;
+ int _tupleLength;
+ int _address = *address;
+
+ /* grab the next tuple length and type */
+ if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) return _tupleType;
+ if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address+2)) < 0) return _tupleLength;
+ _address += 4;
+
+ dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength);
+
+ /* read in the whole tuple */
+ for(i=0; i< _tupleLength; i++) {
+ tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i*2));
+ dprintk(" 0x%02x: 0x%02x %c\n", i, tuple[i] & 0xff, ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
+ }
+ _address += (_tupleLength*2);
+
+ // success
+ *tupleType = _tupleType;
+ *tupleLength = _tupleLength;
+ *address = _address;
+ return 0;
+}
+
+
+/**
+ * Parse attribute memory of a CAM module, extracting Config register, and checking
+ * it is a DVB CAM module.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot)
+{
+ int address = 0;
+ int tupleLength;
+ int tupleType;
+ u8 tuple[257];
+ char* dvb_str;
+ int rasz;
+ int status;
+ int got_cftableentry = 0;
+ int end_chain = 0;
+ int i;
+ u16 manfid = 0;
+ u16 devid = 0;
+
+
+ // CISTPL_DEVICE_0A
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x1D) return -EINVAL;
+
+
+
+ // CISTPL_DEVICE_0C
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x1C) return -EINVAL;
+
+
+
+ // CISTPL_VERS_1
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x15) return -EINVAL;
+
+
+
+ // CISTPL_MANFID
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x20) return -EINVAL;
+ if (tupleLength != 4) return -EINVAL;
+ manfid = (tuple[1] << 8) | tuple[0];
+ devid = (tuple[3] << 8) | tuple[2];
+
+
+
+ // CISTPL_CONFIG
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x1A) return -EINVAL;
+ if (tupleLength < 3) return -EINVAL;
+
+ /* extract the configbase */
+ rasz = tuple[0] & 3;
+ if (tupleLength < (3 + rasz + 14)) return -EINVAL;
+ ca->slot_info[slot].config_base = 0;
+ for(i=0; i< rasz+1; i++) {
+ ca->slot_info[slot].config_base |= (tuple[2+i] << (8*i));
+ }
+
+ /* check it contains the correct DVB string */
+ dvb_str = findstr(tuple, tupleLength, "DVB_CI_V", 8);
+ if (dvb_str == NULL) return -EINVAL;
+ if (tupleLength < ((dvb_str - (char*) tuple) + 12)) return -EINVAL;
+
+ /* is it a version we support? */
+ if (strncmp(dvb_str + 8, "1.00", 4)) {
+ printk("dvb_ca: Unsupported DVB CAM module version %c%c%c%c\n",
+ dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
+ return -EINVAL;
+ }
+
+ /* process the CFTABLE_ENTRY tuples, and any after those */
+ while((!end_chain) && (address < 0x1000)) {
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ switch(tupleType) {
+ case 0x1B: // CISTPL_CFTABLE_ENTRY
+ if (tupleLength < (2+11+17)) break;
+
+ /* if we've already parsed one, just use it */
+ if (got_cftableentry) break;
+
+ /* get the config option */
+ ca->slot_info[slot].config_option = tuple[0] & 0x3f;
+
+ /* OK, check it contains the correct strings */
+ if ((findstr(tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
+ (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) break;
+
+ got_cftableentry = 1;
+ break;
+
+ case 0x14: // CISTPL_NO_LINK
+ break;
+
+ case 0xFF: // CISTPL_END
+ end_chain = 1;
+ break;
+
+ default: /* Unknown tuple type - just skip this tuple and move to the next one */
+ dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType, tupleLength);
+ break;
+ }
+ }
+
+ if ((address > 0x1000) || (!got_cftableentry)) return -EINVAL;
+
+ dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
+ manfid, devid,
+ ca->slot_info[slot].config_base,
+ ca->slot_info[slot].config_option);
+
+ // success!
+ return 0;
+}
+
+
+/**
+ * Set CAM's configoption correctly.
+ *
+ * @param ca CA instance.
+ * @param slot Slot containing the CAM.
+ */
+static int dvb_ca_en50221_set_configoption(struct dvb_ca_private* ca, int slot)
+{
+ int configoption;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* set the config option */
+ ca->pub->write_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option);
+
+ /* check it */
+ configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
+ dprintk("Set configoption 0x%x, read configoption 0x%x\n",
+ ca->slot_info[slot].config_option, configoption & 0x3f);
+
+ /* fine! */
+ return 0;
+
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It reads a buffer of
+ * data from the CAM. The data can either be stored in a supplied buffer, or
+ * automatically be added to the slot's rx_buffer.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to read from.
+ * @param ebuf If non-NULL, the data will be written to this buffer. If NULL,
+ * the data will be added into the buffering system as a normal fragment.
+ * @param ecount Size of ebuf. Ignored if ebuf is NULL.
+ *
+ * @return Number of bytes read, or < 0 on error
+ */
+static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebuf, int ecount)
+{
+ int bytes_read;
+ int status;
+ u8 buf[HOST_LINK_BUF_SIZE];
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* acquire the slot */
+ if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+
+ /* check if we have space for a link buf in the rx_buffer */
+ if (ebuf == NULL) {
+ if (dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer) <
+ (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
+ status = -EAGAIN;
+ goto exit;
+ }
+ }
+
+ /* reset the interface if there's been a tx error */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (status & STATUSREG_TXERR) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ if (!(status & STATUSREG_DA)) {
+ /* no data */
+ status = 0;
+ goto exit;
+ }
+
+ /* read the amount of data */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0) goto exit;
+ bytes_read = status << 8;
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0) goto exit;
+ bytes_read |= status;
+
+ /* check it will fit */
+ if (ebuf == NULL) {
+ if (bytes_read > ca->slot_info[slot].link_buf_size) {
+ printk("dvb_ca: CAM tried to send a buffer larger than the link buffer size!\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ if (bytes_read < 2) {
+ printk("dvb_ca: CAM sent a buffer that was less than 2 bytes!\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ } else {
+ if (bytes_read > ecount) {
+ printk("dvb_ca: CAM tried to send a buffer larger than the ecount size!\n");
+ status = -EIO;
+ goto exit;
+ }
+ }
+
+ /* fill the buffer */
+ for(i=0; i < bytes_read; i++) {
+ /* read byte and check */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0) goto exit;
+
+ /* OK, store it in the buffer */
+ buf[i] = status;
+ }
+
+ /* check for read error (RE should now go to 0) */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (status & STATUSREG_RE) {
+ status = -EIO;
+ goto exit;
+ }
+
+ /* OK, add it to the receive buffer, or copy into external buffer if supplied */
+ if (ebuf == NULL) {
+ dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read, 0);
+ } else {
+ memcpy(ebuf, buf, bytes_read);
+ }
+
+ /* wake up readers when a last_fragment is received */
+ if ((buf[1] & 0x80) == 0x00) {
+ wake_up_interruptible(&ca->wait_queue);
+ }
+
+ status = bytes_read;
+
+exit:
+ up(&ca->slot_info[slot].sem);
+ return status;
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It writes a buffer of data
+ * to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to write to.
+ * @param ebuf The data in this buffer is treated as a complete link-level packet to
+ * be written.
+ * @param count Size of ebuf.
+ *
+ * @return Number of bytes written, or < 0 on error.
+ */
+static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* buf, int bytes_write)
+{
+ int status;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+
+ // sanity check
+ if (bytes_write > ca->slot_info[slot].link_buf_size) return -EINVAL;
+
+ /* acquire the slot */
+ if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+
+ /* reset the interface if there's been a tx error */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exitnowrite;
+ if (status & STATUSREG_TXERR) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exitnowrite;
+ }
+
+ /* check if interface is actually waiting for us to read from it */
+ if (status & STATUSREG_DA) {
+ status = -EAGAIN;
+ goto exitnowrite;
+ }
+
+ /* OK, set HC bit */
+ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_HC)) != 0) goto exit;
+
+ /* check if interface is still free */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (!(status & STATUSREG_FR)) {
+ /* it wasn't free => try again later */
+ status = -EAGAIN;
+ goto exit;
+ }
+
+ /* send the amount of data */
+ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0) goto exit;
+ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW, bytes_write & 0xff)) != 0) goto exit;
+
+ /* send the buffer */
+ for(i=0; i < bytes_write; i++) {
+ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0) goto exit;
+ }
+
+ /* check for write error (WE should now be 0) */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (status & STATUSREG_WE) {
+ status = -EIO;
+ goto exit;
+ }
+ status = bytes_write;
+
+exit:
+ ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
+
+exitnowrite:
+ up(&ca->slot_info[slot].sem);
+ return status;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 higher level functions */
+
+
+/**
+ * A CAM has been removed => shut it down.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to shut down.
+ */
+static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot)
+{
+ int status;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+ ca->pub->slot_shutdown(ca->pub, slot);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+ if (ca->slot_info[slot].rx_buffer.data) vfree(ca->slot_info[slot].rx_buffer.data);
+ ca->slot_info[slot].rx_buffer.data = NULL;
+ up(&ca->slot_info[slot].sem);
+
+ /* need to wake up all processes to check if they're now
+ trying to write to a defunct CAM */
+ wake_up_interruptible(&ca->wait_queue);
+
+ dprintk("Slot %i shutdown\n", slot);
+
+ /* success */
+ return 0;
+}
+
+
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ * @param change_type One of the DVB_CA_CAMCHANGE_* values.
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type)
+{
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+
+ dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
+
+ switch(change_type) {
+ case DVB_CA_EN50221_CAMCHANGE_REMOVED:
+ case DVB_CA_EN50221_CAMCHANGE_INSERTED:
+ break;
+
+ default:
+ return;
+ }
+
+ ca->slot_info[slot].camchange_type = change_type;
+ atomic_inc(&ca->slot_info[slot].camchange_count);
+ dvb_ca_en50221_thread_wakeup(ca);
+}
+
+
+/**
+ * A CAMREADY IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot)
+{
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+
+ dprintk("CAMREADY IRQ slot:%i\n", slot);
+
+ if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+ dvb_ca_en50221_thread_wakeup(ca);
+ }
+}
+
+
+/**
+ * An FR or DA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* pubca, int slot)
+{
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+ int flags;
+
+ dprintk("FR/DA IRQ slot:%i\n", slot);
+
+ switch(ca->slot_info[slot].slot_state) {
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
+ if (flags & STATUSREG_DA) {
+ dprintk("CAM supports DA IRQ\n");
+ ca->slot_info[slot].da_irq_supported = 1;
+ }
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
+ if (flags & STATUSREG_DA) {
+ dvb_ca_en50221_thread_wakeup(ca);
+ }
+ break;
+ }
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 thread functions */
+
+/**
+ * Wake up the DVB CA thread
+ *
+ * @param ca CA instance.
+ */
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private* ca)
+{
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ ca->wakeup = 1;
+ mb();
+ wake_up_interruptible(&ca->thread_queue);
+}
+
+/**
+ * Used by the CA thread to determine if an early wakeup is necessary
+ *
+ * @param ca CA instance.
+ */
+static int dvb_ca_en50221_thread_should_wakeup(struct dvb_ca_private* ca)
+{
+ if (ca->wakeup) {
+ ca->wakeup = 0;
+ return 1;
+ }
+ if (ca->exit) return 1;
+
+ return 0;
+}
+
+
+/**
+ * Update the delay used by the thread.
+ *
+ * @param ca CA instance.
+ */
+static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private* ca)
+{
+ int delay;
+ int curdelay = 100000000;
+ int slot;
+
+ for(slot=0; slot < ca->slot_count; slot++) {
+ switch(ca->slot_info[slot].slot_state) {
+ default:
+ case DVB_CA_SLOTSTATE_NONE:
+ case DVB_CA_SLOTSTATE_INVALID:
+ delay = HZ*60;
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) {
+ delay = HZ/10;
+ }
+ break;
+
+ case DVB_CA_SLOTSTATE_UNINITIALISED:
+ case DVB_CA_SLOTSTATE_WAITREADY:
+ case DVB_CA_SLOTSTATE_VALIDATE:
+ case DVB_CA_SLOTSTATE_WAITFR:
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ delay = HZ/10;
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ delay = HZ*60;
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) {
+ delay = HZ/10;
+ }
+ if (ca->open) {
+ if ((!ca->slot_info[slot].da_irq_supported) ||
+ (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) {
+ delay = HZ/100;
+ }
+ }
+ break;
+ }
+
+ if (delay < curdelay) curdelay = delay;
+ }
+
+ ca->delay = curdelay;
+}
+
+
+
+/**
+ * Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
+ */
+static int dvb_ca_en50221_thread(void* data)
+{
+ struct dvb_ca_private *ca = (struct dvb_ca_private*) data;
+ char name[15];
+ int slot;
+ int flags;
+ int pktcount;
+ void* rxbuf;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* setup kernel thread */
+ snprintf(name, sizeof(name), "kdvb-ca-%i:%i", ca->dvbdev->adapter->num, ca->dvbdev->id);
+ dvb_kernel_thread_setup(name);
+
+ /* choose the correct initial delay */
+ dvb_ca_en50221_thread_update_delay(ca);
+
+ /* main loop */
+ while(!ca->exit) {
+ /* sleep for a bit */
+ if (!ca->wakeup) {
+ flags = wait_event_interruptible_timeout(ca->thread_queue, dvb_ca_en50221_thread_should_wakeup(ca), ca->delay);
+ if ((flags == -ERESTARTSYS) || ca->exit) {
+ /* got signal or quitting */
+ break;
+ }
+ }
+ ca->wakeup = 0;
+
+ /* go through all the slots processing them */
+ for(slot=0; slot < ca->slot_count; slot++) {
+
+ // check the cam status + deal with CAMCHANGEs
+ while(dvb_ca_en50221_check_camstatus(ca, slot)) {
+ /* clear down an old CI slot if necessary */
+ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) dvb_ca_en50221_slot_shutdown(ca, slot);
+
+ /* if a CAM is NOW present, initialise it */
+ if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
+ }
+
+ /* we've handled one CAMCHANGE */
+ dvb_ca_en50221_thread_update_delay(ca);
+ atomic_dec(&ca->slot_info[slot].camchange_count);
+ }
+
+ // CAM state machine
+ switch(ca->slot_info[slot].slot_state) {
+ case DVB_CA_SLOTSTATE_NONE:
+ case DVB_CA_SLOTSTATE_INVALID:
+ // no action needed
+ break;
+
+ case DVB_CA_SLOTSTATE_UNINITIALISED:
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITREADY;
+ ca->pub->slot_reset(ca->pub, slot);
+ ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+ break;
+
+ case DVB_CA_SLOTSTATE_WAITREADY:
+ if (time_after(jiffies, ca->slot_info[slot].timeout)) {
+ printk("dvb_ca: PC card did not respond :(\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ // no other action needed; will automatically change state when ready
+ break;
+
+ case DVB_CA_SLOTSTATE_VALIDATE:
+ if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) {
+ printk("dvb_ca: Invalid PC card inserted :(\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
+ printk("dvb_ca: Unable to initialise CAM :(\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+
+ dprintk("DVB CAM validated successfully\n");
+
+ ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITFR;
+ ca->wakeup = 1;
+ break;
+
+ case DVB_CA_SLOTSTATE_WAITFR:
+ if (time_after(jiffies, ca->slot_info[slot].timeout)) {
+ printk("dvb_ca: DVB CAM did not respond :(\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+
+ flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (flags & STATUSREG_FR) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ ca->wakeup = 1;
+ }
+ break;
+
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ if (dvb_ca_en50221_link_init(ca, slot) != 0) {
+ printk("dvb_ca: DVB CAM link initialisation failed :(\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+
+ rxbuf = vmalloc(RX_BUFFER_SIZE);
+ if (rxbuf == NULL) {
+ printk("dvb_ca: Unable to allocate CAM rx buffer :(\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE);
+
+ ca->pub->slot_ts_enable(ca->pub, slot);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING;
+ dvb_ca_en50221_thread_update_delay(ca);
+ printk("dvb_ca: DVB CAM detected and initialised successfully\n");
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ if (!ca->open) break;
+
+ pktcount = 0;
+ while(dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) {
+ if (!ca->open) break;
+
+ /* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */
+ if (dvb_ca_en50221_check_camstatus(ca, slot)) {
+ // we dont want to sleep on the next iteration so we can handle the cam change
+ ca->wakeup = 1;
+ break;
+ }
+
+ /* check if we've hit our limit this time */
+ if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
+ // dont sleep; there is likely to be more data to read
+ ca->wakeup = 1;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /* completed */
+ ca->thread_pid = 0;
+ mb();
+ wake_up_interruptible (&ca->thread_queue);
+ return 0;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 IO interface functions */
+
+/**
+ * Real ioctl implementation.
+ * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ * @param cmd IOCTL command.
+ * @param arg Associated argument.
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg)
+{
+ struct dvb_device* dvbdev=(struct dvb_device*) file->private_data;
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) dvbdev->priv;
+ int err=0;
+ int slot;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch (cmd) {
+ case CA_RESET:
+ for(slot = 0; slot < ca->slot_count; slot++) {
+ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
+ dvb_ca_en50221_slot_shutdown(ca, slot);
+ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
+ dvb_ca_en50221_camchange_irq(ca->pub, slot, DVB_CA_EN50221_CAMCHANGE_INSERTED);
+ }
+ }
+ ca->next_read_slot = 0;
+ dvb_ca_en50221_thread_wakeup(ca);
+ break;
+
+ case CA_GET_CAP:
+ {
+ struct ca_caps *caps = (struct ca_caps*) parg;
+
+ caps->slot_num=ca->slot_count;
+ caps->slot_type=CA_CI_LINK;
+ caps->descr_num=0;
+ caps->descr_type=0;
+ break;
+ }
+
+
+ case CA_GET_SLOT_INFO:
+ {
+ struct ca_slot_info *info=(struct ca_slot_info *)parg;
+
+ if ((info->num > ca->slot_count) || (info->num < 0))
+ return -EINVAL;
+
+ info->type = CA_CI_LINK;
+ info->flags = 0;
+ if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE) &&
+ (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) {
+ info->flags = CA_CI_MODULE_PRESENT;
+ }
+ if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+ info->flags |= CA_CI_MODULE_READY;
+ }
+ break;
+ }
+
+ default:
+ err=-EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+
+/**
+ * Wrapper for ioctl implementation.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ * @param cmd IOCTL command.
+ * @param arg Associated argument.
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(inode, file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
+}
+
+
+/**
+ * Implementation of write() syscall.
+ *
+ * @param file File structure.
+ * @param buf Source buffer.
+ * @param count Size of source buffer.
+ * @param ppos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv;
+ u8 slot, connection_id;
+ int status;
+ char fragbuf[HOST_LINK_BUF_SIZE];
+ int fragpos = 0;
+ int fraglen;
+ unsigned long timeout;
+ int written;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
+ if (count < 2) return -EINVAL;
+
+ /* extract slot & connection id */
+ if (copy_from_user(&slot, buf, 1)) return -EFAULT;
+ if (copy_from_user(&connection_id, buf+1, 1)) return -EFAULT;
+ buf+=2;
+ count-=2;
+
+ /* check if the slot is actually running */
+ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) return -EINVAL;
+
+ /* fragment the packets & store in the buffer */
+ while(fragpos < count) {
+ fraglen = ca->slot_info[slot].link_buf_size - 2;
+ if ((count - fragpos) < fraglen) fraglen = count - fragpos;
+
+ fragbuf[0] = connection_id;
+ fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
+ if ((status = copy_from_user(fragbuf+2, buf+fragpos, fraglen)) != 0) goto exit;
+
+ timeout = jiffies + HZ/2;
+ written = 0;
+ while(!time_after(jiffies, timeout)) {
+ status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen+2);
+ if (status == (fraglen+2)) {
+ written = 1;
+ break;
+ }
+ if (status != -EAGAIN) goto exit;
+
+ dvb_delay(1);
+ }
+ if (!written) {
+ status = -EIO;
+ goto exit;
+ }
+
+ fragpos += fraglen;
+ }
+ status = count + 2;
+
+exit:
+ return status;
+}
+
+
+/**
+ * Condition for waking up in dvb_ca_en50221_io_read_condition
+ */
+static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* result, int* _slot)
+{
+ int slot;
+ int slot_count = 0;
+ int idx;
+ int fraglen;
+ int connection_id = -1;
+ int found = 0;
+ u8 hdr[2];
+
+ slot = ca->next_read_slot;
+ while((slot_count < ca->slot_count) && (!found)) {
+ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) goto nextslot;
+
+ if ((*result = down_interruptible(&ca->slot_info[slot].sem)) != 0) return 1;
+
+ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
+ while(idx != -1) {
+ dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+ if (connection_id == -1) connection_id = hdr[0];
+ if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) {
+ *_slot = slot;
+ found = 1;
+ break;
+ }
+
+ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
+ }
+
+ if (!found) up(&ca->slot_info[slot].sem);
+
+nextslot:
+ slot = (slot + 1) % ca->slot_count;
+ slot_count++;
+ }
+
+ ca->next_read_slot = slot;
+ return found;
+}
+
+
+/**
+ * Implementation of read() syscall.
+ *
+ * @param file File structure.
+ * @param buf Destination buffer.
+ * @param count Size of destination buffer.
+ * @param ppos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv;
+ int status;
+ int result = 0;
+ u8 hdr[2];
+ int slot;
+ int connection_id = -1;
+ size_t idx, idx2;
+ int last_fragment = 0;
+ size_t fraglen;
+ int pktlen;
+ int dispose = 0;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
+ if (count < 2) return -EINVAL;
+
+ /* wait for some data */
+ if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) {
+
+ /* if we're in nonblocking mode, exit immediately */
+ if (file->f_flags & O_NONBLOCK) return -EWOULDBLOCK;
+
+ /* wait for some data */
+ status = wait_event_interruptible(ca->wait_queue, dvb_ca_en50221_io_read_condition(ca, &result, &slot));
+ }
+ if ((status < 0) || (result < 0)) {
+ if (result) return result;
+ return status;
+ }
+
+ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
+ pktlen = 2;
+ do {
+ if (idx == -1) {
+ printk("dvb_ca: BUG: read packet ended before last_fragment encountered\n");
+ status = -EIO;
+ goto exit;
+ }
+
+ dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+ if (connection_id == -1) connection_id = hdr[0];
+ if (hdr[0] == connection_id) {
+ if (pktlen < count) {
+ if ((pktlen + fraglen - 2) > count) {
+ fraglen = count - pktlen;
+ } else {
+ fraglen -= 2;
+ }
+
+ if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2, buf + pktlen, fraglen, 1)) < 0) {
+ goto exit;
+ }
+ pktlen += fraglen;
+ }
+
+ if ((hdr[1] & 0x80) == 0) last_fragment = 1;
+ dispose = 1;
+ }
+
+ idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
+ if (dispose) dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx);
+ idx = idx2;
+ dispose = 0;
+ } while (!last_fragment);
+
+ hdr[0] = slot;
+ hdr[1] = connection_id;
+ if ((status = copy_to_user(buf, hdr, 2)) != 0) goto exit;
+ status = pktlen;
+
+exit:
+ up(&ca->slot_info[slot].sem);
+ return status;
+}
+
+
+/**
+ * Implementation of file open syscall.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+ struct dvb_ca_private *ca = (struct dvb_ca_private*) dvbdev->priv;
+ int err;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ err=dvb_generic_open(inode, file);
+ if (err<0)
+ return err;
+
+ for(i=0; i< ca->slot_count; i++) {
+ if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+ dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
+ }
+ }
+
+ ca->open = 1;
+ dvb_ca_en50221_thread_update_delay(ca);
+ dvb_ca_en50221_thread_wakeup(ca);
+
+ return 0;
+}
+
+
+/**
+ * Implementation of file close syscall.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv;
+ int err;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* mark the CA device as closed */
+ ca->open = 0;
+ dvb_ca_en50221_thread_update_delay(ca);
+
+ err=dvb_generic_release(inode, file);
+ if (err<0)
+ return err;
+ return 0;
+}
+
+
+/**
+ * Implementation of poll() syscall.
+ *
+ * @param file File concerned.
+ * @param wait poll wait table.
+ *
+ * @return Standard poll mask.
+ */
+static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
+{
+ struct dvb_device* dvbdev = (struct dvb_device*) file->private_data;
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) dvbdev->priv;
+ unsigned int mask=0;
+ int slot;
+ int result = 0;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+ up(&ca->slot_info[slot].sem);
+ mask |= POLLIN;
+ }
+
+ /* if there is something, return now */
+ if (mask) return mask;
+
+ /* wait for something to happen */
+ poll_wait(file, &ca->wait_queue, wait);
+
+ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+ up(&ca->slot_info[slot].sem);
+ mask |= POLLIN;
+ }
+
+ return mask;
+}
+
+
+static struct file_operations dvb_ca_fops = {
+ owner: THIS_MODULE,
+ read: dvb_ca_en50221_io_read,
+ write: dvb_ca_en50221_io_write,
+ ioctl: dvb_ca_en50221_io_ioctl,
+ open: dvb_ca_en50221_io_open,
+ release: dvb_ca_en50221_io_release,
+ poll: dvb_ca_en50221_io_poll,
+};
+
+static struct dvb_device dvbdev_ca = {
+ priv: 0,
+ users: 1,
+ readers: 1,
+ writers: 1,
+ fops: &dvb_ca_fops,
+};
+
+
+/* ******************************************************************************** */
+/* Initialisation/shutdown functions */
+
+
+/**
+ * Initialise a new DVB CA EN50221 interface device.
+ *
+ * @param dvb_adapter DVB adapter to attach the new CA device to.
+ * @param ca The dvb_ca instance.
+ * @param flags Flags describing the CA device (DVB_CA_FLAG_*).
+ * @param slot_count Number of slots supported.
+ *
+ * @return 0 on success, nonzero on failure
+ */
+int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* pubca, int flags, int slot_count)
+{
+ int ret;
+ struct dvb_ca_private* ca = NULL;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (slot_count < 1) return -EINVAL;
+
+ /* initialise the system data */
+ if ((ca = (struct dvb_ca_private*) kmalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ memset(ca, 0, sizeof(struct dvb_ca_private));
+ ca->pub = pubca;
+ ca->flags = flags;
+ ca->slot_count = slot_count;
+ if ((ca->slot_info = kmalloc(sizeof(struct dvb_ca_slot) * slot_count, GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ memset(ca->slot_info, 0, sizeof(struct dvb_ca_slot) * slot_count);
+ init_waitqueue_head(&ca->wait_queue);
+ ca->thread_pid = 0;
+ init_waitqueue_head(&ca->thread_queue);
+ ca->exit = 0;
+ ca->open = 0;
+ ca->wakeup = 0;
+ ca->next_read_slot = 0;
+ pubca->private = ca;
+
+ /* register the DVB device */
+ ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA);
+ if (ret) goto error;
+
+ /* now initialise each slot */
+ for(i=0; i< slot_count; i++) {
+ memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot));
+ ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
+ atomic_set(&ca->slot_info[i].camchange_count, 0);
+ ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ init_MUTEX(&ca->slot_info[i].sem);
+ }
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ goto error;
+ }
+ mb();
+
+ /* create a kthread for monitoring this CA device */
+ ret = kernel_thread (dvb_ca_en50221_thread, ca, 0);
+ if (ret < 0) {
+ printk("dvb_ca_init: failed to start kernel_thread (%d)\n", ret);
+ goto error;
+ }
+ ca->thread_pid = ret;
+ return 0;
+
+error:
+ if (ca != NULL) {
+ if (ca->dvbdev != NULL) dvb_unregister_device(ca->dvbdev);
+ if (ca->slot_info != NULL) kfree(ca->slot_info);
+ kfree(ca);
+ }
+ pubca->private = NULL;
+ return ret;
+}
+
+
+
+/**
+ * Release a DVB CA EN50221 interface device.
+ *
+ * @param ca_dev The dvb_device_t instance for the CA device.
+ * @param ca The associated dvb_ca instance.
+ */
+void dvb_ca_en50221_release(struct dvb_ca_en50221* pubca)
+{
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* shutdown the thread if there was one */
+ if (ca->thread_pid) {
+ if (kill_proc(ca->thread_pid, 0, 1) == -ESRCH) {
+ printk("dvb_ca_release: thread PID %d already died\n", ca->thread_pid);
+ } else {
+ ca->exit = 1;
+ mb();
+ dvb_ca_en50221_thread_wakeup(ca);
+ wait_event_interruptible(ca->thread_queue, ca->thread_pid == 0);
+ }
+ }
+
+ for(i=0; i< ca->slot_count; i++) {
+ dvb_ca_en50221_slot_shutdown(ca, i);
+ }
+ kfree(ca->slot_info);
+ dvb_unregister_device(ca->dvbdev);
+ kfree(ca);
+ pubca->private = NULL;
+}
+
+MODULE_PARM(dvb_ca_en50221_debug,"i");
+
+MODULE_PARM_DESC(dvb_ca_en50221_debug, "enable verbose debug messages");
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
new file mode 100644
index 000000000000..8458f7afdadb
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
@@ -0,0 +1,128 @@
+/*
+ * dvb_ca.h: generic DVB functions for EN50221 CA interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DVB_CA_EN50221_H_
+#define _DVB_CA_EN50221_H_
+
+#include <linux/list.h>
+#include <linux/dvb/ca.h>
+
+#include "dvbdev.h"
+
+#define DVB_CA_EN50221_POLL_CAM_PRESENT 1
+#define DVB_CA_EN50221_POLL_CAM_CHANGED 2
+#define DVB_CA_EN50221_POLL_CAM_READY 4
+
+#define DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE 1
+#define DVB_CA_EN50221_FLAG_IRQ_FR 2
+#define DVB_CA_EN50221_FLAG_IRQ_DA 4
+
+#define DVB_CA_EN50221_CAMCHANGE_REMOVED 0
+#define DVB_CA_EN50221_CAMCHANGE_INSERTED 1
+
+
+
+/* Structure describing a CA interface */
+struct dvb_ca_en50221 {
+
+ /* functions for accessing attribute memory on the CAM */
+ int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
+ int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value);
+
+ /* functions for accessing the control interface on the CAM */
+ int (*read_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address);
+ int (*write_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value);
+
+ /* Functions for controlling slots */
+ int (*slot_reset)(struct dvb_ca_en50221* ca, int slot);
+ int (*slot_shutdown)(struct dvb_ca_en50221* ca, int slot);
+ int (*slot_ts_enable)(struct dvb_ca_en50221* ca, int slot);
+
+ /*
+ * Poll slot status.
+ * Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set
+ */
+ int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot);
+
+ /* private data, used by caller */
+ void* data;
+
+ /* Opaque data used by the dvb_ca core. Do not modify! */
+ void* private;
+};
+
+
+
+
+/* ******************************************************************************** */
+/* Functions for reporting IRQ events */
+
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ * @param change_type One of the DVB_CA_CAMCHANGE_* values
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type);
+
+/**
+ * A CAMREADY IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot);
+
+/**
+ * An FR or a DA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot);
+
+
+
+/* ******************************************************************************** */
+/* Initialisation/shutdown functions */
+
+/**
+ * Initialise a new DVB CA device.
+ *
+ * @param dvb_adapter DVB adapter to attach the new CA device to.
+ * @param ca The dvb_ca instance.
+ * @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*).
+ * @param slot_count Number of slots supported.
+ *
+ * @return 0 on success, nonzero on failure
+ */
+extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count);
+
+/**
+ * Release a DVB CA device.
+ *
+ * @param ca The associated dvb_ca instance.
+ */
+extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca);
+
+
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 6c640223da92..ad096f12128e 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -192,7 +192,6 @@ static inline int dvb_dmx_swfilter_section_feed (struct dvb_demux_feed *feed)
struct dvb_demux *demux = feed->demux;
struct dvb_demux_filter *f = feed->filter;
struct dmx_section_feed *sec = &feed->feed.sec;
- u8 *buf = sec->secbuf;
int section_syntax_indicator;
if (!sec->is_filtering)
@@ -215,8 +214,6 @@ static inline int dvb_dmx_swfilter_section_feed (struct dvb_demux_feed *feed)
sec->seclen = 0;
- memset(buf, 0, DVB_DEMUX_MASK_MAX);
-
return 0;
}
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index d403c3f70c53..94d6cfabf1ef 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -6,6 +6,8 @@
* Holger Waechtler
* for convergence integrated media GmbH
*
+ * Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup)
+ *
* 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
@@ -37,9 +39,41 @@
#include "dvbdev.h"
#include "dvb_functions.h"
+#define FESTATE_IDLE 1
+#define FESTATE_RETUNE 2
+#define FESTATE_TUNING_FAST 4
+#define FESTATE_TUNING_SLOW 8
+#define FESTATE_TUNED 16
+#define FESTATE_ZIGZAG_FAST 32
+#define FESTATE_ZIGZAG_SLOW 64
+#define FESTATE_DISEQC 128
+#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
+#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
+#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
+#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW)
+/*
+ * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling.
+ * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune.
+ * FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress.
+ * FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower.
+ * FESTATE_TUNED. The frontend has successfully locked on.
+ * FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it.
+ * FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower.
+ * FESTATE_DISEQC. A DISEQC command has just been issued.
+ * FESTATE_WAITFORLOCK. When we're waiting for a lock.
+ * FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan.
+ * FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan.
+ * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
+ */
+
static int dvb_frontend_debug = 0;
static int dvb_shutdown_timeout = 5;
+static int dvb_override_frequency_bending = 0;
+static int dvb_force_auto_inversion = 0;
+static int dvb_override_tune_delay = 0;
+
+static int do_frequency_bending = 0;
#define dprintk if (dvb_frontend_debug) printk
@@ -66,13 +100,18 @@ struct dvb_frontend_data {
wait_queue_head_t wait_queue;
pid_t thread_pid;
unsigned long release_jiffies;
- unsigned long lost_sync_jiffies;
- int acquire_signal;
+ int state;
int bending;
int lnb_drift;
- int timeout_count;
- int lost_sync_count;
+ int inversion;
+ int auto_step;
+ int auto_sub_step;
+ int started_auto_step;
+ int min_delay;
+ int max_drift;
+ int step_size;
int exit;
+ int wakeup;
fe_status_t status;
};
@@ -170,7 +209,7 @@ static void dvb_bend_frequency (struct dvb_frontend_data *this_fe, int recursive
frequency += this_fe->lnb_drift;
frequency += this_fe->bending;
- if (this_fe != fe && fe->lost_sync_count != -1 &&
+ if (this_fe != fe && (fe->state != FESTATE_IDLE) &&
frequency > f - stepsize && frequency < f + stepsize)
{
if (recursive % 2)
@@ -193,9 +232,6 @@ static void dvb_call_frontend_notifiers (struct dvb_frontend_data *fe,
{
dprintk ("%s\n", __FUNCTION__);
- if ((fe->status & FE_HAS_LOCK) && !(s & FE_HAS_LOCK))
- fe->lost_sync_jiffies = jiffies;
-
if (((s ^ fe->status) & FE_HAS_LOCK) && (s & FE_HAS_LOCK))
dvb_delay (fe->info->notifier_delay);
@@ -293,40 +329,6 @@ static int dvb_frontend_get_event (struct dvb_frontend_data *fe,
return 0;
}
-
-static int dvb_frontend_set_parameters (struct dvb_frontend_data *fe,
- struct dvb_frontend_parameters *param,
- int first_trial)
-{
- struct dvb_frontend *frontend = &fe->frontend;
- int err;
-
- if (first_trial) {
- fe->timeout_count = 0;
- fe->lost_sync_count = 0;
- fe->lost_sync_jiffies = jiffies;
- fe->lnb_drift = 0;
- fe->acquire_signal = 1;
- if (fe->status & ~FE_TIMEDOUT)
- dvb_frontend_add_event (fe, 0);
- memcpy (&fe->parameters, param,
- sizeof (struct dvb_frontend_parameters));
- }
-
- dvb_bend_frequency (fe, 0);
-
- dprintk ("%s: f == %i, drift == %i\n",
- __FUNCTION__, (int) param->frequency, (int) fe->lnb_drift);
-
- param->frequency += fe->lnb_drift + fe->bending;
- err = dvb_frontend_internal_ioctl (frontend, FE_SET_FRONTEND, param);
- param->frequency -= fe->lnb_drift + fe->bending;
-
- wake_up_interruptible (&fe->wait_queue);
-
- return err;
-}
-
static void dvb_frontend_init (struct dvb_frontend_data *fe)
{
struct dvb_frontend *frontend = &fe->frontend;
@@ -338,8 +340,7 @@ static void dvb_frontend_init (struct dvb_frontend_data *fe)
dvb_frontend_internal_ioctl (frontend, FE_INIT, NULL);
}
-
-static void update_delay (int *quality, int *delay, int locked)
+static void update_delay (int *quality, int *delay, int min_delay, int locked)
{
int q2;
@@ -353,59 +354,101 @@ static void update_delay (int *quality, int *delay, int locked)
q2 = *quality - 128;
q2 *= q2;
- *delay = HZ/20 + q2 * HZ / (128*128);
+ *delay = min_delay + q2 * HZ / (128*128);
}
-
-#define LNB_DRIFT 1024 /* max. tolerated LNB drift, XXX FIXME: adjust! */
-#define TIMEOUT 2*HZ
-
/**
- * here we only come when we have lost the lock bit,
- * let's try to do something useful...
+ * Performs automatic twiddling of frontend parameters.
+ *
+ * @param fe The frontend concerned.
+ * @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT
+ * @returns Number of complete iterations that have been performed.
*/
-static void dvb_frontend_recover (struct dvb_frontend_data *fe)
+static int dvb_frontend_autotune(struct dvb_frontend_data *fe, int check_wrapped)
{
- int j = fe->lost_sync_count;
- int stepsize;
+ int autoinversion;
+ int ready = 0;
+ int original_inversion = fe->parameters.inversion;
+ u32 original_frequency = fe->parameters.frequency;
+
+ // are we using autoinversion?
+ autoinversion = ((!(fe->info->caps & FE_CAN_INVERSION_AUTO)) && (fe->parameters.inversion == INVERSION_AUTO));
+
+ // setup parameters correctly
+ while(!ready) {
+ // calculate the lnb_drift
+ fe->lnb_drift = fe->auto_step * fe->step_size;
+
+ // wrap the auto_step if we've exceeded the maximum drift
+ if (fe->lnb_drift > fe->max_drift) {
+ fe->auto_step = 0;
+ fe->auto_sub_step = 0;
+ fe->lnb_drift = 0;
+ }
- dprintk ("%s\n", __FUNCTION__);
+ // perform inversion and +/- zigzag
+ switch(fe->auto_sub_step) {
+ case 0:
+ // try with the current inversion and current drift setting
+ ready = 1;
+ break;
-#if 0
- if (fe->timeout_count > 3) {
- printk ("%s: frontend seems dead, reinitializing...\n",
- __FUNCTION__);
- dvb_call_frontend_notifiers (fe, 0);
- dvb_frontend_internal_ioctl (&fe->frontend, FE_INIT, NULL);
- dvb_frontend_set_parameters (fe, &fe->parameters, 1);
- dvb_frontend_add_event (fe, FE_REINIT);
- fe->lost_sync_jiffies = jiffies;
- fe->timeout_count = 0;
- return;
- }
-#endif
+ case 1:
+ if (!autoinversion) break;
- /**
- * let's start a zigzag scan to compensate LNB drift...
- */
- if (fe->info->type == FE_QPSK)
- stepsize = fe->parameters.u.qpsk.symbol_rate / 16000;
- else if (fe->info->type == FE_QAM)
- stepsize = 0;
- else
- stepsize = fe->info->frequency_stepsize * 2;
+ fe->inversion = (fe->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+ ready = 1;
+ break;
- if (j % 32 == 0) {
- fe->lnb_drift = 0;
- } else {
+ case 2:
+ if (fe->lnb_drift == 0) break;
+
+ fe->lnb_drift = -fe->lnb_drift;
+ ready = 1;
+ break;
+
+ case 3:
+ if (fe->lnb_drift == 0) break;
+ if (!autoinversion) break;
+
+ fe->inversion = (fe->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
fe->lnb_drift = -fe->lnb_drift;
- if (j % 2)
- fe->lnb_drift += stepsize;
+ ready = 1;
+ break;
+
+ default:
+ fe->auto_step++;
+ fe->auto_sub_step = -1; // it'll be incremented to 0 in a moment
+ break;
}
+
+ if (!ready) fe->auto_sub_step++;
+ }
- dvb_frontend_set_parameters (fe, &fe->parameters, 0);
+ // if this attempt would hit where we started, indicate a complete iteration has occurred
+ if ((fe->auto_step == fe->started_auto_step) && (fe->auto_sub_step == 0) && check_wrapped) {
+ return 1;
+ }
- dvb_frontend_internal_ioctl (&fe->frontend, FE_RESET, NULL);
+ // perform frequency bending if necessary
+ if ((dvb_override_frequency_bending != 1) && do_frequency_bending)
+ dvb_bend_frequency(fe, 0);
+
+ // instrumentation
+ dprintk("%s: drift:%i bending:%i inversion:%i auto_step:%i auto_sub_step:%i started_auto_step:%i\n",
+ __FUNCTION__, fe->lnb_drift, fe->bending, fe->inversion, fe->auto_step, fe->auto_sub_step,
+ fe->started_auto_step);
+
+ // set the frontend itself
+ fe->parameters.frequency += fe->lnb_drift + fe->bending;
+ if (autoinversion) fe->parameters.inversion = fe->inversion;
+ dvb_frontend_internal_ioctl (&fe->frontend, FE_SET_FRONTEND, &fe->parameters);
+ fe->parameters.frequency = original_frequency;
+ fe->parameters.inversion = original_inversion;
+
+ // normal return
+ fe->auto_sub_step++;
+ return 0;
}
@@ -422,6 +465,19 @@ static int dvb_frontend_is_exiting (struct dvb_frontend_data *fe)
return 0;
}
+static int dvb_frontend_should_wakeup (struct dvb_frontend_data *fe)
+{
+ if (fe->wakeup) {
+ fe->wakeup = 0;
+ return 1;
+ }
+ return dvb_frontend_is_exiting(fe);
+}
+
+static void dvb_frontend_wakeup (struct dvb_frontend_data *fe) {
+ fe->wakeup = 1;
+ wake_up_interruptible(&fe->wait_queue);
+}
static int dvb_frontend_thread (void *data)
{
@@ -430,6 +486,7 @@ static int dvb_frontend_thread (void *data)
char name [15];
int quality = 0, delay = 3*HZ;
fe_status_t s;
+ int check_wrapped = 0;
dprintk ("%s\n", __FUNCTION__);
@@ -438,15 +495,14 @@ static int dvb_frontend_thread (void *data)
dvb_kernel_thread_setup (name);
- fe->lost_sync_count = -1;
-
dvb_call_frontend_notifiers (fe, 0);
dvb_frontend_init (fe);
+ fe->wakeup = 0;
while (1) {
up (&fe->sem); /* is locked when we enter the thread... */
- timeout = wait_event_interruptible_timeout(fe->wait_queue,0 != dvb_frontend_is_exiting (fe), delay);
+ timeout = wait_event_interruptible_timeout(fe->wait_queue,0 != dvb_frontend_should_wakeup (fe), delay);
if (-ERESTARTSYS == timeout || 0 != dvb_frontend_is_exiting (fe)) {
/* got signal or quitting */
break;
@@ -455,43 +511,104 @@ static int dvb_frontend_thread (void *data)
if (down_interruptible (&fe->sem))
break;
- if (fe->lost_sync_count == -1)
+ // if we've got no parameters, just keep idling
+ if (fe->state & FESTATE_IDLE) {
+ delay = 3*HZ;
+ quality = 0;
continue;
+ }
+ // get the frontend status
dvb_frontend_internal_ioctl (&fe->frontend, FE_READ_STATUS, &s);
+ if (s != fe->status)
+ dvb_frontend_add_event (fe, s);
- update_delay (&quality, &delay, s & FE_HAS_LOCK);
+ // if we're not tuned, and we have a lock, move to the TUNED state
+ if ((fe->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) {
+ update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
+ fe->state = FESTATE_TUNED;
+
+ // if we're tuned, then we have determined the correct inversion
+ if ((!(fe->info->caps & FE_CAN_INVERSION_AUTO)) && (fe->parameters.inversion == INVERSION_AUTO)) {
+ fe->parameters.inversion = fe->inversion;
+ }
+ continue;
+ }
- s &= ~FE_TIMEDOUT;
+ // if we are tuned already, check we're still locked
+ if (fe->state & FESTATE_TUNED) {
+ update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
+ // we're tuned, and the lock is still good...
if (s & FE_HAS_LOCK) {
- fe->timeout_count = 0;
- fe->lost_sync_count = 0;
- fe->acquire_signal = 0;
+ continue;
} else {
- fe->lost_sync_count++;
- if (!(fe->info->caps & FE_CAN_RECOVER)) {
- if (!(fe->info->caps & FE_CAN_CLEAN_SETUP)) {
- if (fe->lost_sync_count < 10) {
- if (fe->acquire_signal)
- dvb_frontend_internal_ioctl(
- &fe->frontend,
- FE_RESET, NULL);
+ // if we _WERE_ tuned, but now don't have a lock, need to zigzag
+ fe->state = FESTATE_ZIGZAG_FAST;
+ fe->started_auto_step = fe->auto_step;
+ check_wrapped = 0;
+ // fallthrough
+ }
+ }
+
+ // don't actually do anything if we're in the LOSTLOCK state, the frontend is set to
+ // FE_CAN_RECOVER, and the max_drift is 0
+ if ((fe->state & FESTATE_LOSTLOCK) &&
+ (fe->info->caps & FE_CAN_RECOVER) && (fe->max_drift == 0)) {
+ update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
continue;
}
+
+ // don't do anything if we're in the DISEQC state, since this might be someone
+ // with a motorized dish controlled by DISEQC. If its actually a re-tune, there will
+ // be a SET_FRONTEND soon enough.
+ if (fe->state & FESTATE_DISEQC) {
+ update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
+ continue;
}
- dvb_frontend_recover (fe);
- delay = HZ/5;
+
+ // if we're in the RETUNE state, set everything up for a brand new scan,
+ // keeping the current inversion setting, as the next tune is _very_ likely
+ // to require the same
+ if (fe->state & FESTATE_RETUNE) {
+ fe->lnb_drift = 0;
+ fe->auto_step = 0;
+ fe->auto_sub_step = 0;
+ fe->started_auto_step = 0;
+ check_wrapped = 0;
+ }
+
+ // fast zigzag.
+ if ((fe->state & FESTATE_SEARCHING_FAST) || (fe->state & FESTATE_RETUNE)) {
+ delay = fe->min_delay;
+
+ // peform a tune
+ if (dvb_frontend_autotune(fe, check_wrapped)) {
+ // OK, if we've run out of trials at the fast speed. Drop back to
+ // slow for the _next_ attempt
+ fe->state = FESTATE_SEARCHING_SLOW;
+ fe->started_auto_step = fe->auto_step;
+ continue;
}
- if (jiffies - fe->lost_sync_jiffies > TIMEOUT) {
- s |= FE_TIMEDOUT;
- if ((fe->status & FE_TIMEDOUT) == 0)
- fe->timeout_count++;
+ check_wrapped = 1;
+
+ // if we've just retuned, enter the ZIGZAG_FAST state. This ensures
+ // we cannot return from an FE_SET_FRONTEND ioctl before the first frontend
+ // tune occurs
+ if (fe->state & FESTATE_RETUNE) {
+ fe->state = FESTATE_TUNING_FAST;
+ wake_up_interruptible(&fe->wait_queue);
}
}
- if (s != fe->status)
- dvb_frontend_add_event (fe, s);
+ // slow zigzag
+ if (fe->state & FESTATE_SEARCHING_SLOW) {
+ update_delay(&quality, &delay, fe->min_delay, s & FE_HAS_LOCK);
+
+ // Note: don't bother checking for wrapping; we stay in this state
+ // until we get a lock
+ dvb_frontend_autotune(fe, 0);
+ }
};
if (dvb_shutdown_timeout)
@@ -502,7 +619,7 @@ static int dvb_frontend_thread (void *data)
fe->thread_pid = 0;
mb();
- wake_up_interruptible (&fe->wait_queue);
+ dvb_frontend_wakeup(fe);
return 0;
}
@@ -529,13 +646,15 @@ static void dvb_frontend_stop (struct dvb_frontend_data *fe)
}
/* wake up the frontend thread, so it notices that fe->exit == 1 */
- wake_up_interruptible (&fe->wait_queue);
+ dvb_frontend_wakeup(fe);
/* wait until the frontend thread has exited */
ret = wait_event_interruptible(fe->wait_queue,0 == fe->thread_pid);
if (-ERESTARTSYS != ret) {
+ fe->state = FESTATE_IDLE;
return;
}
+ fe->state = FESTATE_IDLE;
/* paranoia check in case a signal arrived */
if (fe->thread_pid)
@@ -562,6 +681,7 @@ static int dvb_frontend_start (struct dvb_frontend_data *fe)
if (down_interruptible (&fe->sem))
return -EINTR;
+ fe->state = FESTATE_IDLE;
fe->exit = 0;
fe->thread_pid = 0;
mb();
@@ -583,6 +703,7 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file,
{
struct dvb_device *dvbdev = file->private_data;
struct dvb_frontend_data *fe = dvbdev->priv;
+ struct dvb_frontend_tune_settings fetunesettings;
int err = 0;
dprintk ("%s\n", __FUNCTION__);
@@ -600,13 +721,59 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file,
if (fe->status)
dvb_call_frontend_notifiers (fe, 0);
dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg);
+ fe->state = FESTATE_DISEQC;
break;
+
case FE_SET_FRONTEND:
- err = dvb_frontend_set_parameters (fe, parg, 1);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);
- wake_up_interruptible(&fe->wait_queue);
+ fe->state = FESTATE_RETUNE;
+
+ memcpy (&fe->parameters, parg,
+ sizeof (struct dvb_frontend_parameters));
+
+ memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
+ memcpy(&fetunesettings.parameters, parg,
+ sizeof (struct dvb_frontend_parameters));
+
+ // force auto frequency inversion if requested
+ if (dvb_force_auto_inversion) {
+ fe->parameters.inversion = INVERSION_AUTO;
+ fetunesettings.parameters.inversion = INVERSION_AUTO;
+ }
+
+ // get frontend-specific tuning settings
+ if (dvb_frontend_internal_ioctl(&fe->frontend, FE_GET_TUNE_SETTINGS, &fetunesettings) == 0) {
+ fe->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
+ fe->max_drift = fetunesettings.max_drift;
+ fe->step_size = fetunesettings.step_size;
+ } else {
+ // default values
+ switch(fe->info->type) {
+ case FE_QPSK:
+ fe->min_delay = HZ/20; // default mindelay of 50ms
+ fe->step_size = fe->parameters.u.qpsk.symbol_rate / 16000;
+ fe->max_drift = fe->parameters.u.qpsk.symbol_rate / 2000;
break;
+
+ case FE_QAM:
+ fe->min_delay = HZ/20; // default mindelay of 50ms
+ fe->step_size = 0;
+ fe->max_drift = 0; // don't want any zigzagging under DVB-C frontends
+ break;
+
+ case FE_OFDM:
+ fe->min_delay = HZ/20; // default mindelay of 50ms
+ fe->step_size = fe->info->frequency_stepsize * 2;
+ fe->max_drift = (fe->info->frequency_stepsize * 2) + 1;
+ break;
+ }
+ }
+ if (dvb_override_tune_delay > 0) {
+ fe->min_delay = (dvb_override_tune_delay * HZ) / 1000;
+ }
+
+ dvb_frontend_add_event (fe, 0);
+ break;
+
case FE_GET_EVENT:
err = dvb_frontend_get_event (fe, parg, file->f_flags);
break;
@@ -615,10 +782,26 @@ static int dvb_frontend_ioctl (struct inode *inode, struct file *file,
sizeof (struct dvb_frontend_parameters));
/* fall-through... */
default:
- dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg);
+ err = dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg);
};
up (&fe->sem);
+ if (err < 0)
+ return err;
+
+ // Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't do it, it is done for it.
+ if ((cmd == FE_GET_INFO) && (err == 0)) {
+ struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) parg;
+ tmp->caps |= FE_CAN_INVERSION_AUTO;
+ }
+
+ // if the frontend has just been set, wait until the first tune has finished.
+ // This ensures the app doesn't start reading data too quickly, perhaps from the
+ // previous lock, which is REALLY CONFUSING TO DEBUG!
+ if ((cmd == FE_SET_FRONTEND) && (err == 0)) {
+ dvb_frontend_wakeup(fe);
+ err = wait_event_interruptible(fe->wait_queue, fe->state & ~FESTATE_RETUNE);
+ }
return err;
}
@@ -915,6 +1098,7 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
fe->frontend.i2c = i2c;
fe->frontend.data = data;
fe->info = info;
+ fe->inversion = INVERSION_OFF;
list_for_each (entry, &frontend_ioctl_list) {
struct dvb_frontend_ioctl_data *ioctl;
@@ -954,6 +1138,9 @@ dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
dvb_register_device (i2c->adapter, &fe->dvbdev, &dvbdev_template,
fe, DVB_DEVICE_FRONTEND);
+ if ((info->caps & FE_NEEDS_BENDING) || (dvb_override_frequency_bending == 2))
+ do_frequency_bending = 1;
+
up (&frontend_mutex);
return 0;
@@ -991,6 +1178,12 @@ int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend,
MODULE_PARM(dvb_frontend_debug,"i");
MODULE_PARM(dvb_shutdown_timeout,"i");
+MODULE_PARM(dvb_override_frequency_bending,"i");
+MODULE_PARM(dvb_force_auto_inversion,"i");
+MODULE_PARM(dvb_override_tune_delay,"i");
+
MODULE_PARM_DESC(dvb_frontend_debug, "enable verbose debug messages");
MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
-
+MODULE_PARM_DESC(dvb_override_frequency_bending, "0: normal (default), 1: never use frequency bending, 2: always use frequency bending");
+MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
+MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index 775afb81dfe8..8d8e21fa8395 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -56,14 +56,25 @@ struct dvb_frontend {
void *data; /* can be used by hardware module... */
};
+struct dvb_frontend_tune_settings {
+ int min_delay_ms;
+ int step_size;
+ int max_drift;
+ struct dvb_frontend_parameters parameters;
+};
+
/**
* private frontend command ioctl's.
* keep them in sync with the public ones defined in linux/dvb/frontend.h
+ *
+ * FE_SLEEP. Ioctl used to put frontend into a low power mode.
+ * FE_INIT. Ioctl used to initialise the frontend.
+ * FE_GET_TUNE_SETTINGS. Get the frontend-specific tuning loop settings for the supplied set of parameters.
*/
#define FE_SLEEP _IO('v', 80)
#define FE_INIT _IO('v', 81)
-#define FE_RESET _IO('v', 82)
+#define FE_GET_TUNE_SETTINGS _IOWR('v', 83, struct dvb_frontend_tune_settings)
extern int
diff --git a/drivers/media/dvb/dvb-core/dvb_ksyms.c b/drivers/media/dvb/dvb-core/dvb_ksyms.c
index 6b471c44c0af..558b3f41ecb7 100644
--- a/drivers/media/dvb/dvb-core/dvb_ksyms.c
+++ b/drivers/media/dvb/dvb-core/dvb_ksyms.c
@@ -10,6 +10,7 @@
#include "dvb_frontend.h"
#include "dvb_net.h"
#include "dvb_filter.h"
+#include "dvb_ca_en50221.h"
EXPORT_SYMBOL(dvb_dmxdev_init);
EXPORT_SYMBOL(dvb_dmxdev_release);
@@ -49,3 +50,8 @@ EXPORT_SYMBOL(dvb_filter_pes2ts_init);
EXPORT_SYMBOL(dvb_filter_pes2ts);
EXPORT_SYMBOL(dvb_filter_get_ac3info);
+EXPORT_SYMBOL(dvb_ca_en50221_init);
+EXPORT_SYMBOL(dvb_ca_en50221_release);
+EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
+EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
+EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
index 4f9a531e76a1..76ac08d1e181 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -5,33 +5,53 @@
* Ralph Metzler <ralph@convergence.de>
* Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
*
+ * ULE Decapsulation code:
+ * Copyright (C) 2003 gcs - Global Communication & Services GmbH.
+ * and Institute for Computer Sciences
+ * Salzburg University.
+ * Hilmar Linder <hlinder@cosy.sbg.ac.at>
+ * and Wolfram Stering <wstering@cosy.sbg.ac.at>
+ *
+ * ULE Decaps according to draft-fair-ipdvb-ule-01.txt.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
- *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
*/
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
#include <linux/dvb/net.h>
+#include <linux/uio.h>
#include <asm/uaccess.h>
+#include <linux/crc32.h>
#include "dvb_demux.h"
#include "dvb_net.h"
#include "dvb_functions.h"
+static inline __u32 iov_crc32( __u32 c, struct iovec *iov, unsigned int cnt )
+{
+ unsigned int j;
+ for (j = 0; j < cnt; j++)
+ c = crc32_be( c, iov[j].iov_base, iov[j].iov_len );
+ return c;
+}
+
+
#if 1
#define dprintk(x...) printk(x)
#else
@@ -41,14 +61,46 @@
#define DVB_NET_MULTICAST_MAX 10
+#define isprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
+
+static void hexdump( const unsigned char *buf, unsigned short len )
+{
+ char str[80], octet[10];
+ int ofs, i, l;
+
+ for (ofs = 0; ofs < len; ofs += 16) {
+ sprintf( str, "%03d: ", ofs );
+
+ for (i = 0; i < 16; i++) {
+ if ((i + ofs) < len)
+ sprintf( octet, "%02x ", buf[ofs + i] );
+ else
+ strcpy( octet, " " );
+
+ strcat( str, octet );
+ }
+ strcat( str, " " );
+ l = strlen( str );
+
+ for (i = 0; (i < 16) && ((i + ofs) < len); i++)
+ str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.';
+
+ str[l] = '\0';
+ printk( KERN_WARNING "%s\n", str );
+ }
+}
+
+
struct dvb_net_priv {
int in_use;
struct net_device_stats stats;
char name[6];
u16 pid;
+ struct dvb_net *host;
struct dmx_demux *demux;
struct dmx_section_feed *secfeed;
struct dmx_section_filter *secfilter;
+ struct dmx_ts_feed *tsfeed;
int multi_num;
struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX];
unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6];
@@ -59,6 +111,18 @@ struct dvb_net_priv {
#define RX_MODE_PROMISC 3
struct work_struct set_multicast_list_wq;
struct work_struct restart_net_feed_wq;
+ unsigned char feedtype;
+ int need_pusi;
+ unsigned char tscc; /* TS continuity counter after sync. */
+ struct sk_buff *ule_skb;
+ unsigned short ule_sndu_len;
+ unsigned short ule_sndu_type;
+ unsigned char ule_sndu_type_1;
+ unsigned char ule_dbit; /* whether the DestMAC address present
+ * bit is set or not. */
+ unsigned char ule_ethhdr_complete; /* whether we have completed the Ethernet
+ * header for the current ULE SNDU. */
+ int ule_sndu_remain;
};
@@ -107,35 +171,442 @@ static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb,
return htons(ETH_P_802_2);
}
+#define TS_SZ 188
+#define TS_SYNC 0x47
+#define TS_TEI 0x80
+#define TS_PUSI 0x40
+#define TS_AF_A 0x20
+#define TS_AF_D 0x10
+
+#define ULE_TEST 0
+#define ULE_BRIDGED 1
+#define ULE_LLC 2
+
+static inline void reset_ule( struct dvb_net_priv *p )
+{
+ p->ule_skb = NULL;
+ p->ule_sndu_len = 0;
+ p->ule_sndu_type = 0;
+ p->ule_sndu_type_1 = 0;
+ p->ule_sndu_remain = 0;
+ p->ule_dbit = 0xFF;
+ p->ule_ethhdr_complete = 0;
+}
+
+static const char eth_dest_addr[] = { 0x0b, 0x0a, 0x09, 0x08, 0x04, 0x03 };
+
+static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+{
+ struct dvb_net_priv *priv = (struct dvb_net_priv *)dev->priv;
+ unsigned long skipped = 0L, skblen = 0L;
+ u8 *ts, *ts_end, *from_where = NULL, ts_remain = 0, how_much = 0, new_ts = 1;
+ struct ethhdr *ethh = NULL;
+ unsigned int emergency_count = 0;
+
+ if (dev == NULL) {
+ printk( KERN_ERR "NO netdev struct!\n" );
+ return;
+ }
+
+ for (ts = (char *)buf, ts_end = (char *)buf + buf_len; ts < ts_end; ) {
+
+ if (emergency_count++ > 200) {
+ /* Huh?? */
+ hexdump(ts, TS_SZ);
+ printk(KERN_WARNING "*** LOOP ALERT! ts %p ts_remain %u "
+ "how_much %u, ule_skb %p, ule_len %u, ule_remain %u\n",
+ ts, ts_remain, how_much, priv->ule_skb,
+ priv->ule_sndu_len, priv->ule_sndu_remain);
+ break;
+ }
+
+ if (new_ts) {
+ if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI)) {
+ printk(KERN_WARNING "Invalid TS cell: SYNC %#x, TEI %u.\n",
+ ts[0], ts[1] & TS_TEI >> 7);
+ continue;
+ }
+ ts_remain = 184;
+ from_where = ts + 4;
+ }
+ /* Synchronize on PUSI, if required. */
+ if (priv->need_pusi) {
+ if (ts[1] & TS_PUSI) {
+ /* Find beginning of first ULE SNDU in current TS cell.
+ * priv->need_pusi = 0; */
+ priv->tscc = ts[3] & 0x0F;
+ /* There is a pointer field here. */
+ if (ts[4] > ts_remain) {
+ printk(KERN_ERR "Invalid ULE packet "
+ "(pointer field %d)\n", ts[4]);
+ continue;
+ }
+ from_where = &ts[5] + ts[4];
+ ts_remain -= 1 + ts[4];
+ skipped = 0;
+ } else {
+ skipped++;
+ continue;
+ }
+ }
+
+ /* Check continuity counter. */
+ if (new_ts) {
+ if ((ts[3] & 0x0F) == priv->tscc)
+ priv->tscc = (priv->tscc + 1) & 0x0F;
+ else {
+ /* TS discontinuity handling: */
+ if (priv->ule_skb) {
+ dev_kfree_skb( priv->ule_skb );
+ /* Prepare for next SNDU. */
+ reset_ule(priv);
+ ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+ ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+ }
+ /* skip to next PUSI. */
+ printk(KERN_WARNING "TS discontinuity: got %#x, "
+ "exptected %#x.\n", ts[3] & 0x0F, priv->tscc);
+ priv->need_pusi = 1;
+ continue;
+ }
+ /* If we still have an incomplete payload, but PUSI is
+ * set, some TS cells are missing.
+ * This is only possible here, if we missed exactly 16 TS
+ * cells (continuity counter). */
+ if (ts[1] & TS_PUSI) {
+ if (! priv->need_pusi) {
+ /* printk(KERN_WARNING "Skipping pointer field %u.\n", *from_where); */
+ if (*from_where > 181) {
+ printk(KERN_WARNING "*** Invalid pointer "
+ "field: %u. Current TS cell "
+ "follows:\n", *from_where);
+ hexdump( ts, TS_SZ );
+ printk(KERN_WARNING "-------------------\n");
+ }
+ /* Skip pointer field (we're processing a
+ * packed payload). */
+ from_where += 1;
+ ts_remain -= 1;
+ } else
+ priv->need_pusi = 0;
+
+ if (priv->ule_sndu_remain > 183) {
+ ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+ ((struct dvb_net_priv *) dev->priv)->stats.rx_length_errors++;
+ printk(KERN_WARNING "Expected %d more SNDU bytes, but "
+ "got PUSI. Flushing incomplete payload.\n",
+ priv->ule_sndu_remain);
+ dev_kfree_skb(priv->ule_skb);
+ /* Prepare for next SNDU. */
+ reset_ule(priv);
+ }
+ }
+ }
+
+ /* Check if new payload needs to be started. */
+ if (priv->ule_skb == NULL) {
+ /* Start a new payload w/ skb.
+ * Find ULE header. It is only guaranteed that the
+ * length field (2 bytes) is contained in the current
+ * TS.
+ * Check ts_remain has to be >= 2 here. */
+ if (ts_remain < 2) {
+ printk(KERN_WARNING "Invalid payload packing: only %d "
+ "bytes left in TS. Resyncing.\n", ts_remain);
+ priv->ule_sndu_len = 0;
+ priv->need_pusi = 1;
+ continue;
+ }
+
+ if (! priv->ule_sndu_len) {
+ priv->ule_sndu_len = from_where[0] << 8 | from_where[1];
+ if (priv->ule_sndu_len & 0x8000) {
+ /* D-Bit is set: no dest mac present. */
+ priv->ule_sndu_len &= 0x7FFF;
+ priv->ule_dbit = 1;
+ } else
+ priv->ule_dbit = 0;
+
+ /* printk(KERN_WARNING "ULE D-Bit: %d, SNDU len %u.\n",
+ priv->ule_dbit, priv->ule_sndu_len); */
+
+ if (priv->ule_sndu_len > 32763) {
+ printk(KERN_WARNING "Invalid ULE SNDU length %u. "
+ "Resyncing.\n", priv->ule_sndu_len);
+ hexdump(ts, TS_SZ);
+ priv->ule_sndu_len = 0;
+ priv->need_pusi = 1;
+ new_ts = 1;
+ ts += TS_SZ;
+ continue;
+ }
+ ts_remain -= 2; /* consume the 2 bytes SNDU length. */
+ from_where += 2;
+ }
+
+ /*
+ * State of current TS:
+ * ts_remain (remaining bytes in the current TS cell)
+ * 0 ule_type is not available now, we need the next TS cell
+ * 1 the first byte of the ule_type is present
+ * >=2 full ULE header present, maybe some payload data as well.
+ */
+ switch (ts_remain) {
+ case 1:
+ priv->ule_sndu_type = from_where[0] << 8;
+ priv->ule_sndu_type_1 = 1; /* first byte of ule_type is set. */
+ /* ts_remain -= 1; from_where += 1;
+ * here not necessary, because we continue. */
+ case 0:
+ new_ts = 1;
+ ts += TS_SZ;
+ continue;
+
+ default: /* complete ULE header is present in current TS. */
+ /* Extract ULE type field. */
+ if (priv->ule_sndu_type_1) {
+ priv->ule_sndu_type |= from_where[0];
+ from_where += 1; /* points to payload start. */
+ ts_remain -= 1;
+ } else {
+ /* Complete type is present in new TS. */
+ priv->ule_sndu_type = from_where[0] << 8 | from_where[1];
+ from_where += 2; /* points to payload start. */
+ ts_remain -= 2;
+ }
+ break;
+ }
+
+ if (priv->ule_sndu_type == ULE_TEST) {
+ /* Test SNDU, discarded by the receiver. */
+ printk(KERN_WARNING "Discarding ULE Test SNDU (%d bytes). "
+ "Resyncing.\n", priv->ule_sndu_len);
+ priv->ule_sndu_len = 0;
+ priv->need_pusi = 1;
+ continue;
+ }
+
+ skblen = priv->ule_sndu_len; /* Including CRC32 */
+ if (priv->ule_sndu_type != ULE_BRIDGED) {
+ skblen += ETH_HLEN;
+#if 1
+ if (! priv->ule_dbit)
+ skblen -= ETH_ALEN;
+#endif
+ }
+ priv->ule_skb = dev_alloc_skb(skblen);
+ if (priv->ule_skb == NULL) {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ ((struct dvb_net_priv *)dev->priv)->stats.rx_dropped++;
+ return;
+ }
+
+#if 0
+ if (priv->ule_sndu_type != ULE_BRIDGED) {
+ // skb_reserve(priv->ule_skb, 2); /* longword align L3 header */
+ // Create Ethernet header.
+ ethh = (struct ethhdr *)skb_put( priv->ule_skb, ETH_HLEN );
+ memset( ethh->h_source, 0x00, ETH_ALEN );
+ if (priv->ule_dbit) {
+ // Dest MAC address not present --> generate our own.
+ memcpy( ethh->h_dest, eth_dest_addr, ETH_ALEN );
+ } else {
+ // Dest MAC address could be split across two TS cells.
+ // FIXME: implement.
+
+ printk( KERN_WARNING "%s: got destination MAC "
+ "address.\n", dev->name );
+ memcpy( ethh->h_dest, eth_dest_addr, ETH_ALEN );
+ }
+ ethh->h_proto = htons(priv->ule_sndu_type == ULE_LLC ?
+ priv->ule_sndu_len : priv->ule_sndu_type);
+ }
+#endif
+ /* this includes the CRC32 _and_ dest mac, if !dbit! */
+ priv->ule_sndu_remain = priv->ule_sndu_len;
+ priv->ule_skb->dev = dev;
+ }
+
+ /* Copy data into our current skb. */
+ how_much = min(priv->ule_sndu_remain, (int)ts_remain);
+ if ((priv->ule_ethhdr_complete < ETH_ALEN) &&
+ (priv->ule_sndu_type != ULE_BRIDGED)) {
+ ethh = (struct ethhdr *)priv->ule_skb->data;
+ if (! priv->ule_dbit) {
+ if (how_much >= (ETH_ALEN - priv->ule_ethhdr_complete)) {
+ /* copy dest mac address. */
+ memcpy(skb_put(priv->ule_skb,
+ (ETH_ALEN - priv->ule_ethhdr_complete)),
+ from_where,
+ (ETH_ALEN - priv->ule_ethhdr_complete));
+ memset(ethh->h_source, 0x00, ETH_ALEN);
+ ethh->h_proto = htons(priv->ule_sndu_type == ULE_LLC ?
+ priv->ule_sndu_len :
+ priv->ule_sndu_type);
+ skb_put(priv->ule_skb, ETH_ALEN + 2);
+
+ how_much -= (ETH_ALEN - priv->ule_ethhdr_complete);
+ priv->ule_sndu_remain -= (ETH_ALEN -
+ priv->ule_ethhdr_complete);
+ ts_remain -= (ETH_ALEN - priv->ule_ethhdr_complete);
+ from_where += (ETH_ALEN - priv->ule_ethhdr_complete);
+ priv->ule_ethhdr_complete = ETH_ALEN;
+ }
+ } else {
+ /* Generate whole Ethernet header. */
+ memcpy(ethh->h_dest, eth_dest_addr, ETH_ALEN);
+ memset(ethh->h_source, 0x00, ETH_ALEN);
+ ethh->h_proto = htons(priv->ule_sndu_type == ULE_LLC ?
+ priv->ule_sndu_len : priv->ule_sndu_type);
+ skb_put(priv->ule_skb, ETH_HLEN);
+ priv->ule_ethhdr_complete = ETH_ALEN;
+ }
+ }
+ /* printk(KERN_WARNING "Copying %u bytes, ule_sndu_remain = %u, "
+ "ule_sndu_len = %u.\n", how_much, priv->ule_sndu_remain,
+ priv->ule_sndu_len); */
+ memcpy(skb_put(priv->ule_skb, how_much), from_where, how_much);
+ priv->ule_sndu_remain -= how_much;
+ ts_remain -= how_much;
+ from_where += how_much;
+
+ if ((priv->ule_ethhdr_complete < ETH_ALEN) &&
+ (priv->ule_sndu_type != ULE_BRIDGED)) {
+ priv->ule_ethhdr_complete += how_much;
+ }
+
+ /* Check for complete payload. */
+ if (priv->ule_sndu_remain <= 0) {
+ /* Check CRC32, we've got it in our skb already. */
+ unsigned short ulen = htons(priv->ule_sndu_len);
+ unsigned short utype = htons(priv->ule_sndu_type);
+ struct iovec iov[4] = {
+ { &ulen, sizeof ulen },
+ { &utype, sizeof utype },
+ { NULL, 0 },
+ { priv->ule_skb->data + ETH_HLEN,
+ priv->ule_skb->len - ETH_HLEN - 4 }
+ };
+ unsigned long ule_crc = ~0L, expected_crc;
+ if (priv->ule_dbit) {
+ /* Set D-bit for CRC32 verification,
+ * if it was set originally. */
+ ulen |= 0x0080;
+ } else {
+ iov[2].iov_base = priv->ule_skb->data;
+ iov[2].iov_len = ETH_ALEN;
+ }
+ ule_crc = iov_crc32(ule_crc, iov, 4);
+ expected_crc = *((u8 *)priv->ule_skb->tail - 4) << 24 |
+ *((u8 *)priv->ule_skb->tail - 3) << 16 |
+ *((u8 *)priv->ule_skb->tail - 2) << 8 |
+ *((u8 *)priv->ule_skb->tail - 1);
+ if (ule_crc != expected_crc) {
+ printk(KERN_WARNING "CRC32 check %s: %#lx / %#lx.\n",
+ ule_crc != expected_crc ? "FAILED" : "OK",
+ ule_crc, expected_crc);
+ hexdump(priv->ule_skb->data + ETH_HLEN,
+ priv->ule_skb->len - ETH_HLEN);
+
+ ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+ ((struct dvb_net_priv *) dev->priv)->stats.rx_crc_errors++;
+ dev_kfree_skb(priv->ule_skb);
+ } else {
+ /* CRC32 was OK. Remove it from skb. */
+ priv->ule_skb->tail -= 4;
+ priv->ule_skb->len -= 4;
+ /* Stuff into kernel's protocol stack. */
+ priv->ule_skb->protocol = dvb_net_eth_type_trans(priv->ule_skb, dev);
+ /* If D-bit is set (i.e. destination MAC address not present),
+ * receive the packet anyhw. */
+ /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST) */
+ priv->ule_skb->pkt_type = PACKET_HOST;
+ ((struct dvb_net_priv *) dev->priv)->stats.rx_packets++;
+ ((struct dvb_net_priv *) dev->priv)->stats.rx_bytes += priv->ule_skb->len;
+ netif_rx(priv->ule_skb);
+ }
+ /* Prepare for next SNDU. */
+ reset_ule(priv);
+ }
+
+ /* More data in current TS (look at the bytes following the CRC32)? */
+ if (ts_remain >= 2 && *((unsigned short *)from_where) != 0xFFFF) {
+ /* Next ULE SNDU starts right there. */
+ new_ts = 0;
+ priv->ule_skb = NULL;
+ priv->ule_sndu_type_1 = 0;
+ priv->ule_sndu_len = 0;
+ // printk(KERN_WARNING "More data in current TS: [%#x %#x %#x %#x]\n",
+ // *(from_where + 0), *(from_where + 1),
+ // *(from_where + 2), *(from_where + 3));
+ // printk(KERN_WARNING "ts @ %p, stopped @ %p:\n", ts, from_where + 0);
+ // hexdump(ts, 188);
+ } else {
+ new_ts = 1;
+ ts += TS_SZ;
+ if (priv->ule_skb == NULL) {
+ priv->need_pusi = 1;
+ priv->ule_sndu_type_1 = 0;
+ priv->ule_sndu_len = 0;
+ }
+ }
+ } /* for all available TS cells */
+}
+
+static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_ts_feed *feed, enum dmx_success success)
+{
+ struct net_device *dev = (struct net_device *)feed->priv;
+
+ if (buffer2 != 0)
+ printk(KERN_WARNING "buffer2 not 0: %p.\n", buffer2);
+ if (buffer1_len > 32768)
+ printk(KERN_WARNING "length > 32k: %u.\n", buffer1_len);
+ /* printk("TS callback: %u bytes, %u TS cells @ %p.\n",
+ buffer1_len, buffer1_len / TS_SZ, buffer1); */
+ dvb_net_ule(dev, buffer1, buffer1_len);
+ return 0;
+}
+
static void dvb_net_sec(struct net_device *dev, u8 *pkt, int pkt_len)
{
u8 *eth;
struct sk_buff *skb;
+ struct net_device_stats *stats = &(((struct dvb_net_priv *) dev->priv)->stats);
/* note: pkt_len includes a 32bit checksum */
if (pkt_len < 16) {
printk("%s: IP/MPE packet length = %d too small.\n",
dev->name, pkt_len);
- ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
- ((struct dvb_net_priv *) dev->priv)->stats.rx_length_errors++;
+ stats->rx_errors++;
+ stats->rx_length_errors++;
return;
}
+/* it seems some ISPs manage to screw up here, so we have to
+ * relax the error checks... */
+#if 0
if ((pkt[5] & 0xfd) != 0xc1) {
/* drop scrambled or broken packets */
- ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
- ((struct dvb_net_priv *) dev->priv)->stats.rx_crc_errors++;
+#else
+ if ((pkt[5] & 0x3c) != 0x00) {
+ /* drop scrambled */
+#endif
+ stats->rx_errors++;
+ stats->rx_crc_errors++;
return;
}
if (pkt[5] & 0x02) {
//FIXME: handle LLC/SNAP
- ((struct dvb_net_priv *)dev->priv)->stats.rx_dropped++;
+ stats->rx_dropped++;
return;
}
if (pkt[7]) {
/* FIXME: assemble datagram from multiple sections */
- ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
- ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+ stats->rx_errors++;
+ stats->rx_frame_errors++;
return;
}
@@ -144,7 +615,7 @@ static void dvb_net_sec(struct net_device *dev, u8 *pkt, int pkt_len)
*/
if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2))) {
//printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
- ((struct dvb_net_priv *) dev->priv)->stats.rx_dropped++;
+ stats->rx_dropped++;
return;
}
skb_reserve(skb, 2); /* longword align L3 header */
@@ -169,12 +640,12 @@ static void dvb_net_sec(struct net_device *dev, u8 *pkt, int pkt_len)
skb->protocol = dvb_net_eth_type_trans(skb, dev);
- ((struct dvb_net_priv *)dev->priv)->stats.rx_packets++;
- ((struct dvb_net_priv *)dev->priv)->stats.rx_bytes+=skb->len;
+ stats->rx_packets++;
+ stats->rx_bytes+=skb->len;
netif_rx(skb);
}
-static int dvb_net_callback(const u8 *buffer1, size_t buffer1_len,
+static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
const u8 *buffer2, size_t buffer2_len,
struct dmx_section_filter *filter,
enum dmx_success success)
@@ -199,7 +670,7 @@ static u8 mask_allmulti[6]={0xff, 0xff, 0xff, 0x00, 0x00, 0x00};
static u8 mac_allmulti[6]={0x01, 0x00, 0x5e, 0x00, 0x00, 0x00};
static u8 mask_promisc[6]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-static int dvb_net_filter_set(struct net_device *dev,
+static int dvb_net_filter_sec_set(struct net_device *dev,
struct dmx_section_filter **secfilter,
u8 *mac, u8 *mac_mask)
{
@@ -257,10 +728,12 @@ static int dvb_net_feed_start(struct net_device *dev)
priv->secfeed=0;
priv->secfilter=0;
+ priv->tsfeed = 0;
+ if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
dprintk("%s: alloc secfeed\n", __FUNCTION__);
ret=demux->allocate_section_feed(demux, &priv->secfeed,
- dvb_net_callback);
+ dvb_net_sec_callback);
if (ret<0) {
printk("%s: could not allocate section feed\n", dev->name);
return ret;
@@ -277,41 +750,74 @@ static int dvb_net_feed_start(struct net_device *dev)
if (priv->rx_mode != RX_MODE_PROMISC) {
dprintk("%s: set secfilter\n", __FUNCTION__);
- dvb_net_filter_set(dev, &priv->secfilter, mac, mask_normal);
+ dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal);
}
switch (priv->rx_mode) {
case RX_MODE_MULTI:
for (i = 0; i < priv->multi_num; i++) {
dprintk("%s: set multi_secfilter[%d]\n", __FUNCTION__, i);
- dvb_net_filter_set(dev, &priv->multi_secfilter[i],
+ dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i],
priv->multi_macs[i], mask_normal);
}
break;
case RX_MODE_ALL_MULTI:
priv->multi_num=1;
dprintk("%s: set multi_secfilter[0]\n", __FUNCTION__);
- dvb_net_filter_set(dev, &priv->multi_secfilter[0],
+ dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0],
mac_allmulti, mask_allmulti);
break;
case RX_MODE_PROMISC:
priv->multi_num=0;
dprintk("%s: set secfilter\n", __FUNCTION__);
- dvb_net_filter_set(dev, &priv->secfilter, mac, mask_promisc);
+ dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc);
break;
}
dprintk("%s: start filtering\n", __FUNCTION__);
priv->secfeed->start_filtering(priv->secfeed);
+ } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+ struct timespec timeout = { 0, 30000000 }; // 30 msec
+
+ /* we have payloads encapsulated in TS */
+ dprintk("%s: alloc tsfeed\n", __FUNCTION__);
+ ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback);
+ if (ret < 0) {
+ printk("%s: could not allocate ts feed\n", dev->name);
+ return ret;
+ }
+
+ /* Set netdevice pointer for ts decaps callback. */
+ priv->tsfeed->priv = (void *)dev;
+ ret = priv->tsfeed->set(priv->tsfeed, priv->pid,
+ TS_PACKET, DMX_TS_PES_OTHER,
+ 188 * 100, /* nr. of bytes delivered per callback */
+ 32768, /* circular buffer size */
+ 0, /* descramble */
+ timeout);
+
+ if (ret < 0) {
+ printk("%s: could not set ts feed\n", dev->name);
+ priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+ priv->tsfeed = 0;
+ return ret;
+ }
+
+ dprintk("%s: start filtering\n", __FUNCTION__);
+ priv->tsfeed->start_filtering(priv->tsfeed);
+ } else
+ return -EINVAL;
+
return 0;
}
-static void dvb_net_feed_stop(struct net_device *dev)
+static int dvb_net_feed_stop(struct net_device *dev)
{
struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
int i;
dprintk("%s\n", __FUNCTION__);
+ if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
if (priv->secfeed) {
if (priv->secfeed->is_filtering) {
dprintk("%s: stop secfeed\n", __FUNCTION__);
@@ -327,7 +833,8 @@ static void dvb_net_feed_stop(struct net_device *dev)
for (i=0; i<priv->multi_num; i++) {
if (priv->multi_secfilter[i]) {
- dprintk("%s: release multi_filter[%d]\n", __FUNCTION__, i);
+ dprintk("%s: release multi_filter[%d]\n",
+ __FUNCTION__, i);
priv->secfeed->release_filter(priv->secfeed,
priv->multi_secfilter[i]);
priv->multi_secfilter[i]=0;
@@ -338,6 +845,20 @@ static void dvb_net_feed_stop(struct net_device *dev)
priv->secfeed=0;
} else
printk("%s: no feed to stop\n", dev->name);
+ } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+ if (priv->tsfeed) {
+ if (priv->tsfeed->is_filtering) {
+ dprintk("%s: stop tsfeed\n", __FUNCTION__);
+ priv->tsfeed->stop_filtering(priv->tsfeed);
+ }
+ priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+ priv->tsfeed = 0;
+ }
+ else
+ printk("%s: no ts feed to stop\n", dev->name);
+ } else
+ return -EINVAL;
+ return 0;
}
@@ -446,8 +967,7 @@ static int dvb_net_stop(struct net_device *dev)
struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
priv->in_use--;
- dvb_net_feed_stop(dev);
- return 0;
+ return dvb_net_feed_stop(dev);
}
static struct net_device_stats * dvb_net_get_stats(struct net_device *dev)
@@ -489,14 +1009,15 @@ static int get_if(struct dvb_net *dvbnet)
return i;
}
-
-static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid)
+static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype)
{
struct net_device *net;
struct dvb_net_priv *priv;
int result;
int if_num;
+ if (feedtype != DVB_NET_FEEDTYPE_MPE && feedtype != DVB_NET_FEEDTYPE_ULE)
+ return -EINVAL;
if ((if_num = get_if(dvbnet)) < 0)
return -EINVAL;
@@ -516,6 +1037,10 @@ static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid)
priv->demux = dvbnet->demux;
priv->pid = pid;
priv->rx_mode = RX_MODE_UNI;
+ priv->need_pusi = 1;
+ priv->tscc = 0;
+ priv->feedtype = feedtype;
+ reset_ule(priv);
INIT_WORK(&priv->set_multicast_list_wq, wq_set_multicast_list, net);
INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed, net);
@@ -570,7 +1095,7 @@ static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- result=dvb_net_add_if(dvbnet, dvbnetif->pid);
+ result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype);
if (result<0)
return result;
dvbnetif->if_num=result;
@@ -584,19 +1109,50 @@ static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
!dvbnet->state[dvbnetif->if_num])
- return -EFAULT;
+ return -EINVAL;
- netdev=(struct net_device*)&dvbnet->device[dvbnetif->if_num];
+ netdev = dvbnet->device[dvbnetif->if_num];
priv_data=(struct dvb_net_priv*)netdev->priv;
dvbnetif->pid=priv_data->pid;
+ dvbnetif->feedtype=priv_data->feedtype;
break;
}
case NET_REMOVE_IF:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
return dvb_net_remove_if(dvbnet, (int) (long) parg);
- default:
+
+ /* binary compatiblity cruft */
+ case __NET_ADD_IF_OLD:
+ {
+ struct __dvb_net_if_old *dvbnetif=(struct __dvb_net_if_old *)parg;
+ int result;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE);
+ if (result<0)
+ return result;
+ dvbnetif->if_num=result;
+ break;
+ }
+ case __NET_GET_IF_OLD:
+ {
+ struct net_device *netdev;
+ struct dvb_net_priv *priv_data;
+ struct __dvb_net_if_old *dvbnetif=(struct __dvb_net_if_old *)parg;
+
+ if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+ !dvbnet->state[dvbnetif->if_num])
return -EINVAL;
+
+ netdev = dvbnet->device[dvbnetif->if_num];
+ priv_data=(struct dvb_net_priv*)netdev->priv;
+ dvbnetif->pid=priv_data->pid;
+ break;
+ }
+ default:
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index 03759e108846..b8bc3e6c4388 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -3,9 +3,10 @@
* dvb_ringbuffer.c: ring buffer implementation for the dvb driver
*
* Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
*
- * based on code originally found in av7110.c:
- * Copyright (C) 1999-2002 Ralph Metzler
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph Metzler
* & Marcus Metzler for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
@@ -24,6 +25,8 @@
*/
+
+#define __KERNEL_SYSCALLS__
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -33,6 +36,8 @@
#include "dvb_ringbuffer.h"
+#define PKT_READY 0
+#define PKT_DISPOSED 1
void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
@@ -158,6 +163,109 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
return len;
}
+ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len, int usermem)
+{
+ int status;
+ ssize_t oldpwrite = rbuf->pwrite;
+
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
+ status = dvb_ringbuffer_write(rbuf, buf, len, usermem);
+
+ if (status < 0) rbuf->pwrite = oldpwrite;
+ return status;
+}
+
+ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8* buf, size_t len, int usermem)
+{
+ size_t todo;
+ size_t split;
+ size_t pktlen;
+
+ pktlen = rbuf->data[idx] << 8;
+ pktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ if (offset > pktlen) return -EINVAL;
+ if ((offset + len) > pktlen) len = pktlen - offset;
+
+ idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
+ todo = len;
+ split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
+ if (split > 0) {
+ if (!usermem)
+ memcpy(buf, rbuf->data+idx, split);
+ else
+ if (copy_to_user(buf, rbuf->data+idx, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ idx = 0;
+ }
+ if (!usermem)
+ memcpy(buf, rbuf->data+idx, todo);
+ else
+ if (copy_to_user(buf, rbuf->data+idx, todo))
+ return -EFAULT;
+
+ return len;
+}
+
+void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
+{
+ size_t pktlen;
+
+ rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
+
+ // clean up disposed packets
+ while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
+ if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
+ pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
+ pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
+ DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
+ } else {
+ // first packet is not disposed, so we stop cleaning now
+ break;
+ }
+ }
+}
+
+ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
+{
+ int consumed;
+ int curpktlen;
+ int curpktstatus;
+
+ if (idx == -1) {
+ idx = rbuf->pread;
+ } else {
+ curpktlen = rbuf->data[idx] << 8;
+ curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+ }
+
+ consumed = (idx - rbuf->pread) % rbuf->size;
+
+ while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
+
+ curpktlen = rbuf->data[idx] << 8;
+ curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
+
+ if (curpktstatus == PKT_READY) {
+ *pktlen = curpktlen;
+ return idx;
+ }
+
+ consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
+ idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+ }
+
+ // no packets available
+ return -1;
+}
+
+
EXPORT_SYMBOL(dvb_ringbuffer_init);
EXPORT_SYMBOL(dvb_ringbuffer_empty);
@@ -167,3 +275,7 @@ EXPORT_SYMBOL(dvb_ringbuffer_flush);
EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
EXPORT_SYMBOL(dvb_ringbuffer_read);
EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_write);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
index 9e6feebe8dac..4158406c65aa 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
@@ -3,9 +3,10 @@
* dvb_ringbuffer.h: ring buffer implementation for the dvb driver
*
* Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
*
- * based on code originally found in av7110.c:
- * Copyright (C) 1999-2002 Ralph Metzler & Marcus Metzler
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
@@ -39,6 +40,8 @@ struct dvb_ringbuffer {
spinlock_t lock;
};
+#define DVB_RINGBUFFER_PKTHDRSIZE 3
+
/*
** Notes:
@@ -120,4 +123,52 @@ extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
size_t len, int usermem);
+
+/**
+ * Write a packet into the ringbuffer.
+ *
+ * <rbuf> Ringbuffer to write to.
+ * <buf> Buffer to write.
+ * <len> Length of buffer (currently limited to 65535 bytes max).
+ * <usermem> Set to 1 if <buf> is in userspace.
+ * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL.
+ */
+extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf,
+ size_t len, int usermem);
+
+/**
+ * Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this
+ * does NOT update the read pointer in the ringbuffer. You must use
+ * dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
+ * <offset> Offset into packet to read from.
+ * <buf> Destination buffer for data.
+ * <len> Size of destination buffer.
+ * <usermem> Set to 1 if <buf> is in userspace.
+ * returns Number of bytes read, or -EFAULT.
+ */
+extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8* buf, size_t len, int usermem);
+
+/**
+ * Dispose of a packet in the ring buffer.
+ *
+ * <rbuf> Ring buffer concerned.
+ * <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
+ */
+extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx);
+
+/**
+ * Get the index of the next packet in a ringbuffer.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Previous packet index, or -1 to return the first packet index.
+ * <pktlen> On success, will be updated to contain the length of the packet in bytes.
+ * returns Packet index (if >=0), or -1 if no packets available.
+ */
+extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
+
+
#endif /* _DVB_RINGBUFFER_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
index 22dcdcb7dc05..fa719c78c097 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.c
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -211,6 +211,8 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
dvbdev->adapter = adap;
dvbdev->priv = priv;
+ dvbdev->fops->owner = adap->module;
+
list_add_tail (&dvbdev->list_head, &adap->device_list);
devfs_mk_cdev(MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
@@ -227,13 +229,15 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
void dvb_unregister_device(struct dvb_device *dvbdev)
{
- if (dvbdev) {
+ if (!dvbdev)
+ return;
+
devfs_remove("dvb/adapter%d/%s%d", dvbdev->adapter->num,
dnames[dvbdev->type], dvbdev->id);
+
list_del(&dvbdev->list_head);
kfree(dvbdev);
}
-}
static int dvbdev_get_free_adapter_num (void)
@@ -257,7 +261,7 @@ skip:
}
-int dvb_register_adapter(struct dvb_adapter **padap, const char *name)
+int dvb_register_adapter(struct dvb_adapter **padap, const char *name, struct module *module)
{
struct dvb_adapter *adap;
int num;
@@ -281,8 +285,10 @@ int dvb_register_adapter(struct dvb_adapter **padap, const char *name)
printk ("DVB: registering new adapter (%s).\n", name);
devfs_mk_dir("dvb/adapter%d", num);
+
adap->num = num;
adap->name = name;
+ adap->module = module;
list_add_tail (&adap->list_head, &dvb_adapter_list);
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
index 175d870acaf3..6c4e3a00a3b1 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -48,6 +48,8 @@ struct dvb_adapter {
struct list_head device_list;
const char *name;
u8 proposed_mac [6];
+
+ struct module *module;
};
@@ -75,7 +77,7 @@ struct dvb_device {
};
-extern int dvb_register_adapter (struct dvb_adapter **padap, const char *name);
+extern int dvb_register_adapter (struct dvb_adapter **padap, const char *name, struct module *module);
extern int dvb_unregister_adapter (struct dvb_adapter *adap);
extern int dvb_register_device (struct dvb_adapter *adap,
diff --git a/drivers/media/dvb/frontends/alps_tdlb7.c b/drivers/media/dvb/frontends/alps_tdlb7.c
index 249140a4a683..7c9f85327259 100644
--- a/drivers/media/dvb/frontends/alps_tdlb7.c
+++ b/drivers/media/dvb/frontends/alps_tdlb7.c
@@ -29,11 +29,11 @@
*/
+#define __KERNEL_SYSCALLS__
#include <linux/module.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
-#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/delay.h>
@@ -56,6 +56,8 @@ static int debug = 0;
#define SP8870_FIRMWARE_OFFSET 0x0A
+static int errno;
+
static struct dvb_frontend_info tdlb7_info = {
.name = "Alps TDLB7",
.type = FE_OFDM,
@@ -74,12 +76,7 @@ static struct dvb_frontend_info tdlb7_info = {
static int sp8870_writereg (struct dvb_i2c_bus *i2c, u16 reg, u16 data)
{
u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff };
- struct i2c_msg msg = {
- .addr = 0x71,
- .flags = 0,
- .buf = buf,
- .len = 4
- };
+ struct i2c_msg msg = { .addr = 0x71, .flags = 0, .buf = buf, .len = 4 };
int err;
if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
@@ -96,20 +93,8 @@ static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg)
int ret;
u8 b0 [] = { reg >> 8 , reg & 0xff };
u8 b1 [] = { 0, 0 };
- struct i2c_msg msg [] = {
- {
- .addr = 0x71,
- .flags = 0,
- .buf = b0,
- .len = 2
- },
- {
- .addr = 0x71,
- .flags = I2C_M_RD,
- .buf = b1,
- .len = 2
- }
- };
+ struct i2c_msg msg [] = { { .addr = 0x71, .flags = 0, .buf = b0, .len = 2 },
+ { .addr = 0x71, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
ret = i2c->xfer (i2c, msg, 2);
@@ -125,12 +110,7 @@ static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg)
static int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
int ret;
- struct i2c_msg msg = {
- .addr = 0x60,
- .flags = 0,
- .buf = data,
- .len =4
- };
+ struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = 4 };
ret = i2c->xfer (i2c, &msg, 1);
@@ -170,13 +150,13 @@ static int sp8870_read_firmware_file (const char *fn, char **fp)
loff_t filesize;
char *dp;
- fd = sys_open(fn, 0, 0);
+ fd = open(fn, 0, 0);
if (fd == -1) {
printk("%s: unable to open '%s'.\n", __FUNCTION__, fn);
return -EIO;
}
- filesize = sys_lseek(fd, 0L, 2);
+ filesize = lseek(fd, 0L, 2);
if (filesize <= 0 || filesize < SP8870_FIRMWARE_OFFSET + SP8870_FIRMWARE_SIZE) {
printk("%s: firmware filesize to small '%s'\n", __FUNCTION__, fn);
sys_close(fd);
@@ -190,8 +170,8 @@ static int sp8870_read_firmware_file (const char *fn, char **fp)
return -EIO;
}
- sys_lseek(fd, SP8870_FIRMWARE_OFFSET, 0);
- if (sys_read(fd, dp, SP8870_FIRMWARE_SIZE) != SP8870_FIRMWARE_SIZE) {
+ lseek(fd, SP8870_FIRMWARE_OFFSET, 0);
+ if (read(fd, dp, SP8870_FIRMWARE_SIZE) != SP8870_FIRMWARE_SIZE) {
printk("%s: failed to read '%s'.\n",__FUNCTION__, fn);
vfree(dp);
sys_close(fd);
@@ -658,9 +638,6 @@ static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_SET_FRONTEND:
return sp8870_set_frontend(i2c, (struct dvb_frontend_parameters*) arg);
- case FE_RESET:
- return -EOPNOTSUPP;
-
case FE_GET_FRONTEND: // FIXME: read known values back from Hardware...
return -EOPNOTSUPP;
@@ -675,6 +652,15 @@ static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
}
break;
+ case FE_GET_TUNE_SETTINGS:
+ {
+ struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
+ fesettings->min_delay_ms = 150;
+ fesettings->step_size = 166667;
+ fesettings->max_drift = 166667*2;
+ return 0;
+ }
+
default:
return -EOPNOTSUPP;
};
@@ -687,21 +673,8 @@ static int tdlb7_attach (struct dvb_i2c_bus *i2c, void **data)
{
u8 b0 [] = { 0x02 , 0x00 };
u8 b1 [] = { 0, 0 };
- struct i2c_msg msg [] =
- {
- {
- .addr = 0x71,
- .flags = 0,
- .buf = b0,
- .len = 2
- },
- {
- .addr = 0x71,
- .flags = I2C_M_RD,
- .buf = b1,
- .len = 2
- }
- };
+ struct i2c_msg msg [] = { { .addr = 0x71, .flags = 0, .buf = b0, .len = 2 },
+ { .addr = 0x71, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
dprintk ("%s\n", __FUNCTION__);
diff --git a/drivers/media/dvb/frontends/alps_tdmb7.c b/drivers/media/dvb/frontends/alps_tdmb7.c
index 6107c2b5e80f..55e4004c8a15 100644
--- a/drivers/media/dvb/frontends/alps_tdmb7.c
+++ b/drivers/media/dvb/frontends/alps_tdmb7.c
@@ -50,7 +50,7 @@ static struct dvb_frontend_info tdmb7_info = {
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
- FE_CAN_CLEAN_SETUP | FE_CAN_RECOVER
+ FE_CAN_RECOVER
};
@@ -390,8 +390,14 @@ static int tdmb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_INIT:
return cx22700_init (i2c);
- case FE_RESET:
- break;
+ case FE_GET_TUNE_SETTINGS:
+ {
+ struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
+ fesettings->min_delay_ms = 150;
+ fesettings->step_size = 166667;
+ fesettings->max_drift = 166667*2;
+ return 0;
+ }
default:
return -EOPNOTSUPP;
diff --git a/drivers/media/dvb/frontends/at76c651.c b/drivers/media/dvb/frontends/at76c651.c
index 16629ef8bb2c..01d438a4fa29 100644
--- a/drivers/media/dvb/frontends/at76c651.c
+++ b/drivers/media/dvb/frontends/at76c651.c
@@ -71,9 +71,7 @@ static struct dvb_frontend_info at76c651_info = {
FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 |
- FE_CAN_QAM_256 /* | FE_CAN_QAM_512 | FE_CAN_QAM_1024 */ |
- FE_CAN_RECOVER | FE_CAN_CLEAN_SETUP | FE_CAN_MUTE_TS
-
+ FE_CAN_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER
};
#if ! defined(__powerpc__)
@@ -361,6 +359,7 @@ static int at76c651_set_parameters(struct dvb_i2c_bus *i2c,
at76c651_set_symbolrate(i2c, p->u.qam.symbol_rate);
at76c651_set_inversion(i2c, p->inversion);
at76c651_set_auto_config(i2c);
+ at76c651_reset(i2c);
return 0;
@@ -462,8 +461,14 @@ static int at76c651_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_INIT:
return at76c651_set_defaults(fe->i2c);
- case FE_RESET:
- return at76c651_reset(fe->i2c);
+ case FE_GET_TUNE_SETTINGS:
+ {
+ struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
+ fesettings->min_delay_ms = 50;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+ }
default:
return -ENOIOCTLCMD;
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
index d429bd3741de..ee4d9a868ece 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -59,8 +59,7 @@ static struct dvb_frontend_info cx24110_info = {
.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_CLEAN_SETUP
+ FE_CAN_QPSK | FE_CAN_RECOVER
};
/* fixme: are these values correct? especially ..._tolerance and caps */
@@ -621,11 +620,6 @@ static int cx24110_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_INIT:
return cx24110_init (i2c);
- case FE_RESET:
-/* no idea what to do for this call */
-/* fixme (medium): fill me in */
- break;
-
case FE_SET_TONE:
return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&~0x10)|((((fe_sec_tone_mode_t) arg)==SEC_TONE_ON)?0x10:0));
case FE_SET_VOLTAGE:
diff --git a/drivers/media/dvb/frontends/dst.c b/drivers/media/dvb/frontends/dst.c
index 859a20348121..431ea9253255 100644
--- a/drivers/media/dvb/frontends/dst.c
+++ b/drivers/media/dvb/frontends/dst.c
@@ -963,7 +963,6 @@ struct lkup {
{FE_GET_FRONTEND, "FE_GET_FRONTEND:" },
{FE_SLEEP, "FE_SLEEP:" },
{FE_INIT, "FE_INIT:" },
- {FE_RESET, "FE_RESET:" },
{FE_SET_TONE, "FE_SET_TONE:" },
{FE_SET_VOLTAGE, "FE_SET_VOLTAGE:" },
};
@@ -1091,9 +1090,6 @@ static int dst_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
dst_init(dst);
break;
- case FE_RESET:
- break;
-
case FE_DISEQC_SEND_MASTER_CMD:
{
struct dvb_diseqc_master_cmd *cmd = (struct dvb_diseqc_master_cmd *)arg;
@@ -1149,8 +1145,8 @@ static int dst_attach (struct dvb_i2c_bus *i2c, void **data)
}
dst_init (dst);
- dprintk("%s: register dst %p bt %p i2c %p\n", __FUNCTION__,
- dst, dst->bt, dst->i2c);
+ dprintk("%s: register dst %8.8x bt %8.8x i2c %8.8x\n", __FUNCTION__,
+ (u32)dst, (u32)(dst->bt), (u32)(dst->i2c));
info = &dst_info_sat;
if (dst->dst_type == DST_TYPE_IS_TERR)
@@ -1166,7 +1162,7 @@ static int dst_attach (struct dvb_i2c_bus *i2c, void **data)
static void dst_detach (struct dvb_i2c_bus *i2c, void *data)
{
dvb_unregister_frontend (dst_ioctl, i2c);
- dprintk("%s: unregister dst %p\n", __FUNCTION__, data);
+ dprintk("%s: unregister dst %8.8x\n", __FUNCTION__, (u32)(data));
if (data)
kfree(data);
}
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c
index ab019e8169ea..6d9e6108e489 100644
--- a/drivers/media/dvb/frontends/dvb_dummy_fe.c
+++ b/drivers/media/dvb/frontends/dvb_dummy_fe.c
@@ -62,8 +62,7 @@ static struct dvb_frontend_info dvb_c_dummyfe_info = {
#endif
.caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
FE_CAN_QAM_128 | FE_CAN_QAM_256 |
- FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO |
- FE_CAN_CLEAN_SETUP
+ FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO
};
static struct dvb_frontend_info dvb_t_dummyfe_info = {
@@ -157,9 +156,6 @@ static int dvbdummyfe_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *ar
case FE_INIT:
return 0;
- case FE_RESET:
- return 0;
-
case FE_SET_TONE:
return -EOPNOTSUPP;
diff --git a/drivers/media/dvb/frontends/grundig_29504-401.c b/drivers/media/dvb/frontends/grundig_29504-401.c
index f4215ff7f0fb..d866dd5cc24d 100644
--- a/drivers/media/dvb/frontends/grundig_29504-401.c
+++ b/drivers/media/dvb/frontends/grundig_29504-401.c
@@ -35,6 +35,9 @@ static int debug = 0;
#define dprintk if (debug) printk
+struct grundig_state {
+ int first:1;
+};
struct dvb_frontend_info grundig_29504_401_info = {
.name = "Grundig 29504-401",
@@ -48,7 +51,7 @@ struct dvb_frontend_info grundig_29504_401_info = {
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
- FE_CAN_MUTE_TS /*| FE_CAN_CLEAN_SETUP*/
+ FE_CAN_MUTE_TS
};
@@ -102,6 +105,7 @@ static int tsa5060_write (struct dvb_i2c_bus *i2c, u8 data [4])
*/
static int tsa5060_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
{
+#if 1
u32 div;
u8 buf [4];
u8 cfg, cpump, band_select;
@@ -118,6 +122,20 @@ static int tsa5060_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
buf [1] = div & 0xff;
buf [2] = ((div >> 10) & 0x60) | cfg;
buf [3] = (cpump << 6) | band_select;
+#else
+ /* old code which seems to work better for at least one person */
+ u32 div;
+ u8 buf [4];
+ u8 cfg;
+
+ div = (36000000 + freq) / 166666;
+ cfg = 0x88;
+
+ buf [0] = (div >> 8) & 0x7f;
+ buf [1] = div & 0xff;
+ buf [2] = ((div >> 10) & 0x60) | cfg;
+ buf [3] = 0xc0;
+#endif
return tsa5060_write (i2c, buf);
}
@@ -276,6 +294,123 @@ static int reset_and_configure (struct dvb_i2c_bus *i2c)
}
+static int get_frontend(struct dvb_i2c_bus* i2c, struct dvb_frontend_parameters* param)
+{
+ int tmp;
+
+
+ tmp = l64781_readreg(i2c, 0x04);
+ switch(tmp & 3) {
+ case 0:
+ param->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ param->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ param->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ param->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+ switch((tmp >> 2) & 3) {
+ case 0:
+ param->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ param->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ default:
+ printk("Unexpected value for transmission_mode\n");
+ }
+
+
+
+ tmp = l64781_readreg(i2c, 0x05);
+ switch(tmp & 7) {
+ case 0:
+ param->u.ofdm.code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ param->u.ofdm.code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ param->u.ofdm.code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ param->u.ofdm.code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ param->u.ofdm.code_rate_HP = FEC_7_8;
+ break;
+ default:
+ printk("Unexpected value for code_rate_HP\n");
+ }
+ switch((tmp >> 3) & 7) {
+ case 0:
+ param->u.ofdm.code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ param->u.ofdm.code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ param->u.ofdm.code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ param->u.ofdm.code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ param->u.ofdm.code_rate_LP = FEC_7_8;
+ break;
+ default:
+ printk("Unexpected value for code_rate_LP\n");
+ }
+
+
+ tmp = l64781_readreg(i2c, 0x06);
+ switch(tmp & 3) {
+ case 0:
+ param->u.ofdm.constellation = QPSK;
+ break;
+ case 1:
+ param->u.ofdm.constellation = QAM_16;
+ break;
+ case 2:
+ param->u.ofdm.constellation = QAM_64;
+ break;
+ default:
+ printk("Unexpected value for constellation\n");
+ }
+ switch((tmp >> 2) & 7) {
+ case 0:
+ param->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ param->u.ofdm.hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ param->u.ofdm.hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ param->u.ofdm.hierarchy_information = HIERARCHY_4;
+ break;
+ default:
+ printk("Unexpected value for hierarchy\n");
+ }
+
+
+ tmp = l64781_readreg (i2c, 0x1d);
+ param->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF;
+
+ tmp = (int) (l64781_readreg (i2c, 0x08) |
+ (l64781_readreg (i2c, 0x09) << 8) |
+ (l64781_readreg (i2c, 0x0a) << 16));
+ param->frequency += tmp;
+
+ return 0;
+}
+
static int init (struct dvb_i2c_bus *i2c)
{
@@ -318,6 +453,9 @@ int grundig_29504_401_ioctl (struct dvb_frontend *fe,
unsigned int cmd, void *arg)
{
struct dvb_i2c_bus *i2c = fe->i2c;
+ int res;
+ struct grundig_state* state = (struct grundig_state*) fe->data;
+
switch (cmd) {
case FE_GET_INFO:
memcpy (arg, &grundig_29504_401_info,
@@ -393,18 +531,33 @@ int grundig_29504_401_ioctl (struct dvb_frontend *fe,
tsa5060_set_tv_freq (i2c, p->frequency);
return apply_frontend_param (i2c, p);
}
+
case FE_GET_FRONTEND:
- /* we could correct the frequency here, but...
- * (...do you want to implement this?;)
- */
- return 0;
+ {
+ struct dvb_frontend_parameters *p = arg;
+ return get_frontend(i2c, p);
+ }
case FE_SLEEP:
/* Power down */
return l64781_writereg (i2c, 0x3e, 0x5a);
case FE_INIT:
- return init (i2c);
+ res = init (i2c);
+ if ((res == 0) && (state->first)) {
+ state->first = 0;
+ dvb_delay(200);
+ }
+ return res;
+
+ case FE_GET_TUNE_SETTINGS:
+ {
+ struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
+ fesettings->min_delay_ms = 200;
+ fesettings->step_size = 166667;
+ fesettings->max_drift = 166667*2;
+ return 0;
+ }
default:
dprintk ("%s: unknown command !!!\n", __FUNCTION__);
@@ -422,6 +575,7 @@ static int l64781_attach (struct dvb_i2c_bus *i2c, void **data)
u8 b1 [] = { 0x00 };
struct i2c_msg msg [] = { { .addr = 0x55, .flags = 0, .buf = b0, .len = 1 },
{ .addr = 0x55, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+ struct grundig_state* state;
/**
* the L64781 won't show up before we send the reset_and_configure()
@@ -465,7 +619,12 @@ static int l64781_attach (struct dvb_i2c_bus *i2c, void **data)
goto bailout;
}
- return dvb_register_frontend (grundig_29504_401_ioctl, i2c, NULL,
+ state = kmalloc(sizeof(struct grundig_state), GFP_KERNEL);
+ if (state == NULL) goto bailout;
+ *data = state;
+ state->first = 1;
+
+ return dvb_register_frontend (grundig_29504_401_ioctl, i2c, state,
&grundig_29504_401_info);
bailout:
@@ -477,6 +636,7 @@ static int l64781_attach (struct dvb_i2c_bus *i2c, void **data)
static void l64781_detach (struct dvb_i2c_bus *i2c, void *data)
{
+ kfree(data);
dvb_unregister_frontend (grundig_29504_401_ioctl, i2c);
}
diff --git a/drivers/media/dvb/frontends/grundig_29504-491.c b/drivers/media/dvb/frontends/grundig_29504-491.c
index a70c769bfbff..68408c7b4401 100644
--- a/drivers/media/dvb/frontends/grundig_29504-491.c
+++ b/drivers/media/dvb/frontends/grundig_29504-491.c
@@ -52,8 +52,7 @@ static struct dvb_frontend_info grundig_29504_491_info = {
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
- FE_CAN_QPSK |
- FE_CAN_MUTE_TS | FE_CAN_CLEAN_SETUP
+ FE_CAN_QPSK | FE_CAN_MUTE_TS
};
@@ -398,11 +397,6 @@ static int grundig_29504_491_ioctl (struct dvb_frontend *fe, unsigned int cmd,
tda8083_writereg (i2c, 0x00, 0x04);
break;
- case FE_RESET:
- tda8083_writereg (i2c, 0x00, 0x3c);
- tda8083_writereg (i2c, 0x00, 0x04);
- break;
-
case FE_DISEQC_SEND_MASTER_CMD:
return tda8083_send_diseqc_msg (i2c, arg);
diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c
index 404378485c0e..b3d5ddf80c71 100644
--- a/drivers/media/dvb/frontends/mt312.c
+++ b/drivers/media/dvb/frontends/mt312.c
@@ -66,8 +66,8 @@ static struct dvb_frontend_info mt312_info = {
.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_RECOVER |
- FE_CAN_CLEAN_SETUP | FE_CAN_MUTE_TS
+ FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS |
+ FE_CAN_RECOVER
};
static int mt312_read(struct dvb_i2c_bus *i2c,
@@ -570,6 +570,8 @@ static int mt312_set_frontend(struct dvb_i2c_bus *i2c,
if ((ret = mt312_write(i2c, SYM_RATE_H, buf, sizeof(buf))) < 0)
return ret;
+ mt312_reset(i2c, 0);
+
return 0;
}
@@ -756,8 +758,14 @@ static int mt312_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
else
return mt312_init(i2c, (long) fe->data, (u8) 60);
- case FE_RESET:
- return mt312_reset(i2c, 0);
+ case FE_GET_TUNE_SETTINGS:
+ {
+ struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
+ fesettings->min_delay_ms = 50;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+ }
default:
return -ENOIOCTLCMD;
diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c
index 14e9cfe2f1d6..7116b0ba5df0 100644
--- a/drivers/media/dvb/frontends/nxt6000.c
+++ b/drivers/media/dvb/frontends/nxt6000.c
@@ -55,7 +55,12 @@ static struct dvb_frontend_info nxt6000_info = {
.symbol_rate_max = 9360000, /* FIXME */
.symbol_rate_tolerance = 4000,
.notifier_delay = 0,
- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO,
};
struct nxt6000_config {
@@ -762,9 +767,6 @@ static int nxt6000_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
nxt6000_setup(fe);
break;
- case FE_RESET:
- break;
-
case FE_SET_FRONTEND:
{
struct nxt6000_config *nxt = FE2NXT(fe);
diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c
index 81743c11b2b6..6cc71cbb2329 100644
--- a/drivers/media/dvb/frontends/sp887x.c
+++ b/drivers/media/dvb/frontends/sp887x.c
@@ -12,13 +12,13 @@
next 0x4000 loaded. This may change in future versions.
*/
+#define __KERNEL_SYSCALLS__
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include <linux/syscalls.h>
#include <linux/fs.h>
#include <linux/unistd.h>
#include <linux/fcntl.h>
@@ -64,19 +64,17 @@ struct dvb_frontend_info sp887x_info = {
.frequency_stepsize = 166666,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_RECOVER
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+ FE_CAN_RECOVER
};
+static int errno;
+
static
int i2c_writebytes (struct dvb_frontend *fe, u8 addr, u8 *buf, u8 len)
{
struct dvb_i2c_bus *i2c = fe->i2c;
- struct i2c_msg msg = {
- .addr = addr,
- .flags = 0,
- .buf = buf,
- .len = len
- };
+ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len };
int err;
LOG("i2c_writebytes", msg.addr, msg.buf, msg.len);
@@ -213,13 +211,13 @@ int sp887x_initial_setup (struct dvb_frontend *fe)
// Load the firmware
set_fs(get_ds());
- fd = sys_open(sp887x_firmware, 0, 0);
+ fd = open(sp887x_firmware, 0, 0);
if (fd < 0) {
printk(KERN_WARNING "%s: Unable to open firmware %s\n", __FUNCTION__,
sp887x_firmware);
return -EIO;
}
- filesize = sys_lseek(fd, 0L, 2);
+ filesize = lseek(fd, 0L, 2);
if (filesize <= 0) {
printk(KERN_WARNING "%s: Firmware %s is empty\n", __FUNCTION__,
sp887x_firmware);
@@ -241,8 +239,8 @@ int sp887x_initial_setup (struct dvb_frontend *fe)
// read it!
// read the first 16384 bytes from the file
// ignore the first 10 bytes
- sys_lseek(fd, 10, 0);
- if (sys_read(fd, firmware, fw_size) != fw_size) {
+ lseek(fd, 10, 0);
+ if (read(fd, firmware, fw_size) != fw_size) {
printk(KERN_WARNING "%s: Failed to read firmware\n", __FUNCTION__);
vfree(firmware);
sys_close(fd);
@@ -635,6 +633,15 @@ int sp887x_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
sp887x_writereg(fe, 0xc18, 0x00d);
break;
+ case FE_GET_TUNE_SETTINGS:
+ {
+ struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
+ fesettings->min_delay_ms = 50;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+ }
+
default:
return -EOPNOTSUPP;
};
@@ -647,12 +654,7 @@ int sp887x_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
static
int sp887x_attach (struct dvb_i2c_bus *i2c, void **data)
{
- struct i2c_msg msg = {
- .addr = 0x70,
- .flags = 0,
- .buf = NULL,
- .len = 0
- };
+ struct i2c_msg msg = {.addr = 0x70, .flags = 0, .buf = NULL, .len = 0 };
dprintk ("%s\n", __FUNCTION__);
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c
index 92ab2c9e0f26..be9d86a1b051 100644
--- a/drivers/media/dvb/frontends/stv0299.c
+++ b/drivers/media/dvb/frontends/stv0299.c
@@ -49,6 +49,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
+#include <linux/slab.h>
#include <asm/div64.h>
#include "dvb_frontend.h"
@@ -61,6 +62,7 @@
#endif
static int stv0299_status = 0;
+static int disable_typhoon = 0;
#define STATUS_BER 0
#define STATUS_UCBLOCKS 1
@@ -68,12 +70,13 @@ static int stv0299_status = 0;
/* frontend types */
#define UNKNOWN_FRONTEND -1
-#define PHILIPS_SU1278_TSA 0 // SU1278 with TSA5959 synth and datasheet recommended settings
+#define PHILIPS_SU1278_TSA 0 // SU1278 with TSA5059 synth and datasheet recommended settings
#define ALPS_BSRU6 1
#define LG_TDQF_S001F 2
#define PHILIPS_SU1278_TUA 3 // SU1278 with TUA6100 synth
#define SAMSUNG_TBMU24112IMB 4
-#define PHILIPS_SU1278_TSA_TT 5 // SU1278 with TSA5959 synth and TechnoTrend settings
+#define PHILIPS_SU1278_TSA_TT 5 // SU1278 with TSA5059 synth and TechnoTrend settings
+#define PHILIPS_SU1278_TSA_TY 6 // SU1278 with TUA5059 synth and Typhoon wiring
/* Master Clock = 88 MHz */
#define M_CLK (88000000UL)
@@ -95,8 +98,16 @@ static struct dvb_frontend_info uni0299_info = {
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
FE_CAN_QPSK |
- FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO |
- FE_CAN_CLEAN_SETUP
+ FE_CAN_FEC_AUTO
+};
+
+
+struct stv0299_state {
+ u8 tuner_type;
+ u8 initialised:1;
+ u32 tuner_frequency;
+ u32 symbol_rate;
+ fe_code_rate_t fec_inner;
};
@@ -253,6 +264,9 @@ static u8 init_tab_su1278_tsa_tt [] = {
0x34, 0x13
};
+static int stv0299_set_FEC (struct dvb_i2c_bus *i2c, fe_code_rate_t fec);
+static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner_type);
+
static int stv0299_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
int ret;
@@ -305,12 +319,8 @@ static int stv0299_readregs (struct dvb_i2c_bus *i2c, u8 reg1, u8 *b, u8 len)
static int pll_write (struct dvb_i2c_bus *i2c, u8 addr, u8 *data, int len)
{
int ret;
- struct i2c_msg msg = {
- .addr = addr,
- .flags = 0,
- .buf = data,
- .len = len
- };
+ struct i2c_msg msg = { addr: addr, .flags = 0, .buf = data, .len = len };
+
stv0299_writereg(i2c, 0x05, 0xb5); /* enable i2c repeater on stv0299 */
@@ -359,11 +369,16 @@ static int tsa5059_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, int ftype, in
if ((freq < 950000) || (freq > 2150000)) return -EINVAL;
+ if (ftype == PHILIPS_SU1278_TSA_TT) {
divisor = 500;
regcode = 2;
+ } else {
+ divisor = 125;
+ regcode = 4;
+ }
// setup frequency divisor
- div = freq / divisor;
+ div = (freq + (divisor - 1)) / divisor; // round correctly
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
buf[2] = 0x80 | ((div & 0x18000) >> 10) | regcode;
@@ -373,20 +388,26 @@ static int tsa5059_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, int ftype, in
switch(ftype) {
case PHILIPS_SU1278_TSA:
case PHILIPS_SU1278_TSA_TT:
+ case PHILIPS_SU1278_TSA_TY:
+ if (ftype == PHILIPS_SU1278_TSA_TY)
+ addr = 0x61;
+ else
addr = 0x60;
+
buf[3] |= 0x20;
if (srate < 4000000) buf[3] |= 1;
- if (freq <= 1250000) buf[3] |= 0;
- else if (freq <= 1550000) buf[3] |= 0x40;
- else if (freq <= 2050000) buf[3] |= 0x80;
- else if (freq <= 2150000) buf[3] |= 0xC0;
+ if (freq < 1250000) buf[3] |= 0;
+ else if (freq < 1550000) buf[3] |= 0x40;
+ else if (freq < 2050000) buf[3] |= 0x80;
+ else if (freq < 2150000) buf[3] |= 0xC0;
break;
case ALPS_BSRU6:
addr = 0x61;
- buf[3] |= 0xC0;
+ buf[3] = 0xC4;
+ if (freq > 1530000) buf[3] = 0xc0;
break;
default:
@@ -595,7 +616,7 @@ static int stv0299_init (struct dvb_i2c_bus *i2c, int ftype)
stv0299_writereg (i2c, init_tab[i], init_tab[i+1]);
/* AGC1 reference register setup */
- if (ftype == PHILIPS_SU1278_TSA)
+ if (ftype == PHILIPS_SU1278_TSA || ftype == PHILIPS_SU1278_TSA_TY)
stv0299_writereg (i2c, 0x0f, 0x92); /* Iagc = Inverse, m1 = 18 */
else if (ftype == PHILIPS_SU1278_TUA)
stv0299_writereg (i2c, 0x0f, 0x94); /* Iagc = Inverse, m1 = 20 */
@@ -618,23 +639,6 @@ static int stv0299_init (struct dvb_i2c_bus *i2c, int ftype)
}
-static int stv0299_check_inversion (struct dvb_i2c_bus *i2c)
-{
- dprintk ("%s\n", __FUNCTION__);
-
- if ((stv0299_readreg (i2c, 0x1b) & 0x98) != 0x98) {
- dvb_delay(30);
- if ((stv0299_readreg (i2c, 0x1b) & 0x98) != 0x98) {
- u8 val = stv0299_readreg (i2c, 0x0c);
- dprintk ("%s : changing inversion\n", __FUNCTION__);
- return stv0299_writereg (i2c, 0x0c, val ^ 0x01);
- }
- }
-
- return 0;
-}
-
-
static int stv0299_set_FEC (struct dvb_i2c_bus *i2c, fe_code_rate_t fec)
{
dprintk ("%s\n", __FUNCTION__);
@@ -824,7 +828,8 @@ static int stv0299_set_tone (struct dvb_i2c_bus *i2c, fe_sec_tone_mode_t tone)
}
-static int stv0299_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
+static int stv0299_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage,
+ int tuner_type)
{
u8 reg0x08;
u8 reg0x0c;
@@ -842,22 +847,26 @@ static int stv0299_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltag
reg0x0c &= 0x0f;
if (voltage == SEC_VOLTAGE_OFF) {
- stv0299_writereg (i2c, 0x08, reg0x08 & ~0x40);
- return stv0299_writereg (i2c, 0x0c, reg0x0c & ~0x40);
- } else {
+ stv0299_writereg (i2c, 0x0c, 0x00); /* LNB power off! */
+ return stv0299_writereg (i2c, 0x08, 0x00); /* LNB power off! */
+ }
+
stv0299_writereg (i2c, 0x08, reg0x08 | 0x40);
- reg0x0c |= 0x40; /* LNB power on */
switch (voltage) {
case SEC_VOLTAGE_13:
- return stv0299_writereg (i2c, 0x0c, reg0x0c);
- case SEC_VOLTAGE_18:
+ if (tuner_type == PHILIPS_SU1278_TSA_TY)
return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x10);
+ else
+ return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x40);
+
+ case SEC_VOLTAGE_18:
+ return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x50);
+
default:
return -EINVAL;
};
}
-}
static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner_type)
@@ -875,6 +884,7 @@ static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner
// calculate value to program
if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT;
big = big << 20;
+ big += (Mclk-1); // round correctly
do_div(big, Mclk);
ratio = big << 4;
@@ -909,6 +919,7 @@ static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner
stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0);
break;
+ case PHILIPS_SU1278_TSA_TY:
case PHILIPS_SU1278_TSA:
aclk = 0xb5;
if (srate < 2000000) bclk = 0x86;
@@ -929,20 +940,18 @@ static int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate, int tuner
case ALPS_BSRU6:
default:
- if (srate <= 1500000) { aclk = 0xb7; bclk = 0x87; }
- else if (srate <= 3000000) { aclk = 0xb7; bclk = 0x8b; }
- else if (srate <= 7000000) { aclk = 0xb7; bclk = 0x8f; }
- else if (srate <= 14000000) { aclk = 0xb7; bclk = 0x93; }
- else if (srate <= 30000000) { aclk = 0xb6; bclk = 0x93; }
- else if (srate <= 45000000) { aclk = 0xb4; bclk = 0x91; }
- m1 = 0x12;
+ if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+ else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+ else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+ else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+ else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+ else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
stv0299_writereg (i2c, 0x13, aclk);
stv0299_writereg (i2c, 0x14, bclk);
stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff);
stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff);
stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0);
- stv0299_writereg (i2c, 0x0f, (stv0299_readreg(i2c, 0x0f) & 0xc0) | m1);
break;
}
@@ -986,11 +995,10 @@ static int stv0299_get_symbolrate (struct dvb_i2c_bus *i2c, int tuner_type)
return srate;
}
-
static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
- int tuner_type = (long) fe->data;
struct dvb_i2c_bus *i2c = fe->i2c;
+ struct stv0299_state *state = (struct stv0299_state *) fe->data;
dprintk ("%s\n", __FUNCTION__);
@@ -1000,7 +1008,7 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) arg;
memcpy (arg, &uni0299_info, sizeof(struct dvb_frontend_info));
- if (tuner_type == PHILIPS_SU1278_TSA_TT) {
+ if (state->tuner_type == PHILIPS_SU1278_TSA_TT) {
tmp->frequency_tolerance = M_CLK_SU1278_TSA_TT / 2000;
}
break;
@@ -1078,23 +1086,67 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_SET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
+ int invval = 0;
dprintk ("%s : FE_SET_FRONTEND\n", __FUNCTION__);
- pll_set_tv_freq (i2c, p->frequency, tuner_type,
- p->u.qpsk.symbol_rate);
+ // set the inversion
+ if (p->inversion == INVERSION_OFF) invval = 0;
+ else if (p->inversion == INVERSION_ON) invval = 1;
+ else {
+ printk("stv0299 does not support auto-inversion\n");
+ return -EINVAL;
+ }
+ if (state->tuner_type == ALPS_BSRU6) invval = (~invval) & 1;
+ stv0299_writereg(i2c, 0x0c, (stv0299_readreg(i2c, 0x0c) & 0xfe) | invval);
+ switch(state->tuner_type) {
+ case PHILIPS_SU1278_TSA_TT:
+ {
+ /* check if we should do a finetune */
+ int frequency_delta = p->frequency - state->tuner_frequency;
+ int minmax = p->u.qpsk.symbol_rate / 2000;
+ if (minmax < 5000) minmax = 5000;
+
+ if ((frequency_delta > -minmax) && (frequency_delta < minmax) && (frequency_delta != 0) &&
+ (state->fec_inner == p->u.qpsk.fec_inner) &&
+ (state->symbol_rate == p->u.qpsk.symbol_rate)) {
+ int Drot_freq = (frequency_delta << 16) / (M_CLK_SU1278_TSA_TT / 1000);
+
+ // zap the derotator registers first
+ stv0299_writereg (i2c, 0x22, 0x00);
+ stv0299_writereg (i2c, 0x23, 0x00);
+
+ // now set them as we want
+ stv0299_writereg (i2c, 0x22, Drot_freq >> 8);
+ stv0299_writereg (i2c, 0x23, Drot_freq);
+ } else {
+ /* A "normal" tune is requested */
+ pll_set_tv_freq (i2c, p->frequency, state->tuner_type, p->u.qpsk.symbol_rate);
+ stv0299_writereg (i2c, 0x32, 0x80);
+ stv0299_writereg (i2c, 0x22, 0x00);
+ stv0299_writereg (i2c, 0x23, 0x00);
+ stv0299_writereg (i2c, 0x32, 0x19);
+ stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, state->tuner_type);
stv0299_set_FEC (i2c, p->u.qpsk.fec_inner);
- stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, tuner_type);
+ }
+ break;
+ }
+
+ default:
+ pll_set_tv_freq (i2c, p->frequency, state->tuner_type, p->u.qpsk.symbol_rate);
+ stv0299_set_FEC (i2c, p->u.qpsk.fec_inner);
+ stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, state->tuner_type);
stv0299_writereg (i2c, 0x22, 0x00);
stv0299_writereg (i2c, 0x23, 0x00);
- if (tuner_type != PHILIPS_SU1278_TSA_TT) {
stv0299_readreg (i2c, 0x23);
stv0299_writereg (i2c, 0x12, 0xb9);
+ break;
}
- stv0299_check_inversion (i2c);
- /* printk ("%s: tsa5059 status: %x\n", __FUNCTION__, tsa5059_read_status(i2c)); */
+ state->tuner_frequency = p->frequency;
+ state->fec_inner = p->u.qpsk.fec_inner;
+ state->symbol_rate = p->u.qpsk.symbol_rate;
break;
}
@@ -1103,8 +1155,9 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
struct dvb_frontend_parameters *p = arg;
s32 derot_freq;
int Mclk = M_CLK;
+ int invval;
- if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT;
+ if (state->tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT;
derot_freq = (s32)(s16) ((stv0299_readreg (i2c, 0x22) << 8)
| stv0299_readreg (i2c, 0x23));
@@ -1114,10 +1167,13 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
derot_freq /= 1000;
p->frequency += derot_freq;
- p->inversion = (stv0299_readreg (i2c, 0x0c) & 1) ?
- INVERSION_OFF : INVERSION_ON;
+
+ invval = stv0299_readreg (i2c, 0x0c) & 1;
+ if (state->tuner_type == ALPS_BSRU6) invval = (~invval) & 1;
+ p->inversion = invval ? INVERSION_ON : INVERSION_OFF;
+
p->u.qpsk.fec_inner = stv0299_get_fec (i2c);
- p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c, tuner_type);
+ p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c, state->tuner_type);
break;
}
@@ -1125,10 +1181,23 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
stv0299_writereg (i2c, 0x0c, 0x00); /* LNB power off! */
stv0299_writereg (i2c, 0x08, 0x00); /* LNB power off! */
stv0299_writereg (i2c, 0x02, 0x80);
+ state->initialised = 0;
break;
case FE_INIT:
- return stv0299_init (i2c, tuner_type);
+ switch(state->tuner_type) {
+ case PHILIPS_SU1278_TSA_TT:
+ state->tuner_frequency = 0;
+ if (!state->initialised) {
+ state->initialised = 1;
+ return stv0299_init (i2c, state->tuner_type);
+ }
+ break;
+
+ default:
+ return stv0299_init (i2c, state->tuner_type);
+ }
+ break;
case FE_DISEQC_SEND_MASTER_CMD:
return stv0299_send_diseqc_msg (i2c, arg);
@@ -1140,7 +1209,39 @@ static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
return stv0299_set_tone (i2c, (fe_sec_tone_mode_t) arg);
case FE_SET_VOLTAGE:
- return stv0299_set_voltage (i2c, (fe_sec_voltage_t) arg);
+ return stv0299_set_voltage (i2c, (fe_sec_voltage_t) arg,
+ state->tuner_type);
+
+ case FE_GET_TUNE_SETTINGS:
+ {
+ struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
+
+ switch(state->tuner_type) {
+ case PHILIPS_SU1278_TSA_TT:
+ fesettings->min_delay_ms = 50;
+ if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) {
+ fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000;
+ fesettings->max_drift = 5000;
+ } else {
+ fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000;
+ fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000;
+ }
+ break;
+
+ default:
+ fesettings->min_delay_ms = 100;
+ if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) {
+ fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000;
+ fesettings->max_drift = 5000;
+ } else {
+ fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000;
+ fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000;
+ }
+ break;
+ }
+
+ return 0;
+ }
default:
return -EOPNOTSUPP;
@@ -1158,48 +1259,12 @@ static long probe_tuner (struct dvb_i2c_bus *i2c)
u8 stat [] = { 0 };
u8 tda6100_buf [] = { 0, 0 };
int ret;
- struct i2c_msg msg1 [] = {
- {
- .addr = 0x68,
- .flags = 0,
- .buf = rpt,
- .len = 2
- },
- {
- .addr = 0x60,
- .flags = I2C_M_RD,
- .buf = stat,
- .len = 1
- }
- };
- struct i2c_msg msg2 [] = {
- {
- .addr = 0x68,
- .flags = 0,
- .buf = rpt,
- .len = 2
- },
- {
- .addr = 0x61,
- .flags = I2C_M_RD,
- .buf = stat,
- .len = 1
- }
- };
- struct i2c_msg msg3 [] = {
- {
- .addr = 0x68,
- .flags = 0,
- .buf = rpt,
- .len = 2
- },
- {
- .addr = 0x60,
- .flags = 0,
- .buf = tda6100_buf,
- .len = 2
- }
- };
+ struct i2c_msg msg1 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, len: 2 },
+ { .addr = 0x60, .flags = I2C_M_RD, .buf = stat, .len = 1 }};
+ struct i2c_msg msg2 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, len: 2 },
+ { .addr = 0x61, .flags = I2C_M_RD, .buf = stat, .len = 1 }};
+ struct i2c_msg msg3 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, len: 2 },
+ { .addr = 0x60, .flags = 0, .buf = tda6100_buf, .len = 2 }};
stv0299_writereg (i2c, 0x01, 0x15);
stv0299_writereg (i2c, 0x02, 0x30);
@@ -1218,18 +1283,28 @@ static long probe_tuner (struct dvb_i2c_bus *i2c)
if ((ret = i2c->xfer(i2c, msg1, 2)) == 2) {
if ( strcmp(adapter->name, "TT-Budget/WinTV-NOVA-CI PCI") == 0 ) {
// technotrend cards require non-datasheet settings
- printk ("%s: setup for tuner SU1278 (TSA5959 synth) on TechnoTrend hardware\n", __FILE__);
+ printk ("%s: setup for tuner SU1278 (TSA5059 synth) on"
+ " TechnoTrend hardware\n", __FILE__);
return PHILIPS_SU1278_TSA_TT;
} else {
// fall back to datasheet-recommended settings
- printk ("%s: setup for tuner SU1278 (TSA5959 synth)\n", __FILE__);
+ printk ("%s: setup for tuner SU1278 (TSA5059 synth)\n",
+ __FILE__);
return PHILIPS_SU1278_TSA;
}
}
if ((ret = i2c->xfer(i2c, msg2, 2)) == 2) {
- //if ((stat[0] & 0x3f) == 0) {
- if (0) {
+ if ( strcmp(adapter->name, "KNC1 DVB-S") == 0 &&
+ !disable_typhoon )
+ {
+ // Typhoon cards have unusual wiring.
+ printk ("%s: setup for tuner SU1278 (TSA5059 synth) on"
+ " Typhoon hardware\n", __FILE__);
+ return PHILIPS_SU1278_TSA_TY;
+ }
+ //else if ((stat[0] & 0x3f) == 0) {
+ else if (0) {
printk ("%s: setup for tuner TDQF-S001F\n", __FILE__);
return LG_TDQF_S001F;
} else {
@@ -1245,7 +1320,8 @@ static long probe_tuner (struct dvb_i2c_bus *i2c)
stv0299_writereg (i2c, 0x02, 0x00);
if ((ret = i2c->xfer(i2c, msg3, 2)) == 2) {
- printk ("%s: setup for tuner Philips SU1278 (TUA6100 synth)\n", __FILE__);
+ printk ("%s: setup for tuner Philips SU1278 (TUA6100 synth)\n",
+ __FILE__);
return PHILIPS_SU1278_TUA;
}
@@ -1259,10 +1335,12 @@ static long probe_tuner (struct dvb_i2c_bus *i2c)
static int uni0299_attach (struct dvb_i2c_bus *i2c, void **data)
{
- long tuner_type;
+ struct stv0299_state* state;
+ int tuner_type;
u8 id;
- stv0299_writereg (i2c, 0x02, 0x00); /* standby off */
+ stv0299_writereg (i2c, 0x02, 0x34); /* standby off */
+ dvb_delay(200);
id = stv0299_readreg (i2c, 0x00);
dprintk ("%s: id == 0x%02x\n", __FUNCTION__, id);
@@ -1275,7 +1353,15 @@ static int uni0299_attach (struct dvb_i2c_bus *i2c, void **data)
if ((tuner_type = probe_tuner(i2c)) < 0)
return -ENODEV;
- return dvb_register_frontend (uni0299_ioctl, i2c, (void*) tuner_type,
+ if ((state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ *data = state;
+ state->tuner_type = tuner_type;
+ state->tuner_frequency = 0;
+ state->initialised = 0;
+ return dvb_register_frontend (uni0299_ioctl, i2c, (void *) state,
&uni0299_info);
}
@@ -1283,6 +1369,7 @@ static int uni0299_attach (struct dvb_i2c_bus *i2c, void **data)
static void uni0299_detach (struct dvb_i2c_bus *i2c, void *data)
{
dprintk ("%s\n", __FUNCTION__);
+ kfree(data);
dvb_unregister_frontend (uni0299_ioctl, i2c);
}
@@ -1305,8 +1392,11 @@ module_init (init_uni0299);
module_exit (exit_uni0299);
MODULE_DESCRIPTION("Universal STV0299/TSA5059/SL1935 DVB Frontend driver");
-MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, Andreas Oberritter");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, Andreas Oberritter, Andrew de Quincey");
MODULE_LICENSE("GPL");
MODULE_PARM(stv0299_status, "i");
MODULE_PARM_DESC(stv0299_status, "Which status value to support (0: BER, 1: UCBLOCKS)");
+
+MODULE_PARM(disable_typhoon, "i");
+MODULE_PARM_DESC(disable_typhoon, "Disable support for Philips SU1278 on Typhoon hardware.");
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index c14cd71b2d00..bc74bb1f4bac 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -32,14 +32,15 @@
*/
+#define __KERNEL_SYSCALLS__
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
-#include <linux/syscalls.h>
#include <linux/fs.h>
+#include <linux/unistd.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
#include "dvb_frontend.h"
@@ -167,7 +168,6 @@ static struct dvb_frontend_info tda10046h_info = {
};
-#pragma pack(1)
struct tda1004x_state {
u8 tda1004x_address;
u8 tuner_address;
@@ -175,7 +175,7 @@ struct tda1004x_state {
u8 tuner_type:2;
u8 fe_type:2;
};
-#pragma pack()
+
struct fwinfo {
int file_size;
@@ -396,13 +396,13 @@ static int tda1004x_fwupload(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda
// Load the firmware
set_fs(get_ds());
- fd = sys_open(tda1004x_firmware, 0, 0);
+ fd = open(tda1004x_firmware, 0, 0);
if (fd < 0) {
printk("%s: Unable to open firmware %s\n", __FUNCTION__,
tda1004x_firmware);
return -EIO;
}
- filesize = sys_lseek(fd, 0L, 2);
+ filesize = lseek(fd, 0L, 2);
if (filesize <= 0) {
printk("%s: Firmware %s is empty\n", __FUNCTION__,
tda1004x_firmware);
@@ -433,8 +433,8 @@ static int tda1004x_fwupload(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda
}
// read it!
- sys_lseek(fd, fw_offset, 0);
- if (sys_read(fd, firmware, fw_size) != fw_size) {
+ lseek(fd, fw_offset, 0);
+ if (read(fd, firmware, fw_size) != fw_size) {
printk("%s: Failed to read firmware\n", __FUNCTION__);
vfree(firmware);
sys_close(fd);
@@ -447,6 +447,7 @@ static int tda1004x_fwupload(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda
switch(tda_state->fe_type) {
case FE_TYPE_TDA10045H:
// reset chip
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x10, 0);
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8);
tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0);
dvb_delay(10);
@@ -457,6 +458,7 @@ static int tda1004x_fwupload(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda
case FE_TYPE_TDA10046H:
// reset chip
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 0);
tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 1, 0);
dvb_delay(10);
@@ -538,6 +540,8 @@ static int tda10045h_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_st
dprintk("%s\n", __FUNCTION__);
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC
+
// Disable the MC44BC374C
tda1004x_enable_tuner_i2c(i2c, tda_state);
tuner_msg.addr = MC44BC374_ADDRESS;
@@ -574,6 +578,8 @@ static int tda10046h_init(struct dvb_i2c_bus *i2c, struct tda1004x_state *tda_st
dprintk("%s\n", __FUNCTION__);
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 0); // wake up the chip
+
// Disable the MC44BC374C
tda1004x_enable_tuner_i2c(i2c, tda_state);
tuner_msg.addr = MC44BC374_ADDRESS;
@@ -1277,12 +1283,27 @@ static int tda1004x_read_ber(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda
return 0;
}
+static int tda1004x_sleep(struct dvb_i2c_bus *i2c, struct tda1004x_state* tda_state)
+{
+ switch(tda_state->fe_type) {
+ case FE_TYPE_TDA10045H:
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFADC1, 0x10, 0x10);
+ break;
+
+ case FE_TYPE_TDA10046H:
+ tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 1);
+ break;
+ }
+
+ return 0;
+}
+
static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
int status = 0;
struct dvb_i2c_bus *i2c = fe->i2c;
- struct tda1004x_state *tda_state = (struct tda1004x_state *) &(fe->data);
+ struct tda1004x_state *tda_state = (struct tda1004x_state *) fe->data;
dprintk("%s: cmd=0x%x\n", __FUNCTION__, cmd);
@@ -1320,7 +1341,12 @@ static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_GET_FRONTEND:
return tda1004x_get_fe(i2c, tda_state, (struct dvb_frontend_parameters*) arg);
+ case FE_SLEEP:
+ tda_state->initialised = 0;
+ return tda1004x_sleep(i2c, tda_state);
+
case FE_INIT:
+
// don't bother reinitialising
if (tda_state->initialised)
return 0;
@@ -1339,6 +1365,15 @@ static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
tda_state->initialised = 1;
return status;
+ case FE_GET_TUNE_SETTINGS:
+ {
+ struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 166667;
+ fesettings->max_drift = 166667*2;
+ return 0;
+ }
+
default:
return -EOPNOTSUPP;
}
@@ -1354,6 +1389,7 @@ static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data)
int fe_type = -1;
int tuner_type = -1;
struct tda1004x_state tda_state;
+ struct tda1004x_state* ptda_state;
struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=0, .len=0 };
static u8 td1344_init[] = { 0x0b, 0xf5, 0x88, 0xab };
static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
@@ -1446,13 +1482,20 @@ static int tda1004x_attach(struct dvb_i2c_bus *i2c, void **data)
// upload firmware
if ((status = tda1004x_fwupload(i2c, &tda_state)) != 0) return status;
+ // create the real state we'll be passing about
+ if ((ptda_state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ memcpy(ptda_state, &tda_state, sizeof(struct tda1004x_state));
+ *data = ptda_state;
+
// register
switch(tda_state.fe_type) {
case FE_TYPE_TDA10045H:
- return dvb_register_frontend(tda1004x_ioctl, i2c, (void *)(*((u32*) &tda_state)), &tda10045h_info);
+ return dvb_register_frontend(tda1004x_ioctl, i2c, ptda_state, &tda10045h_info);
case FE_TYPE_TDA10046H:
- return dvb_register_frontend(tda1004x_ioctl, i2c, (void *)(*((u32*) &tda_state)), &tda10046h_info);
+ return dvb_register_frontend(tda1004x_ioctl, i2c, ptda_state, &tda10046h_info);
}
// should not get here
@@ -1465,6 +1508,7 @@ void tda1004x_detach(struct dvb_i2c_bus *i2c, void *data)
{
dprintk("%s\n", __FUNCTION__);
+ kfree(data);
dvb_unregister_frontend(tda1004x_ioctl, i2c);
}
diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c
index 1652be9d3167..d01d28361c45 100644
--- a/drivers/media/dvb/frontends/ves1820.c
+++ b/drivers/media/dvb/frontends/ves1820.c
@@ -111,8 +111,7 @@ static struct dvb_frontend_info ves1820_info = {
#endif
.caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
FE_CAN_QAM_128 | FE_CAN_QAM_256 |
- FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO |
- FE_CAN_CLEAN_SETUP | FE_CAN_RECOVER
+ FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO,
};
diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c
index e6e6ba3fb458..876246d2485f 100644
--- a/drivers/media/dvb/frontends/ves1x93.c
+++ b/drivers/media/dvb/frontends/ves1x93.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include "dvb_frontend.h"
+#include "dvb_functions.h"
static int debug = 0;
#define dprintk if (debug) printk
@@ -67,10 +68,10 @@ static struct dvb_frontend_info ves1x93_info = {
*/
static u8 init_1893_tab [] = {
- 0x01, 0xa4, 0x35, 0x81, 0x2a, 0x0d, 0x55, 0xc4,
+ 0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4,
0x09, 0x69, 0x00, 0x86, 0x4c, 0x28, 0x7f, 0x00,
0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x80, 0x00, 0x31, 0xb0, 0x14, 0x00, 0xdc, 0x00,
+ 0x80, 0x00, 0x21, 0xb0, 0x14, 0x00, 0xdc, 0x00,
0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x55, 0x00, 0x00, 0x7f, 0x00
@@ -109,6 +110,11 @@ static u8 init_1993_wtab[] =
1,1,1,0,1,1,1,1, 1,1,1,1,1
};
+struct ves1x93_state {
+ fe_spectral_inversion_t inversion;
+};
+
+
static int ves1x93_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
@@ -247,8 +253,16 @@ static int ves1x93_clr_bit (struct dvb_i2c_bus *i2c)
{
ves1x93_writereg (i2c, 0, init_1x93_tab[0] & 0xfe);
ves1x93_writereg (i2c, 0, init_1x93_tab[0]);
+ dvb_delay(5);
+ return 0;
+}
+
+static int ves1x93_init_aquire (struct dvb_i2c_bus *i2c)
+{
ves1x93_writereg (i2c, 3, 0x00);
- return ves1x93_writereg (i2c, 3, init_1x93_tab[3]);
+ ves1x93_writereg (i2c, 3, init_1x93_tab[3]);
+ dvb_delay(5);
+ return 0;
}
@@ -275,10 +289,7 @@ static int ves1x93_set_inversion (struct dvb_i2c_bus *i2c, fe_spectral_inversion
return -EINVAL;
}
- /* needs to be saved for FE_GET_FRONTEND */
- init_1x93_tab[0x0c] = (init_1x93_tab[0x0c] & 0x3f) | val;
-
- return ves1x93_writereg (i2c, 0x0c, init_1x93_tab[0x0c]);
+ return ves1x93_writereg (i2c, 0x0c, (init_1x93_tab[0x0c] & 0x3f) | val);
}
@@ -403,6 +414,25 @@ static int ves1x93_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate)
}
+static int ves1x93_afc (struct dvb_i2c_bus *i2c, u32 freq, u32 srate)
+{
+ int afc;
+
+ afc = ((int)((ves1x93_readreg (i2c, 0x0a) << 1) & 0xff))/2;
+ afc = (afc * (int)(srate/1000/8))/16;
+
+ if (afc) {
+
+ freq -= afc;
+
+ tuner_set_tv_freq (i2c, freq, 0);
+
+ ves1x93_init_aquire (i2c);
+ }
+
+ return 0;
+}
+
static int ves1x93_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
{
switch (voltage) {
@@ -421,6 +451,7 @@ static int ves1x93_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltag
static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
struct dvb_i2c_bus *i2c = fe->i2c;
+ struct ves1x93_state *state = (struct ves1x93_state*) fe->data;
switch (cmd) {
case FE_GET_INFO:
@@ -497,6 +528,8 @@ static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
ves1x93_set_inversion (i2c, p->inversion);
ves1x93_set_fec (i2c, p->u.qpsk.fec_inner);
ves1x93_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
+ ves1x93_afc (i2c, p->frequency, p->u.qpsk.symbol_rate);
+ state->inversion = p->inversion;
break;
}
@@ -514,7 +547,7 @@ static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
* inversion indicator is only valid
* if auto inversion was used
*/
- if (!(init_1x93_tab[0x0c] & 0x80))
+ if (state->inversion == INVERSION_AUTO)
p->inversion = (ves1x93_readreg (i2c, 0x0f) & 2) ?
INVERSION_OFF : INVERSION_ON;
p->u.qpsk.fec_inner = ves1x93_get_fec (i2c);
@@ -530,9 +563,6 @@ static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_INIT:
return ves1x93_init (i2c);
- case FE_RESET:
- return ves1x93_clr_bit (i2c);
-
case FE_SET_TONE:
return -EOPNOTSUPP; /* the ves1893 can generate the 22k */
/* let's implement this when we have */
@@ -552,14 +582,21 @@ static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
static int ves1x93_attach (struct dvb_i2c_bus *i2c, void **data)
{
u8 identity = ves1x93_readreg(i2c, 0x1e);
+ struct ves1x93_state* state;
switch (identity) {
case 0xdc: /* VES1893A rev1 */
+ printk("ves1x93: Detected ves1893a rev1\n");
+ demod_type = DEMOD_VES1893;
+ ves1x93_info.name[4] = '8';
+ break;
case 0xdd: /* VES1893A rev2 */
+ printk("ves1x93: Detected ves1893a rev2\n");
demod_type = DEMOD_VES1893;
ves1x93_info.name[4] = '8';
break;
case 0xde: /* VES1993 */
+ printk("ves1x93: Detected ves1993\n");
demod_type = DEMOD_VES1993;
ves1x93_info.name[4] = '9';
break;
@@ -568,12 +605,19 @@ static int ves1x93_attach (struct dvb_i2c_bus *i2c, void **data)
return -ENODEV;
}
- return dvb_register_frontend (ves1x93_ioctl, i2c, NULL, &ves1x93_info);
+ if ((state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ state->inversion = INVERSION_OFF;
+ *data = state;
+
+ return dvb_register_frontend (ves1x93_ioctl, i2c, (void*) state, &ves1x93_info);
}
static void ves1x93_detach (struct dvb_i2c_bus *i2c, void *data)
{
+ kfree(data);
dvb_unregister_frontend (ves1x93_ioctl, i2c);
}
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index fcd7f125c668..d20b96203349 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -30,6 +30,7 @@
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/delay.h>
@@ -77,7 +78,7 @@ int av7110_debug = 0;
static int vidmode=CVBS_RGB_OUT;
static int pids_off;
static int adac=DVB_ADAC_TI;
-static int hw_sections = 1;
+static int hw_sections = 0;
static int rgb_on = 0;
int av7110_num = 0;
@@ -1241,10 +1242,6 @@ static int master_xfer(struct dvb_i2c_bus *i2c, const struct i2c_msg msgs[], int
****************************************************************************/
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
-#define CONFIG_DVB_AV7110_FIRMWARE_FILE
-#endif
-
static int check_firmware(struct av7110* av7110)
{
u32 crc = 0, len = 0;
@@ -1358,13 +1355,13 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d
return ret;
}
- dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name);
+ dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name, THIS_MODULE);
/* the Siemens DVB needs this if you want to have the i2c chips
get recognized before the main driver is fully loaded */
saa7146_write(dev, GPIO_CTRL, 0x500000);
- saa7146_i2c_adapter_prepare(dev, NULL, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */
+ saa7146_i2c_adapter_prepare(dev, NULL, 0, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */
av7110->i2c_bus = dvb_register_i2c_bus (master_xfer, dev,
av7110->dvb_adapter, 0);
@@ -1440,13 +1437,17 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d
printk ("av7110: Warning, firmware version 0x%04x is too old. "
"System might be unstable!\n", FW_VERSION(av7110->arm_app));
- kernel_thread(arm_thread, (void *) av7110, 0);
+ if (kernel_thread(arm_thread, (void *) av7110, 0) < 0) {
+ printk(KERN_ERR "av7110(%d): faile to start arm_mon kernel thread\n",
+ av7110->dvb_adapter->num);
+ goto err2;
+ }
/* set internal volume control to maximum */
av7110->adac_type = DVB_ADAC_TI;
av7110_set_volume(av7110, 0xff, 0xff);
- VidMode(av7110, vidmode);
+ av7710_set_video_mode(av7110, vidmode);
/* handle different card types */
/* remaining inits according to card and frontend type */
@@ -1498,32 +1499,35 @@ static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_d
ret = av7110_init_v4l(av7110);
if (ret)
- goto err;
+ goto err3;
printk(KERN_INFO "av7110: found av7110-%d.\n",av7110_num);
av7110->device_initialized = 1;
av7110_num++;
return 0;
+err3:
+ av7110->arm_rmmod = 1;
+ wake_up_interruptible(&av7110->arm_wait);
+ while (av7110->arm_thread)
+ dvb_delay(1);
err2:
av7110_ca_exit(av7110);
av7110_av_exit(av7110);
err:
- if (NULL != av7110 ) {
- kfree(av7110);
- }
- if (NULL != av7110->debi_virt) {
- pci_free_consistent(dev->pci, 8192, av7110->debi_virt, av7110->debi_bus);
- }
- if (NULL != av7110->iobuf) {
- vfree(av7110->iobuf);
- }
-
dvb_unregister_i2c_bus (master_xfer,av7110->i2c_bus->adapter,
av7110->i2c_bus->id);
dvb_unregister_adapter (av7110->dvb_adapter);
+ if (NULL != av7110->debi_virt)
+ pci_free_consistent(dev->pci, 8192, av7110->debi_virt, av7110->debi_bus);
+ if (NULL != av7110->iobuf)
+ vfree(av7110->iobuf);
+ if (NULL != av7110 ) {
+ kfree(av7110);
+ }
+
return ret;
}
@@ -1648,6 +1652,7 @@ static int __init av7110_init(void)
{
int retval;
retval = saa7146_register_extension(&av7110_extension);
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
if (retval)
goto failed_saa7146_register;
@@ -1658,13 +1663,16 @@ static int __init av7110_init(void)
failed_av7110_ir_init:
saa7146_unregister_extension(&av7110_extension);
failed_saa7146_register:
+#endif
return retval;
}
static void __exit av7110_exit(void)
{
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
av7110_ir_exit();
+#endif
saa7146_unregister_extension(&av7110_extension);
}
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
index b4fd1cd8f38f..054e2e90d0fe 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -975,7 +975,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY)
av7110_av_stop(av7110, RP_VIDEO);
else
- vidcom(av7110, 0x000e,
+ vidcom(av7110, VIDEO_CMD_STOP,
av7110->videostate.video_blank ? 0 : 1);
av7110->trickmode = TRICK_NONE;
break;
@@ -984,7 +984,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
av7110->trickmode = TRICK_NONE;
if (av7110->videostate.play_state == VIDEO_FREEZED) {
av7110->videostate.play_state = VIDEO_PLAYING;
- vidcom(av7110, 0x000d, 0);
+ vidcom(av7110, VIDEO_CMD_PLAY, 0);
}
if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) {
@@ -993,10 +993,10 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
av7110->playing &= ~RP_VIDEO;
}
av7110_av_start_play(av7110, RP_VIDEO);
- vidcom(av7110, 0x000d, 0);
+ vidcom(av7110, VIDEO_CMD_PLAY, 0);
} else {
//av7110_av_stop(av7110, RP_VIDEO);
- vidcom(av7110, 0x000d, 0);
+ vidcom(av7110, VIDEO_CMD_PLAY, 0);
}
av7110->videostate.play_state = VIDEO_PLAYING;
break;
@@ -1006,14 +1006,14 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
if (av7110->playing & RP_VIDEO)
av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0);
else
- vidcom(av7110, 0x0102, 1);
+ vidcom(av7110, VIDEO_CMD_FREEZE, 1);
av7110->trickmode = TRICK_FREEZE;
break;
case VIDEO_CONTINUE:
if (av7110->playing & RP_VIDEO)
av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0);
- vidcom(av7110, 0x000d, 0);
+ vidcom(av7110, VIDEO_CMD_PLAY, 0);
av7110->videostate.play_state = VIDEO_PLAYING;
av7110->trickmode = TRICK_NONE;
break;
@@ -1094,7 +1094,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
__Scan_I, 2, AV_PES, 0);
else
- vidcom(av7110, 0x16, arg);
+ vidcom(av7110, VIDEO_CMD_FFWD, arg);
av7110->trickmode = TRICK_FAST;
av7110->videostate.play_state = VIDEO_PLAYING;
break;
@@ -1102,11 +1102,11 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
case VIDEO_SLOWMOTION:
if (av7110->playing&RP_VIDEO) {
av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0);
- vidcom(av7110, 0x22, arg);
+ vidcom(av7110, VIDEO_CMD_SLOW, arg);
} else {
- vidcom(av7110, 0x0d, 0);
- vidcom(av7110, 0x0e, 0);
- vidcom(av7110, 0x22, arg);
+ vidcom(av7110, VIDEO_CMD_PLAY, 0);
+ vidcom(av7110, VIDEO_CMD_STOP, 0);
+ vidcom(av7110, VIDEO_CMD_SLOW, arg);
}
av7110->trickmode = TRICK_SLOW;
av7110->videostate.play_state = VIDEO_PLAYING;
@@ -1130,10 +1130,10 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file,
if (av7110->trickmode == TRICK_SLOW) {
av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
__Slow, 2, 0, 0);
- vidcom(av7110, 0x22, arg);
+ vidcom(av7110, VIDEO_CMD_SLOW, arg);
}
if (av7110->trickmode == TRICK_FREEZE)
- vidcom(av7110, 0x000e, 1);
+ vidcom(av7110, VIDEO_CMD_STOP, 1);
}
break;
@@ -1167,26 +1167,26 @@ static int dvb_audio_ioctl(struct inode *inode, struct file *file,
if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
av7110_av_stop(av7110, RP_AUDIO);
else
- audcom(av7110, 1);
+ audcom(av7110, AUDIO_CMD_MUTE);
av7110->audiostate.play_state = AUDIO_STOPPED;
break;
case AUDIO_PLAY:
if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
av7110_av_start_play(av7110, RP_AUDIO);
- audcom(av7110, 2);
+ audcom(av7110, AUDIO_CMD_UNMUTE);
av7110->audiostate.play_state = AUDIO_PLAYING;
break;
case AUDIO_PAUSE:
- audcom(av7110, 1);
+ audcom(av7110, AUDIO_CMD_MUTE);
av7110->audiostate.play_state = AUDIO_PAUSED;
break;
case AUDIO_CONTINUE:
if (av7110->audiostate.play_state == AUDIO_PAUSED) {
av7110->audiostate.play_state = AUDIO_PLAYING;
- audcom(av7110, 0x12);
+ audcom(av7110, AUDIO_CMD_MUTE | AUDIO_CMD_PCM16);
}
break;
@@ -1196,14 +1196,14 @@ static int dvb_audio_ioctl(struct inode *inode, struct file *file,
case AUDIO_SET_MUTE:
{
- audcom(av7110, arg ? 1 : 2);
+ audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE);
av7110->audiostate.mute_state = (int) arg;
break;
}
case AUDIO_SET_AV_SYNC:
av7110->audiostate.AV_sync_state = (int) arg;
- audcom(av7110, arg ? 0x0f : 0x0e);
+ audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF);
break;
case AUDIO_SET_BYPASS_MODE:
@@ -1215,15 +1215,15 @@ static int dvb_audio_ioctl(struct inode *inode, struct file *file,
switch(av7110->audiostate.channel_select) {
case AUDIO_STEREO:
- audcom(av7110, 0x80);
+ audcom(av7110, AUDIO_CMD_STEREO);
break;
case AUDIO_MONO_LEFT:
- audcom(av7110, 0x100);
+ audcom(av7110, AUDIO_CMD_MONO_L);
break;
case AUDIO_MONO_RIGHT:
- audcom(av7110, 0x200);
+ audcom(av7110, AUDIO_CMD_MONO_R);
break;
default:
diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c
index e1a6e76b72e6..f56e9dcfb090 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.c
+++ b/drivers/media/dvb/ttpci/av7110_hw.c
@@ -105,10 +105,8 @@ void av7110_reset_arm(struct av7110 *av7110)
IER_DISABLE(av7110->dev, (MASK_19 | MASK_03));
saa7146_write(av7110->dev, ISR, (MASK_19 | MASK_03));
- //FIXME: are those mdelays really necessary?
- mdelay(800);
saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
- mdelay(800);
+ dvb_delay(30); /* the firmware needs some time to initialize */
ARM_ResetMailBox(av7110);
@@ -129,7 +127,7 @@ static int waitdebi(struct av7110 *av7110, int adr, int state)
for (k = 0; k < 100; k++) {
if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state)
return 0;
- udelay(500);
+ udelay(5);
}
return -1;
}
@@ -186,27 +184,24 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len)
/* we cannot write av7110 DRAM directly, so load a bootloader into
* the DPRAM which implements a simple boot protocol */
static u8 bootcode[] = {
- 0xea, 0x00, 0x00, 0x0e, 0xe1, 0xb0, 0xf0, 0x0e, /* 0x0000 */
- 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x04,
- 0xe2, 0x5e, 0xf0, 0x08, 0xe2, 0x5e, 0xf0, 0x04,
- 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x04,
- 0x2c, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0c,
- 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x34,
- 0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0x5a, 0x5a,
- 0x00, 0x1f, 0x15, 0x55, 0x00, 0x00, 0x00, 0x09,
- 0xe5, 0x9f, 0xd0, 0x5c, 0xe5, 0x9f, 0x40, 0x54, /* 0x0040 */
- 0xe3, 0xa0, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x00,
- 0xe5, 0x84, 0x00, 0x04, 0xe1, 0xd4, 0x10, 0xb0,
- 0xe3, 0x51, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xfc,
- 0xe1, 0xa0, 0x10, 0x0d, 0xe5, 0x94, 0x30, 0x04,
- 0xe1, 0xd4, 0x20, 0xb2, 0xe2, 0x82, 0x20, 0x3f,
- 0xe1, 0xb0, 0x23, 0x22, 0x03, 0xa0, 0x00, 0x02,
- 0xe1, 0xc4, 0x00, 0xb0, 0x0a, 0xff, 0xff, 0xf4,
- 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, /* 0x0080 */
- 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0,
- 0xe2, 0x52, 0x20, 0x01, 0x1a, 0xff, 0xff, 0xf9,
- 0xe2, 0x2d, 0xdb, 0x05, 0xea, 0xff, 0xff, 0xec,
- 0x2c, 0x00, 0x03, 0xf8, 0x2c, 0x00, 0x04, 0x00,
+ 0xea, 0x00, 0x00, 0x0e, 0xe1, 0xb0, 0xf0, 0x0e, 0xe2, 0x5e, 0xf0, 0x04,
+ 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x08, 0xe2, 0x5e, 0xf0, 0x04,
+ 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x04, 0x2c, 0x00, 0x00, 0x24,
+ 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x34,
+ 0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0x5a, 0x5a, 0x00, 0x1f, 0x15, 0x55,
+ 0x00, 0x00, 0x00, 0x09, 0xe5, 0x9f, 0xd0, 0x7c, 0xe5, 0x9f, 0x40, 0x74,
+ 0xe3, 0xa0, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x04,
+ 0xe5, 0x9f, 0x10, 0x70, 0xe5, 0x9f, 0x20, 0x70, 0xe5, 0x9f, 0x30, 0x64,
+ 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe1, 0x51, 0x00, 0x02,
+ 0xda, 0xff, 0xff, 0xfb, 0xe5, 0x9f, 0xf0, 0x50, 0xe1, 0xd4, 0x10, 0xb0,
+ 0xe3, 0x51, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xfc, 0xe1, 0xa0, 0x10, 0x0d,
+ 0xe5, 0x94, 0x30, 0x04, 0xe1, 0xd4, 0x20, 0xb2, 0xe2, 0x82, 0x20, 0x3f,
+ 0xe1, 0xb0, 0x23, 0x22, 0x03, 0xa0, 0x00, 0x02, 0xe1, 0xc4, 0x00, 0xb0,
+ 0x0a, 0xff, 0xff, 0xf4, 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0,
+ 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe2, 0x52, 0x20, 0x01,
+ 0x1a, 0xff, 0xff, 0xf9, 0xe2, 0x2d, 0xdb, 0x05, 0xea, 0xff, 0xff, 0xec,
+ 0x2c, 0x00, 0x03, 0xf8, 0x2c, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x08, 0x00,
+ 0x2c, 0x00, 0x00, 0x74, 0x2c, 0x00, 0x00, 0xc0
};
int av7110_bootarm(struct av7110 *av7110)
@@ -232,7 +227,7 @@ int av7110_bootarm(struct av7110 *av7110)
iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) {
printk(KERN_ERR "dvb: debi test in av7110_bootarm() failed: "
- "%08x != %08x (check your BIOS notplug settings)\n",
+ "%08x != %08x (check your BIOS hotplug settings)\n",
ret, 0x10325476);
return -1;
}
@@ -255,9 +250,7 @@ int av7110_bootarm(struct av7110 *av7110)
return -1;
}
saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
- //FIXME: necessary?
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
+ mdelay(1);
DEB_D(("av7110_bootarm: load dram code\n"));
if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0)
@@ -275,8 +268,7 @@ int av7110_bootarm(struct av7110 *av7110)
return -1;
}
saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
- //FIXME: necessary?
- mdelay(800);
+ dvb_delay(30); /* the firmware needs some time to initialize */
//ARM_ClearIrq(av7110);
ARM_ResetMailBox(av7110);
diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h
index aa674b84e435..826ea877b29e 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.h
+++ b/drivers/media/dvb/ttpci/av7110_hw.h
@@ -200,6 +200,12 @@ enum av7110_rec_play_state {
__Continue
};
+enum av7110_fw_cmd_misc {
+ AV7110_FW_VIDEO_ZOOM = 1,
+ AV7110_FW_VIDEO_COMMAND,
+ AV7110_FW_AUDIO_COMMAND
+};
+
enum av7110_command_type {
COMTYPE_NOCOM,
COMTYPE_PIDFILTER,
@@ -218,6 +224,7 @@ enum av7110_command_type {
COMTYPE_VIDEO,
COMTYPE_AUDIO,
COMTYPE_CI_LL,
+ COMTYPE_MISC = 0x80
};
#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */
@@ -226,6 +233,23 @@ enum av7110_command_type {
#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */
#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */
+/* MPEG video decoder commands */
+#define VIDEO_CMD_STOP 0x000e
+#define VIDEO_CMD_PLAY 0x000d
+#define VIDEO_CMD_FREEZE 0x0102
+#define VIDEO_CMD_FFWD 0x0016
+#define VIDEO_CMD_SLOW 0x0022
+
+/* MPEG audio decoder commands */
+#define AUDIO_CMD_MUTE 0x0001
+#define AUDIO_CMD_UNMUTE 0x0002
+#define AUDIO_CMD_PCM16 0x0010
+#define AUDIO_CMD_STEREO 0x0080
+#define AUDIO_CMD_MONO_L 0x0100
+#define AUDIO_CMD_MONO_R 0x0200
+#define AUDIO_CMD_SYNC_OFF 0x000e
+#define AUDIO_CMD_SYNC_ON 0x000f
+
/* firmware data interface codes */
#define DATA_NONE 0x00
#define DATA_FSECTION 0x01
@@ -457,21 +481,21 @@ static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data)
return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data);
}
-static inline void VidMode(struct av7110 *av7110, int mode)
+static inline void av7710_set_video_mode(struct av7110 *av7110, int mode)
{
av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode);
}
static int inline vidcom(struct av7110 *av7110, u32 com, u32 arg)
{
- return av7110_fw_cmd(av7110, 0x80, 0x02, 4,
+ return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4,
(com>>16), (com&0xffff),
(arg>>16), (arg&0xffff));
}
static int inline audcom(struct av7110 *av7110, u32 com)
{
- return av7110_fw_cmd(av7110, 0x80, 0x03, 4,
+ return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 4,
(com>>16), (com&0xffff));
}
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index e1d3901e5dc6..fef348e130f6 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -177,16 +177,17 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh)
struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
u16 adswitch;
u8 band = 0;
- int source, sync;
- struct saa7146_fh *ov_fh = NULL;
- int restart_overlay = 0;
+ int source, sync, err;
DEB_EE(("av7110: %p\n", av7110));
- if (vv->ov_data != NULL) {
- ov_fh = vv->ov_data->fh;
- saa7146_stop_preview(ov_fh);
- restart_overlay = 1;
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (err != 0) {
+ DEB_D(("warning: suspending video failed\n"));
+ vv->ov_suspend = NULL;
+ }
}
if (0 != av7110->current_input) {
@@ -195,7 +196,7 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh)
source = SAA7146_HPS_SOURCE_PORT_B;
sync = SAA7146_HPS_SYNC_PORT_B;
memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2);
- DEB_S(("av7110: switching to analog TV\n"));
+ printk("av7110: switching to analog TV\n");
msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source
msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source
msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source
@@ -208,7 +209,7 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh)
source = SAA7146_HPS_SOURCE_PORT_A;
sync = SAA7146_HPS_SYNC_PORT_A;
memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
- DEB_S(("av7110: switching DVB mode\n"));
+ printk("av7110: switching DVB mode\n");
msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
@@ -225,8 +226,10 @@ int av7110_dvb_c_switch(struct saa7146_fh *fh)
printk("setting band in demodulator failed.\n");
saa7146_set_hps_source_and_sync(dev, source, sync);
- if (restart_overlay)
- saa7146_start_preview(ov_fh);
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
return 0;
}
@@ -263,10 +266,10 @@ int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
// FIXME: standard / stereo detection is still broken
msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
- DEB_S(("VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det));
+printk("VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
- DEB_S(("VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det));
+ printk("VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
stereo = (s8)(stereo_det >> 8);
if (stereo > 0x10) {
/* stereo */
@@ -624,13 +627,13 @@ int av7110_exit_v4l(struct av7110 *av7110)
static struct saa7146_standard standard[] = {
{
.name = "PAL", .id = V4L2_STD_PAL_BG,
- .v_offset = 0x15, .v_field = 288, .v_calc = 576,
- .h_offset = 0x4a, .h_pixels = 708, .h_calc = 709,
+ .v_offset = 0x15, .v_field = 288,
+ .h_offset = 0x48, .h_pixels = 708,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x10, .v_field = 244, .v_calc = 480,
- .h_offset = 0x40, .h_pixels = 708, .h_calc = 709,
+ .v_offset = 0x10, .v_field = 244,
+ .h_offset = 0x40, .h_pixels = 708,
.v_max_out = 480, .h_max_out = 640,
}
};
@@ -638,13 +641,13 @@ static struct saa7146_standard standard[] = {
static struct saa7146_standard analog_standard[] = {
{
.name = "PAL", .id = V4L2_STD_PAL_BG,
- .v_offset = 0x1b, .v_field = 288, .v_calc = 576,
- .h_offset = 0x08, .h_pixels = 708, .h_calc = 709,
+ .v_offset = 0x1b, .v_field = 288,
+ .h_offset = 0x08, .h_pixels = 708,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x10, .v_field = 244, .v_calc = 480,
- .h_offset = 0x40, .h_pixels = 708, .h_calc = 709,
+ .v_offset = 0x10, .v_field = 244,
+ .h_offset = 0x40, .h_pixels = 708,
.v_max_out = 480, .h_max_out = 640,
}
};
@@ -652,13 +655,13 @@ static struct saa7146_standard analog_standard[] = {
static struct saa7146_standard dvb_standard[] = {
{
.name = "PAL", .id = V4L2_STD_PAL_BG,
- .v_offset = 0x14, .v_field = 288, .v_calc = 576,
- .h_offset = 0x4a, .h_pixels = 708, .h_calc = 709,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x48, .h_pixels = 708,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x10, .v_field = 244, .v_calc = 480,
- .h_offset = 0x40, .h_pixels = 708, .h_calc = 709,
+ .v_offset = 0x10, .v_field = 244,
+ .h_offset = 0x40, .h_pixels = 708,
.v_max_out = 480, .h_max_out = 640,
}
};
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 1b0fc3e82389..6203be434c98 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -39,6 +39,7 @@ struct budget_av {
struct budget budget;
struct video_device vd;
int cur_input;
+ int has_saa7113;
};
/****************************************************************************
@@ -149,6 +150,9 @@ static int saa7113_setinput (struct budget_av *budget_av, int input)
{
struct budget *budget = &budget_av->budget;
+ if ( 1 != budget_av->has_saa7113 )
+ return -ENODEV;
+
if (input == 1) {
i2c_writereg(budget->i2c_bus, 0x4a, 0x02, 0xc7);
i2c_writereg(budget->i2c_bus, 0x4a, 0x09, 0x80);
@@ -170,11 +174,13 @@ static int budget_av_detach (struct saa7146_dev *dev)
DEB_EE(("dev: %p\n",dev));
+ if ( 1 == budget_av->has_saa7113 ) {
saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
dvb_delay(200);
saa7146_unregister_device (&budget_av->vd, dev);
+ }
err = ttpci_budget_deinit (&budget_av->budget);
@@ -221,11 +227,8 @@ static int budget_av_attach (struct saa7146_dev* dev,
saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
dvb_delay(500);
- if ((err = saa7113_init (budget_av))) {
- /* fixme: proper cleanup here */
- ERR(("cannot init saa7113.\n"));
- return err;
- }
+ if ( 0 == saa7113_init(budget_av) ) {
+ budget_av->has_saa7113 = 1;
if ( 0 != saa7146_vv_init(dev,&vv_data)) {
/* fixme: proper cleanup here */
@@ -246,14 +249,12 @@ static int budget_av_attach (struct saa7146_dev* dev,
SAA7146_HPS_SYNC_PORT_A);
saa7113_setinput (budget_av, 0);
+ } else {
+ budget_av->has_saa7113 = 0;
- /* what is this? since we don't support open()/close()
- notifications, we simply put this into the release handler... */
-/*
saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout (20);
-*/
+ }
+
/* fixme: find some sane values here... */
saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
@@ -333,13 +334,13 @@ static int av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
static struct saa7146_standard standard[] = {
{
.name = "PAL", .id = V4L2_STD_PAL,
- .v_offset = 0x17, .v_field = 288, .v_calc = 576,
- .h_offset = 0x14, .h_pixels = 680, .h_calc = 680+1,
- .v_max_out = 576, .h_max_out = 768,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x16, .v_field = 240, .v_calc = 480,
- .h_offset = 0x06, .h_pixels = 708, .h_calc = 708+1,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
.v_max_out = 480, .h_max_out = 640,
}
};
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 45e3161896b6..30c806599196 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -6,6 +6,8 @@
* msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM>
* partially based on the Siemens DVB driver by Ralph+Marcus Metzler
*
+ * CI interface support (c) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
* 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
@@ -34,37 +36,103 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
+#include <linux/spinlock.h>
+
+#include "dvb_functions.h"
+#include "dvb_ca_en50221.h"
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#include "input_fake.h"
+#endif
+
+#define DEBIADDR_IR 0x1234
+#define DEBIADDR_CICONTROL 0x0000
+#define DEBIADDR_CIVERSION 0x4000
+#define DEBIADDR_IO 0x1000
+#define DEBIADDR_ATTR 0x3000
+
+#define CICONTROL_RESET 0x01
+#define CICONTROL_ENABLETS 0x02
+#define CICONTROL_CAMDETECT 0x08
+
+#define DEBICICTL 0x00420000
+#define DEBICICAM 0x02420000
+
+#define SLOTSTATUS_NONE 1
+#define SLOTSTATUS_PRESENT 2
+#define SLOTSTATUS_RESET 4
+#define SLOTSTATUS_READY 8
+#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
struct budget_ci {
struct budget budget;
struct input_dev input_dev;
struct tasklet_struct msp430_irq_tasklet;
+ struct tasklet_struct ciintf_irq_tasklet;
+ spinlock_t debilock;
+ int slot_status;
+ struct dvb_ca_en50221 ca;
+ char ir_dev_name[50];
};
-static u32 budget_debiread4 (struct saa7146_dev *saa, u32 config, int addr, int count)
+static u32 budget_debiread (struct budget_ci* budget_ci, u32 config, int addr, int count)
{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
u32 result = 0;
if (count > 4 || count <= 0)
return 0;
- if (saa7146_wait_for_debi_done(saa) < 0)
+ spin_lock(&budget_ci->debilock);
+
+ if (saa7146_wait_for_debi_done(saa) < 0) {
+ spin_unlock(&budget_ci->debilock);
return 0;
+ }
saa7146_write (saa, DEBI_COMMAND,
(count << 17) | 0x10000 | (addr & 0xffff));
-
saa7146_write(saa, DEBI_CONFIG, config);
+ saa7146_write(saa, DEBI_PAGE, 0);
saa7146_write(saa, MC2, (2 << 16) | 2);
saa7146_wait_for_debi_done(saa);
- result = saa7146_read(saa, DEBI_AD);
+ result = saa7146_read(saa, 0x88);
result &= (0xffffffffUL >> ((4 - count) * 8));
+ spin_unlock(&budget_ci->debilock);
return result;
}
+static u8 budget_debiwrite (struct budget_ci* budget_ci, u32 config, int addr, int count, u32 value)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ if (count > 4 || count <= 0)
+ return 0;
+
+ spin_lock(&budget_ci->debilock);
+
+ if (saa7146_wait_for_debi_done(saa) < 0) {
+ spin_unlock(&budget_ci->debilock);
+ return 0;
+ }
+
+ saa7146_write (saa, DEBI_COMMAND,
+ (count << 17) | 0x00000 | (addr & 0xffff));
+ saa7146_write(saa, DEBI_CONFIG, config);
+ saa7146_write(saa, DEBI_PAGE, 0);
+ saa7146_write(saa, DEBI_AD, value);
+ saa7146_write(saa, MC2, (2 << 16) | 2);
+
+ saa7146_wait_for_debi_done(saa);
+
+ spin_unlock(&budget_ci->debilock);
+ return 0;
+}
+
+
/* from reading the following remotes:
Zenith Universal 7 / TV Mode 807 / VCR Mode 837
Hauppauge (from NOVA-CI-s box product)
@@ -140,9 +208,8 @@ static void msp430_ir_debounce (unsigned long data)
static void msp430_ir_interrupt (unsigned long data)
{
struct budget_ci *budget_ci = (struct budget_ci*) data;
- struct saa7146_dev *saa = budget_ci->budget.dev;
struct input_dev *dev = &budget_ci->input_dev;
- unsigned int code = budget_debiread4(saa, DEBINOSWAP, 0x1234, 2) >> 8;
+ unsigned int code = budget_debiread(budget_ci, DEBINOSWAP, DEBIADDR_IR, 2) >> 8;
if (code & 0x40) {
code &= 0x3f;
@@ -182,7 +249,8 @@ static int msp430_ir_init (struct budget_ci *budget_ci)
memset(&budget_ci->input_dev, 0, sizeof(struct input_dev));
- budget_ci->input_dev.name = saa->name;
+ sprintf (budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name);
+ budget_ci->input_dev.name = budget_ci->ir_dev_name;
set_bit(EV_KEY, budget_ci->input_dev.evbit);
@@ -209,7 +277,6 @@ static void msp430_ir_deinit (struct budget_ci *budget_ci)
saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06);
saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
- saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
if (del_timer(&dev->timer))
input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
@@ -217,6 +284,209 @@ static void msp430_ir_deinit (struct budget_ci *budget_ci)
input_unregister_device(dev);
}
+static int ciintf_read_attribute_mem(struct dvb_ca_en50221* ca, int slot, int address) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+
+ if (slot != 0) return -EINVAL;
+
+ return budget_debiread(budget_ci, DEBICICAM, DEBIADDR_ATTR | (address & 0xfff), 1);
+}
+
+static int ciintf_write_attribute_mem(struct dvb_ca_en50221* ca, int slot, int address, u8 value) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+
+ if (slot != 0) return -EINVAL;
+
+ return budget_debiwrite(budget_ci, DEBICICAM, DEBIADDR_ATTR | (address & 0xfff), 1, value);
+}
+
+static int ciintf_read_cam_control(struct dvb_ca_en50221* ca, int slot, u8 address) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+
+ if (slot != 0) return -EINVAL;
+
+ return budget_debiread(budget_ci, DEBICICAM, DEBIADDR_IO | (address & 3), 1);
+}
+
+static int ciintf_write_cam_control(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+
+ if (slot != 0) return -EINVAL;
+
+ return budget_debiwrite(budget_ci, DEBICICAM, DEBIADDR_IO | (address & 3), 1, value);
+}
+
+static int ciintf_slot_reset(struct dvb_ca_en50221* ca, int slot) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ if (slot != 0) return -EINVAL;
+
+ // trigger on RISING edge during reset so we know when READY is re-asserted
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+ budget_ci->slot_status = SLOTSTATUS_RESET;
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0);
+ dvb_delay(1);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET);
+
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+ return 0;
+}
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221* ca, int slot) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ if (slot != 0) return -EINVAL;
+
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+ return 0;
+}
+
+static int ciintf_slot_ts_enable(struct dvb_ca_en50221* ca, int slot) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ int tmp;
+
+ if (slot != 0) return -EINVAL;
+
+
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO);
+
+ tmp = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, tmp | CICONTROL_ENABLETS);
+
+ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
+ return 0;
+}
+
+
+static void ciintf_interrupt (unsigned long data)
+{
+ struct budget_ci *budget_ci = (struct budget_ci*) data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ unsigned int flags;
+
+ // ensure we don't get spurious IRQs during initialisation
+ if (!budget_ci->budget.ci_present) return;
+
+ flags = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1);
+
+ // always set the GPIO mode back to "normal", in case the card is
+ // yanked at an inopportune moment
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+
+ if (flags & CICONTROL_CAMDETECT) {
+
+ if (budget_ci->slot_status & SLOTSTATUS_NONE) {
+ // CAM insertion IRQ
+ budget_ci->slot_status = SLOTSTATUS_PRESENT;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, DVB_CA_EN50221_CAMCHANGE_INSERTED);
+
+ } else if (budget_ci->slot_status & SLOTSTATUS_RESET) {
+ // CAM ready (reset completed)
+ budget_ci->slot_status = SLOTSTATUS_READY;
+ dvb_ca_en50221_camready_irq(&budget_ci->ca, 0);
+
+ } else if (budget_ci->slot_status & SLOTSTATUS_READY) {
+ // FR/DA IRQ
+ dvb_ca_en50221_frda_irq(&budget_ci->ca, 0);
+ }
+ } else {
+ if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) {
+ budget_ci->slot_status = SLOTSTATUS_NONE;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, DVB_CA_EN50221_CAMCHANGE_REMOVED);
+ }
+ }
+}
+
+static int ciintf_init(struct budget_ci* budget_ci)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ int flags;
+ int result;
+
+ memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
+
+ // enable DEBI pins
+ saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+
+ // test if it is there
+ if ((budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CIVERSION, 1) & 0xa0) != 0xa0) {
+ result = -ENODEV;
+ goto error;
+ }
+
+ // determine whether a CAM is present or not
+ flags = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1);
+ budget_ci->slot_status = SLOTSTATUS_NONE;
+ if (flags & CICONTROL_CAMDETECT) budget_ci->slot_status = SLOTSTATUS_PRESENT;
+
+
+ // register CI interface
+ budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem;
+ budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem;
+ budget_ci->ca.read_cam_control = ciintf_read_cam_control;
+ budget_ci->ca.write_cam_control = ciintf_write_cam_control;
+ budget_ci->ca.slot_reset = ciintf_slot_reset;
+ budget_ci->ca.slot_shutdown = ciintf_slot_shutdown;
+ budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable;
+ budget_ci->ca.data = budget_ci;
+ if ((result = dvb_ca_en50221_init(budget_ci->budget.dvb_adapter,
+ &budget_ci->ca,
+ DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE |
+ DVB_CA_EN50221_FLAG_IRQ_FR |
+ DVB_CA_EN50221_FLAG_IRQ_DA,
+ 1)) != 0) {
+ printk("budget_ci: CI interface detected, but initialisation failed.\n");
+ goto error;
+ }
+
+ // Setup CI slot IRQ
+ tasklet_init (&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci);
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+ saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET);
+
+ // success!
+ printk("budget_ci: CI interface initialised\n");
+ budget_ci->budget.ci_present = 1;
+
+ // forge a fake CI IRQ so the CAM state is setup correctly
+ flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ if (budget_ci->slot_status != SLOTSTATUS_NONE) flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags);
+
+ return 0;
+
+error:
+ saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+ return result;
+}
+
+static void ciintf_deinit(struct budget_ci* budget_ci)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ // disable CI interrupts
+ saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
+ tasklet_kill(&budget_ci->ciintf_irq_tasklet);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0);
+ dvb_delay(1);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET);
+
+ // disable TS data stream to CI interface
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
+
+ // release the CA device
+ dvb_ca_en50221_release(&budget_ci->ca);
+
+ // disable DEBI pins
+ saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+}
static void budget_ci_irq (struct saa7146_dev *dev, u32 *isr)
{
@@ -229,6 +499,9 @@ static void budget_ci_irq (struct saa7146_dev *dev, u32 *isr)
if (*isr & MASK_10)
ttpci_budget_irq10_handler (dev, isr);
+
+ if ((*isr & MASK_03) && (budget_ci->budget.ci_present))
+ tasklet_schedule (&budget_ci->ciintf_irq_tasklet);
}
@@ -244,6 +517,9 @@ static int budget_ci_attach (struct saa7146_dev* dev,
DEB_EE(("budget_ci: %p\n", budget_ci));
+ spin_lock_init(&budget_ci->debilock);
+ budget_ci->budget.ci_present = 0;
+
if ((err = ttpci_budget_init (&budget_ci->budget, dev, info))) {
kfree (budget_ci);
return err;
@@ -256,6 +532,9 @@ static int budget_ci_attach (struct saa7146_dev* dev,
msp430_ir_init (budget_ci);
+ // UNCOMMENT TO TEST CI INTERFACE
+// ciintf_init(budget_ci);
+
return 0;
}
@@ -264,14 +543,20 @@ static int budget_ci_attach (struct saa7146_dev* dev,
static int budget_ci_detach (struct saa7146_dev* dev)
{
struct budget_ci *budget_ci = (struct budget_ci*) dev->ext_priv;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
int err;
+ if (budget_ci->budget.ci_present) ciintf_deinit(budget_ci);
+
err = ttpci_budget_deinit (&budget_ci->budget);
tasklet_kill (&budget_ci->msp430_irq_tasklet);
msp430_ir_deinit (budget_ci);
+ // disable frontend and CI interface
+ saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
+
kfree (budget_ci);
return err;
@@ -304,7 +589,7 @@ static struct saa7146_extension budget_extension = {
.attach = budget_ci_attach,
.detach = budget_ci_detach,
- .irq_mask = MASK_06 | MASK_10,
+ .irq_mask = MASK_03 | MASK_06 | MASK_10,
.irq_func = budget_ci_irq,
};
@@ -325,7 +610,7 @@ module_init(budget_ci_init);
module_exit(budget_ci_exit);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Michael Hunold, Jack Thomasson, others");
+MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others");
MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
"budget PCI DVB cards w/ CI-module produced by "
"Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c
index b31e956fc53e..15cf7442217b 100644
--- a/drivers/media/dvb/ttpci/budget-core.c
+++ b/drivers/media/dvb/ttpci/budget-core.c
@@ -1,3 +1,39 @@
+/*
+ * budget-core.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ * Michael Dreher <michael@5dot1.de>,
+ * Oliver Endriss <o.endriss@gmx.de>,
+ * Andreas 'randy' Weinberger
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
#include "budget.h"
#include "ttpci-eeprom.h"
@@ -38,10 +74,33 @@ static int start_ts_capture (struct budget *budget)
budget->tsf=0xff;
budget->ttbp=0;
+
+ /*
+ * Signal path on the Activy:
+ *
+ * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory
+ *
+ * Since the tuner feeds 204 bytes packets into the SAA7146,
+ * DMA3 is configured to strip the trailing 16 FEC bytes:
+ * Pitch: 188, NumBytes3: 188, NumLines3: 1024
+ */
+
+ if (budget->card->type == BUDGET_FS_ACTIVY) {
+ saa7146_write(dev, DD1_INIT, 0x04000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+ saa7146_write(dev, BRS_CTRL, 0x00000000);
+ } else {
+ if (budget->video_port == BUDGET_VIDEO_PORTA) {
+ saa7146_write(dev, DD1_INIT, 0x06000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x00000000);
+ } else {
saa7146_write(dev, DD1_INIT, 0x02000600);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
-
saa7146_write(dev, BRS_CTRL, 0x60000000);
+ }
+ }
+
saa7146_write(dev, MC2, (MASK_08 | MASK_24));
mdelay(10);
@@ -49,9 +108,15 @@ static int start_ts_capture (struct budget *budget)
saa7146_write(dev, BASE_EVEN3, 0);
saa7146_write(dev, PROT_ADDR3, TS_WIDTH*TS_HEIGHT);
saa7146_write(dev, BASE_PAGE3, budget->pt.dma |ME1|0x90);
- saa7146_write(dev, PITCH3, TS_WIDTH);
+ if (budget->card->type == BUDGET_FS_ACTIVY) {
+ saa7146_write(dev, PITCH3, TS_WIDTH/2);
+ saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT*2)<<16)|(TS_WIDTH/2));
+ } else {
+ saa7146_write(dev, PITCH3, TS_WIDTH);
saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT<<16)|TS_WIDTH);
+ }
+
saa7146_write(dev, MC2, (MASK_04 | MASK_20));
saa7146_write(dev, MC1, (MASK_04 | MASK_20)); // DMA3 on
@@ -99,23 +164,31 @@ static int budget_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct budget *budget = (struct budget*) demux->priv;
+ int status;
DEB_EE(("budget: %p\n",budget));
if (!demux->dmx.frontend)
return -EINVAL;
- return start_ts_capture (budget);
+ spin_lock(&budget->feedlock);
+ status = start_ts_capture (budget);
+ spin_unlock(&budget->feedlock);
+ return status;
}
static int budget_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct budget *budget = (struct budget *) demux->priv;
+ int status;
DEB_EE(("budget: %p\n",budget));
- return stop_ts_capture (budget);
+ spin_lock(&budget->feedlock);
+ status = stop_ts_capture (budget);
+ spin_unlock(&budget->feedlock);
+ return status;
}
@@ -208,18 +281,27 @@ int ttpci_budget_init (struct budget *budget,
budget->card = bi;
budget->dev = (struct saa7146_dev *) dev;
- dvb_register_adapter(&budget->dvb_adapter, budget->card->name);
+ dvb_register_adapter(&budget->dvb_adapter, budget->card->name, THIS_MODULE);
/* set dd1 stream a & b */
saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+ saa7146_write(dev, MC2, (MASK_10 | MASK_26));
saa7146_write(dev, DD1_INIT, 0x02000000);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ if (bi->type != BUDGET_FS_ACTIVY)
+ budget->video_port = BUDGET_VIDEO_PORTB;
+ else
+ budget->video_port = BUDGET_VIDEO_PORTA;
+ spin_lock_init(&budget->feedlock);
+
/* the Siemens DVB needs this if you want to have the i2c chips
get recognized before the main driver is loaded */
- saa7146_write(dev, GPIO_CTRL, 0x500000);
+ if (bi->type != BUDGET_FS_ACTIVY)
+ saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */
- saa7146_i2c_adapter_prepare(dev, NULL, SAA7146_I2C_BUS_BIT_RATE_120);
+ saa7146_i2c_adapter_prepare(dev, NULL, 0, SAA7146_I2C_BUS_BIT_RATE_120);
budget->i2c_bus = dvb_register_i2c_bus (master_xfer, dev,
budget->dvb_adapter, 0);
@@ -242,7 +324,11 @@ int ttpci_budget_init (struct budget *budget,
tasklet_init (&budget->vpe_tasklet, vpeirq, (unsigned long) budget);
- saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); /* frontend power on */
+ /* frontend power on */
+ if (bi->type == BUDGET_FS_ACTIVY)
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+ else
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
if (budget_register(budget) == 0) {
return 0;
@@ -292,10 +378,28 @@ void ttpci_budget_irq10_handler (struct saa7146_dev* dev, u32 *isr)
tasklet_schedule (&budget->vpe_tasklet);
}
+void ttpci_budget_set_video_port(struct saa7146_dev* dev, int video_port)
+{
+ struct budget *budget = (struct budget*)dev->ext_priv;
+
+ spin_lock(&budget->feedlock);
+ budget->video_port = video_port;
+ if (budget->feeding) {
+ int oldfeeding = budget->feeding;
+ budget->feeding = 1;
+ stop_ts_capture(budget);
+ start_ts_capture(budget);
+ budget->feeding = oldfeeding;
+ }
+ spin_unlock(&budget->feedlock);
+}
+
+
EXPORT_SYMBOL_GPL(ttpci_budget_init);
EXPORT_SYMBOL_GPL(ttpci_budget_deinit);
EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
+EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port);
EXPORT_SYMBOL_GPL(budget_debug);
MODULE_PARM(budget_debug,"i");
diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c
index a71a2c64bc04..fbf1d5a6df72 100644
--- a/drivers/media/dvb/ttpci/budget.c
+++ b/drivers/media/dvb/ttpci/budget.c
@@ -8,6 +8,11 @@
* Copyright (C) 1999-2002 Ralph Metzler
* & Marcus Metzler for convergence integrated media GmbH
*
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ * Michael Dreher <michael@5dot1.de>,
+ * Oliver Endriss <o.endriss@gmx.de> and
+ * Andreas 'randy' Weinberger
+ *
* 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
@@ -142,6 +147,49 @@ int budget_diseqc_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
}
+/*
+ * Routines for the Fujitsu Siemens Activy budget card
+ * 22 kHz tone and DiSEqC are handled by the frontend.
+ * Voltage must be set here.
+ */
+static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage)
+{
+ struct saa7146_dev *dev=budget->dev;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
+ break;
+ case SEC_VOLTAGE_18:
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int budget_ioctl_activy (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+{
+ struct budget *budget = fe->before_after_data;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ switch (cmd) {
+ case FE_SET_VOLTAGE:
+ return SetVoltage_Activy (budget, (fe_sec_voltage_t) arg);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+
static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
{
struct budget *budget = NULL;
@@ -160,6 +208,10 @@ static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_
return err;
}
+ if (budget->card->type == BUDGET_FS_ACTIVY)
+ dvb_add_frontend_ioctls (budget->dvb_adapter,
+ budget_ioctl_activy, NULL, budget);
+ else
dvb_add_frontend_ioctls (budget->dvb_adapter,
budget_diseqc_ioctl, NULL, budget);
@@ -174,6 +226,10 @@ static int budget_detach (struct saa7146_dev* dev)
struct budget *budget = (struct budget*) dev->ext_priv;
int err;
+ if (budget->card->type == BUDGET_FS_ACTIVY)
+ dvb_remove_frontend_ioctls (budget->dvb_adapter,
+ budget_ioctl_activy, NULL);
+ else
dvb_remove_frontend_ioctls (budget->dvb_adapter,
budget_diseqc_ioctl, NULL);
@@ -193,6 +249,7 @@ MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(fsacs, "Fujitsu Siemens Activy Budget-S PCI", BUDGET_FS_ACTIVY);
/* Uncomment for Budget Patch */
/*MAKE_BUDGET_INFO(fs_1_3,"Siemens/Technotrend/Hauppauge PCI rev1.3+Budget_Patch", BUDGET_PATCH);*/
@@ -203,6 +260,7 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004),
MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005),
MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
+ MAKE_EXTENSION_PCI(fsacs, 0x1131, 0x4f61),
{
.vendor = 0,
}
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
index 0af6725c11f9..6be1d3bcdf7e 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -46,10 +46,15 @@ struct budget {
int fe_synced;
struct semaphore pid_mutex;
+ int ci_present;
+ int video_port;
+
u8 tsf;
u32 ttbp;
int feeding;
+ spinlock_t feedlock;
+
struct dvb_adapter *dvb_adapter;
void *priv;
};
@@ -73,13 +78,17 @@ static struct saa7146_pci_extension_data x_var = { \
#define BUDGET_TT_HW_DISEQC 1
#define BUDGET_KNC1 2
#define BUDGET_PATCH 3
+#define BUDGET_FS_ACTIVY 4
+#define BUDGET_VIDEO_PORTA 0
+#define BUDGET_VIDEO_PORTB 1
extern int ttpci_budget_init (struct budget *budget,
struct saa7146_dev* dev,
struct saa7146_pci_extension_data *info);
extern int ttpci_budget_deinit (struct budget *budget);
extern void ttpci_budget_irq10_handler (struct saa7146_dev* dev, u32 *isr);
+extern void ttpci_budget_set_video_port(struct saa7146_dev* dev, int video_port);
#endif
diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
index b77bfd54794e..8b927e6f94b3 100644
--- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
+++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
@@ -9,7 +9,6 @@
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/wait.h>
@@ -108,9 +107,10 @@ struct ttusb {
int insync;
- u16 cc; /* MuxCounter - will increment on EVERY MUX PACKET */
+ int cc; /* MuxCounter - will increment on EVERY MUX PACKET */
/* (including stuffing. yes. really.) */
+
u8 last_result[32];
struct ttusb_channel {
@@ -575,7 +575,7 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack,
cc = (muxpack[len - 4] << 8) | muxpack[len - 3];
cc &= 0x7FFF;
- if (cc != ttusb->cc)
+ if ((cc != ttusb->cc) && (ttusb->cc != -1))
printk("%s: cc discontinuity (%d frames missing)\n",
__FUNCTION__, (cc - ttusb->cc) & 0x7FFF);
ttusb->cc = (cc + 1) & 0x7FFF;
@@ -787,7 +787,7 @@ static void ttusb_iso_irq(struct urb *urb, struct pt_regs *ptregs)
ttusb_process_frame(ttusb, data, len);
}
}
- usb_submit_urb(urb, GFP_ATOMIC);
+ usb_submit_urb(urb, GFP_KERNEL);
}
static void ttusb_free_iso_urbs(struct ttusb *ttusb)
@@ -852,6 +852,7 @@ static int ttusb_start_iso_xfer(struct ttusb *ttusb)
return 0;
}
+ ttusb->cc = -1;
ttusb->insync = 0;
ttusb->mux_state = 0;
@@ -864,6 +865,7 @@ static int ttusb_start_iso_xfer(struct ttusb *ttusb)
urb->complete = ttusb_iso_irq;
urb->pipe = ttusb->isoc_in_pipe;
urb->transfer_flags = URB_ISO_ASAP;
+ urb->interval = 1;
urb->number_of_packets = FRAMES_PER_ISO_BUF;
urb->transfer_buffer_length =
ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF;
@@ -1008,7 +1010,6 @@ static int ttusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
static int ttusb_setup_interfaces(struct ttusb *ttusb)
{
- usb_reset_configuration(ttusb->dev);
usb_set_interface(ttusb->dev, 1, 1);
ttusb->bulk_out_pipe = usb_sndbulkpipe(ttusb->dev, 1);
@@ -1088,6 +1089,8 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i
}
+ if (intf->altsetting->desc.bInterfaceNumber != 1) return -ENODEV;
+
if (!(ttusb = kmalloc(sizeof(struct ttusb), GFP_KERNEL)))
return -ENOMEM;
@@ -1112,8 +1115,7 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i
up(&ttusb->sem);
- dvb_register_adapter(&ttusb->adapter,
- "Technotrend/Hauppauge Nova-USB");
+ dvb_register_adapter(&ttusb->adapter, "Technotrend/Hauppauge Nova-USB", THIS_MODULE);
dvb_register_i2c_bus(ttusb_i2c_xfer, ttusb, ttusb->adapter, 0);
dvb_add_frontend_ioctls(ttusb->adapter, ttusb_lnb_ioctl, NULL,
@@ -1180,9 +1182,6 @@ static void ttusb_disconnect(struct usb_interface *intf)
ttusb_stop_iso_xfer(ttusb);
-#if 0
- devfs_remove(TTUSB_BUDGET_NAME);
-#endif
ttusb->dvb_demux.dmx.close(&ttusb->dvb_demux.dmx);
dvb_net_release(&ttusb->dvbnet);
dvb_dmxdev_release(&ttusb->dmxdev);
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
index 860d64bc5767..039a4a1f77e3 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -1,7 +1,7 @@
/*
* TTUSB DEC Driver
*
- * Copyright (C) 2003 Alex Woods <linux-dvb@giblets.org>
+ * Copyright (C) 2003-2004 Alex Woods <linux-dvb@giblets.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,16 +20,19 @@
*/
#include <asm/semaphore.h>
-#include <linux/crc32.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
-#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
+#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE)
+#include <linux/crc32.h>
+#else
+#warning "CRC checking of firmware not available"
+#endif
#include <linux/init.h>
#include "dmxdev.h"
@@ -99,6 +102,7 @@ struct ttusb_dec {
u16 pid[DMX_PES_OTHER];
int hi_band;
+ int voltage;
/* USB bits */
struct usb_device *udev;
@@ -205,22 +209,15 @@ static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command,
{
int result, actual_len, i;
u8 *b;
- u8 *c;
+
+ dprintk("%s\n", __FUNCTION__);
b = kmalloc(COMMAND_PACKET_SIZE + 4, GFP_KERNEL);
if (!b)
return -ENOMEM;
- c = kmalloc(COMMAND_PACKET_SIZE + 4, GFP_KERNEL);
- if (!c) {
- kfree(b);
- return -ENOMEM;
- }
-
- dprintk("%s\n", __FUNCTION__);
if ((result = down_interruptible(&dec->usb_sem))) {
kfree(b);
- kfree(c);
printk("%s: Failed to down usb semaphore.\n", __FUNCTION__);
return result;
}
@@ -248,11 +245,10 @@ static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command,
__FUNCTION__, result);
up(&dec->usb_sem);
kfree(b);
- kfree(c);
return result;
}
- result = usb_bulk_msg(dec->udev, dec->result_pipe, c,
+ result = usb_bulk_msg(dec->udev, dec->result_pipe, b,
COMMAND_PACKET_SIZE + 4, &actual_len, HZ);
if (result) {
@@ -260,25 +256,23 @@ static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command,
__FUNCTION__, result);
up(&dec->usb_sem);
kfree(b);
- kfree(c);
return result;
} else {
if (debug) {
printk("%s: result: ", __FUNCTION__);
for (i = 0; i < actual_len; i++)
- printk("0x%02X ", c[i]);
+ printk("0x%02X ", b[i]);
printk("\n");
}
if (result_length)
- *result_length = c[3];
- if (cmd_result && c[3] > 0)
- memcpy(cmd_result, &c[4], c[3]);
+ *result_length = b[3];
+ if (cmd_result && b[3] > 0)
+ memcpy(cmd_result, &b[4], b[3]);
up(&dec->usb_sem);
kfree(b);
- kfree(c);
return 0;
}
}
@@ -1171,9 +1165,10 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
u16 firmware_csum = 0;
u16 firmware_csum_ns;
u32 firmware_size_nl;
+#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE)
u32 crc32_csum, crc32_check, tmp;
+#endif
const struct firmware *fw_entry = NULL;
-
dprintk("%s\n", __FUNCTION__);
if (request_firmware(&fw_entry, dec->firmware_name, &dec->udev->dev)) {
@@ -1186,7 +1181,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
firmware_size = fw_entry->size;
if (firmware_size < 60) {
- printk("%s: firmware size too small for DSP code (%zu < 60).\n",
+ printk("%s: firmware size too small for DSP code (%u < 60).\n",
__FUNCTION__, firmware_size);
return -1;
}
@@ -1194,6 +1189,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
/* a 32 bit checksum over the first 56 bytes of the DSP Code is stored
at offset 56 of file, so use it to check if the firmware file is
valid. */
+#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE)
crc32_csum = crc32(~0L, firmware, 56) ^ ~0L;
memcpy(&tmp, &firmware[56], 4);
crc32_check = htonl(tmp);
@@ -1203,6 +1199,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
__FUNCTION__, crc32_csum, crc32_check);
return -1;
}
+#endif
memcpy(idstring, &firmware[36], 20);
idstring[20] = '\0';
printk(KERN_INFO "ttusb_dec: found DSP code \"%s\".\n", idstring);
@@ -1287,6 +1284,7 @@ static int ttusb_dec_init_stb(struct ttusb_dec *dec)
give the box */
switch (model) {
case 0x00070008:
+ case 0x0007000c:
ttusb_dec_set_model(dec, TTUSB_DEC3000S);
break;
case 0x00070009:
@@ -1320,7 +1318,7 @@ static int ttusb_dec_init_dvb(struct ttusb_dec *dec)
dprintk("%s\n", __FUNCTION__);
if ((result = dvb_register_adapter(&dec->adapter,
- dec->model_name)) < 0) {
+ dec->model_name, THIS_MODULE)) < 0) {
printk("%s: dvb_register_adapter failed: error %d\n",
__FUNCTION__, result);
@@ -1518,10 +1516,6 @@ static int ttusb_dec_2000t_frontend_ioctl(struct dvb_frontend *fe, unsigned int
dprintk("%s: FE_INIT\n", __FUNCTION__);
break;
- case FE_RESET:
- dprintk("%s: FE_RESET\n", __FUNCTION__);
- break;
-
default:
dprintk("%s: unknown IOCTL (0x%X)\n", __FUNCTION__, cmd);
return -EINVAL;
@@ -1591,13 +1585,13 @@ static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0d,
+ 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
u32 freq;
u32 sym_rate;
u32 band;
-
+ u32 lnb_voltage;
dprintk("%s: FE_SET_FRONTEND\n", __FUNCTION__);
@@ -1613,6 +1607,8 @@ static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe,
memcpy(&b[12], &sym_rate, sizeof(u32));
band = htonl(dec->hi_band ? LOF_HI : LOF_LO);
memcpy(&b[24], &band, sizeof(u32));
+ lnb_voltage = htonl(dec->voltage);
+ memcpy(&b[28], &lnb_voltage, sizeof(u32));
ttusb_dec_send_command(dec, 0x71, sizeof(b), b, NULL, NULL);
@@ -1632,10 +1628,6 @@ static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe,
dprintk("%s: FE_INIT\n", __FUNCTION__);
break;
- case FE_RESET:
- dprintk("%s: FE_RESET\n", __FUNCTION__);
- break;
-
case FE_DISEQC_SEND_MASTER_CMD:
dprintk("%s: FE_DISEQC_SEND_MASTER_CMD\n", __FUNCTION__);
break;
@@ -1653,6 +1645,17 @@ static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe,
case FE_SET_VOLTAGE:
dprintk("%s: FE_SET_VOLTAGE\n", __FUNCTION__);
+ switch ((fe_sec_voltage_t) arg) {
+ case SEC_VOLTAGE_13:
+ dec->voltage = 13;
+ break;
+ case SEC_VOLTAGE_18:
+ dec->voltage = 18;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
break;
default:
diff --git a/drivers/media/video/dpc7146.c b/drivers/media/video/dpc7146.c
index 74572ab02234..86ca8a4385b7 100644
--- a/drivers/media/video/dpc7146.c
+++ b/drivers/media/video/dpc7146.c
@@ -106,7 +106,7 @@ static int dpc_probe(struct saa7146_dev* dev)
video port pins should be enabled here ?! */
saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
- saa7146_i2c_adapter_prepare(dev, &dpc->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ saa7146_i2c_adapter_prepare(dev, &dpc->i2c_adapter, I2C_ADAP_CLASS_TV_ANALOG, SAA7146_I2C_BUS_BIT_RATE_480);
if(i2c_add_adapter(&dpc->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(dpc);
@@ -312,18 +312,18 @@ static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
static struct saa7146_standard standard[] = {
{
.name = "PAL", .id = V4L2_STD_PAL,
- .v_offset = 0x17, .v_field = 288, .v_calc = 576,
- .h_offset = 0x14, .h_pixels = 680, .h_calc = 680+1,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x16, .v_field = 240, .v_calc = 480,
- .h_offset = 0x06, .h_pixels = 708, .h_calc = 708+1,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
- .v_offset = 0x14, .v_field = 288, .v_calc = 576,
- .h_offset = 0x14, .h_pixels = 720, .h_calc = 720+1,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 720,
.v_max_out = 576, .h_max_out = 768,
}
};
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
index 8dd647a90f2e..748ba676c431 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/video/hexium_gemini.c
@@ -159,18 +159,18 @@ static struct hexium_data hexium_input_select[] = {
static struct saa7146_standard hexium_standards[] = {
{
.name = "PAL", .id = V4L2_STD_PAL,
- .v_offset = 28, .v_field = 288, .v_calc = 576,
- .h_offset = 1, .h_pixels = 680, .h_calc = 680+1,
+ .v_offset = 28, .v_field = 288,
+ .h_offset = 1, .h_pixels = 680,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 28, .v_field = 240, .v_calc = 480,
- .h_offset = 1, .h_pixels = 640, .h_calc = 641+1,
+ .v_offset = 28, .v_field = 240,
+ .h_offset = 1, .h_pixels = 640,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
- .v_offset = 28, .v_field = 288, .v_calc = 576,
- .h_offset = 1, .h_pixels = 720, .h_calc = 720+1,
+ .v_offset = 28, .v_field = 288,
+ .h_offset = 1, .h_pixels = 720,
.v_max_out = 576, .h_max_out = 768,
}
};
@@ -250,7 +250,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
/* enable i2c-port pins */
saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
- saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, I2C_ADAP_CLASS_TV_ANALOG, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(hexium);
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
index 91c8c82da9a5..cdb82726c420 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/video/hexium_orion.c
@@ -192,18 +192,18 @@ static struct {
static struct saa7146_standard hexium_standards[] = {
{
.name = "PAL", .id = V4L2_STD_PAL,
- .v_offset = 16, .v_field = 288, .v_calc = 576,
- .h_offset = 1, .h_pixels = 680, .h_calc = 680+1,
+ .v_offset = 16, .v_field = 288,
+ .h_offset = 1, .h_pixels = 680,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 16, .v_field = 240, .v_calc = 480,
- .h_offset = 1, .h_pixels = 640, .h_calc = 641+1,
+ .v_offset = 16, .v_field = 240,
+ .h_offset = 1, .h_pixels = 640,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
- .v_offset = 16, .v_field = 288, .v_calc = 576,
- .h_offset = 1, .h_pixels = 720, .h_calc = 720+1,
+ .v_offset = 16, .v_field = 288,
+ .h_offset = 1, .h_pixels = 720,
.v_max_out = 576, .h_max_out = 768,
}
};
@@ -237,7 +237,7 @@ static int hexium_probe(struct saa7146_dev *dev)
saa7146_write(dev, DD1_STREAM_B, 0x00000000);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
- saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, I2C_ADAP_CLASS_TV_ANALOG, SAA7146_I2C_BUS_BIT_RATE_480);
if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(hexium);
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index f808a1f6b28b..cad4049a35e6 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -223,7 +223,7 @@ static int mxb_probe(struct saa7146_dev* dev)
video port pins should be enabled here ?! */
saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
- saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, I2C_ADAP_CLASS_TV_ANALOG, SAA7146_I2C_BUS_BIT_RATE_480);
if(i2c_add_adapter(&mxb->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
kfree(mxb);
@@ -1007,23 +1007,23 @@ static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
static struct saa7146_standard standard[] = {
{
.name = "PAL-BG", .id = V4L2_STD_PAL_BG,
- .v_offset = 0x17, .v_field = 288, .v_calc = 576,
- .h_offset = 0x14, .h_pixels = 680, .h_calc = 680+1,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "PAL-I", .id = V4L2_STD_PAL_I,
- .v_offset = 0x17, .v_field = 288, .v_calc = 576,
- .h_offset = 0x14, .h_pixels = 680, .h_calc = 680+1,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
.v_max_out = 576, .h_max_out = 768,
}, {
.name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x16, .v_field = 240, .v_calc = 480,
- .h_offset = 0x06, .h_pixels = 708, .h_calc = 708+1,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
.v_max_out = 480, .h_max_out = 640,
}, {
.name = "SECAM", .id = V4L2_STD_SECAM,
- .v_offset = 0x14, .v_field = 288, .v_calc = 576,
- .h_offset = 0x14, .h_pixels = 720, .h_calc = 720+1,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 720,
.v_max_out = 576, .h_max_out = 768,
}
};
diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h
index 9ffb970cf7d8..51a69e389eae 100644
--- a/include/linux/dvb/frontend.h
+++ b/include/linux/dvb/frontend.h
@@ -59,9 +59,9 @@ typedef enum fe_caps {
FE_CAN_BANDWIDTH_AUTO = 0x40000,
FE_CAN_GUARD_INTERVAL_AUTO = 0x80000,
FE_CAN_HIERARCHY_AUTO = 0x100000,
- FE_CAN_RECOVER = 0x20000000,
- FE_CAN_CLEAN_SETUP = 0x40000000,
- FE_CAN_MUTE_TS = 0x80000000
+ FE_NEEDS_BENDING = 0x20000000, // frontend requires frequency bending
+ FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically
+ FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output
} fe_caps_t;
diff --git a/include/linux/dvb/net.h b/include/linux/dvb/net.h
index 4b8fa5177d43..c14101491ac9 100644
--- a/include/linux/dvb/net.h
+++ b/include/linux/dvb/net.h
@@ -30,6 +30,9 @@
struct dvb_net_if {
__u16 pid;
__u16 if_num;
+ __u8 feedtype;
+#define DVB_NET_FEEDTYPE_MPE 0 /* multi protocol encapsulation */
+#define DVB_NET_FEEDTYPE_ULE 1 /* ultra lightweight encapsulation */
};
@@ -37,5 +40,14 @@ struct dvb_net_if {
#define NET_REMOVE_IF _IO('o', 53)
#define NET_GET_IF _IOWR('o', 54, struct dvb_net_if)
-#endif /*_DVBNET_H_*/
+/* binary compatibility cruft: */
+struct __dvb_net_if_old {
+ __u16 pid;
+ __u16 if_num;
+};
+#define __NET_ADD_IF_OLD _IOWR('o', 52, struct __dvb_net_if_old)
+#define __NET_GET_IF_OLD _IOWR('o', 54, struct __dvb_net_if_old)
+
+
+#endif /*_DVBNET_H_*/
diff --git a/include/media/saa7146.h b/include/media/saa7146.h
index 5e9f64e67bc8..e62f03fb4c5d 100644
--- a/include/media/saa7146.h
+++ b/include/media/saa7146.h
@@ -154,7 +154,7 @@ struct saa7146_dev
};
/* from saa7146_i2c.c */
-int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate);
+int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, unsigned int class, u32 bitrate);
int saa7146_i2c_transfer(struct saa7146_dev *saa, const struct i2c_msg msgs[], int num, int retries);
/* from saa7146_core.c */
diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h
index 50ab25907eec..60cd29e52f2d 100644
--- a/include/media/saa7146_vv.h
+++ b/include/media/saa7146_vv.h
@@ -44,11 +44,9 @@ struct saa7146_standard
int v_offset; /* number of lines of vertical offset before processing */
int v_field; /* number of lines in a field for HPS to process */
- int v_calc; /* number of vertical active lines */
int h_offset; /* horizontal offset of processing window */
int h_pixels; /* number of horizontal pixels to process */
- int h_calc; /* number of horizontal active pixels */
int v_max_out;
int h_max_out;
@@ -104,6 +102,9 @@ struct saa7146_fh {
unsigned int resources; /* resource management for device open */
};
+#define STATUS_OVERLAY 0x01
+#define STATUS_CAPTURE 0x02
+
struct saa7146_vv
{
int vbi_minor;
@@ -117,14 +118,17 @@ struct saa7146_vv
int video_minor;
+ int video_status;
+ struct saa7146_fh *video_fh;
+
/* video overlay */
struct v4l2_framebuffer ov_fb;
struct saa7146_format *ov_fmt;
struct saa7146_overlay *ov_data;
+ struct saa7146_fh *ov_suspend;
/* video capture */
struct saa7146_dmaqueue video_q;
- struct saa7146_fh *streaming;
enum v4l2_field last_field;
/* common: fixme? shouldn't this be in saa7146_fh?