V4L/DVB (3281): av7110 driver: improved recovery from ARM crash and crash detection


- Improved recovery from ARM crash and the way a crash is detected.
Minor white space clean-up, debug output fixed.

Signed-off-by: Oliver Endriss <o.endriss@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index eabd4fc..39b4bfa 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -196,19 +196,17 @@
 
 	av7110_bootarm(av7110);
 	msleep(100);
+
+	init_av7110_av(av7110);
+
+	/* card-specific recovery */
+	if (av7110->recover)
+		av7110->recover(av7110);
+
 	restart_feeds(av7110);
 	av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config);
 }
 
-static void arm_error(struct av7110 *av7110)
-{
-	dprintk(4, "%p\n",av7110);
-
-	av7110->arm_errors++;
-	av7110->arm_ready = 0;
-	recover_arm(av7110);
-}
-
 static void av7110_arm_sync(struct av7110 *av7110)
 {
 	av7110->arm_rmmod = 1;
@@ -246,26 +244,22 @@
 
 		if (down_interruptible(&av7110->dcomlock))
 			break;
-
 		newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2);
 		up(&av7110->dcomlock);
 
-		if (newloops == av7110->arm_loops) {
+		if (newloops == av7110->arm_loops || av7110->arm_errors > 3) {
 			printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n",
 			       av7110->dvb_adapter.num);
 
-			arm_error(av7110);
-			av7710_set_video_mode(av7110, vidmode);
-
-			init_av7110_av(av7110);
+			recover_arm(av7110);
 
 			if (down_interruptible(&av7110->dcomlock))
 				break;
-
 			newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1;
 			up(&av7110->dcomlock);
 		}
 		av7110->arm_loops = newloops;
+		av7110->arm_errors = 0;
 	}
 
 	av7110->arm_thread = NULL;
@@ -516,10 +510,6 @@
 		iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
 
 		av7110->video_size.h = h_ar & 0xfff;
-		dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n",
-			av7110->video_size.w,
-			av7110->video_size.h,
-			av7110->video_size.aspect_ratio);
 
 		event.type = VIDEO_EVENT_SIZE_CHANGED;
 		event.u.size.w = av7110->video_size.w;
@@ -541,6 +531,11 @@
 			event.u.size.aspect_ratio = VIDEO_FORMAT_4_3;
 			av7110->videostate.video_format = VIDEO_FORMAT_4_3;
 		}
+
+		dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n",
+			av7110->video_size.w, av7110->video_size.h,
+			av7110->video_size.aspect_ratio);
+
 		dvb_video_add_event(av7110, &event);
 		break;
 	}
@@ -1054,7 +1049,7 @@
 	struct dvb_demux *dvbdmx = &av7110->demux;
 	struct dvb_demux_feed *feed;
 	int mode;
-	int i;
+	int i, j;
 
 	dprintk(4, "%p\n", av7110);
 
@@ -1062,10 +1057,21 @@
 	av7110->playing = 0;
 	av7110->rec_mode = 0;
 
-	for (i = 0; i < dvbdmx->filternum; i++) {
+	for (i = 0; i < dvbdmx->feednum; i++) {
 		feed = &dvbdmx->feed[i];
-		if (feed->state == DMX_STATE_GO)
+		if (feed->state == DMX_STATE_GO) {
+			if (feed->type == DMX_TYPE_SEC) {
+				for (j = 0; j < dvbdmx->filternum; j++) {
+					if (dvbdmx->filter[j].type != DMX_TYPE_SEC)
+						continue;
+					if (dvbdmx->filter[j].filter.parent != &feed->feed.sec)
+						continue;
+					if (dvbdmx->filter[j].state == DMX_STATE_GO)
+						dvbdmx->filter[j].state = DMX_STATE_READY;
+				}
+			}
 			av7110_start_feed(feed);
+		}
 	}
 
 	if (mode)
@@ -2121,8 +2127,10 @@
 	struct av7110* av7110 = fe->dvb->priv;
 
 	int ret = av7110_fe_lock_fix(av7110, 0);
-	if (!ret)
+	if (!ret) {
+		av7110->saved_fe_params = *params;
 		ret = av7110->fe_set_frontend(fe, params);
+	}
 	return ret;
 }
 
@@ -2164,8 +2172,10 @@
 	struct av7110* av7110 = fe->dvb->priv;
 
 	int ret = av7110_fe_lock_fix(av7110, 0);
-	if (!ret)
+	if (!ret) {
+		av7110->saved_master_cmd = *cmd;
 		ret = av7110->fe_diseqc_send_master_cmd(fe, cmd);
+	}
 	return ret;
 }
 
@@ -2174,8 +2184,10 @@
 	struct av7110* av7110 = fe->dvb->priv;
 
 	int ret = av7110_fe_lock_fix(av7110, 0);
-	if (!ret)
+	if (!ret) {
+		av7110->saved_minicmd = minicmd;
 		ret = av7110->fe_diseqc_send_burst(fe, minicmd);
+	}
 	return ret;
 }
 
@@ -2184,8 +2196,10 @@
 	struct av7110* av7110 = fe->dvb->priv;
 
 	int ret = av7110_fe_lock_fix(av7110, 0);
-	if (!ret)
+	if (!ret) {
+		av7110->saved_tone = tone;
 		ret = av7110->fe_set_tone(fe, tone);
+	}
 	return ret;
 }
 
@@ -2194,8 +2208,10 @@
 	struct av7110* av7110 = fe->dvb->priv;
 
 	int ret = av7110_fe_lock_fix(av7110, 0);
-	if (!ret)
+	if (!ret) {
+		av7110->saved_voltage = voltage;
 		ret = av7110->fe_set_voltage(fe, voltage);
+	}
 	return ret;
 }
 
@@ -2209,6 +2225,23 @@
 	return ret;
 }
 
+static void dvb_s_recover(struct av7110* av7110)
+{
+	av7110_fe_init(av7110->fe);
+
+	av7110_fe_set_voltage(av7110->fe, av7110->saved_voltage);
+	if (av7110->saved_master_cmd.msg_len) {
+		msleep(20);
+		av7110_fe_diseqc_send_master_cmd(av7110->fe, &av7110->saved_master_cmd);
+	}
+	msleep(20);
+	av7110_fe_diseqc_send_burst(av7110->fe, av7110->saved_minicmd);
+	msleep(20);
+	av7110_fe_set_tone(av7110->fe, av7110->saved_tone);
+
+	av7110_fe_set_frontend(av7110->fe, &av7110->saved_fe_params);
+}
+
 static u8 read_pwm(struct av7110* av7110)
 {
 	u8 b = 0xff;
@@ -2246,6 +2279,7 @@
 				av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
 				av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst;
 				av7110->fe->ops->set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
 				break;
 			}
 
@@ -2255,6 +2289,7 @@
 				av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
 				av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst;
 				av7110->fe->ops->set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
 				break;
 			}
 
@@ -2264,6 +2299,7 @@
 				av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
 				av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst;
 				av7110->fe->ops->set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
 				break;
 			}
 
@@ -2300,6 +2336,7 @@
 				av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
 				av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst;
 				av7110->fe->ops->set_tone = av7110_set_tone;
+				av7110->recover = dvb_s_recover;
 			}
 			break;
 
@@ -2328,6 +2365,7 @@
 			if (av7110->fe) {
 				av7110->fe->ops->set_voltage = lnbp21_set_voltage;
 				av7110->fe->ops->dishnetwork_send_legacy_command = NULL;
+				av7110->recover = dvb_s_recover;
 			}
 			break;
 		}
diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h
index d5550f4..a817664 100644
--- a/drivers/media/dvb/ttpci/av7110.h
+++ b/drivers/media/dvb/ttpci/av7110.h
@@ -246,6 +246,15 @@
 
 	struct dvb_frontend* fe;
 	fe_status_t fe_status;
+
+	/* crash recovery */
+	void				(*recover)(struct av7110* av7110);
+	struct dvb_frontend_parameters	saved_fe_params;
+	fe_sec_voltage_t		saved_voltage;
+	fe_sec_tone_mode_t		saved_tone;
+	struct dvb_diseqc_master_cmd	saved_master_cmd;
+	fe_sec_mini_cmd_t		saved_minicmd;
+
 	int (*fe_init)(struct dvb_frontend* fe);
 	int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status);
 	int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe);
diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c
index 54279aa..cb37745 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.c
+++ b/drivers/media/dvb/ttpci/av7110_hw.c
@@ -230,6 +230,8 @@
 
 	dprintk(4, "%p\n", av7110);
 
+	av7110->arm_ready = 0;
+
 	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
 
 	/* Disable DEBI and GPIO irq */
@@ -361,6 +363,7 @@
 			break;
 		if (err) {
 			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __FUNCTION__);
+			av7110->arm_errors++;
 			return -ETIMEDOUT;
 		}
 		msleep(1);