V4L/DVB: ivtv: Add firmare monitoring and debug mode to ignore firmware problems
authorIan Armstrong <ian@iarmst.demon.co.uk>
Sat, 12 Jun 2010 16:33:21 +0000 (13:33 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 2 Aug 2010 17:47:55 +0000 (14:47 -0300)
>From Ian's e-mail:
When a device is opened the firmware state will be checked. If it isn't
responding then the open will fail with -EIO.  Due to the nature of the
hardware, a single failed check will block everything since we don't know
exactly what has failed. A side effect of this is the blocking of debug
access, so an additional debug level has been created which allows the block
to be bypassed.

Andy Walls' modifications:
I modified Ian's patch to add a separate fw_debug module parameter to change
the driver's behavior, as opposed to using the normal debug module parameter.
The fw_debug module parameter is only available when CONFIG_VIDEO_ADV_DEBUG
is set.
I also made some minor whitespace adjustments and changed some warning
messages to be a bit more specific.  s/happy/glad/g

Signed-off-by: Ian Armstrong <ian@iarmst.demon.co.uk>
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/ivtv/ivtv-driver.c
drivers/media/video/ivtv/ivtv-driver.h
drivers/media/video/ivtv/ivtv-fileops.c
drivers/media/video/ivtv/ivtv-firmware.c
drivers/media/video/ivtv/ivtv-firmware.h
drivers/media/video/ivtv/ivtv-streams.c

index 1b79475ca134db7797c3ea95075a9fdfec61f983..3737797f20ea1bc972b0f4796668ec4ae57e3ff4 100644 (file)
@@ -130,6 +130,9 @@ static int ivtv_yuv_threshold = -1;
 static int ivtv_pci_latency = 1;
 
 int ivtv_debug;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int ivtv_fw_debug;
+#endif
 
 static int tunertype = -1;
 static int newi2c = -1;
@@ -141,6 +144,9 @@ module_param_string(pal, pal, sizeof(pal), 0644);
 module_param_string(secam, secam, sizeof(secam), 0644);
 module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
 module_param_named(debug,ivtv_debug, int, 0644);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+module_param_named(fw_debug, ivtv_fw_debug, int, 0644);
+#endif
 module_param(ivtv_pci_latency, int, 0644);
 module_param(ivtv_yuv_mode, int, 0644);
 module_param(ivtv_yuv_threshold, int, 0644);
@@ -217,6 +223,10 @@ MODULE_PARM_DESC(debug,
                 "\t\t\t 256/0x0100: yuv\n"
                 "\t\t\t 512/0x0200: i2c\n"
                 "\t\t\t1024/0x0400: high volume\n");
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+MODULE_PARM_DESC(fw_debug,
+                "Enable code for debugging firmware problems.  Default: 0\n");
+#endif
 MODULE_PARM_DESC(ivtv_pci_latency,
                 "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
                 "\t\t\tDefault: Yes");
@@ -1425,6 +1435,9 @@ EXPORT_SYMBOL(ivtv_vapi);
 EXPORT_SYMBOL(ivtv_vapi_result);
 EXPORT_SYMBOL(ivtv_clear_irq_mask);
 EXPORT_SYMBOL(ivtv_debug);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+EXPORT_SYMBOL(ivtv_fw_debug);
+#endif
 EXPORT_SYMBOL(ivtv_reset_ir_gpio);
 EXPORT_SYMBOL(ivtv_udma_setup);
 EXPORT_SYMBOL(ivtv_udma_unmap);
index 5b45fd2b26458f8d5ad77e788e47a5d6bc48d851..c038dc8beb3c2de9749265836e6f4fdad1531085 100644 (file)
 
 /* debugging */
 extern int ivtv_debug;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+extern int ivtv_fw_debug;
+#endif
 
 #define IVTV_DBGFLG_WARN    (1 << 0)
 #define IVTV_DBGFLG_INFO    (1 << 1)
index 3c2cc270ccd548bd455b69073f3f584f6944650a..3fb21e1406a1cf390f5ce81e64604c1cc3d57f31 100644 (file)
@@ -32,6 +32,7 @@
 #include "ivtv-yuv.h"
 #include "ivtv-ioctl.h"
 #include "ivtv-cards.h"
+#include "ivtv-firmware.h"
 #include <media/v4l2-event.h>
 #include <media/saa7115.h>
 
@@ -526,6 +527,7 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
 {
        struct ivtv *itv = id->itv;
        struct ivtv_stream *s = &itv->streams[id->type];
+       int rc;
 
        if (atomic_read(&itv->decoding) == 0) {
                if (ivtv_claim_stream(id, s->type)) {
@@ -533,7 +535,9 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
                        IVTV_DEBUG_WARN("start decode, stream already claimed\n");
                        return -EBUSY;
                }
-               ivtv_start_v4l2_decode_stream(s, 0);
+               rc = ivtv_start_v4l2_decode_stream(s, 0);
+               if (rc < 0)
+                       return rc;
        }
        if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
                return ivtv_set_speed(itv, speed);
@@ -912,12 +916,30 @@ int ivtv_v4l2_close(struct file *filp)
 
 static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
 {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       struct video_device *vdev = video_devdata(filp);
+#endif
        struct ivtv *itv = s->itv;
        struct ivtv_open_id *item;
        int res = 0;
 
        IVTV_DEBUG_FILE("open %s\n", s->name);
 
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       if (ivtv_fw_debug) {
+               IVTV_WARN("Opening %s with dead firmware lockout disabled\n",
+                         video_device_node_name(vdev));
+               IVTV_WARN("Selected firmware errors will be ignored\n");
+       }
+
+       /* Unless ivtv_fw_debug is set, error out if firmware dead. */
+       if (ivtv_firmware_check(itv, "ivtv_serialized_open") && !ivtv_fw_debug)
+               return -EIO;
+#else
+       if (ivtv_firmware_check(itv, "ivtv_serialized_open"))
+               return -EIO;
+#endif
+
        if (s->type == IVTV_DEC_STREAM_TYPE_MPG &&
                test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
                return -EBUSY;
index a71e8ba306b0c97626865243bb09c316bea0a496..efb288d34ca31dff351f69d18d40115f9633cea6 100644 (file)
@@ -271,3 +271,49 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)
        }
        ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
 }
+
+/* Check firmware running state. The checks fall through
+   allowing multiple failures to be logged. */
+int ivtv_firmware_check(struct ivtv *itv, char *where)
+{
+       int res = 0;
+
+       /* Check encoder is still running */
+       if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) {
+               IVTV_WARN("Encoder has died : %s\n", where);
+               res = -1;
+       }
+
+       /* Also check audio. Only check if not in use & encoder is okay */
+       if (!res && !atomic_read(&itv->capturing) &&
+           (!atomic_read(&itv->decoding) ||
+            (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV,
+                                                            &itv->i_flags)))) {
+
+               if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) {
+                       IVTV_WARN("Audio has died (Encoder OK) : %s\n", where);
+                       res = -2;
+               }
+       }
+
+       if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+               /* Second audio check. Skip if audio already failed */
+               if (res != -2 && read_dec(0x100) != read_dec(0x104)) {
+                       /* Wait & try again to be certain. */
+                       ivtv_msleep_timeout(14, 0);
+                       if (read_dec(0x100) != read_dec(0x104)) {
+                               IVTV_WARN("Audio has died (Decoder) : %s\n",
+                                         where);
+                               res = -1;
+                       }
+               }
+
+               /* Check decoder is still running */
+               if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) {
+                       IVTV_WARN("Decoder has died : %s\n", where);
+                       res = -1;
+               }
+       }
+
+       return res;
+}
index 041ba94e65bc46aad55d007f6632ea34444ae85e..52bb4e5598fd398e2af2dc6397e1d7e68720147b 100644 (file)
@@ -26,5 +26,6 @@ int ivtv_firmware_init(struct ivtv *itv);
 void ivtv_firmware_versions(struct ivtv *itv);
 void ivtv_halt_firmware(struct ivtv *itv);
 void ivtv_init_mpeg_decoder(struct ivtv *itv);
+int ivtv_firmware_check(struct ivtv *itv, char *where);
 
 #endif
index a937e2ff9b6ebcad8a8d5e3e5025c140d34ccef3..f0dd011a2cccda6fb35b86d930dc81a1b5e4190d 100644 (file)
@@ -42,6 +42,7 @@
 #include "ivtv-yuv.h"
 #include "ivtv-cards.h"
 #include "ivtv-streams.h"
+#include "ivtv-firmware.h"
 #include <media/v4l2-event.h>
 
 static const struct v4l2_file_operations ivtv_v4l2_enc_fops = {
@@ -674,12 +675,17 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
        /* Decoder sometimes dies here, so wait a moment */
        ivtv_msleep_timeout(10, 0);
 
+       /* Known failure point for firmware, so check */
+       if (ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream") < 0)
+               return -EIO;
+
        return 0;
 }
 
 int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
 {
        struct ivtv *itv = s->itv;
+       int rc;
 
        if (s->vdev == NULL)
                return -EINVAL;
@@ -689,7 +695,11 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
 
        IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
 
-       ivtv_setup_v4l2_decode_stream(s);
+       rc = ivtv_setup_v4l2_decode_stream(s);
+       if (rc < 0) {
+               clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+               return rc;
+       }
 
        /* set dma size to 65536 bytes */
        ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);