Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1cb03b76 authored by Mike Isely's avatar Mike Isely Committed by Mauro Carvalho Chehab
Browse files

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



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

Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent d3f8d8fb
Loading
Loading
Loading
Loading
+69 −0
Original line number Diff line number Diff line
@@ -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)
{
+3 −0
Original line number Diff line number Diff line
@@ -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(
+21 −26
Original line number Diff line number Diff line
@@ -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)
+3 −1
Original line number Diff line number Diff line
@@ -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;
+112 −20
Original line number Diff line number Diff line
@@ -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)
{
Loading