[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250831-icm20948-v1-3-1fe560a38de4@gmail.com>
Date: Sun, 31 Aug 2025 00:12:47 +0530
From: Bharadwaj Raju <bharadwaj.raju777@...il.com>
To: Jonathan Cameron <jic23@...nel.org>,
David Lechner <dlechner@...libre.com>,
Nuno Sá <nuno.sa@...log.com>,
Andy Shevchenko <andy@...nel.org>, Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>
Cc: linux-iio@...r.kernel.org, devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org, shuah@...nel.org,
linux-kernel-mentees@...ts.linux.dev,
Bharadwaj Raju <bharadwaj.raju777@...il.com>
Subject: [PATCH 3/5] iio: imu: icm20948: add support for gyroscope
Add support for reading the gyroscope, which is exposed as another IIO
device under the icm20948 driver.
For now, the only configuration supported is changing the full-scale
range.
Signed-off-by: Bharadwaj Raju <bharadwaj.raju777@...il.com>
---
drivers/iio/imu/inv_icm20948/Makefile | 1 +
drivers/iio/imu/inv_icm20948/inv_icm20948.h | 78 ++++--
drivers/iio/imu/inv_icm20948/inv_icm20948_core.c | 55 ++--
drivers/iio/imu/inv_icm20948/inv_icm20948_gyro.c | 343 +++++++++++++++++++++++
4 files changed, 432 insertions(+), 45 deletions(-)
diff --git a/drivers/iio/imu/inv_icm20948/Makefile b/drivers/iio/imu/inv_icm20948/Makefile
index c508c2dc3eee2c32be20067e3e0868a203d8aa1a..88a37be159e1d6f575da1c070c84ac94cd963020 100644
--- a/drivers/iio/imu/inv_icm20948/Makefile
+++ b/drivers/iio/imu/inv_icm20948/Makefile
@@ -3,6 +3,7 @@
obj-$(CONFIG_INV_ICM20948) += inv-icm20948.o
inv-icm20948-y += inv_icm20948_core.o
inv-icm20948-y += inv_icm20948_temp.o
+inv-icm20948-y += inv_icm20948_gyro.o
obj-$(CONFIG_INV_ICM20948_I2C) += inv-icm20948-i2c.o
inv-icm20948-i2c-y += inv_icm20948_i2c.o
diff --git a/drivers/iio/imu/inv_icm20948/inv_icm20948.h b/drivers/iio/imu/inv_icm20948/inv_icm20948.h
index f9830645fbe96fd02eef7c54d1e5908647d5a0fe..ca2513114378cdcba5bc315fc94cd61f930b4dfa 100644
--- a/drivers/iio/imu/inv_icm20948/inv_icm20948.h
+++ b/drivers/iio/imu/inv_icm20948/inv_icm20948.h
@@ -3,45 +3,83 @@
* Copyright (C) 2025 Bharadwaj Raju <bharadwaj.raju777@...il.com>
*/
-#ifndef INV_ICM20948_H_
-#define INV_ICM20948_H_
+ #ifndef INV_ICM20948_H_
+ #define INV_ICM20948_H_
-#include <linux/bits.h>
-#include <linux/bitfield.h>
-#include <linux/mutex.h>
-#include <linux/regmap.h>
-#include <linux/i2c.h>
-#include <linux/iio/iio.h>
-#include <linux/err.h>
+ #include <linux/bits.h>
+ #include <linux/bitfield.h>
+ #include <linux/mutex.h>
+ #include <linux/regmap.h>
+ #include <linux/i2c.h>
+ #include <linux/iio/iio.h>
+ #include <linux/err.h>
/* accel takes 20ms, gyro takes 35ms to wake from full-chip sleep */
-#define INV_ICM20948_SLEEP_WAKEUP_MS 35
+ #define INV_ICM20948_SLEEP_WAKEUP_MS 35
-#define INV_ICM20948_REG_BANK_SEL 0x7F
-#define INV_ICM20948_BANK_SEL_MASK GENMASK(5, 4)
+ #define INV_ICM20948_REG_BANK_SEL 0x7F
+ #define INV_ICM20948_BANK_SEL_MASK GENMASK(5, 4)
-#define INV_ICM20948_REG_WHOAMI 0x0000
-#define INV_ICM20948_WHOAMI 0xEA
+ #define INV_ICM20948_REG_WHOAMI 0x0000
+ #define INV_ICM20948_WHOAMI 0xEA
-#define INV_ICM20948_REG_FIFO_RW 0x0072
+ #define INV_ICM20948_REG_FIFO_RW 0x0072
-#define INV_ICM20948_REG_PWR_MGMT_1 0x0006
-#define INV_ICM20948_PWR_MGMT_1_DEV_RESET BIT(7)
-#define INV_ICM20948_PWR_MGMT_1_SLEEP BIT(6)
+ #define INV_ICM20948_REG_PWR_MGMT_1 0x0006
+ #define INV_ICM20948_PWR_MGMT_1_DEV_RESET BIT(7)
+ #define INV_ICM20948_PWR_MGMT_1_SLEEP BIT(6)
-#define INV_ICM20948_REG_TEMP_DATA 0x0039
+ #define INV_ICM20948_REG_TEMP_DATA 0x0039
+
+ #define INV_ICM20948_REG_GYRO_DATA_X 0x0033
+ #define INV_ICM20948_REG_GYRO_DATA_Y 0x0035
+ #define INV_ICM20948_REG_GYRO_DATA_Z 0x0037
+
+ #define INV_ICM20948_REG_GYRO_CONFIG_1 0x2001
+ #define INV_ICM20948_GYRO_CONFIG_ENABLE_DLPF BIT(0)
+ #define INV_ICM20948_GYRO_CONFIG_FULLSCALE GENMASK(2, 1)
+ #define INV_ICM20948_GYRO_CONFIG_DLP_CONFIG GENMASK(5, 3)
+
+ #define INV_ICM20948_REG_GYRO_USER_OFFSET_X 0x2003
+ #define INV_ICM20948_REG_GYRO_USER_OFFSET_Y 0x2005
+ #define INV_ICM20948_REG_GYRO_USER_OFFSET_Z 0x2007
extern const struct regmap_config inv_icm20948_regmap_config;
+enum inv_icm20948_gyro_fs {
+ INV_ICM20948_GYRO_FS_250 = 0,
+ INV_ICM20948_GYRO_FS_500 = 1,
+ INV_ICM20948_GYRO_FS_1000 = 2,
+ INV_ICM20948_GYRO_FS_2000 = 3,
+};
+
+enum inv_icm20948_gyro_avg {
+ INV_ICM20948_GYRO_AVG_1X = 0,
+ INV_ICM20948_GYRO_AVG_2X = 1,
+ INV_ICM20948_GYRO_AVG_4X = 2,
+ INV_ICM20948_GYRO_AVG_8X = 3,
+ INV_ICM20948_GYRO_AVG_16X = 4,
+ INV_ICM20948_GYRO_AVG_32X = 5,
+ INV_ICM20948_GYRO_AVG_64X = 6,
+ INV_ICM20948_GYRO_AVG_128X = 7,
+};
+
+struct inv_icm20948_gyro_config {
+ int fsr;
+};
+
struct inv_icm20948_state {
struct device *dev;
struct regmap *regmap;
struct iio_dev *temp_dev;
+ struct iio_dev *gyro_dev;
+ struct inv_icm20948_gyro_config *gyro_conf;
struct mutex lock;
};
extern int inv_icm20948_core_probe(struct regmap *regmap);
struct iio_dev *inv_icm20948_temp_init(struct inv_icm20948_state *state);
+struct iio_dev *inv_icm20948_gyro_init(struct inv_icm20948_state *state);
-#endif
+ #endif
diff --git a/drivers/iio/imu/inv_icm20948/inv_icm20948_core.c b/drivers/iio/imu/inv_icm20948/inv_icm20948_core.c
index ee9e4159cffa261f0326b146a4b3df2cbfbd7697..eb4f940de7013bf4ddeb69b6380a60fbde49964a 100644
--- a/drivers/iio/imu/inv_icm20948/inv_icm20948_core.c
+++ b/drivers/iio/imu/inv_icm20948/inv_icm20948_core.c
@@ -3,7 +3,7 @@
* Copyright (C) 2025 Bharadwaj Raju <bharadwaj.raju777@...il.com>
*/
-#include "inv_icm20948.h"
+ #include "inv_icm20948.h"
static const struct regmap_range_cfg inv_icm20948_regmap_ranges[] = {
{
@@ -66,36 +66,41 @@ EXPORT_SYMBOL_NS_GPL(inv_icm20948_regmap_config, "IIO_ICM20948");
static int inv_icm20948_setup(struct inv_icm20948_state *state)
{
- guard(mutex)(&state->lock);
-
- int reported_whoami;
- int ret = regmap_read(state->regmap, INV_ICM20948_REG_WHOAMI,
- &reported_whoami);
- if (ret)
- return ret;
- if (reported_whoami != INV_ICM20948_WHOAMI) {
- dev_err(state->dev, "invalid whoami %d, expected %d\n",
- reported_whoami, INV_ICM20948_WHOAMI);
- return -ENODEV;
+ scoped_guard(mutex, &state->lock) {
+ int reported_whoami;
+ int ret = regmap_read(state->regmap, INV_ICM20948_REG_WHOAMI,
+ &reported_whoami);
+ if (ret)
+ return ret;
+ if (reported_whoami != INV_ICM20948_WHOAMI) {
+ dev_err(state->dev, "invalid whoami %d, expected %d\n",
+ reported_whoami, INV_ICM20948_WHOAMI);
+ return -ENODEV;
+ }
+
+ ret = regmap_write_bits(state->regmap, INV_ICM20948_REG_PWR_MGMT_1,
+ INV_ICM20948_PWR_MGMT_1_DEV_RESET,
+ INV_ICM20948_PWR_MGMT_1_DEV_RESET);
+ if (ret)
+ return ret;
+ msleep(INV_ICM20948_SLEEP_WAKEUP_MS);
+
+ ret = regmap_write_bits(state->regmap, INV_ICM20948_REG_PWR_MGMT_1,
+ INV_ICM20948_PWR_MGMT_1_SLEEP, 0);
+ if (ret)
+ return ret;
+
+ msleep(INV_ICM20948_SLEEP_WAKEUP_MS);
}
- ret = regmap_write_bits(state->regmap, INV_ICM20948_REG_PWR_MGMT_1,
- INV_ICM20948_PWR_MGMT_1_DEV_RESET,
- INV_ICM20948_PWR_MGMT_1_DEV_RESET);
- if (ret)
- return ret;
- msleep(INV_ICM20948_SLEEP_WAKEUP_MS);
-
- ret = regmap_write_bits(state->regmap, INV_ICM20948_REG_PWR_MGMT_1,
- INV_ICM20948_PWR_MGMT_1_SLEEP, 0);
- if (ret)
- return ret;
- msleep(INV_ICM20948_SLEEP_WAKEUP_MS);
-
state->temp_dev = inv_icm20948_temp_init(state);
if (IS_ERR(state->temp_dev))
return PTR_ERR(state->temp_dev);
+ state->gyro_dev = inv_icm20948_gyro_init(state);
+ if (IS_ERR(state->gyro_dev))
+ return PTR_ERR(state->gyro_dev);
+
return 0;
}
diff --git a/drivers/iio/imu/inv_icm20948/inv_icm20948_gyro.c b/drivers/iio/imu/inv_icm20948/inv_icm20948_gyro.c
new file mode 100644
index 0000000000000000000000000000000000000000..2d4d598eb21c8ce98d4ee3c72504554ab49ea596
--- /dev/null
+++ b/drivers/iio/imu/inv_icm20948/inv_icm20948_gyro.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2025 Bharadwaj Raju <bharadwaj.raju777@...il.com>
+ */
+
+#include <linux/bits.h>
+
+#include <linux/iio/iio.h>
+
+#include "inv_icm20948.h"
+
+/* IIO int + nano format */
+static const int inv_icm20948_gyro_scale[] = {
+ /* 250 dps == 0.000133158 rad/s per LSB */
+ [2 * INV_ICM20948_GYRO_FS_250] = 0,
+ [2 * INV_ICM20948_GYRO_FS_250 + 1] = 133158,
+ /* 500 dps == 0.000266316 rad/s per LSB */
+ [2 * INV_ICM20948_GYRO_FS_500] = 0,
+ [2 * INV_ICM20948_GYRO_FS_500 + 1] = 266316,
+ /* 1000 dps == 0.000532632 rad/s per LSB */
+ [2 * INV_ICM20948_GYRO_FS_1000] = 0,
+ [2 * INV_ICM20948_GYRO_FS_1000 + 1] = 532632,
+ /* 2000 dps == 0.001065264 rad/s per LSB */
+ [2 * INV_ICM20948_GYRO_FS_1000] = 0,
+ [2 * INV_ICM20948_GYRO_FS_1000 + 1] = 1065264,
+};
+
+/* Calibration bias, IIO range format int + nano */
+/* raw value -2**15 to +2**15, 0.0305 dps per LSB step */
+static const int inv_icm20948_gyro_calibbias_range[] = {
+ -17, 443239423, /* min */
+ 0, 532325, /* step */
+ +17, 443239423, /* max */
+};
+
+#define INV_ICM20948_GYRO_CHAN(_dir) \
+ { \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_dir, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = INV_ICM20948_GYRO_SCAN_##_dir, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+enum inv_icm20948_gyro_scan {
+ INV_ICM20948_GYRO_SCAN_X,
+ INV_ICM20948_GYRO_SCAN_Y,
+ INV_ICM20948_GYRO_SCAN_Z,
+};
+
+static const struct iio_chan_spec inv_icm20948_gyro_channels[] = {
+ INV_ICM20948_GYRO_CHAN(X),
+ INV_ICM20948_GYRO_CHAN(Y),
+ INV_ICM20948_GYRO_CHAN(Z),
+};
+
+static int inv_icm20948_gyro_apply_config(struct inv_icm20948_state *state)
+{
+ guard(mutex)(&state->lock);
+
+ return regmap_write_bits(state->regmap, INV_ICM20948_REG_GYRO_CONFIG_1,
+ INV_ICM20948_GYRO_CONFIG_FULLSCALE,
+ state->gyro_conf->fsr << 1);
+}
+
+static int inv_icm20948_gyro_read_sensor(struct inv_icm20948_state *state,
+ struct iio_chan_spec const *chan,
+ s16 *val)
+{
+ unsigned int reg;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM20948_REG_GYRO_DATA_X;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM20948_REG_GYRO_DATA_Y;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM20948_REG_GYRO_DATA_Z;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ __be16 raw;
+ int ret = regmap_bulk_read(state->regmap, reg, &raw, sizeof(raw));
+
+ if (ret)
+ return ret;
+
+ *val = (s16)be16_to_cpu(raw);
+
+ return 0;
+}
+
+static int inv_icm20948_gyro_read_calibbias(struct inv_icm20948_state *state,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2)
+{
+ guard(mutex)(&state->lock);
+
+ unsigned int reg;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM20948_REG_GYRO_USER_OFFSET_X;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM20948_REG_GYRO_USER_OFFSET_Y;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM20948_REG_GYRO_USER_OFFSET_Z;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ __be16 offset_raw;
+ int ret = regmap_bulk_read(state->regmap, reg, &offset_raw,
+ sizeof(offset_raw));
+
+ if (ret)
+ return ret;
+ int offset = be16_to_cpu(offset_raw);
+
+ /* step size = 0.0305 dps/LSB => +/- 999.24 dps over 16-bit range */
+ /* offset * 0.0305 * Pi * 10**9 (for nano) / 180 */
+ /* offset * 95818575.93448868 / 180 */
+ /* offset * 95818576 / 180 */
+ s64 val64 = (s64)offset * 95818576;
+ /* for rounding, add or subtract divisor/2 */
+ if (val64 >= 0)
+ val64 += 180 / 2;
+ else
+ val64 -= 180 / 2;
+
+ s64 bias = div_s64(val64, 180);
+
+ *val = bias / 1000000000L;
+ *val2 = bias % 1000000000L;
+
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm20948_gyro_read_raw(struct iio_dev *gyro_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm20948_state *state = iio_device_get_drvdata(gyro_dev);
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(gyro_dev))
+ return -EBUSY;
+ s16 raw;
+ int ret = inv_icm20948_gyro_read_sensor(state, chan, &raw);
+
+ iio_device_release_direct(gyro_dev);
+ if (ret)
+ return ret;
+ *val = raw;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = inv_icm20948_gyro_scale[2 * state->gyro_conf->fsr + 1];
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return inv_icm20948_gyro_read_calibbias(state, chan, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm20948_gyro_write_scale(struct inv_icm20948_state *state,
+ int val, int val2)
+{
+ /* all supported scales are < 1.0 and > 0.0 */
+ if (val != 0)
+ return -EINVAL;
+
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(inv_icm20948_gyro_scale); idx += 2) {
+ if (val2 == inv_icm20948_gyro_scale[idx + 1])
+ break;
+ }
+
+ if (idx >= ARRAY_SIZE(inv_icm20948_gyro_scale))
+ return -EINVAL;
+
+ state->gyro_conf->fsr = idx / 2;
+ return inv_icm20948_gyro_apply_config(state);
+}
+
+static int inv_icm20948_write_calibbias(struct inv_icm20948_state *state,
+ struct iio_chan_spec const *chan,
+ int val, int val2)
+{
+ guard(mutex)(&state->lock);
+
+ unsigned int reg;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM20948_REG_GYRO_USER_OFFSET_X;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM20948_REG_GYRO_USER_OFFSET_Y;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM20948_REG_GYRO_USER_OFFSET_Z;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ s64 bias = (s64)val * 100000000L + val2;
+ s64 val64 = bias * 180;
+
+ if (val64 >= 0)
+ val64 -= 180 / 2;
+ else
+ val64 += 180 / 2;
+
+ s64 offset64 = div_s64(val64, 95818576L);
+ s16 offset = clamp(offset64, (s64)S16_MIN, (s64)S16_MAX);
+ __be16 offset_write = cpu_to_be16(offset);
+
+ return regmap_bulk_write(state->regmap, reg, &offset_write,
+ sizeof(offset_write));
+}
+
+static int inv_icm20948_gyro_write_raw(struct iio_dev *gyro_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct inv_icm20948_state *state = iio_device_get_drvdata(gyro_dev);
+
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (!iio_device_claim_direct(gyro_dev))
+ return -EBUSY;
+ ret = inv_icm20948_gyro_write_scale(state, val, val2);
+ iio_device_release_direct(gyro_dev);
+ return ret;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (!iio_device_claim_direct(gyro_dev))
+ return -EBUSY;
+ ret = inv_icm20948_write_calibbias(state, chan, val, val2);
+ iio_device_release_direct(gyro_dev);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm20948_gyro_read_avail(struct iio_dev *gyro_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = inv_icm20948_gyro_scale;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm20948_gyro_scale);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *vals = inv_icm20948_gyro_calibbias_range;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm20948_gyro_calibbias_range);
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info inv_icm20948_gyro_info = {
+ .read_raw = inv_icm20948_gyro_read_raw,
+ .write_raw = inv_icm20948_gyro_write_raw,
+ .read_avail = inv_icm20948_gyro_read_avail,
+};
+
+struct iio_dev *inv_icm20948_gyro_init(struct inv_icm20948_state *state)
+{
+ struct iio_dev *gyro_dev = devm_iio_device_alloc(state->dev, 0);
+
+ if (!gyro_dev)
+ return ERR_PTR(-ENOMEM);
+
+ iio_device_set_drvdata(gyro_dev, state);
+
+ gyro_dev->name = "icm20948-gyro";
+ gyro_dev->info = &inv_icm20948_gyro_info;
+ gyro_dev->modes = INDIO_DIRECT_MODE;
+ gyro_dev->channels = inv_icm20948_gyro_channels;
+ gyro_dev->num_channels = ARRAY_SIZE(inv_icm20948_gyro_channels);
+
+ int ret = devm_iio_device_register(state->dev, gyro_dev);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ state->gyro_conf =
+ devm_kzalloc(state->dev, sizeof(*state->gyro_conf), GFP_KERNEL);
+ if (!state->gyro_conf)
+ return ERR_PTR(-ENOMEM);
+
+ state->gyro_conf->fsr = INV_ICM20948_GYRO_FS_250;
+ ret = inv_icm20948_gyro_apply_config(state);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return gyro_dev;
+}
--
2.51.0
Powered by blists - more mailing lists