mirror of
https://github.com/rust-vmm/vhost-device.git
synced 2026-01-05 00:39:40 +00:00
scmi: Kernel instructions and changes for IIO added
Followup patches will allow connecting vhost-user-scmi to industrial I/O devices. On host without IIO devices, it’s possible to use emulated devices for testing. This patch documents how to use them and also provides a slightly customized IIO dummy kernel module. Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
This commit is contained in:
parent
e0fdfa4c38
commit
4014a6366f
@ -65,9 +65,17 @@ The currently supported SCMI protocols are:
|
||||
Basically only the mandatory and necessary parts of the protocols are
|
||||
implemented.
|
||||
|
||||
## Kernel support for testing
|
||||
|
||||
`kernel` subdirectory contains
|
||||
[instructions](kernel/iio-dummy/README.md) how to create emulated
|
||||
industrial I/O devices for testing.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
- [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0
|
||||
- [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause)
|
||||
|
||||
unless specified in particular files otherwise.
|
||||
|
||||
7
crates/scmi/kernel/iio-dummy/.gitignore
vendored
Normal file
7
crates/scmi/kernel/iio-dummy/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
*.cmd
|
||||
*.ko
|
||||
*.mod
|
||||
*.mod.[co]
|
||||
*.o
|
||||
Module.symvers
|
||||
modules.order
|
||||
19
crates/scmi/kernel/iio-dummy/Makefile
Normal file
19
crates/scmi/kernel/iio-dummy/Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for the IIO Dummy Driver
|
||||
#
|
||||
# Modified by Milan Zamazal <mzamazal@redhat.com> in 2023 for out of
|
||||
# tree compilation.
|
||||
#
|
||||
|
||||
obj-m += iio_modified_dummy.o
|
||||
|
||||
on_nixos = $(wildcard /etc/NIXOS)
|
||||
ifeq ($(on_nixos), /etc/NIXOS)
|
||||
nix_prefix = $(shell nix-build -E '(import <nixpkgs> {}).linux.dev' --no-out-link)
|
||||
endif
|
||||
|
||||
all:
|
||||
make -C $(nix_prefix)/lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
clean:
|
||||
make -C $(nix_prefix)/lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
184
crates/scmi/kernel/iio-dummy/README.md
Normal file
184
crates/scmi/kernel/iio-dummy/README.md
Normal file
@ -0,0 +1,184 @@
|
||||
# Using emulated industrial I/O devices
|
||||
|
||||
This is a modified version of the Linux [industrial I/O dummy
|
||||
driver](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/iio/dummy).
|
||||
Both the original driver and this modification can provide emulated
|
||||
industrial I/O devices for testing vhost-device-scmi.
|
||||
|
||||
## Modifications in this module
|
||||
|
||||
If the stock industrial I/O dummy driver is enough for you, use it
|
||||
(but you may still want to read the instructions below).
|
||||
|
||||
Otherwise, this alternative is provided with the following changes:
|
||||
|
||||
- Simplified Makefile for out of tree compilation.
|
||||
- The accelerometer has three axes instead of just one.
|
||||
|
||||
Of course, you can modified it further for your liking if needed.
|
||||
|
||||
## How to create emulated industrial I/O devices
|
||||
|
||||
Make sure your kernel supports software industrial I/O devices and
|
||||
industrial I/O with configfs. You can check this by running `modprobe
|
||||
industrialio_sw_device && modprobe industrialio_configfs`. If any of
|
||||
the modules is not present, follow the [instructions for recompiling
|
||||
kernel](#recompiling-kernel-with-industrial-io) below.
|
||||
|
||||
Make sure you have the right kernel version. Since Linux 5.19, the
|
||||
dummy industrial I/O driver is broken. This will be probably fixed in
|
||||
Linux 6.6.
|
||||
|
||||
If you have a broken kernel version, apply the
|
||||
[fix](./iio-dummy-fix.patch) and compile and install the modified
|
||||
kernel.
|
||||
|
||||
If you want to use the modified module from here, compile it. In
|
||||
order to do this, you must have kernel development environment
|
||||
installed, for example:
|
||||
|
||||
- Fedora or derivatives: `dnf install kernel-devel kernel-modules make`
|
||||
- Debian or derivatives: `apt install linux-headers-$(uname -r) make`
|
||||
- NixOS: `nix-shell '<nixpkgs>' -A linux.dev`
|
||||
|
||||
Then you can compile the module, simply running `make` should work on
|
||||
most distributions.
|
||||
|
||||
Insert a dummy industrial I/O kernel module. Either the stock one:
|
||||
|
||||
```
|
||||
# modprobe iio-dummy
|
||||
```
|
||||
|
||||
or the modified one from here:
|
||||
|
||||
```
|
||||
# modprobe industrialio
|
||||
# modprobe industrialio_configfs
|
||||
# modprobe industrialio_sw_device
|
||||
# insmod ./iio-dummy-modified.ko
|
||||
```
|
||||
|
||||
Find out where configfs is mounted: `mount | grep configfs`. It's
|
||||
typically `/sys/kernel/config`. If configfs is not mounted, mount it
|
||||
somewhere: `mount -t configfs none MOUNTPOINT`.
|
||||
|
||||
Now you can create emulated industrial I/O devices with the stock driver:
|
||||
|
||||
```
|
||||
# mkdir /sys/kernel/config/iio/devices/dummy/my-device
|
||||
```
|
||||
|
||||
And/or with the modified driver from here:
|
||||
|
||||
```
|
||||
# mkdir /sys/kernel/config/iio/devices/dummy-modified/my-device
|
||||
```
|
||||
|
||||
If everything is OK then you can find the device in
|
||||
`/sys/bus/iio/devices/`.
|
||||
|
||||
## Recompiling kernel with industrial I/O
|
||||
|
||||
Making a custom kernel is different on each GNU/Linux distribution.
|
||||
The corresponding documentation can be found for example here:
|
||||
|
||||
- Fedora: [https://fedoraproject.org/wiki/Building_a_custom_kernel](https://fedoraproject.org/wiki/Building_a_custom_kernel)
|
||||
- CentOS Stream: [https://wiki.centos.org/HowTos/BuildingKernelModules](https://wiki.centos.org/HowTos/BuildingKernelModules)
|
||||
(looks more useful for Fedora builds than CentOS)
|
||||
- Debian: [https://kernel-team.pages.debian.net/kernel-handbook/ch-common-tasks.html#s-common-official](https://kernel-team.pages.debian.net/kernel-handbook/ch-common-tasks.html#s-common-official)
|
||||
- NixOS: [https://nixos.wiki/wiki/Linux_kernel](https://nixos.wiki/wiki/Linux_kernel)
|
||||
|
||||
Here are instructions for Fedora, similar steps can be used for other
|
||||
distributions, with distribution specifics as described in the links
|
||||
above. This is not necessarily the most official or the best way to
|
||||
do it but it's a way that *actually works* for me.
|
||||
|
||||
Note on CentOS Stream 9: The kernel there doesn't contain the needed
|
||||
modules. Recompiling the kernel on CentOS Stream may be challenging
|
||||
due to missing build dependencies. If it doesn't work for you, you
|
||||
can try to use Fedora kernel and modules on CentOS Stream, including
|
||||
the dummy module compiled on Fedora.
|
||||
|
||||
### Install kernel sources
|
||||
|
||||
```
|
||||
# dnf install 'dnf-command(download)'
|
||||
$ dnf download --source kernel
|
||||
$ rpm -i kernel-*.src.rpm
|
||||
# dnf builddep ~/rpmbuild/SPECS/kernel.spec
|
||||
```
|
||||
|
||||
### Change kernel configuration
|
||||
|
||||
Not needed for current Fedora but may be needed for e.g. CentOS Stream.
|
||||
|
||||
```
|
||||
# dnf install kernel-devel kernel-modules make rpm-build python3-devel ncurses-devel
|
||||
$ rpmbuild -bp ~/rpmbuild/SPECS/kernel.spec
|
||||
$ cd ~/rpmbuild/BUILD/kernel-*/linux-*/
|
||||
$ cp configs/kernel-VERSION-YOURARCH.config .config
|
||||
$ make nconfig
|
||||
```
|
||||
|
||||
Configuration options that must be enabled:
|
||||
|
||||
- Device Drivers -> Industrial I/O Support -> Enable IIO configuration via configfs
|
||||
- Device Drivers -> Industrial I/O Support -> Enable software IIO device support
|
||||
|
||||
Optionally (you can use the alternative driver from here instead):
|
||||
|
||||
- Device Drivers -> Industrial I/O Support -> IIO dummy drive -> An example driver with no hardware requirements
|
||||
|
||||
Then copy `.config` back to its original file and don't forget to add
|
||||
the original architecture specification line there.
|
||||
|
||||
### Apply the kernel fix
|
||||
|
||||
If the kernel fix from here is needed, copy it to the sources:
|
||||
|
||||
```
|
||||
cp .../iio-dummy-fix.patch ~/rpmbuild/SOURCES/
|
||||
```
|
||||
|
||||
Edit `~/rpmbuild/SPECS/kernel.spec`:
|
||||
|
||||
- Uncomment: `%define buildid .local`.
|
||||
|
||||
- Add the patch file before: `Patch999999: linux-kernel-test.patch`.
|
||||
|
||||
- Add the patch file before: `ApplyOptionalPatch linux-kernel-test.patch`.
|
||||
|
||||
### Build the kernel
|
||||
|
||||
You can use different options, if you don't need anything extra then
|
||||
the following builds the most important rpm's:
|
||||
|
||||
```
|
||||
$ rpmbuild -bb --with baseonly --without debug --without debuginfo ~/rpmbuild/SPECS/kernel.spec
|
||||
```
|
||||
|
||||
## Adding industrial I/O dummy module to your kernel
|
||||
|
||||
If all you need is to add a missing stock I/O dummy module, you can
|
||||
try to compile just the module. Switch to kernel sources and run:
|
||||
|
||||
```
|
||||
$ make oldconfig
|
||||
$ make prepare
|
||||
$ make modules_prepare
|
||||
$ make M=drivers/iio/dummy
|
||||
```
|
||||
|
||||
And insert the module:
|
||||
|
||||
```
|
||||
# modprobe industrialio
|
||||
# modprobe industrialio_configfs
|
||||
# modprobe industrialio_sw_device
|
||||
# insmod ./drivers/iio/dummy/iio-dummy.ko
|
||||
```
|
||||
|
||||
If this fails, inspect `dmesg` output and try to figure out what's
|
||||
wrong. If this fails too, rebuild the whole kernel with the given
|
||||
module enabled.
|
||||
55
crates/scmi/kernel/iio-dummy/iio-dummy-fix.patch
Normal file
55
crates/scmi/kernel/iio-dummy/iio-dummy-fix.patch
Normal file
@ -0,0 +1,55 @@
|
||||
Commit 813665564b3d ("iio: core: Convert to use firmware node handle
|
||||
instead of OF node") switched the kind of nodes to use for label
|
||||
retrieval in device registration. Probably an unwanted change in that
|
||||
commit was that if the device has no parent then NULL pointer is
|
||||
accessed. This is what happens in the stock IIO dummy driver when a
|
||||
new entry is created in configfs:
|
||||
|
||||
# mkdir /sys/kernel/config/iio/devices/dummy/foo
|
||||
BUG: kernel NULL pointer dereference, address: ...
|
||||
...
|
||||
Call Trace:
|
||||
__iio_device_register
|
||||
iio_dummy_probe
|
||||
|
||||
Since there seems to be no reason to make a parent device of an IIO
|
||||
dummy device mandatory, let’s prevent the invalid memory access in
|
||||
__iio_device_register when the parent device is NULL. With this
|
||||
change, the IIO dummy driver works fine with configfs.
|
||||
|
||||
Fixes: 813665564b3d ("iio: core: Convert to use firmware node handle instead of OF node")
|
||||
Reviewed-by: Andy Shevchenko <andriy.shevchenko-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
|
||||
Signed-off-by: Milan Zamazal <mzamazal-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
|
||||
---
|
||||
drivers/iio/industrialio-core.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
|
||||
index c117f50d0cf3..adcba832e6fa 100644
|
||||
--- a/drivers/iio/industrialio-core.c
|
||||
+++ b/drivers/iio/industrialio-core.c
|
||||
@@ -1888,7 +1888,7 @@ static const struct iio_buffer_setup_ops noop_ring_setup_ops;
|
||||
int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
|
||||
- struct fwnode_handle *fwnode;
|
||||
+ struct fwnode_handle *fwnode = NULL;
|
||||
int ret;
|
||||
|
||||
if (!indio_dev->info)
|
||||
@@ -1899,7 +1899,8 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
|
||||
/* If the calling driver did not initialize firmware node, do it here */
|
||||
if (dev_fwnode(&indio_dev->dev))
|
||||
fwnode = dev_fwnode(&indio_dev->dev);
|
||||
- else
|
||||
+ /* The default dummy IIO device has no parent */
|
||||
+ else if (indio_dev->dev.parent)
|
||||
fwnode = dev_fwnode(indio_dev->dev.parent);
|
||||
device_set_node(&indio_dev->dev, fwnode);
|
||||
|
||||
--
|
||||
|
||||
2.40.1
|
||||
|
||||
|
||||
|
||||
693
crates/scmi/kernel/iio-dummy/iio_modified_dummy.c
Normal file
693
crates/scmi/kernel/iio-dummy/iio_modified_dummy.c
Normal file
@ -0,0 +1,693 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2011 Jonathan Cameron
|
||||
*
|
||||
* A reference industrial I/O driver to illustrate the functionality available.
|
||||
*
|
||||
* There are numerous real drivers to illustrate the finer points.
|
||||
* The purpose of this driver is to provide a driver with far more comments
|
||||
* and explanatory notes than any 'real' driver would have.
|
||||
* Anyone starting out writing an IIO driver should first make sure they
|
||||
* understand all of this driver except those bits specifically marked
|
||||
* as being present to allow us to 'fake' the presence of hardware.
|
||||
*
|
||||
* Changes by Milan Zamazal <mzamazal@redhat.com> 2023, for testing
|
||||
* with vhost-device-scmi:
|
||||
*
|
||||
* - Dropped conditional parts.
|
||||
* - Use 3 axes in the accelerometer device.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/sw_device.h>
|
||||
#include "iio_modified_dummy.h"
|
||||
|
||||
static const struct config_item_type iio_dummy_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iio_dummy_accel_calibscale - realworld to register mapping
|
||||
* @val: first value in read_raw - here integer part.
|
||||
* @val2: second value in read_raw etc - here micro part.
|
||||
* @regval: register value - magic device specific numbers.
|
||||
*/
|
||||
struct iio_dummy_accel_calibscale {
|
||||
int val;
|
||||
int val2;
|
||||
int regval; /* what would be written to hardware */
|
||||
};
|
||||
|
||||
static const struct iio_dummy_accel_calibscale dummy_scales[] = {
|
||||
{ 0, 100, 0x8 }, /* 0.000100 */
|
||||
{ 0, 133, 0x7 }, /* 0.000133 */
|
||||
{ 733, 13, 0x9 }, /* 733.000013 */
|
||||
};
|
||||
|
||||
/*
|
||||
* iio_dummy_channels - Description of available channels
|
||||
*
|
||||
* This array of structures tells the IIO core about what the device
|
||||
* actually provides for a given channel.
|
||||
*/
|
||||
static const struct iio_chan_spec iio_dummy_channels[] = {
|
||||
/* indexed ADC channel in_voltage0_raw etc */
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
/* Channel has a numeric index of 0 */
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
/* What other information is available? */
|
||||
.info_mask_separate =
|
||||
/*
|
||||
* in_voltage0_raw
|
||||
* Raw (unscaled no bias removal etc) measurement
|
||||
* from the device.
|
||||
*/
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
/*
|
||||
* in_voltage0_offset
|
||||
* Offset for userspace to apply prior to scale
|
||||
* when converting to standard units (microvolts)
|
||||
*/
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
/*
|
||||
* in_voltage0_scale
|
||||
* Multipler for userspace to apply post offset
|
||||
* when converting to standard units (microvolts)
|
||||
*/
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
/*
|
||||
* sampling_frequency
|
||||
* The frequency in Hz at which the channels are sampled
|
||||
*/
|
||||
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
/* The ordering of elements in the buffer via an enum */
|
||||
.scan_index = DUMMY_INDEX_VOLTAGE_0,
|
||||
.scan_type = { /* Description of storage in buffer */
|
||||
.sign = 'u', /* unsigned */
|
||||
.realbits = 13, /* 13 bits */
|
||||
.storagebits = 16, /* 16 bits used for storage */
|
||||
.shift = 0, /* zero shift */
|
||||
},
|
||||
},
|
||||
/* Differential ADC channel in_voltage1-voltage2_raw etc*/
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.differential = 1,
|
||||
/*
|
||||
* Indexing for differential channels uses channel
|
||||
* for the positive part, channel2 for the negative.
|
||||
*/
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.channel2 = 2,
|
||||
/*
|
||||
* in_voltage1-voltage2_raw
|
||||
* Raw (unscaled no bias removal etc) measurement
|
||||
* from the device.
|
||||
*/
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
/*
|
||||
* in_voltage-voltage_scale
|
||||
* Shared version of scale - shared by differential
|
||||
* input channels of type IIO_VOLTAGE.
|
||||
*/
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
/*
|
||||
* sampling_frequency
|
||||
* The frequency in Hz at which the channels are sampled
|
||||
*/
|
||||
.scan_index = DUMMY_INDEX_DIFFVOLTAGE_1M2,
|
||||
.scan_type = { /* Description of storage in buffer */
|
||||
.sign = 's', /* signed */
|
||||
.realbits = 12, /* 12 bits */
|
||||
.storagebits = 16, /* 16 bits used for storage */
|
||||
.shift = 0, /* zero shift */
|
||||
},
|
||||
},
|
||||
/* Differential ADC channel in_voltage3-voltage4_raw etc*/
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.differential = 1,
|
||||
.indexed = 1,
|
||||
.channel = 3,
|
||||
.channel2 = 4,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = DUMMY_INDEX_DIFFVOLTAGE_3M4,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 11,
|
||||
.storagebits = 16,
|
||||
.shift = 0,
|
||||
},
|
||||
},
|
||||
/*
|
||||
* 'modified' (i.e. axis specified) acceleration channel
|
||||
* in_accel_[xyz]_raw
|
||||
*/
|
||||
{
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
/* Channel 2 is use for modifiers */
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
/*
|
||||
* Internal bias and gain correction values. Applied
|
||||
* by the hardware or driver prior to userspace
|
||||
* seeing the readings. Typically part of hardware
|
||||
* calibration.
|
||||
*/
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
||||
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = DUMMY_INDEX_ACCEL_X,
|
||||
.scan_type = { /* Description of storage in buffer */
|
||||
.sign = 's', /* signed */
|
||||
.realbits = 16, /* 16 bits */
|
||||
.storagebits = 16, /* 16 bits used for storage */
|
||||
.shift = 0, /* zero shift */
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
/* Channel 2 is use for modifiers */
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
||||
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = DUMMY_INDEX_ACCEL_Y,
|
||||
.scan_type = { /* Description of storage in buffer */
|
||||
.sign = 's', /* signed */
|
||||
.realbits = 16, /* 16 bits */
|
||||
.storagebits = 16, /* 16 bits used for storage */
|
||||
.shift = 0, /* zero shift */
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
/* Channel 2 is use for modifiers */
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
||||
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_index = DUMMY_INDEX_ACCEL_Z,
|
||||
.scan_type = { /* Description of storage in buffer */
|
||||
.sign = 's', /* signed */
|
||||
.realbits = 16, /* 16 bits */
|
||||
.storagebits = 16, /* 16 bits used for storage */
|
||||
.shift = 0, /* zero shift */
|
||||
},
|
||||
},
|
||||
/*
|
||||
* Convenience macro for timestamps. 4 is the index in
|
||||
* the buffer.
|
||||
*/
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
/* DAC channel out_voltage0_raw */
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.scan_index = -1, /* No buffer support */
|
||||
.output = 1,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
},
|
||||
{
|
||||
.type = IIO_STEPS,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_ENABLE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBHEIGHT),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.scan_index = -1, /* No buffer support */
|
||||
},
|
||||
{
|
||||
.type = IIO_ACTIVITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_RUNNING,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.scan_index = -1, /* No buffer support */
|
||||
},
|
||||
{
|
||||
.type = IIO_ACTIVITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_WALKING,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.scan_index = -1, /* No buffer support */
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* iio_dummy_read_raw() - data read function.
|
||||
* @indio_dev: the struct iio_dev associated with this device instance
|
||||
* @chan: the channel whose data is to be read
|
||||
* @val: first element of returned value (typically INT)
|
||||
* @val2: second element of returned value (typically MICRO)
|
||||
* @mask: what we actually want to read as per the info_mask_*
|
||||
* in iio_chan_spec.
|
||||
*/
|
||||
static int iio_dummy_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct iio_dummy_state *st = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW: /* magic value - channel value read */
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if (chan->output) {
|
||||
/* Set integer part to cached value */
|
||||
*val = st->dac_val;
|
||||
ret = IIO_VAL_INT;
|
||||
} else if (chan->differential) {
|
||||
if (chan->channel == 1)
|
||||
*val = st->differential_adc_val[0];
|
||||
else
|
||||
*val = st->differential_adc_val[1];
|
||||
ret = IIO_VAL_INT;
|
||||
} else {
|
||||
*val = st->single_ended_adc_val;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
break;
|
||||
case IIO_ACCEL:
|
||||
switch(chan->scan_index) {
|
||||
case DUMMY_INDEX_ACCEL_X:
|
||||
*val = st->accel_val[0];
|
||||
break;
|
||||
case DUMMY_INDEX_ACCEL_Y:
|
||||
*val = st->accel_val[1];
|
||||
break;
|
||||
case DUMMY_INDEX_ACCEL_Z:
|
||||
*val = st->accel_val[2];
|
||||
break;
|
||||
default:
|
||||
*val = 0;
|
||||
}
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_STEPS:
|
||||
*val = st->steps;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_ACTIVITY:
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_RUNNING:
|
||||
*val = st->activity_running;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_MOD_WALKING:
|
||||
*val = st->activity_walking;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* only single ended adc -> 7 */
|
||||
*val = 7;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
switch (chan->differential) {
|
||||
case 0:
|
||||
/* only single ended adc -> 0.001333 */
|
||||
*val = 0;
|
||||
*val2 = 1333;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case 1:
|
||||
/* all differential adc -> 0.000001344 */
|
||||
*val = 0;
|
||||
*val2 = 1344;
|
||||
ret = IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
/* only the acceleration axis - read from cache */
|
||||
*val = st->accel_calibbias;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
*val = st->accel_calibscale->val;
|
||||
*val2 = st->accel_calibscale->val2;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = 3;
|
||||
*val2 = 33;
|
||||
ret = IIO_VAL_INT_PLUS_NANO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
switch (chan->type) {
|
||||
case IIO_STEPS:
|
||||
*val = st->steps_enabled;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBHEIGHT:
|
||||
switch (chan->type) {
|
||||
case IIO_STEPS:
|
||||
*val = st->height;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_dummy_write_raw() - data write function.
|
||||
* @indio_dev: the struct iio_dev associated with this device instance
|
||||
* @chan: the channel whose data is to be written
|
||||
* @val: first element of value to set (typically INT)
|
||||
* @val2: second element of value to set (typically MICRO)
|
||||
* @mask: what we actually want to write as per the info_mask_*
|
||||
* in iio_chan_spec.
|
||||
*
|
||||
* Note that all raw writes are assumed IIO_VAL_INT and info mask elements
|
||||
* are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt
|
||||
* in struct iio_info is provided by the driver.
|
||||
*/
|
||||
static int iio_dummy_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
struct iio_dummy_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if (chan->output == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Locking not required as writing single value */
|
||||
mutex_lock(&st->lock);
|
||||
st->dac_val = val;
|
||||
mutex_unlock(&st->lock);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_STEPS:
|
||||
mutex_lock(&st->lock);
|
||||
st->steps = val;
|
||||
mutex_unlock(&st->lock);
|
||||
return 0;
|
||||
case IIO_ACTIVITY:
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
if (val > 100)
|
||||
val = 100;
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_RUNNING:
|
||||
st->activity_running = val;
|
||||
return 0;
|
||||
case IIO_MOD_WALKING:
|
||||
st->activity_walking = val;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
mutex_lock(&st->lock);
|
||||
/* Compare against table - hard matching here */
|
||||
for (i = 0; i < ARRAY_SIZE(dummy_scales); i++)
|
||||
if (val == dummy_scales[i].val &&
|
||||
val2 == dummy_scales[i].val2)
|
||||
break;
|
||||
if (i == ARRAY_SIZE(dummy_scales))
|
||||
ret = -EINVAL;
|
||||
else
|
||||
st->accel_calibscale = &dummy_scales[i];
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
mutex_lock(&st->lock);
|
||||
st->accel_calibbias = val;
|
||||
mutex_unlock(&st->lock);
|
||||
return 0;
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
switch (chan->type) {
|
||||
case IIO_STEPS:
|
||||
mutex_lock(&st->lock);
|
||||
st->steps_enabled = val;
|
||||
mutex_unlock(&st->lock);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBHEIGHT:
|
||||
switch (chan->type) {
|
||||
case IIO_STEPS:
|
||||
st->height = val;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Device type specific information.
|
||||
*/
|
||||
static const struct iio_info iio_dummy_info = {
|
||||
.read_raw = &iio_dummy_read_raw,
|
||||
.write_raw = &iio_dummy_write_raw,
|
||||
};
|
||||
|
||||
/**
|
||||
* iio_dummy_init_device() - device instance specific init
|
||||
* @indio_dev: the iio device structure
|
||||
*
|
||||
* Most drivers have one of these to set up default values,
|
||||
* reset the device to known state etc.
|
||||
*/
|
||||
static int iio_dummy_init_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dummy_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->dac_val = 0;
|
||||
st->single_ended_adc_val = 73;
|
||||
st->differential_adc_val[0] = 33;
|
||||
st->differential_adc_val[1] = -34;
|
||||
st->accel_val[0] = 34;
|
||||
st->accel_val[1] = 37;
|
||||
st->accel_val[2] = 40;
|
||||
st->accel_calibbias = -7;
|
||||
st->accel_calibscale = &dummy_scales[0];
|
||||
st->steps = 47;
|
||||
st->activity_running = 98;
|
||||
st->activity_walking = 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_dummy_probe() - device instance probe
|
||||
* @name: name of this instance.
|
||||
*
|
||||
* Arguments are bus type specific.
|
||||
* I2C: iio_dummy_probe(struct i2c_client *client,
|
||||
* const struct i2c_device_id *id)
|
||||
* SPI: iio_dummy_probe(struct spi_device *spi)
|
||||
*/
|
||||
static struct iio_sw_device *iio_dummy_probe(const char *name)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_dummy_state *st;
|
||||
struct iio_sw_device *swd;
|
||||
struct device *parent;
|
||||
|
||||
/*
|
||||
* With hardware: Set the parent device.
|
||||
* parent = &spi->dev;
|
||||
* parent = &client->dev;
|
||||
*/
|
||||
|
||||
swd = kzalloc(sizeof(*swd), GFP_KERNEL);
|
||||
if (!swd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* Allocate an IIO device.
|
||||
*
|
||||
* This structure contains all generic state
|
||||
* information about the device instance.
|
||||
* It also has a region (accessed by iio_priv()
|
||||
* for chip specific state information.
|
||||
*/
|
||||
indio_dev = iio_device_alloc(parent, sizeof(*st));
|
||||
if (!indio_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_swd;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
|
||||
iio_dummy_init_device(indio_dev);
|
||||
|
||||
/*
|
||||
* Make the iio_dev struct available to remove function.
|
||||
* Bus equivalents
|
||||
* i2c_set_clientdata(client, indio_dev);
|
||||
* spi_set_drvdata(spi, indio_dev);
|
||||
*/
|
||||
swd->device = indio_dev;
|
||||
|
||||
/*
|
||||
* Set the device name.
|
||||
*
|
||||
* This is typically a part number and obtained from the module
|
||||
* id table.
|
||||
* e.g. for i2c and spi:
|
||||
* indio_dev->name = id->name;
|
||||
* indio_dev->name = spi_get_device_id(spi)->name;
|
||||
*/
|
||||
indio_dev->name = kstrdup(name, GFP_KERNEL);
|
||||
if (!indio_dev->name) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_device;
|
||||
}
|
||||
|
||||
/* Provide description of available channels */
|
||||
indio_dev->channels = iio_dummy_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(iio_dummy_channels);
|
||||
|
||||
/*
|
||||
* Provide device type specific interface functions and
|
||||
* constant data.
|
||||
*/
|
||||
indio_dev->info = &iio_dummy_info;
|
||||
|
||||
/* Specify that device provides sysfs type interfaces */
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto error_free_name;
|
||||
|
||||
iio_swd_group_init_type_name(swd, name, &iio_dummy_type);
|
||||
|
||||
return swd;
|
||||
error_free_name:
|
||||
kfree(indio_dev->name);
|
||||
error_free_device:
|
||||
iio_device_free(indio_dev);
|
||||
error_free_swd:
|
||||
kfree(swd);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_dummy_remove() - device instance removal function
|
||||
* @swd: pointer to software IIO device abstraction
|
||||
*
|
||||
* Parameters follow those of iio_dummy_probe for buses.
|
||||
*/
|
||||
static int iio_dummy_remove(struct iio_sw_device *swd)
|
||||
{
|
||||
/*
|
||||
* Get a pointer to the device instance iio_dev structure
|
||||
* from the bus subsystem. E.g.
|
||||
* struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
* struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
*/
|
||||
struct iio_dev *indio_dev = swd->device;
|
||||
|
||||
/* Unregister the device */
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
/* Free all structures */
|
||||
kfree(indio_dev->name);
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* module_iio_sw_device_driver() - device driver registration
|
||||
*
|
||||
* Varies depending on bus type of the device. As there is no device
|
||||
* here, call probe directly. For information on device registration
|
||||
* i2c:
|
||||
* Documentation/i2c/writing-clients.rst
|
||||
* spi:
|
||||
* Documentation/spi/spi-summary.rst
|
||||
*/
|
||||
static const struct iio_sw_device_ops iio_dummy_device_ops = {
|
||||
.probe = iio_dummy_probe,
|
||||
.remove = iio_dummy_remove,
|
||||
};
|
||||
|
||||
static struct iio_sw_device_type iio_dummy_device = {
|
||||
.name = "dummy-modified",
|
||||
.owner = THIS_MODULE,
|
||||
.ops = &iio_dummy_device_ops,
|
||||
};
|
||||
|
||||
module_iio_sw_device_driver(iio_dummy_device);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("IIO dummy driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
68
crates/scmi/kernel/iio-dummy/iio_modified_dummy.h
Normal file
68
crates/scmi/kernel/iio-dummy/iio_modified_dummy.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/**
|
||||
* Copyright (c) 2011 Jonathan Cameron
|
||||
*
|
||||
* Join together the various functionality of iio_modified_dummy driver
|
||||
*
|
||||
* Changes by Milan Zamazal <mzamazal@redhat.com> 2023, for testing
|
||||
* with vhost-device-scmi:
|
||||
*
|
||||
* - Dropped conditional parts.
|
||||
* - Use 3 axes in the accelerometer device.
|
||||
*/
|
||||
|
||||
#ifndef _IIO_MODIFIED_DUMMY_H_
|
||||
#define _IIO_MODIFIED_DUMMY_H_
|
||||
#include <linux/kernel.h>
|
||||
|
||||
struct iio_dummy_accel_calibscale;
|
||||
struct iio_dummy_regs;
|
||||
|
||||
/**
|
||||
* struct iio_dummy_state - device instance specific state.
|
||||
* @dac_val: cache for dac value
|
||||
* @single_ended_adc_val: cache for single ended adc value
|
||||
* @differential_adc_val: cache for differential adc value
|
||||
* @accel_val: cache for acceleration value
|
||||
* @accel_calibbias: cache for acceleration calibbias
|
||||
* @accel_calibscale: cache for acceleration calibscale
|
||||
* @lock: lock to ensure state is consistent
|
||||
* @event_irq: irq number for event line (faked)
|
||||
* @event_val: cache for event threshold value
|
||||
* @event_en: cache of whether event is enabled
|
||||
*/
|
||||
struct iio_dummy_state {
|
||||
int dac_val;
|
||||
int single_ended_adc_val;
|
||||
int differential_adc_val[2];
|
||||
int accel_val[3];
|
||||
int accel_calibbias;
|
||||
int activity_running;
|
||||
int activity_walking;
|
||||
const struct iio_dummy_accel_calibscale *accel_calibscale;
|
||||
struct mutex lock;
|
||||
struct iio_dummy_regs *regs;
|
||||
int steps_enabled;
|
||||
int steps;
|
||||
int height;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iio_modified_dummy_scan_elements - scan index enum
|
||||
* @DUMMY_INDEX_VOLTAGE_0: the single ended voltage channel
|
||||
* @DUMMY_INDEX_DIFFVOLTAGE_1M2: first differential channel
|
||||
* @DUMMY_INDEX_DIFFVOLTAGE_3M4: second differential channel
|
||||
* @DUMMY_INDEX_ACCELX: acceleration channel
|
||||
*
|
||||
* Enum provides convenient numbering for the scan index.
|
||||
*/
|
||||
enum iio_modified_dummy_scan_elements {
|
||||
DUMMY_INDEX_VOLTAGE_0,
|
||||
DUMMY_INDEX_DIFFVOLTAGE_1M2,
|
||||
DUMMY_INDEX_DIFFVOLTAGE_3M4,
|
||||
DUMMY_INDEX_ACCEL_X,
|
||||
DUMMY_INDEX_ACCEL_Y,
|
||||
DUMMY_INDEX_ACCEL_Z,
|
||||
};
|
||||
|
||||
#endif /* _IIO_MODIFIED_DUMMY_H_ */
|
||||
Loading…
Reference in New Issue
Block a user