>> Adding a "bus lock" to af9015_i2c_xfer() will not work as demod/tuner >> accesses will take multiple i2c transactions. >> >> Therefore, the following patch overrides the dvb_frontend_ops >> functions to add a per-device lock around them: only one frontend can >> now use the i2c bus at a time. Testing with the scripts above shows >> this has eliminated the errors. > > This have annoyed me too, but since it does not broken functionality much I > haven't put much effort for fixing it. I like that fix since it is in AF9015 > driver where it logically belongs to. But it looks still rather complex. I > see you have also considered "bus lock" to af9015_i2c_xfer() which could be > much smaller in code size (that's I have tried to implement long time back). > > I would like to ask if it possible to check I2C gate open / close inside > af9015_i2c_xfer() and lock according that? Something like: Hmm, I did think about that, but I felt overriding the functions was just cleaner: I felt it was more obvious what it was doing. Doing exactly this sort of tweaking was one of the main reasons we added that function overriding feature. I don't like the idea of returning "error locked by FE" since that'll mean the tuning will randomly fail sometimes in a way visible to userspace (unless we change the core dvb_frontend code), which was one of the things I was trying to avoid. Unless, of course, I've misunderstood your proposal. However, looking at the code again, I realise it is possible to simplify it. Since its only the demod gates that cause a problem, we only /actually/ need to lock the get_frontend() and set_frontend() calls. Signed-off-by: Andrew de Quincey <adq_dvb@xxxxxxxxxxxxx>
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 31c0a0e..52ac6ef 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -1083,6 +1083,34 @@ static int af9015_i2c_init(struct dvb_usb_device *d) return ret; } +static int af9015_lock_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + int result; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct af9015_state *state = adap->dev->priv; + + if (mutex_lock_interruptible(&adap->dev->usb_mutex)) + return -EAGAIN; + + result = state->fe_ops[adap->id].set_frontend(fe, params); + mutex_unlock(&adap->dev->usb_mutex); + return result; +} + +static int af9015_lock_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +{ + int result; + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct af9015_state *state = adap->dev->priv; + + if (mutex_lock_interruptible(&adap->dev->usb_mutex)) + return -EAGAIN; + + result = state->fe_ops[adap->id].get_frontend(fe, params); + mutex_unlock(&adap->dev->usb_mutex); + return result; +} + static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) { int ret; @@ -1116,6 +1144,12 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) /* attach demodulator */ adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id], i2c_adap); + + memcpy(&state->fe_ops[adap->id], &adap->fe->ops, sizeof(struct dvb_frontend_ops)); + if (adap->fe->ops.set_frontend) + adap->fe->ops.set_frontend = af9015_lock_set_frontend; + if (adap->fe->ops.get_frontend) + adap->fe->ops.get_frontend = af9015_lock_get_frontend; return adap->fe == NULL ? -ENODEV : 0; } diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index f20cfa6..759bb3f 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -102,6 +102,7 @@ struct af9015_state { struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */ u8 rc_repeat; u32 rc_keycode; + struct dvb_frontend_ops fe_ops[2]; }; struct af9015_config {