Sketching a Silhouette for ADXRS290’s Driver - Part 2 of 2
GSoC 2020 [ 0x03 ]
28 Jun 2020Carrying on from where we have left Part 1, this post presents the structure of a bare-minimal IIO driver which, when “bound” to our ADXRS290 slave on the SPI master bus, prints a sweet greeting message during its probe() callback.
The kernel documentation should always be the first thing you refer whenever a query spawns in your brain. The kernel tree also hosts docs which I believe is a reliable place for getting started; in our case: drivers/staging/iio/Documentation/device.txt.
struct iio_dev
is what abstracts and represents an IIO device in software. It is inherited from struct dev
- struct iio_dev
embeds struct device
(just like how struct device
embeds struct kobject
). So, all we need to represent our ADXRS290 as a struct iio_dev
is by populating its members appropriately. The probe()
callback to the driver is expected to register the device (iio_device_register()
) to the IIO core after allocating memory (iio_device_alloc()
) for it in the kernel. I use the wrappers around iio_device_{register, alloc}
, the devm (device-managed) family of functions because (quoting the authors of the comments in the source code: drivers/iio/industrialio-core.c),
iio_dev
allocated with this function (devm_iio_device_alloc()
) is automatically freed on driver detach.
(So no need to call iio_device_free()
explicitily at adxrs290_remove()
- the remove function of the driver. Although I’m still confused on why is there a devm_iio_device_free()
).
The pointer to our allocated iio_dev
is received after the memory allocation call. With this we set on to fill the struct members as guided in drivers/staging/iio/Documentation/device.txt. The parent device in our case is the instance of struct device
from which the spi_device
is inherited. Note that the probe is called by the SPI bus for our SPI slave device, spi_device
(explained in detail below).
Our driver, the adxrs290_driver
is enumerated under the SPI bus by registering it as an instance of struct spi_driver
(inherited by struct device_driver
) by using the module_spi_driver()
macro and mentioning what kind of devices it’ll be able to handle (w/ the of_match_table
!). This spi_driver
is called the “protocol” driver because (quoting the authors of the comments in the source code: include/linux/spi/spi.h)
[…] it works through messages rather than talking directly to SPI hardware (which is what the underlying SPI controller driver does to pass those messages). These protocols are defined in the specification for the device(s) supported by the driver.
So, in a way, our adxrs290_driver
works on top of the SPI controller driver. In here, we also supply our adxrs290_{probe, remove}()
functions to register with the SPI core. When our SPI slave device (the ADXRS290) is found by the SPI bus in some defined address space, the SPI core creates a spi_dev
instance. The SPI core then walks the list of all drivers registered with the bus (remember, drivers are bus-specific) to match a driver for this device. What the matching looks like:
# driver/spi/spi.c
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
Once a match is found, the probe function specified in the struct spi_driver
is called with a pointer to the struct spi_device
(the device we want to support) in its arg. list. Now, the driver is handed the responsibility of allocating, registering and initialising essentials needed to support the device.
The IIO channels, needed to fill indio_dev->channels
member, are captured in the array of struct iio_chan_spec
, namely adxrs290_channels
, is left empty for now but this is where we put in our channel specifications in the future (what type of channel is it, direction of the channel (i/p or o/p), etc.)
adxrs290_info
, an instance of struct iio_info
, houses the required read_raw()
and write_raw()
functions to read the data channels. Those do nothing and simply “chill” for now, as their brown parents would say :P
Once we have channels to read/write, the CHIP_read_raw()
and CHIP_write_raw()
functions simply make SPI transactions with the device to read its registers or use cached values in the CHIP_state
, based on what channel/attribute we’re reading/writing (represented as bitmasks!) from the sysfs userspace interface. CHIP_state
(in our case, adxrs290_state
)
[…] is a structure of local state data for this instance of the chip.
(Quoted from drivers/staging/iio/Documentation/device.txt)
Note how memory is allocated to house the private structure (adxrs290_state
) whilst making a call to allocate memory for the iio_dev
. This section of memory can be accesed by the iio_priv()
function which yields a pointer to it (its a lot cryptic on how it actually does it)!
This is what the skeleton of the ADXRS290 driver finally looks like:
# drivers/iio/gyro/adxrs290.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADXRS290 SPI Gyroscope Driver
*
* Copyright (C) 2020 Analog Devices, Inc.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
struct adxrs290_state {
struct spi_device *spi;
};
static int adxrs290_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
return 0;
}
static int adxrs290_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
return 0;
}
static const struct iio_chan_spec adxrs290_channels[] = {
};
static const struct iio_info adxrs290_info = {
.read_raw = &adxrs290_read_raw,
.write_raw = &adxrs290_write_raw,
};
static int adxrs290_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct adxrs290_state *st;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->spi = spi;
spi_set_drvdata(spi, indio_dev);
indio_dev->dev.parent = &spi->dev;
indio_dev->name = "adxrs290";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxrs290_channels;
indio_dev->num_channels = ARRAY_SIZE(adxrs290_channels);
indio_dev->info = &adxrs290_info;
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct of_device_id adxrs290_of_match[] = {
{ .compatible = "adi,adxrs290" },
{ },
};
MODULE_DEVICE_TABLE(of, adxrs290_of_match);
static struct spi_driver adxrs290_driver = {
.driver = {
.name = "adxrs290",
.of_match_table = adxrs290_of_match,
},
.probe = adxrs290_probe,
};
module_spi_driver(adxrs290_driver);
MODULE_AUTHOR("Nishant Malpani <nish.malpani25@gmail.com>");
MODULE_DESCRIPTION("Analog Devices ADXRS290 Gyroscope SPI driver");
MODULE_LICENSE("GPL");
Note how at the end we fill in some crucial module meta-data which appears when we do a modinfo
.
Put in a greeting message in adxrs290_probe()
, something like:
pr_err("%s: Hello from the probe side\n", module_name(THIS_MODULE));
to ensure successful binding of the driver with the device when we actually test it out later.
Check out my merged pull request on ADI’s kernel tree to get an idea of what is to come next for us to be able to compile this kernel module (our driver) and test the binding-to-device part with real hardware, the ADXRS290 gyroscope. Hint: device-tree overlays, Kbuild flow and cross-compilation of statically built kernel modules for Raspberry Pi. Do follow the informative comments/reviews made by Alexandru Ardelean (the one with Stewie Griffin’s lovely picture :P) from ADI on my PR to get an understanding of the current practises towards driver development in the IIO community.
Upon successfully (not yet tested but ok) crafting the outline for the ADXRS290’s driver, we deserve some entertainment in the form of Stewie Griffin making edgy and satirical jokes (and perhaps incredibly offensive, so yeah, a disclaimer: not for the thin-skinned and faint-hearted):
Now, in the words of Stewie, blaaaaast!