V4L/DVB (3281): av7110 driver: improved recovery from ARM crash and crash detection
authorOliver Endriss <o.endriss@gmx.de>
Mon, 9 Jan 2006 17:32:42 +0000 (15:32 -0200)
committerMauro Carvalho Chehab <mchehab@brturbo.com.br>
Mon, 9 Jan 2006 17:32:42 +0000 (15:32 -0200)
- 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>
drivers/media/dvb/ttpci/av7110.c
drivers/media/dvb/ttpci/av7110.h
drivers/media/dvb/ttpci/av7110_hw.c

index eabd4fcaf246440b5870bfc9470ca96510b39434..39b4bfa0ab7b2e7e3559f3e5fea06b7e4430800d 100644 (file)
@@ -196,17 +196,15 @@ static void recover_arm(struct av7110 *av7110)
 
        av7110_bootarm(av7110);
        msleep(100);
-       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);
+       init_av7110_av(av7110);
+
+       /* card-specific recovery */
+       if (av7110->recover)
+               av7110->recover(av7110);
 
-       av7110->arm_errors++;
-       av7110->arm_ready = 0;
-       recover_arm(av7110);
+       restart_feeds(av7110);
+       av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config);
 }
 
 static void av7110_arm_sync(struct av7110 *av7110)
@@ -246,26 +244,22 @@ static int arm_thread(void *data)
 
                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 @@ static void gpioirq(unsigned long data)
                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 @@ static void gpioirq(unsigned long data)
                        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 @@ static void restart_feeds(struct av7110 *av7110)
        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 @@ static void restart_feeds(struct av7110 *av7110)
        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 @@ static int av7110_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_p
        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 @@ static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe,
        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 @@ static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_
        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 @@ static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
        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 @@ static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t volta
        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 @@ static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, un
        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 @@ static int frontend_init(struct av7110 *av7110)
                                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 @@ static int frontend_init(struct av7110 *av7110)
                                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 @@ static int frontend_init(struct av7110 *av7110)
                                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 @@ static int frontend_init(struct av7110 *av7110)
                                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 @@ static int frontend_init(struct av7110 *av7110)
                        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;
                }
index d5550f46206206faf8746aa6421ff81cd03abb1d..a817664f13e040515653e85dc616032c3d34af3e 100644 (file)
@@ -246,6 +246,15 @@ struct av7110 {
 
        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);
index 54279aaa4828c6dac82786af75535656a4ea47e6..cb377452b57dcfef26ffa9cd070588f060fe55f2 100644 (file)
@@ -230,6 +230,8 @@ int av7110_bootarm(struct av7110 *av7110)
 
        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 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
                        break;
                if (err) {
                        printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __FUNCTION__);
+                       av7110->arm_errors++;
                        return -ETIMEDOUT;
                }
                msleep(1);