]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
V4L/DVB (6691): pvrusb2: Rework pipeline state control
authorMike Isely <isely@pobox.com>
Mon, 26 Nov 2007 04:48:52 +0000 (01:48 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 25 Jan 2008 21:03:01 +0000 (19:03 -0200)
This is a new implementation for video pipeline control within the
pvrusb2 driver.  Actual start/stop of the pipeline is moved to the
driver's kernel thread.  Pipeline stages are controlled autonomously
based on surrounding pipeline or application control state.  Kernel
thread management is also cleaned up and moved into the internal
control structure of the driver, solving a set up / tear down race
along the way.  Better failure recovery is implemented with this new
control strategy.  Also with this change comes better control of the
cx23416 encoder, building on additional information learned about the
peculiarities of controlling this part (this information was the
original trigger for this rework).  With this change, overall encoder
stability should be considerably improved.  Yes, this is a large
change for this driver, but due to the nature of the feature being
worked on, the changes are fairly pervasive and would be difficult to
break into smaller pieces with any semblence of step-wise stability.

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
13 files changed:
drivers/media/video/pvrusb2/pvrusb2-context.c
drivers/media/video/pvrusb2/pvrusb2-context.h
drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
drivers/media/video/pvrusb2/pvrusb2-debug.h
drivers/media/video/pvrusb2/pvrusb2-debugifc.c
drivers/media/video/pvrusb2/pvrusb2-encoder.c
drivers/media/video/pvrusb2/pvrusb2-encoder.h
drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
drivers/media/video/pvrusb2/pvrusb2-hdw.c
drivers/media/video/pvrusb2/pvrusb2-hdw.h
drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
drivers/media/video/pvrusb2/pvrusb2-v4l2.c
drivers/media/video/pvrusb2/pvrusb2-video-v4l.c

index 22719ba861aceb2d0e0c3f2b70b467b39ef26de9..9d94aed2e12dcacb2f66cbf339db899fbd1ba18d 100644 (file)
 
 static void pvr2_context_destroy(struct pvr2_context *mp)
 {
-       if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
        pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp);
-       if (mp->workqueue) {
-               flush_workqueue(mp->workqueue);
-               destroy_workqueue(mp->workqueue);
-       }
+       if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
        kfree(mp);
 }
 
 
-static void pvr2_context_trigger_poll(struct pvr2_context *mp)
-{
-       queue_work(mp->workqueue,&mp->workpoll);
-}
-
-
-static void pvr2_context_poll(struct work_struct *work)
-{
-       struct pvr2_context *mp =
-               container_of(work, struct pvr2_context, workpoll);
-       pvr2_context_enter(mp); do {
-               pvr2_hdw_poll(mp->hdw);
-       } while (0); pvr2_context_exit(mp);
-}
-
-
-static void pvr2_context_setup(struct work_struct *work)
+static void pvr2_context_state_check(struct pvr2_context *mp)
 {
-       struct pvr2_context *mp =
-               container_of(work, struct pvr2_context, workinit);
+       if (mp->init_flag) return;
+
+       switch (pvr2_hdw_get_state(mp->hdw)) {
+       case PVR2_STATE_WARM: break;
+       case PVR2_STATE_ERROR: break;
+       case PVR2_STATE_READY: break;
+       case PVR2_STATE_RUN: break;
+       default: return;
+       }
 
        pvr2_context_enter(mp); do {
-               if (!pvr2_hdw_dev_ok(mp->hdw)) break;
-               pvr2_hdw_setup(mp->hdw);
-               pvr2_hdw_setup_poll_trigger(
-                       mp->hdw,
-                       (void (*)(void *))pvr2_context_trigger_poll,
-                       mp);
-               if (!pvr2_hdw_dev_ok(mp->hdw)) break;
-               if (!pvr2_hdw_init_ok(mp->hdw)) break;
+               mp->init_flag = !0;
                mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw);
                if (mp->setup_func) {
                        mp->setup_func(mp);
                }
        } while (0); pvr2_context_exit(mp);
-}
+ }
 
 
 struct pvr2_context *pvr2_context_create(
@@ -96,11 +76,10 @@ struct pvr2_context *pvr2_context_create(
                mp = NULL;
                goto done;
        }
-
-       mp->workqueue = create_singlethread_workqueue("pvrusb2");
-       INIT_WORK(&mp->workinit, pvr2_context_setup);
-       INIT_WORK(&mp->workpoll, pvr2_context_poll);
-       queue_work(mp->workqueue,&mp->workinit);
+       pvr2_hdw_set_state_callback(mp->hdw,
+                                   (void (*)(void *))pvr2_context_state_check,
+                                   mp);
+       pvr2_context_state_check(mp);
  done:
        return mp;
 }
index 6327fa1f7e4f24864a6377aa2090f4ab96a19119..a04187a932251dd86b11020916f55c31c3da51c5 100644 (file)
@@ -45,14 +45,11 @@ struct pvr2_context {
        struct pvr2_context_stream video_stream;
        struct mutex mutex;
        int disconnect_flag;
+       int init_flag;
 
        /* Called after pvr2_context initialization is complete */
        void (*setup_func)(struct pvr2_context *);
 
-       /* Work queue overhead for out-of-line processing */
-       struct workqueue_struct *workqueue;
-       struct work_struct workinit;
-       struct work_struct workpoll;
 };
 
 struct pvr2_channel {
index e8a9252c7df6a2250cb301f7c8f792fd50527c5f..2cca817e41445f182cf261c5a3fb0d45ff6cc20f 100644 (file)
@@ -140,7 +140,7 @@ static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = {
 static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt)
 {
        ctxt->client->handler = NULL;
-       ctxt->hdw->decoder_ctrl = NULL;
+       pvr2_hdw_set_decoder(ctxt->hdw,NULL);
        kfree(ctxt);
 }
 
@@ -241,7 +241,7 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw,
        ctxt->client = cp;
        ctxt->hdw = hdw;
        ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
-       hdw->decoder_ctrl = &ctxt->ctrl;
+       pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
        cp->handler = &ctxt->handler;
        {
                /*
index da6441b88f31af8ebae32a62533f9d6330f2823c..fca49d8a9311e70544e89b5cd713fc677d91cf05 100644 (file)
@@ -34,25 +34,26 @@ extern int pvrusb2_debug;
 #define PVR2_TRACE_INIT       (1 <<  5) /* misc initialization steps */
 #define PVR2_TRACE_START_STOP (1 <<  6) /* Streaming start / stop */
 #define PVR2_TRACE_CTL        (1 <<  7) /* commit of control changes */
-#define PVR2_TRACE_DEBUG      (1 <<  8) /* Temporary debug code */
-#define PVR2_TRACE_EEPROM     (1 <<  9) /* eeprom parsing / report */
-#define PVR2_TRACE_STRUCT     (1 << 10) /* internal struct creation */
-#define PVR2_TRACE_OPEN_CLOSE (1 << 11) /* application open / close */
-#define PVR2_TRACE_CREG       (1 << 12) /* Main critical region entry / exit */
-#define PVR2_TRACE_SYSFS      (1 << 13) /* Sysfs driven I/O */
-#define PVR2_TRACE_FIRMWARE   (1 << 14) /* firmware upload actions */
-#define PVR2_TRACE_CHIPS      (1 << 15) /* chip broadcast operation */
-#define PVR2_TRACE_I2C        (1 << 16) /* I2C related stuff */
-#define PVR2_TRACE_I2C_CMD    (1 << 17) /* Software commands to I2C modules */
-#define PVR2_TRACE_I2C_CORE   (1 << 18) /* I2C core debugging */
-#define PVR2_TRACE_I2C_TRAF   (1 << 19) /* I2C traffic through the adapter */
-#define PVR2_TRACE_V4LIOCTL   (1 << 20) /* v4l ioctl details */
-#define PVR2_TRACE_ENCODER    (1 << 21) /* mpeg2 encoder operation */
-#define PVR2_TRACE_BUF_POOL   (1 << 22) /* Track buffer pool management */
-#define PVR2_TRACE_BUF_FLOW   (1 << 23) /* Track buffer flow in system */
-#define PVR2_TRACE_DATA_FLOW  (1 << 24) /* Track data flow */
-#define PVR2_TRACE_DEBUGIFC   (1 << 25) /* Debug interface actions */
-#define PVR2_TRACE_GPIO       (1 << 26) /* GPIO state bit changes */
+#define PVR2_TRACE_STATE      (1 <<  8) /* Device state changes */
+#define PVR2_TRACE_STBITS     (1 <<  9) /* Individual bit state changes */
+#define PVR2_TRACE_EEPROM     (1 << 10) /* eeprom parsing / report */
+#define PVR2_TRACE_STRUCT     (1 << 11) /* internal struct creation */
+#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */
+#define PVR2_TRACE_CREG       (1 << 13) /* Main critical region entry / exit */
+#define PVR2_TRACE_SYSFS      (1 << 14) /* Sysfs driven I/O */
+#define PVR2_TRACE_FIRMWARE   (1 << 15) /* firmware upload actions */
+#define PVR2_TRACE_CHIPS      (1 << 16) /* chip broadcast operation */
+#define PVR2_TRACE_I2C        (1 << 17) /* I2C related stuff */
+#define PVR2_TRACE_I2C_CMD    (1 << 18) /* Software commands to I2C modules */
+#define PVR2_TRACE_I2C_CORE   (1 << 19) /* I2C core debugging */
+#define PVR2_TRACE_I2C_TRAF   (1 << 20) /* I2C traffic through the adapter */
+#define PVR2_TRACE_V4LIOCTL   (1 << 21) /* v4l ioctl details */
+#define PVR2_TRACE_ENCODER    (1 << 22) /* mpeg2 encoder operation */
+#define PVR2_TRACE_BUF_POOL   (1 << 23) /* Track buffer pool management */
+#define PVR2_TRACE_BUF_FLOW   (1 << 24) /* Track buffer flow in system */
+#define PVR2_TRACE_DATA_FLOW  (1 << 25) /* Track data flow */
+#define PVR2_TRACE_DEBUGIFC   (1 << 26) /* Debug interface actions */
+#define PVR2_TRACE_GPIO       (1 << 27) /* GPIO state bit changes */
 
 
 #endif /* __PVRUSB2_HDW_INTERNAL_H */
index 6f135f4a2497d89d55d760d6f6f812cc87a5253f..b0687430fdd421fc8798ecb4bde590512b42f2d5 100644 (file)
@@ -31,14 +31,6 @@ struct debugifc_mask_item {
        unsigned long msk;
 };
 
-static struct debugifc_mask_item mask_items[] = {
-       {"ENC_FIRMWARE",(1<<PVR2_SUBSYS_B_ENC_FIRMWARE)},
-       {"ENC_CFG",(1<<PVR2_SUBSYS_B_ENC_CFG)},
-       {"DIG_RUN",(1<<PVR2_SUBSYS_B_DIGITIZER_RUN)},
-       {"USB_RUN",(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)},
-       {"ENC_RUN",(1<<PVR2_SUBSYS_B_ENC_RUN)},
-};
-
 
 static unsigned int debugifc_count_whitespace(const char *buf,
                                              unsigned int count)
@@ -148,134 +140,14 @@ static int debugifc_match_keyword(const char *buf,unsigned int count,
 }
 
 
-static unsigned long debugifc_find_mask(const char *buf,unsigned int count)
-{
-       struct debugifc_mask_item *mip;
-       unsigned int idx;
-       for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) {
-               mip = mask_items + idx;
-               if (debugifc_match_keyword(buf,count,mip->name)) {
-                       return mip->msk;
-               }
-       }
-       return 0;
-}
-
-
-static int debugifc_print_mask(char *buf,unsigned int sz,
-                              unsigned long msk,unsigned long val)
-{
-       struct debugifc_mask_item *mip;
-       unsigned int idx;
-       int bcnt = 0;
-       int ccnt;
-       for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) {
-               mip = mask_items + idx;
-               if (!(mip->msk & msk)) continue;
-               ccnt = scnprintf(buf,sz,"%s%c%s",
-                                (bcnt ? " " : ""),
-                                ((mip->msk & val) ? '+' : '-'),
-                                mip->name);
-               sz -= ccnt;
-               buf += ccnt;
-               bcnt += ccnt;
-       }
-       return bcnt;
-}
-
-static unsigned int debugifc_parse_subsys_mask(const char *buf,
-                                              unsigned int count,
-                                              unsigned long *mskPtr,
-                                              unsigned long *valPtr)
-{
-       const char *wptr;
-       unsigned int consume_cnt = 0;
-       unsigned int scnt;
-       unsigned int wlen;
-       int mode;
-       unsigned long m1,msk,val;
-
-       msk = 0;
-       val = 0;
-
-       while (count) {
-               scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
-               if (!scnt) break;
-               consume_cnt += scnt; count -= scnt; buf += scnt;
-               if (!wptr) break;
-
-               mode = 0;
-               if (wlen) switch (wptr[0]) {
-               case '+':
-                       wptr++;
-                       wlen--;
-                       break;
-               case '-':
-                       mode = 1;
-                       wptr++;
-                       wlen--;
-                       break;
-               }
-               if (!wlen) continue;
-               m1 = debugifc_find_mask(wptr,wlen);
-               if (!m1) break;
-               msk |= m1;
-               if (!mode) val |= m1;
-       }
-       *mskPtr = msk;
-       *valPtr = val;
-       return consume_cnt;
-}
-
-
 int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
 {
        int bcnt = 0;
        int ccnt;
-       struct pvr2_hdw_debug_info dbg;
-
-       pvr2_hdw_get_debug_info(hdw,&dbg);
-
-       ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s",
-                        (dbg.big_lock_held ? "held" : "free"),
-                        (dbg.ctl_lock_held ? "held" : "free"));
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       if (dbg.ctl_lock_held) {
-               ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d"
-                                " cmd_wlen=%d cmd_rlen=%d"
-                                " wpend=%d rpend=%d tmout=%d rstatus=%d"
-                                " wstatus=%d",
-                                dbg.cmd_debug_state,dbg.cmd_code,
-                                dbg.cmd_debug_write_len,
-                                dbg.cmd_debug_read_len,
-                                dbg.cmd_debug_write_pend,
-                                dbg.cmd_debug_read_pend,
-                                dbg.cmd_debug_timeout,
-                                dbg.cmd_debug_rstatus,
-                                dbg.cmd_debug_wstatus);
-               bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       }
-       ccnt = scnprintf(buf,acnt,"\n");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(
-               buf,acnt,"driver flags: %s %s %s\n",
-               (dbg.flag_init_ok ? "initialized" : "uninitialized"),
-               (dbg.flag_ok ? "ok" : "fail"),
-               (dbg.flag_disconnected ? "disconnected" : "connected"));
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
+       ccnt = scnprintf(buf,acnt,"Driver state info:\n");
        bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags);
+       ccnt = pvr2_hdw_state_report(hdw,buf,acnt);
        bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(buf,acnt,"\n");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags);
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(buf,acnt,"\n");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
        ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n");
        bcnt += ccnt; acnt -= ccnt; buf += ccnt;
        ccnt = pvr2_i2c_report(hdw,buf,acnt);
@@ -290,7 +162,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
 {
        int bcnt = 0;
        int ccnt;
-       unsigned long msk;
        int ret;
        u32 gpio_dir,gpio_in,gpio_out;
 
@@ -311,28 +182,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
                         pvr2_hdw_get_streaming(hdw) ? "on" : "off");
        bcnt += ccnt; acnt -= ccnt; buf += ccnt;
 
-       msk = pvr2_hdw_subsys_get(hdw);
-       ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = debugifc_print_mask(buf,acnt,msk,msk);
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(buf,acnt,"\n");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = debugifc_print_mask(buf,acnt,~msk,msk);
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(buf,acnt,"\n");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
-       msk = pvr2_hdw_subsys_stream_get(hdw);
-       ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: ");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = debugifc_print_mask(buf,acnt,msk,msk);
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-       ccnt = scnprintf(buf,acnt,"\n");
-       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
-
        return bcnt;
 }
 
@@ -369,28 +218,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
                        return pvr2_upload_firmware2(hdw);
                } else if (debugifc_match_keyword(wptr,wlen,"decoder")) {
                        return pvr2_hdw_cmd_decoder_reset(hdw);
+               } else if (debugifc_match_keyword(wptr,wlen,"worker")) {
+                       return pvr2_hdw_untrip(hdw);
                }
                return -EINVAL;
-       } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) {
-               unsigned long msk = 0;
-               unsigned long val = 0;
-               if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
-                       pvr2_trace(PVR2_TRACE_DEBUGIFC,
-                                  "debugifc parse error on subsys mask");
-                       return -EINVAL;
-               }
-               pvr2_hdw_subsys_bit_chg(hdw,msk,val);
-               return 0;
-       } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) {
-               unsigned long msk = 0;
-               unsigned long val = 0;
-               if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
-                       pvr2_trace(PVR2_TRACE_DEBUGIFC,
-                                  "debugifc parse error on stream mask");
-                       return -EINVAL;
-               }
-               pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val);
-               return 0;
        } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) {
                scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
                if (!scnt) return -EINVAL;
index 205087a3e136fa226cc52495c5055b7121da5b10..5ca548cc7e7f3f44761d32960971966008d1b46e 100644 (file)
@@ -209,7 +209,7 @@ static int pvr2_encoder_cmd(void *ctxt,
 
        LOCK_TAKE(hdw->ctl_lock); do {
 
-               if (!hdw->flag_encoder_ok) {
+               if (!hdw->state_encoder_ok) {
                        ret = -EIO;
                        break;
                }
@@ -278,7 +278,11 @@ static int pvr2_encoder_cmd(void *ctxt,
                        ret = -EBUSY;
                }
                if (ret) {
-                       hdw->flag_encoder_ok = 0;
+                       hdw->state_encoder_ok = 0;
+                       pvr2_trace(PVR2_TRACE_STBITS,
+                                  "State bit %s <-- %s",
+                                  "state_encoder_ok",
+                                  (hdw->state_encoder_ok ? "true" : "false"));
                        pvr2_trace(
                                PVR2_TRACE_ERROR_LEGS,
                                "Giving up on command."
@@ -394,6 +398,24 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
        return ret;
 }
 
+int pvr2_encoder_adjust(struct pvr2_hdw *hdw)
+{
+       int ret;
+       ret = cx2341x_update(hdw,pvr2_encoder_cmd,
+                            (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
+                            &hdw->enc_ctl_state);
+       if (ret) {
+               pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                          "Error from cx2341x module code=%d",ret);
+       } else {
+               memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
+                      sizeof(struct cx2341x_mpeg_params));
+               hdw->enc_cur_valid = !0;
+       }
+       return ret;
+}
+
+
 int pvr2_encoder_configure(struct pvr2_hdw *hdw)
 {
        int ret;
@@ -436,18 +458,10 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
                return ret;
        }
 
-       ret = cx2341x_update(hdw,pvr2_encoder_cmd,
-                            (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
-                            &hdw->enc_ctl_state);
-       if (ret) {
-               pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                          "Error from cx2341x module code=%d",ret);
-               return ret;
-       }
-
-       ret = 0;
+       ret = pvr2_encoder_adjust(hdw);
+       if (ret) return ret;
 
-       if (!ret) ret = pvr2_encoder_vcmd(
+       ret = pvr2_encoder_vcmd(
                hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
 
        if (ret) {
@@ -456,10 +470,6 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
                return ret;
        }
 
-       hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
-       memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
-              sizeof(struct cx2341x_mpeg_params));
-       hdw->enc_cur_valid = !0;
        return 0;
 }
 
@@ -478,7 +488,7 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw)
        pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
                          hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
 
-       switch (hdw->config) {
+       switch (hdw->active_stream_type) {
        case pvr2_config_vbi:
                status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
                                           0x01,0x14);
@@ -492,9 +502,6 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw)
                                           0,0x13);
                break;
        }
-       if (!status) {
-               hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
-       }
        return status;
 }
 
@@ -505,7 +512,7 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw)
        /* mask all interrupts */
        pvr2_write_register(hdw, 0x0048, 0xffffffff);
 
-       switch (hdw->config) {
+       switch (hdw->active_stream_type) {
        case pvr2_config_vbi:
                status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
                                           0x01,0x01,0x14);
@@ -526,9 +533,6 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw)
        pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
        pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
 
-       if (!status) {
-               hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN);
-       }
        return status;
 }
 
index 01b5a0b89c03893d3d30f34759ad803e777b522b..54caf2e3c428adadc7ff16b76261ffeac60a95c1 100644 (file)
@@ -25,6 +25,7 @@
 
 struct pvr2_hdw;
 
+int pvr2_encoder_adjust(struct pvr2_hdw *);
 int pvr2_encoder_configure(struct pvr2_hdw *);
 int pvr2_encoder_start(struct pvr2_hdw *);
 int pvr2_encoder_stop(struct pvr2_hdw *);
index f873994b088ce3287a7a5b1bfdb4763b0f818fb6..8ee4549b7a9f225ac9ab65202c202753e13d935e 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <linux/videodev2.h>
 #include <linux/i2c.h>
+#include <linux/workqueue.h>
 #include <linux/mutex.h>
 #include "pvrusb2-hdw.h"
 #include "pvrusb2-io.h"
@@ -179,6 +180,12 @@ struct pvr2_hdw {
        /* Device type, one of PVR2_HDW_TYPE_xxxxx */
        unsigned int hdw_type;
 
+       /* Kernel worker thread handling */
+       struct workqueue_struct *workqueue;
+       struct work_struct workpoll;     /* Update driver state */
+       struct work_struct worki2csync;  /* Update i2c clients */
+       struct work_struct workinit;     /* Driver initialization sequence */
+
        /* Video spigot */
        struct pvr2_stream *vid_stream;
 
@@ -186,9 +193,6 @@ struct pvr2_hdw {
        struct mutex big_lock_mutex;
        int big_lock_held;  /* For debugging */
 
-       void (*poll_trigger_func)(void *);
-       void *poll_trigger_data;
-
        char name[32];
 
        /* I2C stuff */
@@ -225,14 +229,48 @@ struct pvr2_hdw {
        unsigned int cmd_debug_write_len;  //
        unsigned int cmd_debug_read_len;   //
 
+       /* Bits of state that describe what is going on with various parts
+          of the driver. */
+       volatile int state_encoder_ok;         /* Encoder is operational */
+       volatile int state_encoder_run;        /* Encoder is running */
+       volatile int state_encoder_config;     /* Encoder is configured */
+       volatile int state_encoder_waitok;     /* Encoder pre-wait done */
+       volatile int state_decoder_run;        /* Decoder is running */
+       volatile int state_usbstream_run;      /* FX2 is streaming */
+       volatile int state_decoder_quiescent;  /* Decoder idle for > 50msec */
+       volatile int state_pipeline_config;    /* Pipeline is configured */
+       int state_pipeline_req;                /* Somebody wants to stream */
+       int state_pipeline_pause;              /* Pipeline must be paused */
+       int state_pipeline_idle;               /* Pipeline not running */
+
+       /* This is the master state of the driver.  It is the combined
+          result of other bits of state.  Examining this will indicate the
+          overall state of the driver.  Values here are one of
+          PVR2_STATE_xxxx */
+       unsigned int master_state;
+
+       /* True if states must be re-evaluated */
+       int state_stale;
+
+       void (*state_func)(void *);
+       void *state_data;
+
+       /* Timer for measuring decoder settling time */
+       struct timer_list quiescent_timer;
+
+       /* Timer for measuring encoder pre-wait time */
+       struct timer_list encoder_wait_timer;
+
+       /* Place to block while waiting for state changes */
+       wait_queue_head_t state_wait_data;
+
+
        int flag_ok;            /* device in known good state */
        int flag_disconnected;  /* flag_ok == 0 due to disconnect */
        int flag_init_ok;       /* true if structure is fully initialized */
-       int flag_streaming_enabled; /* true if streaming should be on */
        int fw1_state;          /* current situation with fw1 */
-       int flag_encoder_ok;    /* True if encoder is healthy */
-
-       int flag_decoder_is_tuned;
+       int flag_decoder_missed;/* We've noticed missing decoder */
+       int flag_tripped;       /* Indicates overall failure to start */
 
        struct pvr2_decoder_ctrl *decoder_ctrl;
 
@@ -241,12 +279,6 @@ struct pvr2_hdw {
        unsigned int fw_size;
        int fw_cpu_flag; /* True if we are dealing with the CPU */
 
-       // Which subsystem pieces have been enabled / configured
-       unsigned long subsys_enabled_mask;
-
-       // Which subsystems are manipulated to enable streaming
-       unsigned long subsys_stream_mask;
-
        // True if there is a request to trigger logging of state in each
        // module.
        int log_requested;
@@ -296,13 +328,16 @@ struct pvr2_hdw {
        /* Location of eeprom or a negative number if none */
        int eeprom_addr;
 
-       enum pvr2_config config;
+       enum pvr2_config active_stream_type;
+       enum pvr2_config desired_stream_type;
 
        /* Control state needed for cx2341x module */
        struct cx2341x_mpeg_params enc_cur_state;
        struct cx2341x_mpeg_params enc_ctl_state;
        /* True if an encoder attribute has changed */
        int enc_stale;
+       /* True if an unsafe encoder attribute has changed */
+       int enc_unsafe_stale;
        /* True if enc_cur_state is valid */
        int enc_cur_valid;
 
@@ -332,6 +367,7 @@ struct pvr2_hdw {
 
 /* This function gets the current frequency */
 unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *);
+void pvr2_hdw_set_decoder(struct pvr2_hdw *,struct pvr2_decoder_ctrl *);
 
 #endif /* __PVRUSB2_HDW_INTERNAL_H */
 
index 402c59488253768ce077708c8787711886988aa1..4e55a2a84073d57eef84eded9421452c8fc5fef8 100644 (file)
@@ -246,32 +246,46 @@ static const char *control_values_hsm[] = {
 };
 
 
-static const char *control_values_subsystem[] = {
-       [PVR2_SUBSYS_B_ENC_FIRMWARE]  = "enc_firmware",
-       [PVR2_SUBSYS_B_ENC_CFG] = "enc_config",
-       [PVR2_SUBSYS_B_DIGITIZER_RUN] = "digitizer_run",
-       [PVR2_SUBSYS_B_USBSTREAM_RUN] = "usbstream_run",
-       [PVR2_SUBSYS_B_ENC_RUN] = "enc_run",
+static const char *pvr2_state_names[] = {
+       [PVR2_STATE_NONE] =    "none",
+       [PVR2_STATE_DEAD] =    "dead",
+       [PVR2_STATE_COLD] =    "cold",
+       [PVR2_STATE_WARM] =    "warm",
+       [PVR2_STATE_ERROR] =   "error",
+       [PVR2_STATE_READY] =   "ready",
+       [PVR2_STATE_RUN] =     "run",
 };
 
+
+static void pvr2_hdw_state_sched(struct pvr2_hdw *);
+static int pvr2_hdw_state_eval(struct pvr2_hdw *);
 static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
+static void pvr2_hdw_worker_i2c(struct work_struct *work);
+static void pvr2_hdw_worker_poll(struct work_struct *work);
+static void pvr2_hdw_worker_init(struct work_struct *work);
+static int pvr2_hdw_wait(struct pvr2_hdw *,int state);
+static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
+static void pvr2_hdw_state_log_state(struct pvr2_hdw *);
 static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
-static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw);
+static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);
 static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
 static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
 static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
-static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw);
-static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
-                                           unsigned long msk,
-                                           unsigned long val);
-static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
-                                                  unsigned long msk,
-                                                  unsigned long val);
+static void pvr2_hdw_quiescent_timeout(unsigned long);
+static void pvr2_hdw_encoder_wait_timeout(unsigned long);
 static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
                                unsigned int timeout,int probe_fl,
                                void *write_data,unsigned int write_len,
                                void *read_data,unsigned int read_len);
 
+
+static void trace_stbit(const char *name,int val)
+{
+       pvr2_trace(PVR2_TRACE_STBITS,
+                  "State bit %s <-- %s",
+                  name,(val ? "true" : "false"));
+}
+
 static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
 {
        struct pvr2_hdw *hdw = cptr->hdw;
@@ -480,6 +494,7 @@ static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
 static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
 {
        cptr->hdw->enc_stale = 0;
+       cptr->hdw->enc_unsafe_stale = 0;
 }
 
 static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
@@ -502,6 +517,7 @@ static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
 static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
 {
        int ret;
+       struct pvr2_hdw *hdw = cptr->hdw;
        struct v4l2_ext_controls cs;
        struct v4l2_ext_control c1;
        memset(&cs,0,sizeof(cs));
@@ -510,10 +526,22 @@ static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
        cs.count = 1;
        c1.id = cptr->info->v4l_id;
        c1.value = v;
-       ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs,
+       ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
+                               hdw->state_encoder_run, &cs,
                                VIDIOC_S_EXT_CTRLS);
+       if (ret == -EBUSY) {
+               /* Oops.  cx2341x is telling us it's not safe to change
+                  this control while we're capturing.  Make a note of this
+                  fact so that the pipeline will be stopped the next time
+                  controls are committed.  Then go on ahead and store this
+                  change anyway. */
+               ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
+                                       0, &cs,
+                                       VIDIOC_S_EXT_CTRLS);
+               if (!ret) hdw->enc_unsafe_stale = !0;
+       }
        if (ret) return ret;
-       cptr->hdw->enc_stale = !0;
+       hdw->enc_stale = !0;
        return 0;
 }
 
@@ -544,7 +572,13 @@ static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
 
 static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
 {
-       *vp = cptr->hdw->flag_streaming_enabled;
+       *vp = cptr->hdw->state_pipeline_req;
+       return 0;
+}
+
+static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp)
+{
+       *vp = cptr->hdw->master_state;
        return 0;
 }
 
@@ -657,29 +691,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
        return 0;
 }
 
-static int ctrl_subsys_get(struct pvr2_ctrl *cptr,int *vp)
-{
-       *vp = cptr->hdw->subsys_enabled_mask;
-       return 0;
-}
-
-static int ctrl_subsys_set(struct pvr2_ctrl *cptr,int m,int v)
-{
-       pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,m,v);
-       return 0;
-}
-
-static int ctrl_subsys_stream_get(struct pvr2_ctrl *cptr,int *vp)
-{
-       *vp = cptr->hdw->subsys_stream_mask;
-       return 0;
-}
-
-static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v)
-{
-       pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,m,v);
-       return 0;
-}
 
 static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v)
 {
@@ -914,6 +925,11 @@ static const struct pvr2_ctl_info control_defs[] = {
                .name = "usb_speed",
                .get_value = ctrl_hsm_get,
                DEFENUM(control_values_hsm),
+       },{
+               .desc = "Master State",
+               .name = "master_state",
+               .get_value = ctrl_masterstate_get,
+               DEFENUM(pvr2_state_names),
        },{
                .desc = "Signal Present",
                .name = "signal_present",
@@ -954,20 +970,6 @@ static const struct pvr2_ctl_info control_defs[] = {
                .val_to_sym = ctrl_std_val_to_sym,
                .sym_to_val = ctrl_std_sym_to_val,
                .type = pvr2_ctl_bitmask,
-       },{
-               .desc = "Subsystem enabled mask",
-               .name = "debug_subsys_mask",
-               .skip_init = !0,
-               .get_value = ctrl_subsys_get,
-               .set_value = ctrl_subsys_set,
-               DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem),
-       },{
-               .desc = "Subsystem stream mask",
-               .name = "debug_subsys_stream_mask",
-               .skip_init = !0,
-               .get_value = ctrl_subsys_stream_get,
-               .set_value = ctrl_subsys_stream_set,
-               DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem),
        },{
                .desc = "Video Standard Name",
                .name = "video_standard",
@@ -1248,8 +1250,6 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
           time we configure the encoder, then we'll fully configure it. */
        hdw->enc_cur_valid = 0;
 
-       hdw->flag_encoder_ok = 0;
-
        /* First prepare firmware loading */
        ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
        ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
@@ -1347,293 +1347,129 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
        if (ret) {
                pvr2_trace(PVR2_TRACE_ERROR_LEGS,
                           "firmware2 upload post-proc failure");
-       } else {
-               hdw->flag_encoder_ok = !0;
-               hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_FIRMWARE);
        }
        return ret;
 }
 
 
-#define FIRMWARE_RECOVERY_BITS \
-       ((1<<PVR2_SUBSYS_B_ENC_CFG) | \
-        (1<<PVR2_SUBSYS_B_ENC_RUN) | \
-        (1<<PVR2_SUBSYS_B_ENC_FIRMWARE) | \
-        (1<<PVR2_SUBSYS_B_USBSTREAM_RUN))
-
-/*
-
-  This single function is key to pretty much everything.  The pvrusb2
-  device can logically be viewed as a series of subsystems which can be
-  stopped / started or unconfigured / configured.  To get things streaming,
-  one must configure everything and start everything, but there may be
-  various reasons over time to deconfigure something or stop something.
-  This function handles all of this activity.  Everything EVERYWHERE that
-  must affect a subsystem eventually comes here to do the work.
-
-  The current state of all subsystems is represented by a single bit mask,
-  known as subsys_enabled_mask.  The bit positions are defined by the
-  PVR2_SUBSYS_xxxx macros, with one subsystem per bit position.  At any
-  time the set of configured or active subsystems can be queried just by
-  looking at that mask.  To change bits in that mask, this function here
-  must be called.  The "msk" argument indicates which bit positions to
-  change, and the "val" argument defines the new values for the positions
-  defined by "msk".
-
-  There is a priority ordering of starting / stopping things, and for
-  multiple requested changes, this function implements that ordering.
-  (Thus we will act on a request to load encoder firmware before we
-  configure the encoder.)  In addition to priority ordering, there is a
-  recovery strategy implemented here.  If a particular step fails and we
-  detect that failure, this function will clear the affected subsystem bits
-  and restart.  Thus we have a means for recovering from a dead encoder:
-  Clear all bits that correspond to subsystems that we need to restart /
-  reconfigure and start over.
-
-*/
-static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
-                                           unsigned long msk,
-                                           unsigned long val)
-{
-       unsigned long nmsk;
-       unsigned long vmsk;
-       int ret;
-       unsigned int tryCount = 0;
-
-       if (!hdw->flag_ok) return;
-
-       msk &= PVR2_SUBSYS_ALL;
-       nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk);
-       nmsk &= PVR2_SUBSYS_ALL;
-
-       for (;;) {
-               tryCount++;
-               if (!((nmsk ^ hdw->subsys_enabled_mask) &
-                     PVR2_SUBSYS_ALL)) break;
-               if (tryCount > 4) {
-                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                                  "Too many retries when configuring device;"
-                                  " giving up");
-                       pvr2_hdw_render_useless(hdw);
-                       break;
-               }
-               if (tryCount > 1) {
-                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                                  "Retrying device reconfiguration");
-               }
-               pvr2_trace(PVR2_TRACE_INIT,
-                          "subsys mask changing 0x%lx:0x%lx"
-                          " from 0x%lx to 0x%lx",
-                          msk,val,hdw->subsys_enabled_mask,nmsk);
-
-               vmsk = (nmsk ^ hdw->subsys_enabled_mask) &
-                       hdw->subsys_enabled_mask;
-               if (vmsk) {
-                       if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) {
-                               pvr2_trace(PVR2_TRACE_CTL,
-                                          "/*---TRACE_CTL----*/"
-                                          " pvr2_encoder_stop");
-                               ret = pvr2_encoder_stop(hdw);
-                               if (ret) {
-                                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                                                  "Error recovery initiated");
-                                       hdw->subsys_enabled_mask &=
-                                               ~FIRMWARE_RECOVERY_BITS;
-                                       continue;
-                               }
-                       }
-                       if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) {
-                               pvr2_trace(PVR2_TRACE_CTL,
-                                          "/*---TRACE_CTL----*/"
-                                          " pvr2_hdw_cmd_usbstream(0)");
-                               pvr2_hdw_cmd_usbstream(hdw,0);
-                       }
-                       if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) {
-                               pvr2_trace(PVR2_TRACE_CTL,
-                                          "/*---TRACE_CTL----*/"
-                                          " decoder disable");
-                               if (hdw->decoder_ctrl) {
-                                       hdw->decoder_ctrl->enable(
-                                               hdw->decoder_ctrl->ctxt,0);
-                               } else {
-                                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                                                  "WARNING:"
-                                                  " No decoder present");
-                               }
-                               hdw->subsys_enabled_mask &=
-                                       ~(1<<PVR2_SUBSYS_B_DIGITIZER_RUN);
-                       }
-                       if (vmsk & PVR2_SUBSYS_CFG_ALL) {
-                               hdw->subsys_enabled_mask &=
-                                       ~(vmsk & PVR2_SUBSYS_CFG_ALL);
-                       }
-               }
-               vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk;
-               if (vmsk) {
-                       if (vmsk & (1<<PVR2_SUBSYS_B_ENC_FIRMWARE)) {
-                               pvr2_trace(PVR2_TRACE_CTL,
-                                          "/*---TRACE_CTL----*/"
-                                          " pvr2_upload_firmware2");
-                               ret = pvr2_upload_firmware2(hdw);
-                               if (ret) {
-                                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                                                  "Failure uploading encoder"
-                                                  " firmware");
-                                       pvr2_hdw_render_useless(hdw);
-                                       break;
-                               }
-                       }
-                       if (vmsk & (1<<PVR2_SUBSYS_B_ENC_CFG)) {
-                               pvr2_trace(PVR2_TRACE_CTL,
-                                          "/*---TRACE_CTL----*/"
-                                          " pvr2_encoder_configure");
-                               ret = pvr2_encoder_configure(hdw);
-                               if (ret) {
-                                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                                                  "Error recovery initiated");
-                                       hdw->subsys_enabled_mask &=
-                                               ~FIRMWARE_RECOVERY_BITS;
-                                       continue;
-                               }
-                       }
-                       if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) {
-                               pvr2_trace(PVR2_TRACE_CTL,
-                                          "/*---TRACE_CTL----*/"
-                                          " decoder enable");
-                               if (hdw->decoder_ctrl) {
-                                       hdw->decoder_ctrl->enable(
-                                               hdw->decoder_ctrl->ctxt,!0);
-                               } else {
-                                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                                                  "WARNING:"
-                                                  " No decoder present");
-                               }
-                               hdw->subsys_enabled_mask |=
-                                       (1<<PVR2_SUBSYS_B_DIGITIZER_RUN);
-                       }
-                       if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) {
-                               pvr2_trace(PVR2_TRACE_CTL,
-                                          "/*---TRACE_CTL----*/"
-                                          " pvr2_hdw_cmd_usbstream(1)");
-                               pvr2_hdw_cmd_usbstream(hdw,!0);
-                       }
-                       if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) {
-                               pvr2_trace(PVR2_TRACE_CTL,
-                                          "/*---TRACE_CTL----*/"
-                                          " pvr2_encoder_start");
-                               ret = pvr2_encoder_start(hdw);
-                               if (ret) {
-                                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                                                  "Error recovery initiated");
-                                       hdw->subsys_enabled_mask &=
-                                               ~FIRMWARE_RECOVERY_BITS;
-                                       continue;
-                               }
-                       }
-               }
+static const char *pvr2_get_state_name(unsigned int st)
+{
+       if (st < ARRAY_SIZE(pvr2_state_names)) {
+               return pvr2_state_names[st];
        }
+       return "???";
 }
 
-
-void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
-                            unsigned long msk,unsigned long val)
+static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
 {
-       LOCK_TAKE(hdw->big_lock); do {
-               pvr2_hdw_subsys_bit_chg_no_lock(hdw,msk,val);
-       } while (0); LOCK_GIVE(hdw->big_lock);
+       if (!hdw->decoder_ctrl) {
+               if (!hdw->flag_decoder_missed) {
+                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                                  "WARNING: No decoder present");
+                       hdw->flag_decoder_missed = !0;
+                       trace_stbit("flag_decoder_missed",
+                                   hdw->flag_decoder_missed);
+               }
+               return -EIO;
+       }
+       hdw->decoder_ctrl->enable(hdw->decoder_ctrl->ctxt,enablefl);
+       return 0;
 }
 
 
-unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw)
+void pvr2_hdw_set_decoder(struct pvr2_hdw *hdw,struct pvr2_decoder_ctrl *ptr)
 {
-       return hdw->subsys_enabled_mask;
+       if (hdw->decoder_ctrl == ptr) return;
+       hdw->decoder_ctrl = ptr;
+       if (hdw->decoder_ctrl && hdw->flag_decoder_missed) {
+               hdw->flag_decoder_missed = 0;
+               trace_stbit("flag_decoder_missed",
+                           hdw->flag_decoder_missed);
+               pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                          "Decoder has appeared");
+               pvr2_hdw_state_sched(hdw);
+       }
 }
 
 
-unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw)
+int pvr2_hdw_get_state(struct pvr2_hdw *hdw)
 {
-       return hdw->subsys_stream_mask;
+       return hdw->master_state;
 }
 
 
-static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
-                                                  unsigned long msk,
-                                                  unsigned long val)
+static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw)
 {
-       unsigned long val2;
-       msk &= PVR2_SUBSYS_ALL;
-       val2 = ((hdw->subsys_stream_mask & ~msk) | (val & msk));
-       pvr2_trace(PVR2_TRACE_INIT,
-                  "stream mask changing 0x%lx:0x%lx from 0x%lx to 0x%lx",
-                  msk,val,hdw->subsys_stream_mask,val2);
-       hdw->subsys_stream_mask = val2;
+       if (!hdw->flag_tripped) return 0;
+       hdw->flag_tripped = 0;
+       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                  "Clearing driver error statuss");
+       return !0;
 }
 
 
-void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
-                                   unsigned long msk,
-                                   unsigned long val)
+int pvr2_hdw_untrip(struct pvr2_hdw *hdw)
 {
+       int fl;
        LOCK_TAKE(hdw->big_lock); do {
-               pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,msk,val);
+               fl = pvr2_hdw_untrip_unlocked(hdw);
        } while (0); LOCK_GIVE(hdw->big_lock);
+       if (fl) pvr2_hdw_state_sched(hdw);
+       return 0;
 }
 
 
-static int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl)
+const char *pvr2_hdw_get_state_name(unsigned int id)
 {
-       if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0;
-       if (enableFl) {
-               pvr2_trace(PVR2_TRACE_START_STOP,
-                          "/*--TRACE_STREAM--*/ enable");
-               pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,~0);
-       } else {
-               pvr2_trace(PVR2_TRACE_START_STOP,
-                          "/*--TRACE_STREAM--*/ disable");
-               pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
-       }
-       if (!hdw->flag_ok) return -EIO;
-       hdw->flag_streaming_enabled = enableFl != 0;
-       return 0;
+       if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL;
+       return pvr2_state_names[id];
 }
 
 
 int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
 {
-       return hdw->flag_streaming_enabled != 0;
+       return hdw->state_pipeline_req != 0;
 }
 
 
 int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
 {
-       int ret;
+       int ret,st;
        LOCK_TAKE(hdw->big_lock); do {
-               ret = pvr2_hdw_set_streaming_no_lock(hdw,enable_flag);
+               pvr2_hdw_untrip_unlocked(hdw);
+               if ((!enable_flag) != !(hdw->state_pipeline_req)) {
+                       hdw->state_pipeline_req = enable_flag != 0;
+                       pvr2_trace(PVR2_TRACE_START_STOP,
+                                  "/*--TRACE_STREAM--*/ %s",
+                                  enable_flag ? "enable" : "disable");
+               }
+               pvr2_hdw_state_sched(hdw);
        } while (0); LOCK_GIVE(hdw->big_lock);
-       return ret;
-}
-
-
-static int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw,
-                                           enum pvr2_config config)
-{
-       unsigned long sm = hdw->subsys_enabled_mask;
-       if (!hdw->flag_ok) return -EIO;
-       pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
-       hdw->config = config;
-       pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,sm);
+       if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret;
+       if (enable_flag) {
+               while ((st = hdw->master_state) != PVR2_STATE_RUN) {
+                       if (st != PVR2_STATE_READY) return -EIO;
+                       if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret;
+               }
+       }
        return 0;
 }
 
 
 int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
 {
-       int ret;
-       if (!hdw->flag_ok) return -EIO;
+       int fl;
        LOCK_TAKE(hdw->big_lock);
-       ret = pvr2_hdw_set_stream_type_no_lock(hdw,config);
+       if ((fl = (hdw->desired_stream_type != config)) != 0) {
+               hdw->desired_stream_type = config;
+               hdw->state_pipeline_config = 0;
+               trace_stbit("state_pipeline_config",
+                           hdw->state_pipeline_config);
+               pvr2_hdw_state_sched(hdw);
+       }
        LOCK_GIVE(hdw->big_lock);
-       return ret;
+       if (fl) return 0;
+       return pvr2_hdw_wait(hdw,0);
 }
 
 
@@ -1866,12 +1702,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
            (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
                pvr2_hdw_cmd_powerup(hdw);
                if (!pvr2_hdw_dev_ok(hdw)) return;
-
-               if (pvr2_upload_firmware2(hdw)){
-                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,"device unstable!!");
-                       pvr2_hdw_render_useless(hdw);
-                       return;
-               }
        }
 
        // This step MUST happen after the earlier powerup step.
@@ -1924,8 +1754,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
 
        if (!pvr2_hdw_dev_ok(hdw)) return;
 
-       pvr2_hdw_commit_ctl_internal(hdw);
-       if (!pvr2_hdw_dev_ok(hdw)) return;
+       pvr2_hdw_commit_setup(hdw);
 
        hdw->vid_stream = pvr2_stream_create();
        if (!pvr2_hdw_dev_ok(hdw)) return;
@@ -1945,25 +1774,25 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
 
        if (!pvr2_hdw_dev_ok(hdw)) return;
 
-       /* Make sure everything is up to date */
-       pvr2_i2c_core_sync(hdw);
-
-       if (!pvr2_hdw_dev_ok(hdw)) return;
-
        hdw->flag_init_ok = !0;
+
+       pvr2_hdw_state_sched(hdw);
 }
 
 
-int pvr2_hdw_setup(struct pvr2_hdw *hdw)
+/* Set up the structure and attempt to put the device into a usable state.
+   This can be a time-consuming operation, which is why it is not done
+   internally as part of the create() step. */
+static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
 {
        pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
-       LOCK_TAKE(hdw->big_lock); do {
+       do {
                pvr2_hdw_setup_low(hdw);
                pvr2_trace(PVR2_TRACE_INIT,
                           "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
-                          hdw,hdw->flag_ok,hdw->flag_init_ok);
+                          hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok);
                if (pvr2_hdw_dev_ok(hdw)) {
-                       if (pvr2_hdw_init_ok(hdw)) {
+                       if (hdw->flag_init_ok) {
                                pvr2_trace(
                                        PVR2_TRACE_INFO,
                                        "Device initialization"
@@ -2013,9 +1842,8 @@ int pvr2_hdw_setup(struct pvr2_hdw *hdw)
                                " the pvrusb2 device"
                                " in order to recover.");
                }
-       } while (0); LOCK_GIVE(hdw->big_lock);
+       } while (0);
        pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw);
-       return hdw->flag_init_ok;
 }
 
 
@@ -2044,6 +1872,19 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
                   hdw,pvr2_device_names[hdw_type]);
        if (!hdw) goto fail;
+
+       init_timer(&hdw->quiescent_timer);
+       hdw->quiescent_timer.data = (unsigned long)hdw;
+       hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout;
+
+       init_timer(&hdw->encoder_wait_timer);
+       hdw->encoder_wait_timer.data = (unsigned long)hdw;
+       hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout;
+
+       hdw->master_state = PVR2_STATE_DEAD;
+
+       init_waitqueue_head(&hdw->state_wait_data);
+
        hdw->tuner_signal_stale = !0;
        cx2341x_fill_defaults(&hdw->enc_ctl_state);
 
@@ -2184,18 +2025,16 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
        hdw->name[cnt1] = 0;
 
+       hdw->workqueue = create_singlethread_workqueue(hdw->name);
+       INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll);
+       INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c);
+       INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init);
+
        pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
                   hdw->unit_number,hdw->name);
 
        hdw->tuner_type = -1;
        hdw->flag_ok = !0;
-       /* Initialize the mask of subsystems that we will shut down when we
-          stop streaming. */
-       hdw->subsys_stream_mask = PVR2_SUBSYS_RUN_ALL;
-       hdw->subsys_stream_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
-
-       pvr2_trace(PVR2_TRACE_INIT,"subsys_stream_mask: 0x%lx",
-                  hdw->subsys_stream_mask);
 
        hdw->usb_intf = intf;
        hdw->usb_dev = interface_to_usbdev(intf);
@@ -2211,15 +2050,25 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        mutex_init(&hdw->ctl_lock_mutex);
        mutex_init(&hdw->big_lock_mutex);
 
+       queue_work(hdw->workqueue,&hdw->workinit);
        return hdw;
  fail:
        if (hdw) {
+               del_timer_sync(&hdw->quiescent_timer);
+               del_timer_sync(&hdw->encoder_wait_timer);
+               if (hdw->workqueue) {
+                       flush_workqueue(hdw->workqueue);
+                       destroy_workqueue(hdw->workqueue);
+                       hdw->workqueue = NULL;
+               }
                usb_free_urb(hdw->ctl_read_urb);
                usb_free_urb(hdw->ctl_write_urb);
                kfree(hdw->ctl_read_buffer);
                kfree(hdw->ctl_write_buffer);
                kfree(hdw->controls);
                kfree(hdw->mpeg_ctrl_info);
+               kfree(hdw->std_defs);
+               kfree(hdw->std_enum_names);
                kfree(hdw);
        }
        return NULL;
@@ -2250,10 +2099,10 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
                kfree(hdw->ctl_write_buffer);
                hdw->ctl_write_buffer = NULL;
        }
-       pvr2_hdw_render_useless_unlocked(hdw);
        hdw->flag_disconnected = !0;
        hdw->usb_dev = NULL;
        hdw->usb_intf = NULL;
+       pvr2_hdw_render_useless(hdw);
 }
 
 
@@ -2262,6 +2111,13 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
 {
        if (!hdw) return;
        pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
+       del_timer_sync(&hdw->quiescent_timer);
+       del_timer_sync(&hdw->encoder_wait_timer);
+       if (hdw->workqueue) {
+               flush_workqueue(hdw->workqueue);
+               destroy_workqueue(hdw->workqueue);
+               hdw->workqueue = NULL;
+       }
        if (hdw->fw_buffer) {
                kfree(hdw->fw_buffer);
                hdw->fw_buffer = NULL;
@@ -2290,12 +2146,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
 }
 
 
-int pvr2_hdw_init_ok(struct pvr2_hdw *hdw)
-{
-       return hdw->flag_init_ok;
-}
-
-
 int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
 {
        return (hdw && hdw->flag_ok);
@@ -2473,17 +2323,11 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
 }
 
 
-/* Commit all control changes made up to this point.  Subsystems can be
-   indirectly affected by these changes.  For a given set of things being
-   committed, we'll clear the affected subsystem bits and then once we're
-   done committing everything we'll make a request to restore the subsystem
-   state(s) back to their previous value before this function was called.
-   Thus we can automatically reconfigure affected pieces of the driver as
-   controls are changed. */
-static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
+/* Figure out if we need to commit control changes.  If so, mark internal
+   state flags to indicate this fact and return true.  Otherwise do nothing
+   else and return false. */
+static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
 {
-       unsigned long saved_subsys_mask = hdw->subsys_enabled_mask;
-       unsigned long stale_subsys_mask = 0;
        unsigned int idx;
        struct pvr2_ctrl *cptr;
        int value;
@@ -2518,6 +2362,25 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
                return 0;
        }
 
+       hdw->state_pipeline_config = 0;
+       trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
+       pvr2_hdw_state_sched(hdw);
+
+       return !0;
+}
+
+
+/* Perform all operations needed to commit all control changes.  This must
+   be performed in synchronization with the pipeline state and is thus
+   expected to be called as part of the driver's worker thread.  Return
+   true if commit successful, otherwise return false to indicate that
+   commit isn't possible at this time. */
+static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
+{
+       unsigned int idx;
+       struct pvr2_ctrl *cptr;
+       int disruptive_change;
+
        /* When video standard changes, reset the hres and vres values -
           but if the user has pending changes there, then let the changes
           take priority. */
@@ -2536,24 +2399,26 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
                }
        }
 
-       if (hdw->std_dirty ||
-           hdw->enc_stale ||
-           hdw->srate_dirty ||
-           hdw->res_ver_dirty ||
-           hdw->res_hor_dirty ||
-           0) {
-               /* If any of this changes, then the encoder needs to be
-                  reconfigured, and we need to reset the stream. */
-               stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
-       }
-
-       if (hdw->input_dirty) {
-               /* pk: If input changes to or from radio, then the encoder
-                  needs to be restarted (for ENC_MUTE_VIDEO to work) */
-               stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
+       /* If any of the below has changed, then we can't do the update
+          while the pipeline is running.  Pipeline must be paused first
+          and decoder -> encoder connection be made quiescent before we
+          can proceed. */
+       disruptive_change =
+               (hdw->std_dirty ||
+                hdw->enc_unsafe_stale ||
+                hdw->srate_dirty ||
+                hdw->res_ver_dirty ||
+                hdw->res_hor_dirty ||
+                hdw->input_dirty ||
+                (hdw->active_stream_type != hdw->desired_stream_type));
+       if (disruptive_change && !hdw->state_pipeline_idle) {
+               /* Pipeline is not idle; we can't proceed.  Arrange to
+                  cause pipeline to stop so that we can try this again
+                  later.... */
+               hdw->state_pipeline_pause = !0;
+               return 0;
        }
 
-
        if (hdw->srate_dirty) {
                /* Write new sample rate into control structure since
                 * the master copy is stale.  We must track srate
@@ -2582,51 +2447,88 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
                cptr->info->clear_dirty(cptr);
        }
 
+       if (hdw->active_stream_type != hdw->desired_stream_type) {
+               /* Handle any side effects of stream config here */
+               hdw->active_stream_type = hdw->desired_stream_type;
+       }
+
        /* Now execute i2c core update */
        pvr2_i2c_core_sync(hdw);
 
-       pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0);
-       pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask);
+       if (hdw->state_encoder_run) {
+               /* If encoder isn't running, then this will get worked out
+                  later when we start the encoder. */
+               if (pvr2_encoder_adjust(hdw) < 0) return !0;
+       }
 
-       return 0;
+       hdw->state_pipeline_config = !0;
+       trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
+       return !0;
 }
 
 
 int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
 {
+       int fl;
+       LOCK_TAKE(hdw->big_lock);
+       fl = pvr2_hdw_commit_setup(hdw);
+       LOCK_GIVE(hdw->big_lock);
+       if (!fl) return 0;
+       return pvr2_hdw_wait(hdw,0);
+}
+
+
+static void pvr2_hdw_worker_i2c(struct work_struct *work)
+{
+       struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,worki2csync);
        LOCK_TAKE(hdw->big_lock); do {
-               pvr2_hdw_commit_ctl_internal(hdw);
+               pvr2_i2c_core_sync(hdw);
        } while (0); LOCK_GIVE(hdw->big_lock);
-       return 0;
 }
 
 
-void pvr2_hdw_poll(struct pvr2_hdw *hdw)
+static void pvr2_hdw_worker_poll(struct work_struct *work)
 {
+       int fl = 0;
+       struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll);
        LOCK_TAKE(hdw->big_lock); do {
-               pvr2_i2c_core_sync(hdw);
+               fl = pvr2_hdw_state_eval(hdw);
        } while (0); LOCK_GIVE(hdw->big_lock);
+       if (fl && hdw->state_func) {
+               hdw->state_func(hdw->state_data);
+       }
 }
 
 
-void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *hdw,
-                                void (*func)(void *),
-                                void *data)
+static void pvr2_hdw_worker_init(struct work_struct *work)
 {
+       struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit);
        LOCK_TAKE(hdw->big_lock); do {
-               hdw->poll_trigger_func = func;
-               hdw->poll_trigger_data = data;
+               pvr2_hdw_setup(hdw);
        } while (0); LOCK_GIVE(hdw->big_lock);
 }
 
 
-void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw)
+static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
 {
-       if (hdw->poll_trigger_func) {
-               hdw->poll_trigger_func(hdw->poll_trigger_data);
-       }
+       return wait_event_interruptible(
+               hdw->state_wait_data,
+               (hdw->state_stale == 0) &&
+               (!state || (hdw->master_state != state)));
+}
+
+
+void pvr2_hdw_set_state_callback(struct pvr2_hdw *hdw,
+                                void (*callback_func)(void *),
+                                void *callback_data)
+{
+       LOCK_TAKE(hdw->big_lock); do {
+               hdw->state_data = callback_data;
+               hdw->state_func = callback_func;
+       } while (0); LOCK_GIVE(hdw->big_lock);
 }
 
+
 /* Return name for this driver instance */
 const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
 {
@@ -2689,6 +2591,7 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
                pvr2_i2c_core_sync(hdw);
                pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
                cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
+               pvr2_hdw_state_log_state(hdw);
                printk(KERN_INFO "pvrusb2: ==================  END STATUS CARD #%d  ==================\n", nr);
        } while (0); LOCK_GIVE(hdw->big_lock);
 }
@@ -2959,7 +2862,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
                           " without lock!!");
                return -EDEADLK;
        }
-       if ((!hdw->flag_ok) && !probe_fl) {
+       if (!hdw->flag_ok && !probe_fl) {
                pvr2_trace(PVR2_TRACE_ERROR_LEGS,
                           "Attempted to execute control transfer"
                           " when device not ok");
@@ -3167,7 +3070,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
 
        hdw->cmd_debug_state = 0;
        if ((status < 0) && (!probe_fl)) {
-               pvr2_hdw_render_useless_unlocked(hdw);
+               pvr2_hdw_render_useless(hdw);
        }
        return status;
 }
@@ -3227,24 +3130,17 @@ static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
 }
 
 
-static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw)
+void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
 {
        if (!hdw->flag_ok) return;
-       pvr2_trace(PVR2_TRACE_INIT,"render_useless");
-       hdw->flag_ok = 0;
+       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                  "Device being rendered inoperable");
        if (hdw->vid_stream) {
                pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
        }
-       hdw->flag_streaming_enabled = 0;
-       hdw->subsys_enabled_mask = 0;
-}
-
-
-void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
-{
-       LOCK_TAKE(hdw->ctl_lock);
-       pvr2_hdw_render_useless_unlocked(hdw);
-       LOCK_GIVE(hdw->ctl_lock);
+       hdw->flag_ok = 0;
+       trace_stbit("flag_ok",hdw->flag_ok);
+       pvr2_hdw_state_sched(hdw);
 }
 
 
@@ -3299,7 +3195,6 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
        int status;
        LOCK_TAKE(hdw->ctl_lock); do {
                pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset");
-               hdw->flag_ok = !0;
                hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET;
                status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
        } while (0); LOCK_GIVE(hdw->ctl_lock);
@@ -3349,26 +3244,473 @@ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
                        (runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF);
                status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
        } while (0); LOCK_GIVE(hdw->ctl_lock);
-       if (!status) {
-               hdw->subsys_enabled_mask =
-                       ((hdw->subsys_enabled_mask &
-                         ~(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) |
-                        (runFl ? (1<<PVR2_SUBSYS_B_USBSTREAM_RUN) : 0));
-       }
        return status;
 }
 
 
-void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
-                            struct pvr2_hdw_debug_info *ptr)
+/* Evaluate whether or not state_encoder_ok can change */
+static int state_eval_encoder_ok(struct pvr2_hdw *hdw)
+{
+       if (hdw->state_encoder_ok) return 0;
+       if (hdw->flag_tripped) return 0;
+       if (hdw->state_encoder_run) return 0;
+       if (hdw->state_encoder_config) return 0;
+       if (hdw->state_decoder_run) return 0;
+       if (hdw->state_usbstream_run) return 0;
+       if (pvr2_upload_firmware2(hdw) < 0) {
+               hdw->flag_tripped = !0;
+               trace_stbit("flag_tripped",hdw->flag_tripped);
+               return !0;
+       }
+       hdw->state_encoder_ok = !0;
+       trace_stbit("state_encoder_ok",hdw->state_encoder_ok);
+       return !0;
+}
+
+
+/* Evaluate whether or not state_encoder_config can change */
+static int state_eval_encoder_config(struct pvr2_hdw *hdw)
+{
+       if (hdw->state_encoder_config) {
+               if (hdw->state_encoder_ok) {
+                       if (hdw->state_pipeline_req &&
+                           !hdw->state_pipeline_pause) return 0;
+               }
+               hdw->state_encoder_config = 0;
+               hdw->state_encoder_waitok = 0;
+               trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
+               /* paranoia - solve race if timer just completed */
+               del_timer_sync(&hdw->encoder_wait_timer);
+       } else {
+               if (!hdw->state_encoder_ok ||
+                   !hdw->state_pipeline_idle ||
+                   hdw->state_pipeline_pause ||
+                   !hdw->state_pipeline_req ||
+                   !hdw->state_pipeline_config) {
+                       /* We must reset the enforced wait interval if
+                          anything has happened that might have disturbed
+                          the encoder.  This should be a rare case. */
+                       if (timer_pending(&hdw->encoder_wait_timer)) {
+                               del_timer_sync(&hdw->encoder_wait_timer);
+                       }
+                       if (hdw->state_encoder_waitok) {
+                               /* Must clear the state - therefore we did
+                                  something to a state bit and must also
+                                  return true. */
+                               hdw->state_encoder_waitok = 0;
+                               trace_stbit("state_encoder_waitok",
+                                           hdw->state_encoder_waitok);
+                               return !0;
+                       }
+                       return 0;
+               }
+               if (!hdw->state_encoder_waitok) {
+                       if (!timer_pending(&hdw->encoder_wait_timer)) {
+                               /* waitok flag wasn't set and timer isn't
+                                  running.  Check flag once more to avoid
+                                  a race then start the timer.  This is
+                                  the point when we measure out a minimal
+                                  quiet interval before doing something to
+                                  the encoder. */
+                               if (!hdw->state_encoder_waitok) {
+                                       hdw->encoder_wait_timer.expires =
+                                               jiffies + (HZ*50/1000);
+                                       add_timer(&hdw->encoder_wait_timer);
+                               }
+                       }
+                       /* We can't continue until we know we have been
+                          quiet for the interval measured by this
+                          timer. */
+                       return 0;
+               }
+               pvr2_encoder_configure(hdw);
+               if (hdw->state_encoder_ok) hdw->state_encoder_config = !0;
+       }
+       trace_stbit("state_encoder_config",hdw->state_encoder_config);
+       return !0;
+}
+
+
+/* Evaluate whether or not state_encoder_run can change */
+static int state_eval_encoder_run(struct pvr2_hdw *hdw)
+{
+       if (hdw->state_encoder_run) {
+               if (hdw->state_encoder_ok) {
+                       if (hdw->state_decoder_run) return 0;
+                       if (pvr2_encoder_stop(hdw) < 0) return !0;
+               }
+               hdw->state_encoder_run = 0;
+       } else {
+               if (!hdw->state_encoder_ok) return 0;
+               if (!hdw->state_decoder_run) return 0;
+               if (pvr2_encoder_start(hdw) < 0) return !0;
+               hdw->state_encoder_run = !0;
+       }
+       trace_stbit("state_encoder_run",hdw->state_encoder_run);
+       return !0;
+}
+
+
+/* Timeout function for quiescent timer. */
+static void pvr2_hdw_quiescent_timeout(unsigned long data)
+{
+       struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+       hdw->state_decoder_quiescent = !0;
+       trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
+       hdw->state_stale = !0;
+       queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+/* Timeout function for encoder wait timer. */
+static void pvr2_hdw_encoder_wait_timeout(unsigned long data)
+{
+       struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+       hdw->state_encoder_waitok = !0;
+       trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
+       hdw->state_stale = !0;
+       queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+/* Evaluate whether or not state_decoder_run can change */
+static int state_eval_decoder_run(struct pvr2_hdw *hdw)
+{
+       if (hdw->state_decoder_run) {
+               if (hdw->state_encoder_ok) {
+                       if (hdw->state_pipeline_req &&
+                           !hdw->state_pipeline_pause) return 0;
+               }
+               if (!hdw->flag_decoder_missed) {
+                       pvr2_decoder_enable(hdw,0);
+               }
+               hdw->state_decoder_quiescent = 0;
+               hdw->state_decoder_run = 0;
+               /* paranoia - solve race if timer just completed */
+               del_timer_sync(&hdw->quiescent_timer);
+       } else {
+               if (!hdw->state_decoder_quiescent) {
+                       if (!timer_pending(&hdw->quiescent_timer)) {
+                               /* We don't do something about the
+                                  quiescent timer until right here because
+                                  we also want to catch cases where the
+                                  decoder was already not running (like
+                                  after initialization) as opposed to
+                                  knowing that we had just stopped it.
+                                  The second flag check is here to cover a
+                                  race - the timer could have run and set
+                                  this flag just after the previous check
+                                  but before we did the pending check. */
+                               if (!hdw->state_decoder_quiescent) {
+                                       hdw->quiescent_timer.expires =
+                                               jiffies + (HZ*50/1000);
+                                       add_timer(&hdw->quiescent_timer);
+                               }
+                       }
+                       /* Don't allow decoder to start again until it has
+                          been quiesced first.  This little detail should
+                          hopefully further stabilize the encoder. */
+                       return 0;
+               }
+               if (!hdw->state_pipeline_req ||
+                   hdw->state_pipeline_pause ||
+                   !hdw->state_pipeline_config ||
+                   !hdw->state_encoder_config ||
+                   !hdw->state_encoder_ok) return 0;
+               del_timer_sync(&hdw->quiescent_timer);
+               if (hdw->flag_decoder_missed) return 0;
+               if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
+               hdw->state_decoder_quiescent = 0;
+               hdw->state_decoder_run = !0;
+       }
+       trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
+       trace_stbit("state_decoder_run",hdw->state_decoder_run);
+       return !0;
+}
+
+
+/* Evaluate whether or not state_usbstream_run can change */
+static int state_eval_usbstream_run(struct pvr2_hdw *hdw)
+{
+       if (hdw->state_usbstream_run) {
+               if (hdw->state_encoder_ok) {
+                       if (hdw->state_encoder_run) return 0;
+               }
+               pvr2_hdw_cmd_usbstream(hdw,0);
+               hdw->state_usbstream_run = 0;
+       } else {
+               if (!hdw->state_encoder_ok ||
+                   !hdw->state_encoder_run ||
+                   !hdw->state_pipeline_req ||
+                   hdw->state_pipeline_pause) return 0;
+               if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0;
+               hdw->state_usbstream_run = !0;
+       }
+       trace_stbit("state_usbstream_run",hdw->state_usbstream_run);
+       return !0;
+}
+
+
+/* Attempt to configure pipeline, if needed */
+static int state_eval_pipeline_config(struct pvr2_hdw *hdw)
+{
+       if (hdw->state_pipeline_config ||
+           hdw->state_pipeline_pause) return 0;
+       pvr2_hdw_commit_execute(hdw);
+       return !0;
+}
+
+
+/* Update pipeline idle and pipeline pause tracking states based on other
+   inputs.  This must be called whenever the other relevant inputs have
+   changed. */
+static int state_update_pipeline_state(struct pvr2_hdw *hdw)
+{
+       unsigned int st;
+       int updatedFl = 0;
+       /* Update pipeline state */
+       st = !(hdw->state_encoder_run ||
+              hdw->state_decoder_run ||
+              hdw->state_usbstream_run ||
+              (!hdw->state_decoder_quiescent));
+       if (!st != !hdw->state_pipeline_idle) {
+               hdw->state_pipeline_idle = st;
+               updatedFl = !0;
+       }
+       if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) {
+               hdw->state_pipeline_pause = 0;
+               updatedFl = !0;
+       }
+       return updatedFl;
+}
+
+
+typedef int (*state_eval_func)(struct pvr2_hdw *);
+
+/* Set of functions to be run to evaluate various states in the driver. */
+const static state_eval_func eval_funcs[] = {
+       state_eval_pipeline_config,
+       state_eval_encoder_ok,
+       state_eval_encoder_config,
+       state_eval_decoder_run,
+       state_eval_encoder_run,
+       state_eval_usbstream_run,
+};
+
+
+/* Process various states and return true if we did anything interesting. */
+static int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
+{
+       unsigned int i;
+       int state_updated = 0;
+       int check_flag;
+
+       if (!hdw->state_stale) return 0;
+       if ((hdw->fw1_state != FW1_STATE_OK) ||
+           !hdw->flag_ok) {
+               hdw->state_stale = 0;
+               return !0;
+       }
+       /* This loop is the heart of the entire driver.  It keeps trying to
+          evaluate various bits of driver state until nothing changes for
+          one full iteration.  Each "bit of state" tracks some global
+          aspect of the driver, e.g. whether decoder should run, if
+          pipeline is configured, usb streaming is on, etc.  We separately
+          evaluate each of those questions based on other driver state to
+          arrive at the correct running configuration. */
+       do {
+               check_flag = 0;
+               state_update_pipeline_state(hdw);
+               /* Iterate over each bit of state */
+               for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) {
+                       if ((*eval_funcs[i])(hdw)) {
+                               check_flag = !0;
+                               state_updated = !0;
+                               state_update_pipeline_state(hdw);
+                       }
+               }
+       } while (check_flag && hdw->flag_ok);
+       hdw->state_stale = 0;
+       trace_stbit("state_stale",hdw->state_stale);
+       return state_updated;
+}
+
+
+static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
+                                            char *buf,unsigned int acnt)
+{
+       switch (which) {
+       case 0:
+               return scnprintf(
+                       buf,acnt,
+                       "driver:%s%s%s%s%s",
+                       (hdw->flag_ok ? " <ok>" : " <fail>"),
+                       (hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
+                       (hdw->flag_disconnected ? " <disconnected>" :
+                        " <connected>"),
+                       (hdw->flag_tripped ? " <tripped>" : ""),
+                       (hdw->flag_decoder_missed ? " <no decoder>" : ""));
+       case 1:
+               return scnprintf(
+                       buf,acnt,
+                       "pipeline:%s%s%s%s",
+                       (hdw->state_pipeline_idle ? " <idle>" : ""),
+                       (hdw->state_pipeline_config ?
+                        " <configok>" : " <stale>"),
+                       (hdw->state_pipeline_req ? " <req>" : ""),
+                       (hdw->state_pipeline_pause ? " <pause>" : ""));
+       case 2:
+               return scnprintf(
+                       buf,acnt,
+                       "worker:%s%s%s%s%s%s",
+                       (hdw->state_decoder_run ?
+                        " <decode:run>" :
+                        (hdw->state_decoder_quiescent ?
+                         "" : " <decode:stop>")),
+                       (hdw->state_decoder_quiescent ?
+                        " <decode:quiescent>" : ""),
+                       (hdw->state_encoder_ok ?
+                        "" : " <encode:init>"),
+                       (hdw->state_encoder_run ?
+                        " <encode:run>" : " <encode:stop>"),
+                       (hdw->state_encoder_config ?
+                        " <encode:configok>" :
+                        (hdw->state_encoder_waitok ?
+                         "" : " <encode:wait>")),
+                       (hdw->state_usbstream_run ?
+                        " <usb:run>" : " <usb:stop>"));
+               break;
+       case 3:
+               return scnprintf(
+                       buf,acnt,
+                       "state: %s",
+                       pvr2_get_state_name(hdw->master_state));
+               break;
+       default: break;
+       }
+       return 0;
+}
+
+
+unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+                                  char *buf,unsigned int acnt)
+{
+       unsigned int bcnt,ccnt,idx;
+       bcnt = 0;
+       LOCK_TAKE(hdw->big_lock);
+       for (idx = 0; ; idx++) {
+               ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt);
+               if (!ccnt) break;
+               bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+               if (!acnt) break;
+               buf[0] = '\n'; ccnt = 1;
+               bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+       }
+       LOCK_GIVE(hdw->big_lock);
+       return bcnt;
+}
+
+
+static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
+{
+       char buf[128];
+       unsigned int idx,ccnt;
+
+       for (idx = 0; ; idx++) {
+               ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
+               if (!ccnt) break;
+               printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf);
+       }
+}
+
+
+/* Evaluate and update the driver's current state, taking various actions
+   as appropriate for the update. */
+static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
+{
+       unsigned int st;
+       int state_updated = 0;
+       int callback_flag = 0;
+
+       pvr2_trace(PVR2_TRACE_STBITS,
+                  "Drive state check START");
+       if (pvrusb2_debug & PVR2_TRACE_STBITS) {
+               pvr2_hdw_state_log_state(hdw);
+       }
+
+       /* Process all state and get back over disposition */
+       state_updated = pvr2_hdw_state_update(hdw);
+
+       /* Update master state based upon all other states. */
+       if (!hdw->flag_ok) {
+               st = PVR2_STATE_DEAD;
+       } else if (hdw->fw1_state != FW1_STATE_OK) {
+               st = PVR2_STATE_COLD;
+       } else if (!hdw->state_encoder_ok) {
+               st = PVR2_STATE_WARM;
+       } else if (hdw->flag_tripped || hdw->flag_decoder_missed) {
+               st = PVR2_STATE_ERROR;
+       } else if (hdw->state_encoder_run &&
+                  hdw->state_decoder_run &&
+                  hdw->state_usbstream_run) {
+               st = PVR2_STATE_RUN;
+       } else {
+               st = PVR2_STATE_READY;
+       }
+       if (hdw->master_state != st) {
+               pvr2_trace(PVR2_TRACE_STATE,
+                          "Device state change from %s to %s",
+                          pvr2_get_state_name(hdw->master_state),
+                          pvr2_get_state_name(st));
+               hdw->master_state = st;
+               state_updated = !0;
+               callback_flag = !0;
+       }
+       if (state_updated) {
+               /* Trigger anyone waiting on any state changes here. */
+               wake_up(&hdw->state_wait_data);
+       }
+
+       if (pvrusb2_debug & PVR2_TRACE_STBITS) {
+               pvr2_hdw_state_log_state(hdw);
+       }
+       pvr2_trace(PVR2_TRACE_STBITS,
+                  "Drive state check DONE callback=%d",callback_flag);
+
+       return callback_flag;
+}
+
+
+/* Cause kernel thread to check / update driver state */
+static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
+{
+       if (hdw->state_stale) return;
+       hdw->state_stale = !0;
+       trace_stbit("state_stale",hdw->state_stale);
+       queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw,
+                                     struct pvr2_hdw_debug_info *ptr)
 {
        ptr->big_lock_held = hdw->big_lock_held;
        ptr->ctl_lock_held = hdw->ctl_lock_held;
-       ptr->flag_ok = hdw->flag_ok;
        ptr->flag_disconnected = hdw->flag_disconnected;
        ptr->flag_init_ok = hdw->flag_init_ok;
-       ptr->flag_streaming_enabled = hdw->flag_streaming_enabled;
-       ptr->subsys_flags = hdw->subsys_enabled_mask;
+       ptr->flag_ok = hdw->flag_ok;
+       ptr->fw1_state = hdw->fw1_state;
+       ptr->flag_decoder_missed = hdw->flag_decoder_missed;
+       ptr->flag_tripped = hdw->flag_tripped;
+       ptr->state_encoder_ok = hdw->state_encoder_ok;
+       ptr->state_encoder_run = hdw->state_encoder_run;
+       ptr->state_decoder_run = hdw->state_decoder_run;
+       ptr->state_usbstream_run = hdw->state_usbstream_run;
+       ptr->state_decoder_quiescent = hdw->state_decoder_quiescent;
+       ptr->state_pipeline_config = hdw->state_pipeline_config;
+       ptr->state_pipeline_req = hdw->state_pipeline_req;
+       ptr->state_pipeline_pause = hdw->state_pipeline_pause;
+       ptr->state_pipeline_idle = hdw->state_pipeline_idle;
        ptr->cmd_debug_state = hdw->cmd_debug_state;
        ptr->cmd_code = hdw->cmd_debug_code;
        ptr->cmd_debug_write_len = hdw->cmd_debug_write_len;
@@ -3381,6 +3723,15 @@ void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
 }
 
 
+void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
+                                   struct pvr2_hdw_debug_info *ptr)
+{
+       LOCK_TAKE(hdw->ctl_lock); do {
+               pvr2_hdw_get_debug_info_unlocked(hdw,ptr);
+       } while(0); LOCK_GIVE(hdw->ctl_lock);
+}
+
+
 int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
 {
        return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
index e2f9d5e4cb653c191f1a07ed94bf14a5799d3a4f..383685f7c81f83509b6ba098c86c77f14a9442fd 100644 (file)
 #define PVR2_CVAL_INPUT_COMPOSITE 2
 #define PVR2_CVAL_INPUT_RADIO 3
 
-/* Subsystem definitions - these are various pieces that can be
-   independently stopped / started.  Usually you don't want to mess with
-   this directly (let the driver handle things itself), but it is useful
-   for debugging. */
-#define PVR2_SUBSYS_B_ENC_FIRMWARE        0
-#define PVR2_SUBSYS_B_ENC_CFG             1
-#define PVR2_SUBSYS_B_DIGITIZER_RUN       2
-#define PVR2_SUBSYS_B_USBSTREAM_RUN       3
-#define PVR2_SUBSYS_B_ENC_RUN             4
-
-#define PVR2_SUBSYS_CFG_ALL ( \
-       (1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \
-       (1 << PVR2_SUBSYS_B_ENC_CFG) )
-#define PVR2_SUBSYS_RUN_ALL ( \
-       (1 << PVR2_SUBSYS_B_DIGITIZER_RUN) | \
-       (1 << PVR2_SUBSYS_B_USBSTREAM_RUN) | \
-       (1 << PVR2_SUBSYS_B_ENC_RUN) )
-#define PVR2_SUBSYS_ALL ( \
-       PVR2_SUBSYS_CFG_ALL | \
-       PVR2_SUBSYS_RUN_ALL )
-
 enum pvr2_config {
        pvr2_config_empty,    /* No configuration */
        pvr2_config_mpeg,     /* Encoded / compressed video */
@@ -79,8 +58,41 @@ enum pvr2_v4l_type {
        pvr2_v4l_type_radio,
 };
 
+/* Major states that we can be in:
+ *
+ *  DEAD - Device is in an unusable state and cannot be recovered.  This
+ *  can happen if we completely lose the ability to communicate with it
+ *  (but it might still on the bus).  In this state there's nothing we can
+ *  do; it must be replugged in order to recover.
+ *
+ *  COLD - Device is in an unusuable state, needs microcontroller firmware.
+ *
+ *  WARM - We can communicate with the device and the proper
+ *  microcontroller firmware is running, but other device initialization is
+ *  still needed (e.g. encoder firmware).
+ *
+ *  ERROR - A problem prevents capture operation (e.g. encoder firmware
+ *  missing).
+ *
+ *  READY - Device is operational, but not streaming.
+ *
+ *  RUN - Device is streaming.
+ *
+ */
+#define PVR2_STATE_NONE 0
+#define PVR2_STATE_DEAD 1
+#define PVR2_STATE_COLD 2
+#define PVR2_STATE_WARM 3
+#define PVR2_STATE_ERROR 4
+#define PVR2_STATE_READY 5
+#define PVR2_STATE_RUN 6
+
+/* Translate configuration enum to a string label */
 const char *pvr2_config_get_name(enum pvr2_config);
 
+/* Translate a master state enum to a string label */
+const char *pvr2_hdw_get_state_name(unsigned int);
+
 struct pvr2_hdw;
 
 /* Create and return a structure for interacting with the underlying
@@ -88,28 +100,13 @@ struct pvr2_hdw;
 struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
                                 const struct usb_device_id *devid);
 
-/* Poll for background activity (if any) */
-void pvr2_hdw_poll(struct pvr2_hdw *);
-
-/* Trigger a poll to take place later at a convenient time */
-void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *);
-
-/* Register a callback used to trigger a future poll */
-void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *,
-                                void (*func)(void *),
-                                void *data);
-
 /* Destroy hardware interaction structure */
 void pvr2_hdw_destroy(struct pvr2_hdw *);
 
-/* Set up the structure and attempt to put the device into a usable state.
-   This can be a time-consuming operation, which is why it is not done
-   internally as part of the create() step.  Return value is exactly the
-   same as pvr2_hdw_init_ok(). */
-int pvr2_hdw_setup(struct pvr2_hdw *);
-
-/* Initialization succeeded */
-int pvr2_hdw_init_ok(struct pvr2_hdw *);
+/* Register a function to be called whenever the master state changes. */
+void pvr2_hdw_set_state_callback(struct pvr2_hdw *,
+                                void (*callback_func)(void *),
+                                void *callback_data);
 
 /* Return true if in the ready (normal) state */
 int pvr2_hdw_dev_ok(struct pvr2_hdw *);
@@ -167,6 +164,9 @@ int pvr2_hdw_set_streaming(struct pvr2_hdw *,int);
 /* Find out if streaming is on */
 int pvr2_hdw_get_streaming(struct pvr2_hdw *);
 
+/* Retrieve driver overall state */
+int pvr2_hdw_get_state(struct pvr2_hdw *);
+
 /* Configure the type of stream to generate */
 int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config);
 
@@ -177,26 +177,6 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
 int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
                               unsigned int idx);
 
-/* Enable / disable various pieces of hardware.  Items to change are
-   identified by bit positions within msk, and new state for each item is
-   identified by corresponding bit positions within val. */
-void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
-                            unsigned long msk,unsigned long val);
-
-/* Retrieve mask indicating which pieces of hardware are currently enabled
-   / configured. */
-unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *);
-
-/* Adjust mask of what get shut down when streaming is stopped.  This is a
-   debugging aid. */
-void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
-                                   unsigned long msk,unsigned long val);
-
-/* Retrieve mask indicating which pieces of hardware are disabled when
-   streaming is turned off. */
-unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *);
-
-
 /* Enable / disable retrieval of CPU firmware or prom contents.  This must
    be enabled before pvr2_hdw_cpufw_get() will function.  Note that doing
    this may prevent the device from running (and leaving this mode may
@@ -253,6 +233,9 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int);
 /* Execute a USB-commanded device reset */
 void pvr2_hdw_device_reset(struct pvr2_hdw *);
 
+/* Reset worker's error trapping circuit breaker */
+int pvr2_hdw_untrip(struct pvr2_hdw *);
+
 /* Execute hard reset command (after this point it's likely that the
    encoder will have to be reconfigured).  This also clears the "useless"
    state. */
@@ -275,11 +258,21 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val);
 struct pvr2_hdw_debug_info {
        int big_lock_held;
        int ctl_lock_held;
-       int flag_ok;
        int flag_disconnected;
        int flag_init_ok;
-       int flag_streaming_enabled;
-       unsigned long subsys_flags;
+       int flag_ok;
+       int fw1_state;
+       int flag_decoder_missed;
+       int flag_tripped;
+       int state_encoder_ok;
+       int state_encoder_run;
+       int state_decoder_run;
+       int state_usbstream_run;
+       int state_decoder_quiescent;
+       int state_pipeline_config;
+       int state_pipeline_req;
+       int state_pipeline_pause;
+       int state_pipeline_idle;
        int cmd_debug_state;
        int cmd_debug_write_len;
        int cmd_debug_read_len;
@@ -295,8 +288,20 @@ struct pvr2_hdw_debug_info {
    diagnosing lockups.  Note that this operation is completed without any
    kind of locking and so it is not atomic and may yield inconsistent
    results.  This is *purely* a debugging aid. */
-void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
-                            struct pvr2_hdw_debug_info *);
+void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw,
+                                     struct pvr2_hdw_debug_info *);
+
+/* Intrusively retrieve internal state info - this is useful for
+   diagnosing overall driver state.  This operation synchronizes against
+   the overall driver mutex - so if there are locking problems this will
+   likely hang!  This is *purely* a debugging aid. */
+void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
+                                   struct pvr2_hdw_debug_info *);
+
+/* Report out several lines of text that describes driver internal state.
+   Results are written into the passed-in buffer. */
+unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+                                  char *buf_ptr,unsigned int buf_size);
 
 /* Cause modules to log their state once */
 void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw);
index c817c864e6a020b94ee5039744190fe4193c2642..f8b7bd1e0d89bad59888272e07b185dd140c5bdf 100644 (file)
@@ -895,7 +895,7 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client)
                list_add_tail(&cp->list,&hdw->i2c_clients);
                hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
        } while (0); mutex_unlock(&hdw->i2c_list_lock);
-       if (fl) pvr2_hdw_poll_trigger_unlocked(hdw);
+       if (fl) queue_work(hdw->workqueue,&hdw->worki2csync);
        return 0;
 }
 
index 7a596ea7cfe698bc59d7f39299845d73e91dfd1c..6f06f595b86cbbd5bde578ab693f4083b3161893 100644 (file)
@@ -1015,10 +1015,8 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
        sp = fh->dev_info->stream->stream;
        pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
        pvr2_hdw_set_stream_type(hdw,fh->dev_info->config);
-       pvr2_hdw_set_streaming(hdw,!0);
-       ret = pvr2_ioread_set_enabled(fh->rhp,!0);
-
-       return ret;
+       if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
+       return pvr2_ioread_set_enabled(fh->rhp,!0);
 }
 
 
index 61efa6f02200e74dee598bf1d1ee765aed3faea9..767e49022f9b7ca8365c5a0594d5d11e1109bdfd 100644 (file)
@@ -129,7 +129,7 @@ static const struct pvr2_v4l_decoder_ops decoder_ops[] = {
 static void decoder_detach(struct pvr2_v4l_decoder *ctxt)
 {
        ctxt->client->handler = NULL;
-       ctxt->hdw->decoder_ctrl = NULL;
+       pvr2_hdw_set_decoder(ctxt->hdw,NULL);
        kfree(ctxt);
 }
 
@@ -217,7 +217,7 @@ int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw,
        ctxt->client = cp;
        ctxt->hdw = hdw;
        ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
-       hdw->decoder_ctrl = &ctxt->ctrl;
+       pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
        cp->handler = &ctxt->handler;
        pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up",
                   cp->client->addr);