};
 
 
-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;
 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)
 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));
        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;
 }
 
 
 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;
 }
 
        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)
 {
                .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",
                .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",
           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*/
        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);
 }
 
 
            (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.
 
        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;
 
        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"
                                " 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;
 }
 
 
        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);
 
        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);
        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;
                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);
 }
 
 
 {
        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;
 }
 
 
-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);
 }
 
 
-/* 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;
                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. */
                }
        }
 
-       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
                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)
 {
                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);
 }
                           " 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");
 
        hdw->cmd_debug_state = 0;
        if ((status < 0) && (!probe_fl)) {
-               pvr2_hdw_render_useless_unlocked(hdw);
+               pvr2_hdw_render_useless(hdw);
        }
        return status;
 }
 }
 
 
-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);
 }
 
 
        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);
                        (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;
 }
 
 
+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);