2004-04-26 13:40:13

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 0/9] LinuxTV.org DVB update

Hello Linus, Andrew,

I'm sending you a set of 9 patches that sync the
LinuxTV.org CVS with latest linux-2.6.5.

I'm glad that I can announce that we have recently
forked off our 2.6 development tree, so we are really
looking forward to implement
- proper kobject/sysfs support
- proper firmware loading for frontend drivers
- removal of dvb-i2c and use of kernel i2c instead
- other interesting 2.6 specific things

This is the last patchset that rides the dead "2.4 compatiblity"
horse.

As usual, detailed informations about what changed can be
found at the top of each file.

Please apply!

CU
Michael.



2004-04-26 13:44:12

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 2/9] DVB: Documentation and Kconfig updazes

- [DVB] misc. documentation updates, KConfig help file updates
- [DVB] make Twinhan driver depend on bt8xx
diff -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/Kconfig linux-2.6.5-patched/drivers/media/dvb/frontends/Kconfig
--- xx-linux-2.6.5/drivers/media/dvb/frontends/Kconfig 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/Kconfig 2004-04-10 16:43:36.000000000 +0200
@@ -3,7 +3,7 @@

config DVB_TWINHAN_DST
tristate "TWINHAN DST based DVB-S frontend (QPSK)"
- depends on DVB_CORE
+ depends on DVB_CORE && DVB_BT8XX
help
Used in such cards as the VP-1020/1030, Twinhan DST,
VVmer TV@SAT. Say Y when you want to support frontends
diff -urawBN xx-linux-2.6.5/Documentation/dvb/cards.txt linux-2.6.5-patched/Documentation/dvb/cards.txt
--- xx-linux-2.6.5/Documentation/dvb/cards.txt 2004-03-12 20:31:01.000000000 +0100
+++ linux-2.6.5-patched/Documentation/dvb/cards.txt 2004-03-19 18:13:54.000000000 +0100
@@ -50,6 +50,8 @@
- 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 -urawBN xx-linux-2.6.5/Documentation/dvb/contributors.txt linux-2.6.5-patched/Documentation/dvb/contributors.txt
--- xx-linux-2.6.5/Documentation/dvb/contributors.txt 2004-02-22 14:48:36.000000000 +0100
+++ linux-2.6.5-patched/Documentation/dvb/contributors.txt 2004-03-19 18:13:54.000000000 +0100
@@ -62,5 +62,13 @@
for his work on calculating and checking the crc's for the
TechnoTrend/Hauppauge DEC driver firmware

+Michael Dreher <[email protected]>
+Andreas 'randy' Weinberger
+ for the support of the Fujitsu-Siemens Activy budget DVB-S
+
+Kenneth Aafl?y <[email protected]>
+ 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 -urawBN xx-linux-2.6.5/Documentation/dvb/faq.txt linux-2.6.5-patched/Documentation/dvb/faq.txt
--- xx-linux-2.6.5/Documentation/dvb/faq.txt 2004-03-12 20:31:01.000000000 +0100
+++ linux-2.6.5-patched/Documentation/dvb/faq.txt 2004-04-08 17:54:49.000000000 +0200
@@ -94,6 +94,11 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/Kconfig linux-2.6.5-patched/drivers/media/dvb/Kconfig
--- xx-linux-2.6.5/drivers/media/dvb/Kconfig 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/Kconfig 2003-10-13 06:08:45.000000000 +0200
@@ -18,12 +18,11 @@
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



2004-04-26 13:42:46

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 1/9] V4L: Update the saa7146 driver

- [DVB] saa7146 driver updates:
- remove bogus v_calc and h_calc parameters, which can be easily retrieved from other values
- add class parameter to i2c initialization
- let resource handling provide more useful informations
- sanitize overlay/capture locking
diff -urawBN xx-linux-2.6.5/drivers/media/common/saa7146_fops.c linux-2.6.5-patched/drivers/media/common/saa7146_fops.c
--- xx-linux-2.6.5/drivers/media/common/saa7146_fops.c 2004-02-22 14:48:47.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/common/saa7146_fops.c 2004-03-11 11:58:36.000000000 +0100
@@ -10,14 +10,14 @@
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 @@
/* 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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/common/saa7146_hlp.c linux-2.6.5-patched/drivers/media/common/saa7146_hlp.c
--- xx-linux-2.6.5/drivers/media/common/saa7146_hlp.c 2004-02-22 14:48:47.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/common/saa7146_hlp.c 2004-03-15 20:38:14.000000000 +0100
@@ -536,13 +536,13 @@
/* 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 -urawBN xx-linux-2.6.5/drivers/media/common/saa7146_i2c.c linux-2.6.5-patched/drivers/media/common/saa7146_i2c.c
--- xx-linux-2.6.5/drivers/media/common/saa7146_i2c.c 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/common/saa7146_i2c.c 2004-04-06 15:05:23.000000000 +0200
@@ -400,7 +400,7 @@
.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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/common/saa7146_video.c linux-2.6.5-patched/drivers/media/common/saa7146_video.c
--- xx-linux-2.6.5/drivers/media/common/saa7146_video.c 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/common/saa7146_video.c 2004-03-19 15:15:04.000000000 +0100
@@ -5,6 +5,12 @@
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 @@
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 @@
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 @@

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 @@
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 @@
/* 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 @@
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 @@
return -EINVAL;
}

+ down(&dev->lock);
+
switch (ctrl->type) {
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_MENU:
@@ -528,35 +550,31 @@
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 @@
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 @@
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 @@
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 @@
resource = RESOURCE_DMA1_HPS;
dmas = MASK_22;
}
- saa7146_res_free(fh, resource);
-
spin_lock_irqsave(&dev->slock,flags);

/* disable rps0 */
@@ -770,13 +803,20 @@
/* 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 @@
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 @@
{
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,12 +924,15 @@
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;
@@ -904,10 +941,6 @@
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 @@
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 @@
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 @@
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 @@
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 *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 @@

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 @@
{
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 @@
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 @@
}

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 -urawBN xx-linux-2.6.5/include/media/saa7146.h linux-2.6.5-patched/include/media/saa7146.h
--- xx-linux-2.6.5/include/media/saa7146.h 2004-02-22 14:49:05.000000000 +0100
+++ linux-2.6.5-patched/include/media/saa7146.h 2004-04-23 22:07:10.000000000 +0200
@@ -154,7 +166,7 @@
};

/* 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 -urawBN xx-linux-2.6.5/include/media/saa7146_vv.h linux-2.6.5-patched/include/media/saa7146_vv.h
--- xx-linux-2.6.5/include/media/saa7146_vv.h 2004-02-22 14:49:05.000000000 +0100
+++ linux-2.6.5-patched/include/media/saa7146_vv.h 2004-03-15 20:38:16.000000000 +0100
@@ -44,11 +44,9 @@

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 @@
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 @@

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?


2004-04-26 13:47:17

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 3/9] DVB: Update DVB budget drivers

- [DVB] budget-av: patch by Kenneth Aafl?y to add support for Typhoon DVB-S budget card
- [DVB] budget.c: support for Fujitsu-Siemens Activy Card
- [DVB] budget-ci: add preliminary CI support
diff -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/budget-av.c linux-2.6.5-patched/drivers/media/dvb/ttpci/budget-av.c
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/budget-av.c 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/budget-av.c 2004-03-19 18:13:56.000000000 +0100
@@ -39,6 +39,7 @@
struct budget budget;
struct video_device vd;
int cur_input;
+ int has_saa7113;
};

/****************************************************************************
@@ -149,6 +150,9 @@
{
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 @@

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 @@
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 @@
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 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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/budget.c linux-2.6.5-patched/drivers/media/dvb/ttpci/budget.c
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/budget.c 2003-12-18 03:58:57.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/budget.c 2004-02-27 14:45:59.000000000 +0100
@@ -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 <[email protected]>,
+ * Oliver Endriss <[email protected]> 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 @@
}


+/*
+ * 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 @@
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 @@
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(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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/budget-ci.c linux-2.6.5-patched/drivers/media/dvb/ttpci/budget-ci.c
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/budget-ci.c 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/budget-ci.c 2004-04-21 01:23:32.000000000 +0200
@@ -6,6 +6,8 @@
* msp430 IR support contributed by Jack Thomasson <[email protected]>
* partially based on the Siemens DVB driver by Ralph+Marcus Metzler
*
+ * CI interface support (c) 2004 Andrew de Quincey <[email protected]>
+ *
* 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,22 +36,59 @@
#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));
@@ -53,18 +92,47 @@

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_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 @@

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 @@

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 @@
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 @@

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 @@

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 @@

msp430_ir_init (budget_ci);

+ // UNCOMMENT TO TEST CI INTERFACE
+// ciintf_init(budget_ci);
+
return 0;
}

@@ -264,14 +543,20 @@
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 @@
.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_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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/budget-core.c linux-2.6.5-patched/drivers/media/dvb/ttpci/budget-core.c
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/budget-core.c 2003-12-18 03:58:39.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/budget-core.c 2004-04-21 01:23:32.000000000 +0200
@@ -1,3 +1,39 @@
+/*
+ * budget-core.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <[email protected]>
+ *
+ * Copyright (C) 2002 Ralph Metzler <[email protected]>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ * Michael Dreher <[email protected]>,
+ * Oliver Endriss <[email protected]>,
+ * 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 @@

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 @@
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 @@
{
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 @@
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 @@

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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/budget.h linux-2.6.5-patched/drivers/media/dvb/ttpci/budget.h
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/budget.h 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/budget.h 2004-04-21 01:23:32.000000000 +0200
@@ -46,10 +46,15 @@
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 @@
#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



2004-04-26 13:54:02

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 6/9] DVB: AV7110 DVB driver updates

- [DVB] av7110 update:
- speed up firmware loading
- follow internal API changes in saa7146 driver
- introduced some symbolic constants for a/v dec cmds
- change default for hw_sections to 0 to enable crc checks
diff -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110_av.c linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110_av.c
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110_av.c 2004-02-22 14:48:47.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110_av.c 2004-03-23 18:56:53.000000000 +0100
@@ -975,7 +975,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@

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 @@

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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110.c linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110.c
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110.c 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110.c 2004-04-23 22:03:29.000000000 +0200
@@ -30,6 +30,7 @@
*/


+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/delay.h>
@@ -77,7 +78,7 @@
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 +1241,6 @@
****************************************************************************/


-#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 +1354,13 @@
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 +1436,17 @@
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 +1498,35 @@
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 +1651,7 @@
{
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 +1662,16 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110_hw.c linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110_hw.c
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110_hw.c 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110_hw.c 2004-04-12 23:10:22.000000000 +0200
@@ -105,10 +105,8 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110_hw.h linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110_hw.h
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110_hw.h 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110_hw.h 2004-03-23 18:56:53.000000000 +0100
@@ -200,6 +200,12 @@
__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 @@
COMTYPE_VIDEO,
COMTYPE_AUDIO,
COMTYPE_CI_LL,
+ COMTYPE_MISC = 0x80
};

#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */
@@ -226,6 +233,23 @@
#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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110_v4l.c linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110_v4l.c
--- xx-linux-2.6.5/drivers/media/dvb/ttpci/av7110_v4l.c 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttpci/av7110_v4l.c 2004-03-15 20:38:15.000000000 +0100
@@ -177,16 +177,17 @@
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 @@
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 @@
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 @@
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 @@

// 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 @@
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 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 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,
}
};


2004-04-26 13:52:51

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 4/9] DVB: Add EN50221 cam support to dvb-core

- [DVB] add generic functions for EN50221 CAM interfaces
diff -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ca_en50221.c linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ca_en50221.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ca_en50221.c 2004-04-23 21:51:15.000000000 +0200
@@ -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 <[email protected]>
+ *
+ * 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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ca_en50221.h linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ca_en50221.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ca_en50221.h 2004-04-05 14:17:33.000000000 +0200
@@ -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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ksyms.c linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ksyms.c
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ksyms.c 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ksyms.c 2004-04-05 14:17:33.000000000 +0200
@@ -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);
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ringbuffer.c linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ringbuffer.c 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ringbuffer.c 2004-04-14 12:31:21.000000000 +0200
@@ -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 @@
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_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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ringbuffer.h linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_ringbuffer.h 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_ringbuffer.h 2004-04-05 14:17:33.000000000 +0200
@@ -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 @@
spinlock_t lock;
};

+#define DVB_RINGBUFFER_PKTHDRSIZE 3
+

/*
** Notes:
@@ -120,4 +123,52 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/Makefile linux-2.6.5-patched/drivers/media/dvb/dvb-core/Makefile
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/Makefile 2003-12-18 03:59:56.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/Makefile 2004-04-05 14:17:33.000000000 +0200
@@ -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


2004-04-26 13:57:57

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 5/9] DVB: Other DVB core updates

- [DVB] remove superflous memset() which caused section data to be overwritten when a) there are two sections in one TS packet, and b) the first section was smaller than 18 bytes; thanks to Jean-Claude Repetto for tracking this down
- [DVB] starting a ts filter on a running section filter's pid did break the section filter; fixed.
- [DVB] integrate ULE Decapsulation code, thanks to gcs - Global Communication & Services GmbH. and Institute for Computer Sciences Salzburg University. Hilmar Linder <[email protected]> and Wolfram Stering <[email protected]>
- [DVB] fix the module use count bugs, thanks to Hernan A.Perez Masci for his initial work on this problem
- [DVB] if dvb_frontend_internal_ioctl() returns an error code, be sure to deliver it to the calling application, don't ignore it (fixes the bug that the frontend0 doesn't respond properly to unknown ioctls...)
- [DVB] major frontend code clean up, rewritten core tuning loop. Thanks to Andrew de Quincey.
- [DVB] follow changes in dvb-core in skystar2, dvb-bt8xx
diff -urawBN xx-linux-2.6.5/drivers/media/dvb/b2c2/skystar2.c linux-2.6.5-patched/drivers/media/dvb/b2c2/skystar2.c
--- xx-linux-2.6.5/drivers/media/dvb/b2c2/skystar2.c 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/b2c2/skystar2.c 2004-03-31 11:55:31.000000000 +0200
@@ -2242,7 +2243,7 @@
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 +2343,8 @@
{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 -urawBN xx-linux-2.6.5/drivers/media/dvb/bt8xx/dvb-bt8xx.c linux-2.6.5-patched/drivers/media/dvb/bt8xx/dvb-bt8xx.c
--- xx-linux-2.6.5/drivers/media/dvb/bt8xx/dvb-bt8xx.c 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/bt8xx/dvb-bt8xx.c 2004-04-23 21:55:25.000000000 +0200
@@ -286,7 +286,7 @@

}

- 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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_demux.c linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_demux.c
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_demux.c 2004-02-22 14:48:47.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_demux.c 2004-03-03 16:48:27.000000000 +0100
@@ -192,7 +194,6 @@
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 +216,6 @@

sec->seclen = 0;

- memset(buf, 0, DVB_DEMUX_MASK_MAX);
-
return 0;
}

diff -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvbdev.c linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvbdev.c
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvbdev.c 2003-12-18 03:58:18.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvbdev.c 2004-04-23 21:50:41.000000000 +0200
@@ -211,6 +210,8 @@
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,9 +228,12 @@

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);
}
@@ -233,7 +237,6 @@
list_del(&dvbdev->list_head);
kfree(dvbdev);
}
-}


static int dvbdev_get_free_adapter_num (void)
@@ -257,7 +260,7 @@
}


-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 +284,10 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvbdev.h linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvbdev.h
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvbdev.h 2003-12-18 03:58:39.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvbdev.h 2004-04-23 21:48:36.000000000 +0200
@@ -48,6 +48,8 @@
struct list_head device_list;
const char *name;
u8 proposed_mac [6];
+
+ struct module *module;
};


@@ -75,7 +74,7 @@
};


-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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_frontend.c linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_frontend.c
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_frontend.c 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_frontend.c 2004-04-23 21:51:35.000000000 +0200
@@ -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 @@
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 @@
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 @@
{
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 @@
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 @@
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 @@
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++;
+ }
+
+ // 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_set_parameters (fe, &fe->parameters, 0);
+ // 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;

- dvb_frontend_internal_ioctl (&fe->frontend, FE_RESET, NULL);
+ // normal return
+ fe->auto_sub_step++;
+ return 0;
}


@@ -422,6 +465,19 @@
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 @@
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 @@

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 @@
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 @@
fe->thread_pid = 0;
mb();

- wake_up_interruptible (&fe->wait_queue);
+ dvb_frontend_wakeup(fe);
return 0;
}

@@ -529,13 +646,15 @@
}

/* 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 @@
if (down_interruptible (&fe->sem))
return -EINTR;

+ fe->state = FESTATE_IDLE;
fe->exit = 0;
fe->thread_pid = 0;
mb();
@@ -583,6 +703,7 @@
{
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 @@
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 +783,26 @@
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 +1099,7 @@
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 +1139,9 @@
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 +1179,12 @@

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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_frontend.h linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_frontend.h
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_frontend.h 2003-12-18 03:59:42.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_frontend.h 2004-03-11 19:40:44.000000000 +0100
@@ -56,14 +56,25 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_net.c linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_net.c
--- xx-linux-2.6.5/drivers/media/dvb/dvb-core/dvb_net.c 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/dvb-core/dvb_net.c 2004-04-23 21:52:51.000000000 +0200
@@ -5,33 +5,53 @@
* Ralph Metzler <[email protected]>
* Copyright (C) 2002 Ralph Metzler <[email protected]>
*
+ * ULE Decapsulation code:
+ * Copyright (C) 2003 gcs - Global Communication & Services GmbH.
+ * and Institute for Computer Sciences
+ * Salzburg University.
+ * Hilmar Linder <[email protected]>
+ * and Wolfram Stering <[email protected]>
+ *
+ * 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 @@
#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 @@
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 @@
*/
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 @@

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 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 @@

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 @@

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 @@

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 @@
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 @@
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 +1007,15 @@
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 +1035,10 @@
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 +1091,7 @@

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 +1105,50 @@

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 -urawBN xx-linux-2.6.5/include/linux/dvb/frontend.h linux-2.6.5-patched/include/linux/dvb/frontend.h
--- xx-linux-2.6.5/include/linux/dvb/frontend.h 2003-12-18 03:58:49.000000000 +0100
+++ linux-2.6.5-patched/include/linux/dvb/frontend.h 2004-03-11 19:40:45.000000000 +0100
@@ -59,9 +59,9 @@
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 -urawBN xx-linux-2.6.5/include/linux/dvb/net.h linux-2.6.5-patched/include/linux/dvb/net.h
--- xx-linux-2.6.5/include/linux/dvb/net.h 2003-12-18 03:58:08.000000000 +0100
+++ linux-2.6.5-patched/include/linux/dvb/net.h 2004-04-14 19:10:57.000000000 +0200
@@ -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 @@
#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_*/


2004-04-26 13:58:48

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 8/9] DVB: Misc. DVB USB driver updates

- [DVB] ttusb-dec:
- Add a parameter to dvb_filter_pes2ts function to specify whether the packet is a payload unit start or not
- Use the hotplug firmware loader for 2.6 kernels instead of compiling the firmware into the module.
- Correct the USB id of the DEC3000-s, add basic support
- [DVB] ttusb-budget:
- Remove spurious discontinuity message when starting streaming
diff -urawBN xx-linux-2.6.5/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c linux-2.6.5-patched/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
--- xx-linux-2.6.5/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c 2004-04-23 22:02:54.000000000 +0200
@@ -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 @@

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 @@

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 @@
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 @@
return 0;
}

+ ttusb->cc = -1;
ttusb->insync = 0;
ttusb->mux_state = 0;

@@ -864,6 +865,7 @@
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_setup_interfaces(struct ttusb *ttusb)
{
- usb_set_configuration(ttusb->dev, 1);
usb_set_interface(ttusb->dev, 1, 1);

ttusb->bulk_out_pipe = usb_sndbulkpipe(ttusb->dev, 1);
@@ -1077,6 +1078,8 @@

udev = interface_to_usbdev(intf);

+ if (intf->altsetting->desc.bInterfaceNumber != 1) return -ENODEV;
+
if (!(ttusb = kmalloc(sizeof(struct ttusb), GFP_KERNEL)))
return -ENOMEM;

@@ -1101,8 +1104,7 @@

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,
@@ -1169,9 +1169,6 @@

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 -urawBN xx-linux-2.6.5/drivers/media/dvb/ttusb-dec/ttusb_dec.c linux-2.6.5-patched/drivers/media/dvb/ttusb-dec/ttusb_dec.c
--- xx-linux-2.6.5/drivers/media/dvb/ttusb-dec/ttusb_dec.c 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/ttusb-dec/ttusb_dec.c 2004-04-23 21:58:20.000000000 +0200
@@ -1,7 +1,7 @@
/*
* TTUSB DEC Driver
*
- * Copyright (C) 2003 Alex Woods <[email protected]>
+ * Copyright (C) 2003-2004 Alex Woods <[email protected]>
*
* 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 @@

u16 pid[DMX_PES_OTHER];
int hi_band;
+ int voltage;

/* USB bits */
struct usb_device *udev;
@@ -205,22 +209,15 @@
{
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 @@
__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 @@
__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 +1164,10 @@
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 +1180,7 @@
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 +1188,7 @@
/* 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 +1198,7 @@
__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 +1283,7 @@
give the box */
switch (model) {
case 0x00070008:
+ case 0x0007000c:
ttusb_dec_set_model(dec, TTUSB_DEC3000S);
break;
case 0x00070009:
@@ -1320,7 +1317,7 @@
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 +1514,6 @@
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 +1582,13 @@
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 +1604,8 @@
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 +1625,6 @@
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 +1642,17 @@

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:


2004-04-26 14:02:32

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 7/9] DVB: Misc. DVB frontend driver updates

- [DVB] follow changes in dvb-core for frontend drivers (ves1x93, ves1820, nxt6000, sp887x, tda1004x, stv0299, mt312, alps_tdlb7, alps_tdmb7, at76c651, cx24110, dst, dvb_dummy_fe, grundig_29504-401, grundig_29504-491)
- [DVB] tda1004x: updated timeout to 800ms, implemented FE_SLEEP
- [DVB] cx24110: add FE_CAN_RECOVER to reduce kdvb-fe CPU load
- [DVB] grundig_29504-401: added 200ms delay after first FE_INIT, Implemented FE_GET_FRONTEND
- [DVB] alps_tdlb7, alps_tdmb7: upped tuning delays to fix tuning
diff -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/alps_tdlb7.c linux-2.6.5-patched/drivers/media/dvb/frontends/alps_tdlb7.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/alps_tdlb7.c 2004-04-09 17:42:17.000000000 +0200
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/alps_tdlb7.c 2004-03-25 20:07:25.000000000 +0100
@@ -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 @@
#define SP8870_FIRMWARE_OFFSET 0x0A


+static int errno;
+
static struct dvb_frontend_info tdlb7_info = {
.name = "Alps TDLB7",
.type = FE_OFDM,
@@ -74,12 +75,7 @@
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 +92,8 @@
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 +109,7 @@
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 +149,13 @@
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 +169,8 @@
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 +637,6 @@
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 +651,15 @@
}
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 +672,8 @@
{
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/alps_tdmb7.c linux-2.6.5-patched/drivers/media/dvb/frontends/alps_tdmb7.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/alps_tdmb7.c 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/alps_tdmb7.c 2004-03-25 20:07:25.000000000 +0100
@@ -50,7 +50,7 @@
.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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/at76c651.c linux-2.6.5-patched/drivers/media/dvb/frontends/at76c651.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/at76c651.c 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/at76c651.c 2004-03-11 19:40:44.000000000 +0100
@@ -71,9 +71,7 @@
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 +358,7 @@
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 +459,14 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/cx24110.c linux-2.6.5-patched/drivers/media/dvb/frontends/cx24110.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/cx24110.c 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/cx24110.c 2004-04-08 17:10:52.000000000 +0200
@@ -59,8 +59,7 @@
.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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/dst.c linux-2.6.5-patched/drivers/media/dvb/frontends/dst.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/dst.c 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/dst.c 2004-03-11 19:40:44.000000000 +0100
@@ -963,7 +963,6 @@
{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 @@
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 @@
}

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 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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/dvb_dummy_fe.c linux-2.6.5-patched/drivers/media/dvb/frontends/dvb_dummy_fe.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/dvb_dummy_fe.c 2003-12-18 03:58:38.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/dvb_dummy_fe.c 2004-03-11 19:40:44.000000000 +0100
@@ -62,8 +62,7 @@
#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 @@
case FE_INIT:
return 0;

- case FE_RESET:
- return 0;
-
case FE_SET_TONE:
return -EOPNOTSUPP;

diff -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/grundig_29504-401.c linux-2.6.5-patched/drivers/media/dvb/frontends/grundig_29504-401.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/grundig_29504-401.c 2003-12-18 03:58:15.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/grundig_29504-401.c 2004-03-25 22:09:41.000000000 +0100
@@ -35,6 +35,9 @@

#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 @@
.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_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 @@
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 +295,123 @@
}


+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 +454,9 @@
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 +532,33 @@
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 +576,7 @@
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 +620,12 @@
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 +637,7 @@

static void l64781_detach (struct dvb_i2c_bus *i2c, void *data)
{
+ kfree(data);
dvb_unregister_frontend (grundig_29504_401_ioctl, i2c);
}

diff -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/grundig_29504-491.c linux-2.6.5-patched/drivers/media/dvb/frontends/grundig_29504-491.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/grundig_29504-491.c 2003-12-18 03:59:20.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/grundig_29504-491.c 2004-03-11 19:40:44.000000000 +0100
@@ -52,8 +52,7 @@
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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/mt312.c linux-2.6.5-patched/drivers/media/dvb/frontends/mt312.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/mt312.c 2004-01-16 18:25:17.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/mt312.c 2004-03-11 19:40:44.000000000 +0100
@@ -66,8 +66,8 @@
.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 @@
if ((ret = mt312_write(i2c, SYM_RATE_H, buf, sizeof(buf))) < 0)
return ret;

+ mt312_reset(i2c, 0);
+
return 0;
}

@@ -756,8 +758,14 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/nxt6000.c linux-2.6.5-patched/drivers/media/dvb/frontends/nxt6000.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/nxt6000.c 2004-03-12 20:31:28.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/nxt6000.c 2004-03-11 19:40:44.000000000 +0100
@@ -55,7 +52,12 @@
.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 +662,6 @@
nxt6000_setup(fe);
break;

- case FE_RESET:
- break;
-
case FE_SET_FRONTEND:
{
struct nxt6000_config *nxt = FE2NXT(fe);
diff -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/sp887x.c linux-2.6.5-patched/drivers/media/dvb/frontends/sp887x.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/sp887x.c 2004-04-09 17:42:17.000000000 +0200
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/sp887x.c 2004-03-11 19:40:44.000000000 +0100
@@ -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 @@
.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 @@

// 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 @@
// 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 @@
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 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/stv0299.c linux-2.6.5-patched/drivers/media/dvb/frontends/stv0299.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/stv0299.c 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/stv0299.c 2004-03-19 18:13:55.000000000 +0100
@@ -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 @@

/* 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 @@
.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 @@
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 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 */

@@ -353,17 +363,22 @@
u8 addr;
u32 div;
u8 buf[4];
- int i, divisor, regcode;
+ int divisor, regcode;

dprintk ("%s: freq %i, ftype %i\n", __FUNCTION__, freq, ftype);

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,7 +388,12 @@
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;
@@ -378,15 +398,16 @@

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 @@
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_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_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 @@
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 @@
// 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 +920,7 @@
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 +941,18 @@

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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 void uni0299_detach (struct dvb_i2c_bus *i2c, void *data)
{
dprintk ("%s\n", __FUNCTION__);
+ kfree(data);
dvb_unregister_frontend (uni0299_ioctl, i2c);
}

@@ -1305,8 +1391,11 @@
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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/tda1004x.c linux-2.6.5-patched/drivers/media/dvb/frontends/tda1004x.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/tda1004x.c 2004-04-09 17:42:17.000000000 +0200
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/tda1004x.c 2004-04-23 10:55:47.000000000 +0200
@@ -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 @@
};


-#pragma pack(1)
struct tda1004x_state {
u8 tda1004x_address;
u8 tuner_address;
@@ -175,7 +175,7 @@
u8 tuner_type:2;
u8 fe_type:2;
};
-#pragma pack()
+

struct fwinfo {
int file_size;
@@ -397,13 +396,13 @@

// 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);
@@ -434,8 +433,8 @@
}

// 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);
@@ -448,6 +447,7 @@
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);
@@ -458,6 +458,7 @@

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);

@@ -539,6 +540,8 @@

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;
@@ -575,6 +578,8 @@

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;
@@ -1278,12 +1282,27 @@
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);

@@ -1321,7 +1340,12 @@
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;
@@ -1340,6 +1364,15 @@
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;
}
@@ -1355,6 +1388,7 @@
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 };
@@ -1447,13 +1481,20 @@
// 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
@@ -1466,6 +1507,7 @@
{
dprintk("%s\n", __FUNCTION__);

+ kfree(data);
dvb_unregister_frontend(tda1004x_ioctl, i2c);
}

diff -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/ves1820.c linux-2.6.5-patched/drivers/media/dvb/frontends/ves1820.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/ves1820.c 2004-03-12 20:31:29.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/ves1820.c 2004-03-11 19:40:44.000000000 +0100
@@ -111,8 +111,7 @@
#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 -urawBN xx-linux-2.6.5/drivers/media/dvb/frontends/ves1x93.c linux-2.6.5-patched/drivers/media/dvb/frontends/ves1x93.c
--- xx-linux-2.6.5/drivers/media/dvb/frontends/ves1x93.c 2004-02-22 14:48:47.000000000 +0100
+++ linux-2.6.5-patched/drivers/media/dvb/frontends/ves1x93.c 2004-03-14 14:46:20.000000000 +0100
@@ -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 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 @@
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 @@
{
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 @@
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_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_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 @@
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 @@
* 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 @@
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_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 @@
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);
}



2004-04-26 14:02:50

by Michael Hunold

[permalink] [raw]
Subject: [PATCH 9/9] DVB: Follow saa7146 changes in affected V4L drivers

- [V4L] follow changes in saa7146 driver: mxb, dpc7146, hexium_orion, hexium_gemini
diff -ura linux-2.6.5.q/drivers/media/video/dpc7146.c linux-2.6.5.p/drivers/media/video/dpc7146.c
--- linux-2.6.5.q/drivers/media/video/dpc7146.c 2004-04-24 18:01:00.000000000 +0200
+++ linux-2.6.5.p/drivers/media/video/dpc7146.c 2004-04-24 18:02:26.000000000 +0200
@@ -106,7 +106,7 @@
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 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 -ura linux-2.6.5.q/drivers/media/video/hexium_gemini.c linux-2.6.5.p/drivers/media/video/hexium_gemini.c
--- linux-2.6.5.q/drivers/media/video/hexium_gemini.c 2004-04-24 18:00:54.000000000 +0200
+++ linux-2.6.5.p/drivers/media/video/hexium_gemini.c 2004-04-24 18:02:34.000000000 +0200
@@ -159,18 +159,18 @@
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 @@
/* 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 -ura linux-2.6.5.q/drivers/media/video/hexium_orion.c linux-2.6.5.p/drivers/media/video/hexium_orion.c
--- linux-2.6.5.q/drivers/media/video/hexium_orion.c 2004-04-24 18:00:51.000000000 +0200
+++ linux-2.6.5.p/drivers/media/video/hexium_orion.c 2004-04-24 18:03:09.000000000 +0200
@@ -192,18 +192,18 @@
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 @@
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 -ura linux-2.6.5.q/drivers/media/video/mxb.c linux-2.6.5.p/drivers/media/video/mxb.c
--- linux-2.6.5.q/drivers/media/video/mxb.c 2004-04-24 17:57:34.000000000 +0200
+++ linux-2.6.5.p/drivers/media/video/mxb.c 2004-04-24 17:58:44.000000000 +0200
@@ -183,7 +183,7 @@
}
memset(mxb, 0x0, sizeof(struct mxb));

- 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);
@@ -942,23 +942,23 @@
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,
}
};


2004-04-26 14:08:47

by Måns Rullgård

[permalink] [raw]
Subject: Re: [PATCH 2/9] DVB: Documentation and Kconfig updazes

Michael Hunold <[email protected]> writes:

> diff -urawBN xx-linux-2.6.5/drivers/media/dvb/Kconfig linux-2.6.5-patched/drivers/media/dvb/Kconfig
> --- xx-linux-2.6.5/drivers/media/dvb/Kconfig 2004-03-12 20:31:28.000000000 +0100
> +++ linux-2.6.5-patched/drivers/media/dvb/Kconfig 2003-10-13 06:08:45.000000000 +0200
> @@ -18,12 +18,11 @@
> 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

This looks wrong for a 2.6 kernel.

--
M?ns Rullg?rd
[email protected]