aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/pvrusb2/pvrusb2-hdw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c')
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c1301
1 files changed, 809 insertions, 492 deletions
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 402c59488253..41ae980405ed 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -41,47 +41,6 @@
#define TV_MIN_FREQ 55250000L
#define TV_MAX_FREQ 850000000L
-struct usb_device_id pvr2_device_table[] = {
- [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) },
- [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, pvr2_device_table);
-
-static const char *pvr2_device_names[] = {
- [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx",
- [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx",
-};
-
-struct pvr2_string_table {
- const char **lst;
- unsigned int cnt;
-};
-
-// Names of other client modules to request for 24xxx model hardware
-static const char *pvr2_client_24xxx[] = {
- "cx25840",
- "tuner",
- "wm8775",
-};
-
-// Names of other client modules to request for 29xxx model hardware
-static const char *pvr2_client_29xxx[] = {
- "msp3400",
- "saa7115",
- "tuner",
-};
-
-static struct pvr2_string_table pvr2_client_lists[] = {
- [PVR2_HDW_TYPE_29XXX] = {
- pvr2_client_29xxx, ARRAY_SIZE(pvr2_client_29xxx)
- },
- [PVR2_HDW_TYPE_24XXX] = {
- pvr2_client_24xxx, ARRAY_SIZE(pvr2_client_24xxx)
- },
-};
-
static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
static DEFINE_MUTEX(pvr2_unit_mtx);
@@ -246,32 +205,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;
@@ -380,8 +353,8 @@ static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
{
- /* Actual minimum depends on device type. */
- if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ /* Actual minimum depends on device digitizer type. */
+ if (cptr->hdw->hdw_desc->flag_has_cx25840) {
*vp = 75;
} else {
*vp = 17;
@@ -480,6 +453,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 +476,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 +485,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 +531,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 +650,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)
{
@@ -915,6 +885,11 @@ static const struct pvr2_ctl_info control_defs[] = {
.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",
.get_value = ctrl_signal_get,
@@ -955,20 +930,6 @@ static const struct pvr2_ctl_info control_defs[] = {
.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",
.internal_id = PVR2_CID_STDENUM,
@@ -1129,25 +1090,13 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
unsigned int pipe;
int ret;
u16 address;
- static const char *fw_files_29xxx[] = {
- "v4l-pvrusb2-29xxx-01.fw",
- };
- static const char *fw_files_24xxx[] = {
- "v4l-pvrusb2-24xxx-01.fw",
- };
- static const struct pvr2_string_table fw_file_defs[] = {
- [PVR2_HDW_TYPE_29XXX] = {
- fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx)
- },
- [PVR2_HDW_TYPE_24XXX] = {
- fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx)
- },
- };
- if ((hdw->hdw_type >= ARRAY_SIZE(fw_file_defs)) ||
- (!fw_file_defs[hdw->hdw_type].lst)) {
+ if (!hdw->hdw_desc->fx2_firmware.cnt) {
hdw->fw1_state = FW1_STATE_OK;
- return 0;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Connected device type defines"
+ " no firmware to upload; ignoring firmware");
+ return -ENOTTY;
}
hdw->fw1_state = FW1_STATE_FAILED; // default result
@@ -1155,8 +1104,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
trace_firmware("pvr2_upload_firmware1");
ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
- fw_file_defs[hdw->hdw_type].cnt,
- fw_file_defs[hdw->hdw_type].lst);
+ hdw->hdw_desc->fx2_firmware.cnt,
+ hdw->hdw_desc->fx2_firmware.lst);
if (ret < 0) {
if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
return ret;
@@ -1231,8 +1180,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
CX2341X_FIRM_ENC_FILENAME,
};
- if ((hdw->hdw_type != PVR2_HDW_TYPE_29XXX) &&
- (hdw->hdw_type != PVR2_HDW_TYPE_24XXX)) {
+ if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
return 0;
}
@@ -1248,8 +1196,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 +1293,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);
}
@@ -1646,6 +1428,7 @@ static int get_default_tuner_type(struct pvr2_hdw *hdw)
}
if (tp < 0) return -EINVAL;
hdw->tuner_type = tp;
+ hdw->tuner_updated = !0;
return 0;
}
@@ -1656,8 +1439,9 @@ static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
int tp = 0;
if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
tp = video_std[unit_number];
+ if (tp) return tp;
}
- return tp;
+ return 0;
}
@@ -1731,7 +1515,7 @@ const static struct pvr2_std_hack std_eeprom_maps[] = {
},
{ /* PAL(D/D1/K) */
.pat = V4L2_STD_DK,
- .std = V4L2_STD_PAL_D/V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
+ .std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
},
};
@@ -1739,18 +1523,20 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
{
char buf[40];
unsigned int bcnt;
- v4l2_std_id std1,std2;
+ v4l2_std_id std1,std2,std3;
std1 = get_default_standard(hdw);
+ std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask;
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
pvr2_trace(PVR2_TRACE_STD,
- "Supported video standard(s) reported by eeprom: %.*s",
+ "Supported video standard(s) reported available"
+ " in hardware: %.*s",
bcnt,buf);
hdw->std_mask_avail = hdw->std_mask_eeprom;
- std2 = std1 & ~hdw->std_mask_avail;
+ std2 = (std1|std3) & ~hdw->std_mask_avail;
if (std2) {
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
pvr2_trace(PVR2_TRACE_STD,
@@ -1772,6 +1558,16 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
pvr2_hdw_internal_find_stdenum(hdw);
return;
}
+ if (std3) {
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3);
+ pvr2_trace(PVR2_TRACE_STD,
+ "Initial video standard"
+ " (determined by device type): %.*s",bcnt,buf);
+ hdw->std_mask_cur = std3;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return;
+ }
{
unsigned int idx;
@@ -1816,8 +1612,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
unsigned int idx;
struct pvr2_ctrl *cptr;
int reloadFl = 0;
- if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) ||
- (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
+ if (hdw->hdw_desc->fx2_firmware.cnt) {
if (!reloadFl) {
reloadFl =
(hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
@@ -1853,25 +1648,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
}
if (!pvr2_hdw_dev_ok(hdw)) return;
- if (hdw->hdw_type < ARRAY_SIZE(pvr2_client_lists)) {
- for (idx = 0;
- idx < pvr2_client_lists[hdw->hdw_type].cnt;
- idx++) {
- request_module(
- pvr2_client_lists[hdw->hdw_type].lst[idx]);
- }
+ for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) {
+ request_module(hdw->hdw_desc->client_modules.lst[idx]);
}
- if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) ||
- (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) {
+ if (!hdw->hdw_desc->flag_no_powerup) {
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.
@@ -1899,15 +1682,22 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
// thread-safe against the normal pvr2_send_request() mechanism.
// (We should make it thread safe).
- ret = pvr2_hdw_get_eeprom_addr(hdw);
- if (!pvr2_hdw_dev_ok(hdw)) return;
- if (ret < 0) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Unable to determine location of eeprom, skipping");
- } else {
- hdw->eeprom_addr = ret;
- pvr2_eeprom_analyze(hdw);
+ if (hdw->hdw_desc->flag_has_hauppauge_rom) {
+ ret = pvr2_hdw_get_eeprom_addr(hdw);
if (!pvr2_hdw_dev_ok(hdw)) return;
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Unable to determine location of eeprom,"
+ " skipping");
+ } else {
+ hdw->eeprom_addr = ret;
+ pvr2_eeprom_analyze(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ }
+ } else {
+ hdw->tuner_type = hdw->hdw_desc->default_tuner_type;
+ hdw->tuner_updated = !0;
+ hdw->std_mask_eeprom = V4L2_STD_ALL;
}
pvr2_hdw_setup_std(hdw);
@@ -1918,14 +1708,12 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
hdw->tuner_type);
}
- hdw->tuner_updated = !0;
pvr2_i2c_core_check_stale(hdw);
hdw->tuner_updated = 0;
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 +1733,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 +1801,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;
}
@@ -2026,24 +1813,32 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
{
unsigned int idx,cnt1,cnt2;
struct pvr2_hdw *hdw;
- unsigned int hdw_type;
int valid_std_mask;
struct pvr2_ctrl *cptr;
+ const struct pvr2_device_desc *hdw_desc;
__u8 ifnum;
struct v4l2_queryctrl qctrl;
struct pvr2_ctl_info *ciptr;
- hdw_type = devid - pvr2_device_table;
- if (hdw_type >= ARRAY_SIZE(pvr2_device_names)) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Bogus device type of %u reported",hdw_type);
- return NULL;
- }
+ hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
- hdw,pvr2_device_names[hdw_type]);
+ hdw,hdw_desc->description);
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);
@@ -2052,7 +1847,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt,
GFP_KERNEL);
if (!hdw->controls) goto fail;
- hdw->hdw_type = hdw_type;
+ hdw->hdw_desc = hdw_desc;
for (idx = 0; idx < hdw->control_cnt; idx++) {
cptr = hdw->controls + idx;
cptr->hdw = hdw;
@@ -2184,18 +1979,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 +2004,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 +2053,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 +2065,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 +2100,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 +2277,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 +2316,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 +2353,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 +2401,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)
{
@@ -2634,6 +2490,18 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
}
+const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw)
+{
+ return hdw->hdw_desc->description;
+}
+
+
+const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw)
+{
+ return hdw->hdw_desc->shortname;
+}
+
+
int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
{
int result;
@@ -2689,6 +2557,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 +2828,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 +3036,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 +3096,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 +3161,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 +3210,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 +3689,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);