V4L/DVB (7719): pvrusb2: Implement input selection enforcement

In the pvrusb2 driver, different interfaces (e.g. V4L, DVB) have

Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
Mike Isely 2008-04-21 03:47:43 -03:00 committed by Mauro Carvalho Chehab
parent d3f8d8fb30
commit 1cb03b76d0
7 changed files with 246 additions and 74 deletions

View File

@ -245,6 +245,22 @@ struct pvr2_context *pvr2_context_create(
}
static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
{
unsigned int tmsk,mmsk;
struct pvr2_channel *cp;
struct pvr2_hdw *hdw = mp->hdw;
mmsk = pvr2_hdw_get_input_available(hdw);
tmsk = mmsk;
for (cp = mp->mc_first; cp; cp = cp->mc_next) {
if (!cp->input_mask) continue;
tmsk &= cp->input_mask;
}
pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
pvr2_hdw_commit_ctl(hdw);
}
static void pvr2_context_enter(struct pvr2_context *mp)
{
mutex_lock(&mp->mutex);
@ -300,7 +316,9 @@ void pvr2_channel_done(struct pvr2_channel *cp)
{
struct pvr2_context *mp = cp->mc_head;
pvr2_context_enter(mp);
cp->input_mask = 0;
pvr2_channel_disclaim_stream(cp);
pvr2_context_reset_input_limits(mp);
if (cp->mc_next) {
cp->mc_next->mc_prev = cp->mc_prev;
} else {
@ -316,6 +334,57 @@ void pvr2_channel_done(struct pvr2_channel *cp)
}
int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
{
unsigned int tmsk,mmsk;
int ret = 0;
struct pvr2_channel *p2;
struct pvr2_hdw *hdw = cp->hdw;
mmsk = pvr2_hdw_get_input_available(hdw);
cmsk &= mmsk;
if (cmsk == cp->input_mask) {
/* No change; nothing to do */
return 0;
}
pvr2_context_enter(cp->mc_head);
do {
if (!cmsk) {
cp->input_mask = 0;
pvr2_context_reset_input_limits(cp->mc_head);
break;
}
tmsk = mmsk;
for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
if (p2 == cp) continue;
if (!p2->input_mask) continue;
tmsk &= p2->input_mask;
}
if (!(tmsk & cmsk)) {
ret = -EPERM;
break;
}
tmsk &= cmsk;
if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
/* Internal failure changing allowed list; probably
should not happen, but react if it does. */
break;
}
cp->input_mask = cmsk;
pvr2_hdw_commit_ctl(hdw);
} while (0);
pvr2_context_exit(cp->mc_head);
return ret;
}
unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
{
return cp->input_mask;
}
int pvr2_channel_claim_stream(struct pvr2_channel *cp,
struct pvr2_context_stream *sp)
{

View File

@ -62,6 +62,7 @@ struct pvr2_channel {
struct pvr2_channel *mc_prev;
struct pvr2_context_stream *stream;
struct pvr2_hdw *hdw;
unsigned int input_mask;
void (*check_func)(struct pvr2_channel *);
};
@ -72,6 +73,8 @@ void pvr2_context_disconnect(struct pvr2_context *);
void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *);
void pvr2_channel_done(struct pvr2_channel *);
int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int);
unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *);
int pvr2_channel_claim_stream(struct pvr2_channel *,
struct pvr2_context_stream *);
struct pvr2_ioread *pvr2_channel_create_mpeg_stream(

View File

@ -244,13 +244,10 @@ static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
{
/* TO DO: This function will call into the core and request for
* input to be set to 'dtv' if (acquire) and if it isn't set already.
*
* If (!acquire) then we should do nothing -- don't switch inputs
* again unless the analog side of the driver requests the bus.
*/
return 0;
struct pvr2_dvb_adapter *adap = fe->dvb->priv;
return pvr2_channel_limit_inputs(
&adap->channel,
(acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0));
}
static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap)
@ -320,32 +317,26 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
{
struct pvr2_hdw *hdw = adap->channel.hdw;
struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props;
int ret;
int ret = 0;
if (dvb_props == NULL) {
err("fe_props not defined!");
return -EINVAL;
}
/* FIXME: This code should be moved into the core,
* and should only be called if we don't already have
* control of the bus.
*
* We can't call "pvr2_dvb_bus_ctrl(adap->fe, 1)" from here,
* because adap->fe isn't defined yet.
*/
ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_by_id(hdw,
PVR2_CID_INPUT),
PVR2_CVAL_INPUT_DTV);
if (ret != 0)
ret = pvr2_channel_limit_inputs(
&adap->channel,
(1 << PVR2_CVAL_INPUT_DTV));
if (ret) {
err("failed to grab control of dtv input (code=%d)",
ret);
return ret;
pvr2_hdw_commit_ctl(hdw);
}
if (dvb_props->frontend_attach == NULL) {
err("frontend_attach not defined!");
return -EINVAL;
ret = -EINVAL;
goto done;
}
if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) {
@ -354,7 +345,8 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
err("frontend registration failed!");
dvb_frontend_detach(adap->fe);
adap->fe = NULL;
return -ENODEV;
ret = -ENODEV;
goto done;
}
if (dvb_props->tuner_attach)
@ -368,10 +360,13 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
} else {
err("no frontend was attached!");
return -ENODEV;
ret = -ENODEV;
return ret;
}
return 0;
done:
pvr2_channel_limit_inputs(&adap->channel, 0);
return ret;
}
static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap)

View File

@ -336,8 +336,10 @@ struct pvr2_hdw {
int v4l_minor_number_vbi;
int v4l_minor_number_radio;
/* Bit mask of PVR2_CVAL_INPUT choices which are valid */
/* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */
unsigned int input_avail_mask;
/* Bit mask of PVR2_CVAL_INPUT choices which are currenly allowed */
unsigned int input_allowed_mask;
/* Location of eeprom or a negative number if none */
int eeprom_addr;

View File

@ -249,6 +249,7 @@ static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = {
};
static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v);
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);
@ -404,30 +405,12 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp)
static int ctrl_check_input(struct pvr2_ctrl *cptr,int v)
{
return ((1 << v) & cptr->hdw->input_avail_mask) != 0;
return ((1 << v) & cptr->hdw->input_allowed_mask) != 0;
}
static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v)
{
struct pvr2_hdw *hdw = cptr->hdw;
if (hdw->input_val != v) {
hdw->input_val = v;
hdw->input_dirty = !0;
}
/* Handle side effects - if we switch to a mode that needs the RF
tuner, then select the right frequency choice as well and mark
it dirty. */
if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
hdw->freqSelector = 0;
hdw->freqDirty = !0;
} else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) ||
(hdw->input_val == PVR2_CVAL_INPUT_DTV)) {
hdw->freqSelector = 1;
hdw->freqDirty = !0;
}
return 0;
return pvr2_hdw_set_input(cptr->hdw,v);
}
static int ctrl_isdirty_input(struct pvr2_ctrl *cptr)
@ -1916,6 +1899,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE;
if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO;
hdw->input_avail_mask = m;
hdw->input_allowed_mask = hdw->input_avail_mask;
/* If not a hybrid device, pathway_state never changes. So
initialize it here to what it should forever be. */
@ -3948,6 +3932,24 @@ static int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
}
static unsigned int print_input_mask(unsigned int msk,
char *buf,unsigned int acnt)
{
unsigned int idx,ccnt;
unsigned int tcnt = 0;
for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) {
if (!((1 << idx) & msk)) continue;
ccnt = scnprintf(buf+tcnt,
acnt-tcnt,
"%s%s",
(tcnt ? ", " : ""),
control_values_input[idx]);
tcnt += ccnt;
}
return tcnt;
}
static const char *pvr2_pathway_state_name(int id)
{
switch (id) {
@ -4016,6 +4018,28 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
"state: %s",
pvr2_get_state_name(hdw->master_state));
case 4: {
unsigned int tcnt = 0;
unsigned int ccnt;
ccnt = scnprintf(buf,
acnt,
"Hardware supported inputs: ");
tcnt += ccnt;
tcnt += print_input_mask(hdw->input_avail_mask,
buf+tcnt,
acnt-tcnt);
if (hdw->input_avail_mask != hdw->input_allowed_mask) {
ccnt = scnprintf(buf+tcnt,
acnt-tcnt,
"; allowed inputs: ");
tcnt += ccnt;
tcnt += print_input_mask(hdw->input_allowed_mask,
buf+tcnt,
acnt-tcnt);
}
return tcnt;
}
case 5: {
struct pvr2_stream_stats stats;
if (!hdw->vid_stream) break;
pvr2_stream_get_stats(hdw->vid_stream,
@ -4210,6 +4234,74 @@ unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
}
unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw)
{
return hdw->input_allowed_mask;
}
static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v)
{
if (hdw->input_val != v) {
hdw->input_val = v;
hdw->input_dirty = !0;
}
/* Handle side effects - if we switch to a mode that needs the RF
tuner, then select the right frequency choice as well and mark
it dirty. */
if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
hdw->freqSelector = 0;
hdw->freqDirty = !0;
} else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) ||
(hdw->input_val == PVR2_CVAL_INPUT_DTV)) {
hdw->freqSelector = 1;
hdw->freqDirty = !0;
}
return 0;
}
int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw,
unsigned int change_mask,
unsigned int change_val)
{
int ret = 0;
unsigned int nv,m,idx;
LOCK_TAKE(hdw->big_lock);
do {
nv = hdw->input_allowed_mask & ~change_mask;
nv |= (change_val & change_mask);
nv &= hdw->input_avail_mask;
if (!nv) {
/* No legal modes left; return error instead. */
ret = -EPERM;
break;
}
hdw->input_allowed_mask = nv;
if ((1 << hdw->input_val) & hdw->input_allowed_mask) {
/* Current mode is still in the allowed mask, so
we're done. */
break;
}
/* Select and switch to a mode that is still in the allowed
mask */
if (!hdw->input_allowed_mask) {
/* Nothing legal; give up */
break;
}
m = hdw->input_allowed_mask;
for (idx = 0; idx < (sizeof(m) << 3); idx++) {
if (!((1 << idx) & m)) continue;
pvr2_hdw_set_input(hdw,idx);
break;
}
} while (0);
LOCK_GIVE(hdw->big_lock);
return ret;
}
/* Find I2C address of eeprom */
static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
{

View File

@ -149,6 +149,19 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *);
* will be according to PVR_CVAL_INPUT_xxxx definitions. */
unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *);
/* Return a bit mask of allowed input selections for this device. Mask bits
* will be according to PVR_CVAL_INPUT_xxxx definitions. */
unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *);
/* Change the set of allowed input selections for this device. Both
change_mask and change_valu are mask bits according to
PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate
which settings are being changed and the change_val parameter indicates
whether corresponding settings are being set or cleared. */
int pvr2_hdw_set_input_allowed(struct pvr2_hdw *,
unsigned int change_mask,
unsigned int change_val);
/* Return name for this driver instance */
const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *);

View File

@ -57,7 +57,6 @@ struct pvr2_v4l2_fh {
struct pvr2_v4l2_fh *vprev;
wait_queue_head_t wait_data;
int fw_mode_flag;
int prev_input_val;
};
struct pvr2_v4l2 {
@ -900,20 +899,6 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file)
v4l2_prio_close(&vp->prio, &fhp->prio);
file->private_data = NULL;
/* Restore the previous input selection, if it makes sense
to do so. */
if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) {
struct pvr2_ctrl *cp;
int pval;
cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
pvr2_ctrl_get_value(cp,&pval);
/* Only restore if we're still selecting the radio */
if (pval == PVR2_CVAL_INPUT_RADIO) {
pvr2_ctrl_set_value(cp,fhp->prev_input_val);
pvr2_hdw_commit_ctl(hdw);
}
}
if (fhp->vnext) {
fhp->vnext->vprev = fhp->vprev;
} else {
@ -944,6 +929,8 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file)
struct pvr2_v4l2_fh *fhp;
struct pvr2_v4l2 *vp;
struct pvr2_hdw *hdw;
unsigned int input_mask = 0;
int ret = 0;
dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
@ -969,6 +956,29 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file)
pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
if (dip->v4l_type == VFL_TYPE_RADIO) {
/* Opening device as a radio, legal input selection subset
is just the radio. */
input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
} else {
/* Opening the main V4L device, legal input selection
subset includes all analog inputs. */
input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
(1 << PVR2_CVAL_INPUT_TV) |
(1 << PVR2_CVAL_INPUT_COMPOSITE) |
(1 << PVR2_CVAL_INPUT_SVIDEO));
}
ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
if (ret) {
pvr2_channel_done(&fhp->channel);
pvr2_trace(PVR2_TRACE_STRUCT,
"Destroying pvr_v4l2_fh id=%p (input mask error)",
fhp);
kfree(fhp);
return ret;
}
fhp->vnext = NULL;
fhp->vprev = vp->vlast;
if (vp->vlast) {
@ -979,18 +989,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file)
vp->vlast = fhp;
fhp->vhead = vp;
/* Opening the /dev/radioX device implies a mode switch.
So execute that here. Note that you can get the
IDENTICAL effect merely by opening the normal video
device and setting the input appropriately. */
if (dip->v4l_type == VFL_TYPE_RADIO) {
struct pvr2_ctrl *cp;
cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
pvr2_ctrl_get_value(cp,&fhp->prev_input_val);
pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO);
pvr2_hdw_commit_ctl(hdw);
}
fhp->file = file;
file->private_data = fhp;
v4l2_prio_open(&vp->prio,&fhp->prio);