Re: [PATCH v2 1/2] iio: mma8452: Fix probe failing when an i2c_device_id is used

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 2/7/22 22:22, Jonathan Cameron wrote:
> On Mon,  7 Feb 2022 11:34:18 +0100
> Hans de Goede <hdegoede@xxxxxxxxxx> wrote:
> 
>> The mma8452_driver declares both of_match_table and i2c_driver.id_table
>> match-tables, but its probe() function only checked for of matches.
>>
>> Add support for i2c_device_id matches. This fixes the driver not loading
>> on some x86 tablets (e.g. the Nextbook Ares 8) where the i2c_client is
>> instantiated by platform code using an i2c_device_id.
>>
>> Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx>
> We've lost the fixes tag from the v1 discussion. 

Ah right, so that would be:

Fixes: c3cdd6e48e35 ("iio: mma8452: refactor for seperating chip specific data")

I'll add that for v3.

>> ---
>> Changes in v2:
>> - Fix the following smatch warning:
>>   drivers/iio/accel/mma8452.c:1595 mma8452_probe() error: we previously assumed 'id' could be null (see line 1536)
>>   Reported-by: kernel test robot <lkp@xxxxxxxxx>
>>   Reported-by: Dan Carpenter <dan.carpenter@xxxxxxxxxx>
>> ---
>>  drivers/iio/accel/mma8452.c | 25 ++++++++++++++++---------
>>  1 file changed, 16 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
>> index 64b82b4503ad..eaa236cfbabb 100644
>> --- a/drivers/iio/accel/mma8452.c
>> +++ b/drivers/iio/accel/mma8452.c
>> @@ -1523,12 +1523,7 @@ static int mma8452_probe(struct i2c_client *client,
>>  	struct iio_dev *indio_dev;
>>  	int ret;
>>  	const struct of_device_id *match;
>> -
>> -	match = of_match_device(mma8452_dt_ids, &client->dev);
>> -	if (!match) {
>> -		dev_err(&client->dev, "unknown device model\n");
>> -		return -ENODEV;
>> -	}
>> +	const char *compatible;
>>  
>>  	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
>>  	if (!indio_dev)
>> @@ -1537,7 +1532,19 @@ static int mma8452_probe(struct i2c_client *client,
>>  	data = iio_priv(indio_dev);
>>  	data->client = client;
>>  	mutex_init(&data->lock);
>> -	data->chip_info = match->data;
>> +
>> +	if (id) {
>> +		compatible = id->name;
>> +		data->chip_info = &mma_chip_info_table[id->driver_data];
>> +	} else {
>> +		match = of_match_device(mma8452_dt_ids, &client->dev);
>> +		if (!match) {
>> +			dev_err(&client->dev, "unknown device model\n");
>> +			return -ENODEV;
>> +		}
>> +		compatible = match->compatible;
> 
> Won't this be "fsl,mma8452" or similar when we want "mma8452"?
> That doesn't matter for the dev_info() but it does matter for
> indio_dev->name which is part of the userspace ABI.

If we hit the "else" path then yes, this will be e.g. "fsl,mma8452"
but note how indio_dev->name was previously unconditionally set
to id->name. This did not cause a NULL ptr deref because the i2c-core
sets client->name by using of_modalias_node() which strips the
"fsl," vendor-prefix.

client->name being just "mma8452" means that the id pointer will
be non NULL even for of-instantiated i2c-clients, instead it
will point to the "mma8452" i2c_device_id so we will hit the
if (id) {} path even for of-instantiated i2c-client.

The only case where we can hit the else is if there is an
entry added to the mma8452_dt_ids[] array which is not present
in the mma8452_id[] array, which atm is not the case
(which is confirmed by the lack of bug reports about NULL ptr
derefs).

Note that since client->name matches on of the mma8452_id[]
entries the entire mma8452_dt_ids[] array + the else path
could be completely dropped and things will still work
through a fallback on only using the mma8452_id[] array.

But AFAIK the preferred method for of enumerated clients
is to use of-matches.

So ideally I guess we would do something like this:

match = of_match_device(mma8452_dt_ids, &client->dev);
if (match) {
        compatible = match->compatible;
        data->chip_info = match->data;
} else if (id) {
        compatible = id->name;
        data->chip_info = &mma_chip_info_table[id->driver_data];
} else {
	dev_err(&client->dev, "unknown device model\n");
	return -ENODEV;
}

And then for indio_dev->name do:

indio_dev->name = client->name;

Since that has the vendor-prefix stripped, just like the old
id->name to which it used to be set.

> Probably easiest way to work around this is to just put the names
> as an extra entry in the mma_chip_info_table[] so they can
> be trivially retrieved in either path.
> Sure it's duplication of a string but they are pretty small and
> it makes for less special casing in the code.
> 
> However, looking again at this code I noticed that you haven't
> actually introduced the fact that id->name wouldn't be set which
> made me remind myself of how the i2c-core-of.c code works.
> It has a quirk.  It will actually always provide the id via
> the following path:
> 
> of_i2c_register_device()
> -> of_i2c_get_board_info()
>   -> info->type set in of_modalias_node to the part of the compatible after the comma.
> 
> Then
> of_i2c_register_device()
> -> of_i2c_new_client_device()
>   which copies info->type into client->name
> 
> Then via i2c_device_probe() for the i2c bus the probe is
> called with an i2c_match_id(driver->id_table, client)
> to provide the id parameter.
> 
> So for devicetree you won't hit your else above as if (id)
> will also pass (which is why the id->name before was working).
> 
> This path is dropped if we ever move to the probe_new() callback
> but for now I think it will just work.
> 
> Now, what to do about this.. In similar cases we do
> if (client->dev.of_node) {
>  of option.
> } else {
>  id option
> }
> though this is mostly because people don't feel confident
> the i2c id path will always work for device tree just because
> (assuming I followed it through correctly) it works today.

I should have read on before replying to your initial remark
right away, oops. Right as we both say id will be set even for
of-matches, but only when the of_match and the old i2c_client_id
match tables both have an entry for the model.

> Now for ACPI there isn't such a path so when we move to
> generic device properties we can't assume id is anything other
> than NULL. Note that this driver hasn't previously been converted
> to generic fw properties because of the absence of a suitable
> fwnode_irq_get_by_name() but Andy pointed out this week that
> we now have one available:
> https://lore.kernel.org/all/YflfEpKj0ilHnQQm@xxxxxxxxxxxxxxxxxx/
> https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git/commit/?h=i2c/alert-for-acpi&id=ca0acb511c21738b32386ce0f85c284b351d919e
> 
> Given that conversion is likely to happen shortly I'd like to
> use the pattern above rather than temporarily relying on
> the struct i2c_device_id always being available.

Hmm, I see, yes my proposed:

	indio_dev->name = client->name;

trick will not work once ACPI also comes into play and I see
that e.g. the bmc150-accel driver already gets the indio_dev->name
from the chip_info_table[] in the ACPI case, so I will prepare
a v3 doing so.

I guess that makes this more 5.18 material then 5.17 though,
but that is fine the platform code instantiating an old
fashioned i2c-client relying on i2c_client_id matching
won't land till 5.18 either.

Regards,

Hans



>> +		data->chip_info = match->data;
>> +	}
>>  
>>  	data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
>>  	if (IS_ERR(data->vdd_reg))
>> @@ -1581,11 +1588,11 @@ static int mma8452_probe(struct i2c_client *client,
>>  	}
>>  
>>  	dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
>> -		 match->compatible, data->chip_info->chip_id);
>> +		 compatible, data->chip_info->chip_id);
>>  
>>  	i2c_set_clientdata(client, indio_dev);
>>  	indio_dev->info = &mma8452_info;
>> -	indio_dev->name = id->name;
>> +	indio_dev->name = compatible;
>>  	indio_dev->modes = INDIO_DIRECT_MODE;
>>  	indio_dev->channels = data->chip_info->channels;
>>  	indio_dev->num_channels = data->chip_info->num_channels;
> 




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux