All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michael Hunold <hunold@linuxtv.org>
To: hunold@linuxtv.org, torvalds@osdl.org, akpm@osdl.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH 5/9] DVB: Other DVB core updates
Date: Mon, 26 Apr 2004 09:42:11 -0400	[thread overview]
Message-ID: <10829868582688@convergence.de> (raw)
In-Reply-To: <1082986832779@convergence.de>

- [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 <hlinder@cosy.sbg.ac.at> and Wolfram Stering <wstering@cosy.sbg.ac.at>
- [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 <ralph@convergence.de>
  * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
  *
+ * ULE Decapsulation code:
+ * Copyright (C) 2003 gcs - Global Communication & Services GmbH.
+ *                and Institute for Computer Sciences
+ *                    Salzburg University.
+ *                    Hilmar Linder <hlinder@cosy.sbg.ac.at>
+ *                and Wolfram Stering <wstering@cosy.sbg.ac.at>
+ *
+ * ULE Decaps according to draft-fair-ipdvb-ule-01.txt.
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
  * 
- *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  * 
- *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- * 
  */
 
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/dvb/net.h>
+#include <linux/uio.h>
 #include <asm/uaccess.h>
+#include <linux/crc32.h>
 
 #include "dvb_demux.h"
 #include "dvb_net.h"
 #include "dvb_functions.h"
 
 
+static inline __u32 iov_crc32( __u32 c, struct iovec *iov, unsigned int cnt )
+{
+	unsigned int j;
+	for (j = 0; j < cnt; j++)
+		c = crc32_be( c, iov[j].iov_base, iov[j].iov_len );
+	return c;
+}
+
+
 #if 1
 #define dprintk(x...) printk(x)
 #else
@@ -41,14 +61,46 @@
 
 #define DVB_NET_MULTICAST_MAX 10
 
+#define isprint(c)	((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
+
+static void hexdump( const unsigned char *buf, unsigned short len )
+{
+	char str[80], octet[10];
+	int ofs, i, l;
+
+	for (ofs = 0; ofs < len; ofs += 16) {
+		sprintf( str, "%03d: ", ofs );
+
+		for (i = 0; i < 16; i++) {
+			if ((i + ofs) < len)
+				sprintf( octet, "%02x ", buf[ofs + i] );
+			else
+				strcpy( octet, "   " );
+
+			strcat( str, octet );
+		}
+		strcat( str, "  " );
+		l = strlen( str );
+
+		for (i = 0; (i < 16) && ((i + ofs) < len); i++)
+			str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.';
+
+		str[l] = '\0';
+		printk( KERN_WARNING "%s\n", str );
+	}
+}
+
+
 struct dvb_net_priv {
 	int in_use;
         struct net_device_stats stats;
         char name[6];
 	u16 pid;
+	struct dvb_net *host;
         struct dmx_demux *demux;
 	struct dmx_section_feed *secfeed;
 	struct dmx_section_filter *secfilter;
+	struct dmx_ts_feed *tsfeed;
 	int multi_num;
 	struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX];
 	unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6];
@@ -59,6 +111,18 @@
 #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_*/



  reply	other threads:[~2004-04-26 13:57 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-04-26 13:40 [PATCH 0/9] LinuxTV.org DVB update Michael Hunold
2004-04-26 13:40 ` [PATCH 1/9] V4L: Update the saa7146 driver Michael Hunold
2004-04-26 13:41   ` [PATCH 2/9] DVB: Documentation and Kconfig updazes Michael Hunold
2004-04-26 13:41     ` [PATCH 3/9] DVB: Update DVB budget drivers Michael Hunold
2004-04-26 13:42       ` [PATCH 4/9] DVB: Add EN50221 cam support to dvb-core Michael Hunold
2004-04-26 13:42         ` Michael Hunold [this message]
2004-04-26 13:42           ` [PATCH 6/9] DVB: AV7110 DVB driver updates Michael Hunold
2004-04-26 13:42             ` [PATCH 7/9] DVB: Misc. DVB frontend " Michael Hunold
2004-04-26 13:42               ` [PATCH 8/9] DVB: Misc. DVB USB " Michael Hunold
2004-04-26 13:42                 ` [PATCH 9/9] DVB: Follow saa7146 changes in affected V4L drivers Michael Hunold
2004-04-26 14:05     ` [PATCH 2/9] DVB: Documentation and Kconfig updazes Måns Rullgård

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=10829868582688@convergence.de \
    --to=hunold@linuxtv.org \
    --cc=akpm@osdl.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@osdl.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.