mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 16:41:58 +00:00
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:
parent
d3f8d8fb30
commit
1cb03b76d0
@ -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)
|
||||
{
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 *);
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user