diff --git a/CODEOWNERS b/CODEOWNERS index 7da0fe19cdc6f2..17c1c5e87886d5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -210,6 +210,7 @@ /doc/develop/tools/coccinelle.rst @himanshujha199640 @JuliaLawall /doc/services/device_mgmt/smp_protocol.rst @de-nordic @nordicjm /doc/services/device_mgmt/smp_groups/ @de-nordic @nordicjm +/doc/services/sensing/ @lixuzha @ghu0510 @qianruh /doc/CMakeLists.txt @carlescufi /doc/_scripts/ @carlescufi /doc/connectivity/bluetooth/ @alwa-nordic @jhedberg @Vudentz @@ -575,6 +576,8 @@ /dts/bindings/sensor/*bme680* @BoschSensortec /dts/bindings/sensor/*ina23* @bbilas /dts/bindings/sensor/st* @avisconti +/dts/bindings/sensor/zephyr,sensing.yaml @lixuzha @ghu0510 @qianruh +/dts/bindings/sensor/zephyr,sensing*.yaml @lixuzha @ghu0510 @qianruh /dts/bindings/smbus/ @finikorg /dts/bindings/sip_svc/ @maheshraotm /dts/bindings/cpu/intel,niosv.yaml @sweeaun @@ -671,6 +674,7 @@ /include/zephyr/pm/pm.h @nashif @ceolin /include/zephyr/drivers/ptp_clock.h @tbursztyka /include/zephyr/rtio/ @teburd +/include/zephyr/sensing/ @lixuzha @ghu0510 @qianruh /include/zephyr/shared_irq.h @dcpleung @nashif @andyross /include/zephyr/shell/ @jakub-uC @nordic-krch /include/zephyr/shell/shell_mqtt.h @ycsin @@ -694,6 +698,7 @@ /lib/posix/ @cfriedt /lib/posix/getopt/ @jakub-uC /subsys/portability/ @nashif +/subsys/sensing/ @lixuzha @ghu0510 @qianruh /lib/libc/ @nashif /lib/libc/arcmwdt/ @abrodkin @ruuddw @evgeniy-paltsev /misc/ @tejlmand @@ -740,6 +745,7 @@ /samples/subsys/usb/ @jfischer-no /samples/subsys/usb_c/ @sambhurst /samples/subsys/pm/ @nashif @ceolin +/samples/subsys/sensing/ @lixuzha @ghu0510 @qianruh /samples/tfm_integration/ @microbuilder /samples/userspace/ @dcpleung @nashif /scripts/release/bug_bash.py @cfriedt diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index bb68a4cc3ab211..081cddb1ab1d1a 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -2002,6 +2002,24 @@ Retention: labels: - "area: Retention" +Sensor Subsystem: + status: maintained + maintainers: + - lixuzha + - ghu0510 + - yperess + collaborators: + - qianruh + files: + - dts/bindings/sensor/zephyr,sensing.yaml + - dts/bindings/sensor/zephyr,sensing*.yaml + - include/zephyr/sensing/ + - doc/services/sensing/ + - subsys/sensing/ + - samples/subsys/sensing/ + labels: + - "area: Sensor Subsystem" + Twister: status: maintained maintainers: diff --git a/cmake/linker_script/common/common-ram.cmake b/cmake/linker_script/common/common-ram.cmake index 27461cc1c5ce57..b625ac9daa58bc 100644 --- a/cmake/linker_script/common/common-ram.cmake +++ b/cmake/linker_script/common/common-ram.cmake @@ -54,6 +54,10 @@ if(CONFIG_NETWORKING) zephyr_iterable_section(NAME eth_bridge GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) endif() +if(CONFIG_SENSING) + zephyr_iterable_section(NAME sensing_sensor GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) +endif() + if(CONFIG_UART_MUX) zephyr_iterable_section(NAME uart_mux GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN 4) endif() diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake index ed9f3fe7c8c389..811a681a8c2fc7 100644 --- a/cmake/linker_script/common/common-rom.cmake +++ b/cmake/linker_script/common/common-rom.cmake @@ -136,6 +136,10 @@ if(CONFIG_SETTINGS) zephyr_iterable_section(NAME settings_handler_static KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4) endif() +if(CONFIG_SENSING) + zephyr_iterable_section(NAME sensing_sensor_info KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4) +endif() + if(CONFIG_SENSOR_INFO) zephyr_iterable_section(NAME sensor_info KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4) endif() diff --git a/doc/services/index.rst b/doc/services/index.rst index 7e2e7641348f64..1a00ed326b87ff 100644 --- a/doc/services/index.rst +++ b/doc/services/index.rst @@ -26,6 +26,7 @@ OS Services settings/index.rst smf/index.rst storage/index.rst + sensing/index.rst task_wdt/index.rst tfm/index usb/index.rst diff --git a/doc/services/sensing/images/sensing_api_org.png b/doc/services/sensing/images/sensing_api_org.png new file mode 100644 index 00000000000000..e747e9daff8d2f Binary files /dev/null and b/doc/services/sensing/images/sensing_api_org.png differ diff --git a/doc/services/sensing/images/sensing_solution.png b/doc/services/sensing/images/sensing_solution.png new file mode 100644 index 00000000000000..7672fd4a8b0316 Binary files /dev/null and b/doc/services/sensing/images/sensing_solution.png differ diff --git a/doc/services/sensing/images/sensor_config_flow.png b/doc/services/sensing/images/sensor_config_flow.png new file mode 100755 index 00000000000000..d1e5533f690c1a Binary files /dev/null and b/doc/services/sensing/images/sensor_config_flow.png differ diff --git a/doc/services/sensing/images/sensor_data_flow.png b/doc/services/sensing/images/sensor_data_flow.png new file mode 100755 index 00000000000000..84ae087f84cb6d Binary files /dev/null and b/doc/services/sensing/images/sensor_data_flow.png differ diff --git a/doc/services/sensing/images/sensor_top.png b/doc/services/sensing/images/sensor_top.png new file mode 100755 index 00000000000000..3e8e4f4f9f209b Binary files /dev/null and b/doc/services/sensing/images/sensor_top.png differ diff --git a/doc/services/sensing/index.rst b/doc/services/sensing/index.rst new file mode 100644 index 00000000000000..bc54598dc25c6c --- /dev/null +++ b/doc/services/sensing/index.rst @@ -0,0 +1,274 @@ +.. _sensing_api: + +Sensing Subsystem +######################## + +.. contents:: + :local: + :depth: 2 + +Overview +******** + +Sensing Subsystem is a high level sensor framework inside the OS user +space service layer. It is a framework focused on sensor fusion, client +arbitration, sampling, timing, scheduling and sensor based power management. + +Key concepts in Sensing Subsystem include physical sensor and virtual sensor objects, +and a scheduling framework over sensor object relationships. +Physical sensors do not depend on any other sensor objects for input, and +will directly interact with existing zephyr sensor device drivers. +Virtual sensors rely on other sensor objects (physical or virtual) as +report inputs. + +The sensing subsystem relies on Zephyr sensor device APIs (existing version or update in future) +to leverage Zephyr's large library of sensor device drivers (100+). + +Use of the sensing subsystem is optional. Applications that only need to access simple sensors +devices can use the Zephyr :ref:`sensor_api` API directly. + +Since the sensing subsystem is separated from device driver layer or +kernel space and could support various customizations and sensor +algorithms in user space with virtual sensor concepts. The existing +sensor device driver can focus on low layer device side works, can keep +simple as much as possible, just provide device HW abstraction and +operations etc. This is very good for system stability. + +The sensing subsystem is decoupled with any sensor expose/transfer +protocols, the target is to support various up-layer frameworks and +Applications with different sensor expose/transfer protocols, +such as `CHRE `_, HID sensors Applications, +MQTT sensor Applications according different products requirements. Or even support multiple +Applications with different up-layer sensor protocols at the same time +with it's multiple clients support design. + +Sensing subsystem can help build a unified Zephyr sensing architecture for +cross host OSes support and as well as IoT sensor solutions. + +The diagram below illustrates how the Sensing Subsystem integrates with up-layer frameworks. + +.. image:: images/sensing_solution.png + :align: center + :alt: Unified Zephyr sensing architecture. + +Configurability +=============== + +* Reusable and configurable standalone subsystem. +* Based on Zephyr existing low-level Sensor API (reuse 100+ existing sensor device drivers) +* Provide Zephyr high-level Sensing Subsystem API for Applications. +* Separate option CHRE Sensor PAL Implementation module to support CHRE. +* Decoupled with any host link protocols, it's Zephyr Application's role to handle different + protocols (MQTT, HID or Private, all configurable) + +Main Features +============= + +* Scope + * Focus on framework for sensor fusion, multiple clients, arbitration, data sampling, timing + management and scheduling. + +* Sensor Abstraction + * ``Physical sensor``: interacts with Zephyr sensor device drivers, focus on data collecting. + * ``Virtual sensor``: relies on other sensor(s), ``physical`` or ``virtual``, focus on + data fusion. + +* Data Driven Model + * ``Polling mode``: periodical sampling rate + * ``Interrupt mode``: data ready, threshold interrupt etc. + +* Scheduling + * single thread main loop for all sensor objects sampling and process. + +* Buffer Mode for Batching + +* Configurable Via Device Tree + +API Design +********** + +API Organization +================ + +* Sensing Subsystem + * Sensor Types + + .. doxygengroup:: sensing_sensor_types + + * Data Types + + .. doxygengroup:: sensing_datatypes + + * Sensing Subsystem API + + .. doxygengroup:: sensing_api + + * Sensing Sensor API + + .. doxygengroup:: sensing_sensor + +Below diagram shows the API position and scope: + +.. image:: images/sensing_api_org.png + :align: center + :alt: Sensing subsystem API organization. + +``Sensing Subsystem API`` is for Applications. +``Sensing Sensor API`` is for development ``sensors``. + + +Major Flows +========================= + +* Sensor Configuration Flow + +.. image:: images/sensor_config_flow.png + :align: center + :alt: Sensor Configuration Flow (App set report interval to hinge angel sensor example). + +* Sensor Data Flow + +.. image:: images/sensor_data_flow.png + :align: center + :alt: Sensor Data Flow (App receive hinge angel data through data event callback example). + +Sensor Types And Instance +========================= + +The ``Sensing Subsystem`` supports multiple instances of the same sensor type, +there're two methods for Applications to identify and open an unique sensor instance: + +* Enumerate all sensor instances + + :c:func:`sensing_get_sensors` returns all current board configuration supported sensor instances' + information in a :c:struct:`sensing_sensor_info` pointer array . + + Then Applications can use :c:func:`sensing_open_sensor` to + open specific sensor instance for future accessing, configuration and receive sensor data etc. + + This method is suitable for supporting some up-layer frameworks like ``CHRE``, ``HID`` which need + to dynamically enumerate the underlying platform's sensor instances. + +* Open the sensor instance by devicetree node directly + + Applications can use :c:func:`sensing_open_sensor_by_dt` to open a sensor instance directly with + sensor devicetree node identifier. + + For example: + +.. code-block:: c + + sensing_open_sensor_by_dt(DEVICE_DT_GET(DT_NODELABLE(base_accel)), cb_list, handle); + sensing_open_sensor_by_dt(DEVICE_DT_GET(DT_CHOSEN(zephyr_sensing_base_accel)), cb_list, handle); + +This method is useful and easy use for some simple Application which just want to access specific +sensor(s). + + +``Sensor type`` follows the +`HID standard sensor types definition `_. + +See :zephyr_file:`include/zephyr/sensing/sensing_sensor_types.h` + +Sensor Instance Handler +========================= + +Clients using a :c:type:`sensing_sensor_handle_t` type handler to handle a opened sensor +instance, and all subsequent operations on this sensor instance need use this handler, +such as set configurations, read sensor sample data, etc. + +For a sensor instance, could have two kinds of clients: +``Application clients`` and ``Sensor clients``. + +``Application clients`` can use :c:func:`sensing_open_sensor` to open a sensor instance +and get it's handler. + +For ``Sensor clients``, there is no open API for opening a reporter, because the client-report +relationship is built at the sensor's registration stage with devicetree. + +The ``Sensing Subsystem`` will auto open and create ``handlers`` for client sensor +to it's reporter sensors. +``Sensor clients`` can get it's reporters' handlers via :c:func:`sensing_sensor_get_reporters`. + +.. image:: images/sensor_top.png + :align: center + :alt: Sensor Reporting Topology. + +.. note:: + Sensors inside the Sensing Subsystem, the reporting relationship between them are all auto + generated by Sensing Subsystem according devicetree definitions, handlers between client sensor + and reporter sensors are auto created. + Application(s) need to call :c:func:`sensing_open_sensor` to explicitly open the sensor instance. + +Sensor Sample Value +================================== + +* Data Structure + + Each sensor sample value defines as a common ``header`` + ``readings[]`` data structure, like + :c:struct:`sensing_sensor_value_3d_q31`, :c:struct:`sensing_sensor_value_q31`, and + :c:struct:`sensing_sensor_value_uint32`. + + The ``header`` definition :c:func:`sensing_sensor_value_header`. + + +* Time Stamp + + Time stamp unit in sensing subsystem is ``micro seconds``. + + The ``header`` defines a **base_timestamp**, and + each element in the **readings[]** array defines **timestamp_delta**. + + The **timestamp_delta** is is in relation to the previous **readings** (or the **base_timestamp**) + + For example: + + * timestamp of ``readings[0]`` is ``header.base_timestamp`` + ``readings[0].timestamp_delta``. + + * timestamp of ``readings[1]`` is ``timestamp of readings[0]`` + ``readings[1].timestamp_delta``. + + Since timestamp unit is micro seconds, + the max **timestamp_delta** (``uint32_t``) is ``4295`` seconds. + + If a sensor has batched data where two consecutive readings differ by more than ``4295`` seconds, + the sensing subsystem runtime will split them across multiple instances of the readings structure, + and send multiple events. + + This concept is referred from `CHRE Sensor API `_. + +* Data Format + + ``Sensing Subsystem`` uses per sensor type defined data format structure, + and support ``Q Format`` defined in :zephyr_file:`include/zephyr/dsp/types.h` + for ``zdsp`` lib support. + + For example :c:struct:`sensing_sensor_value_3d_q31` can be used by 3D IMU sensors like + :c:macro:`SENSING_SENSOR_TYPE_MOTION_ACCELEROMETER_3D`, + :c:macro:`SENSING_SENSOR_TYPE_MOTION_UNCALIB_ACCELEROMETER_3D`, + and :c:macro:`SENSING_SENSOR_TYPE_MOTION_GYROMETER_3D`. + + :c:struct:`sensing_sensor_value_uint32` can be used by + :c:macro:`SENSING_SENSOR_TYPE_LIGHT_AMBIENTLIGHT` sensor, + + and :c:struct:`sensing_sensor_value_q31` can be used by + :c:macro:`SENSING_SENSOR_TYPE_MOTION_HINGE_ANGLE` sensor + + See :zephyr_file:`include/zephyr/sensing/sensing_datatypes.h` + + +Device Tree Configuration +************************* + +Sensing subsystem using device tree to configuration all sensor instances and their properties, +reporting relationships. + +See the example :zephyr_file:`samples/subsys/sensing/simple/boards/native_posix.overlay` + +API Reference +************* + +Sensing +======== + +.. doxygengroup:: sensing diff --git a/dts/bindings/sensor/zephyr,sensing-phy-sensor.yaml b/dts/bindings/sensor/zephyr,sensing-phy-sensor.yaml new file mode 100644 index 00000000000000..8219f77a210392 --- /dev/null +++ b/dts/bindings/sensor/zephyr,sensing-phy-sensor.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2023, Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +description: Sensing subsystem physical sensor properties bindings. + +include: zephyr,sensing-sensor.yaml + +properties: + underlying-device: + type: phandle + required: true + description: underlying sensor device for physical sensor diff --git a/dts/bindings/sensor/zephyr,sensing-sensor.yaml b/dts/bindings/sensor/zephyr,sensing-sensor.yaml new file mode 100644 index 00000000000000..5bcaa39f2570e0 --- /dev/null +++ b/dts/bindings/sensor/zephyr,sensing-sensor.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2023, Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +description: Sensing subsystem sensor common properties bindings. + +include: sensor-device.yaml + +properties: + sensor-type: + type: int + required: true + description: sensor type id (follow HID spec definition) + + friendly-name: + required: true + + minimal-interval: + type: int + required: true + description: sensor minimal report interval + + reporters: + type: phandles + description: sensor reporters diff --git a/dts/bindings/sensor/zephyr,sensing.yaml b/dts/bindings/sensor/zephyr,sensing.yaml new file mode 100644 index 00000000000000..00b5452a8471a4 --- /dev/null +++ b/dts/bindings/sensor/zephyr,sensing.yaml @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +description: Sensing Subsystem + +compatible: "zephyr,sensing" + +# To add sensor subsystem related common feature diff --git a/dts/bindings/sensor/zephyr,senss-phy-3d-sensor.yaml b/dts/bindings/sensor/zephyr,senss-phy-3d-sensor.yaml new file mode 100644 index 00000000000000..ed8cb8af19495c --- /dev/null +++ b/dts/bindings/sensor/zephyr,senss-phy-3d-sensor.yaml @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +description: Sensing subsystem physical 3d sensors(accel, gyro, mag) bindings. + +compatible: "zephyr,sensing-phy-3d-sensor" + +include: zephyr,sensing-phy-sensor.yaml diff --git a/include/zephyr/linker/common-ram.ld b/include/zephyr/linker/common-ram.ld index b4f67293610d6f..a2983930630964 100644 --- a/include/zephyr/linker/common-ram.ld +++ b/include/zephyr/linker/common-ram.ld @@ -120,6 +120,10 @@ ITERABLE_SECTION_RAM(rtio_cqe_pool, 4) #endif /* CONFIG_RTIO */ +#if defined(CONFIG_SENSING) + ITERABLE_SECTION_RAM(sensing_sensor, 4) +#endif /* CONFIG_SENSING */ + #ifdef CONFIG_USERSPACE _static_kernel_objects_end = .; #endif diff --git a/include/zephyr/linker/common-rom/common-rom-misc.ld b/include/zephyr/linker/common-rom/common-rom-misc.ld index 51d4865fa3ab13..35c6f011305dd0 100644 --- a/include/zephyr/linker/common-rom/common-rom-misc.ld +++ b/include/zephyr/linker/common-rom/common-rom-misc.ld @@ -10,6 +10,10 @@ ITERABLE_SECTION_ROM(settings_handler_static, 4) #endif +#if defined(CONFIG_SENSING) + ITERABLE_SECTION_ROM(sensing_sensor_info, 4) +#endif + #if defined(CONFIG_SENSOR_INFO) ITERABLE_SECTION_ROM(sensor_info, 4) #endif diff --git a/include/zephyr/sensing/sensing.h b/include/zephyr/sensing/sensing.h new file mode 100644 index 00000000000000..3b11d1b410ca4a --- /dev/null +++ b/include/zephyr/sensing/sensing.h @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2022-2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SENSING_H_ +#define ZEPHYR_INCLUDE_SENSING_H_ + +/** + * @defgroup sensing Sensing + * @defgroup sensing_api Sensing Subsystem API + * @ingroup sensing + * @defgroup sensing_sensor_types Sensor Types + * @ingroup sensing + * @defgroup sensing_datatypes Data Types + * @ingroup sensing + */ + +#include +#include +#include + +/** + * @brief Sensing Subsystem API + * @addtogroup sensing_api + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @struct sensing_sensor_version + * @brief Sensor Version + */ +struct sensing_sensor_version { + union { + uint32_t value; + struct { + uint8_t major; + uint8_t minor; + uint8_t hotfix; + uint8_t build; + }; + }; +}; + +#define SENSING_SENSOR_VERSION(_major, _minor, _hotfix, _build) \ + (FIELD_PREP(GENMASK(31, 24), _major) | \ + FIELD_PREP(GENMASK(23, 16), _minor) | \ + FIELD_PREP(GENMASK(15, 8), _hotfix) | \ + FIELD_PREP(GENMASK(7, 0), _build)) + + +/** + * @brief Sensor flag indicating if this sensor is on event reporting data. + * + * Reporting sensor data when the sensor event occurs, such as a motion detect sensor reporting + * a motion or motionless detected event. + */ +#define SENSING_SENSOR_FLAG_REPORT_ON_EVENT BIT(0) + +/** + * @brief Sensor flag indicating if this sensor is on change reporting data. + * + * Reporting sensor data when the sensor data changes. + * + * Exclusive with \ref SENSING_SENSOR_FLAG_REPORT_ON_EVENT + */ +#define SENSING_SENSOR_FLAG_REPORT_ON_CHANGE BIT(1) + + +/** + * @brief Sensing subsystem sensor state. + * + */ +enum sensing_sensor_state { + SENSING_SENSOR_STATE_READY = 0, + SENSING_SENSOR_STATE_OFFLINE = 1, +}; + +/** + * @brief Sensing subsystem sensor config attribute + * + */ +enum sensing_sensor_attribute { + SENSING_SENSOR_ATTRIBUTE_INTERVAL = 0, + SENSING_SENSOR_ATTRIBUTE_SENSITIVITY = 1, + SENSING_SENSOR_ATTRIBUTE_LATENCY = 2, + SENSING_SENSOR_ATTRIBUTE_MAX, +}; + + +/** + * @brief Define Sensing subsystem sensor handle + * + */ +typedef void *sensing_sensor_handle_t; + + +/** + * @brief Sensor data event receive callback. + * + * @param handle The sensor instance handle. + * + * @param buf The data buffer with sensor data. + */ +typedef void (*sensing_data_event_t)( + sensing_sensor_handle_t handle, + const void *buf); + +/** + * @struct sensing_sensor_info + * @brief Sensor basic constant information + * + */ +struct sensing_sensor_info { + /** Name of the sensor instance */ + const char *name; + + /** Friendly name of the sensor instance */ + const char *friendly_name; + + /** Vendor name of the sensor instance */ + const char *vendor; + + /** Model name of the sensor instance */ + const char *model; + + /** Sensor type */ + const int32_t type; + + /** Minimal report interval in micro seconds */ + const uint32_t minimal_interval; +}; + +/** + * @struct sensing_callback_list + * @brief Sensing subsystem event callback list + * + */ +struct sensing_callback_list { + sensing_data_event_t on_data_event; +}; +/** + * @struct sensing_sensor_config + * @brief Sensing subsystem sensor configure, including interval, sensitivity, latency + * + */ +struct sensing_sensor_config { + enum sensing_sensor_attribute attri; + int8_t data_field; + union { + uint32_t interval; + uint32_t sensitivity; + uint64_t latency; + }; +}; + + + /** + * @brief Get all supported sensor instances' information. + * + * This API just returns read only information of sensor instances, pointer info will + * directly point to internal buffer, no need for caller to allocate buffer, + * no side effect to sensor instances. + * + * @param num_sensors Get number of sensor instances. + * + * @param info For receiving sensor instances' information array pointer. + * + * @return 0 on success or negative error value on failure. + */ +int sensing_get_sensors(int *num_sensors, const struct sensing_sensor_info **info); + +/** + * @brief Open sensor instance by sensing sensor info + * + * Application clients use it to open a sensor instance and get its handle. + * Support multiple Application clients for open same sensor instance, + * in this case, the returned handle will different for different clients. + * meanwhile, also register sensing callback list + * + * @param info The sensor info got from \ref sensing_get_sensors + * + * @param cb_list callback list to be registered to sensing. + * + * @param *handle The opened instance handle, if failed will be set to NULL. + * + * @return 0 on success or negative error value on failure. + */ +int sensing_open_sensor( + const struct sensing_sensor_info *info, + const struct sensing_callback_list *cb_list, + sensing_sensor_handle_t *handle); + +/** + * @brief Open sensor instance by device. + * + * Application clients use it to open a sensor instance and get its handle. + * Support multiple Application clients for open same sensor instance, + * in this case, the returned handle will different for different clients. + * meanwhile, also register sensing callback list. + * + * @param dev pointer device get from device tree. + * + * @param cb_list callback list to be registered to sensing. + * + * @param *handle The opened instance handle, if failed will be set to NULL. + * + * @return 0 on success or negative error value on failure. + */ +int sensing_open_sensor_by_dt( + const struct device *dev, const struct sensing_callback_list *cb_list, + sensing_sensor_handle_t *handle); + +/** + * @brief Close sensor instance. + * + * @param handle The sensor instance handle need to close. + * + * @return 0 on success or negative error value on failure. + */ +int sensing_close_sensor( + sensing_sensor_handle_t *handle); + +/** + * @brief Set current config items to Sensing subsystem. + * + * @param handle The sensor instance handle. + * + * @param configs The configs to be set according to config attribute. + * + * @param count count of configs. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +int sensing_set_config( + sensing_sensor_handle_t handle, + struct sensing_sensor_config *configs, int count); + +/** + * @brief Get current config items from Sensing subsystem. + * + * @param handle The sensor instance handle. + * + * @param configs The configs to be get according to config attribute. + * + * @param count count of configs. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +int sensing_get_config( + sensing_sensor_handle_t handle, + struct sensing_sensor_config *configs, int count); + +/** + * @brief Get sensor information from sensor instance handle. + * + * @param handle The sensor instance handle. + * + * @return a const pointer to \ref sensing_sensor_info on success or NULL on failure. + */ +const struct sensing_sensor_info *sensing_get_sensor_info( + sensing_sensor_handle_t handle); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + + +#endif /*ZEPHYR_INCLUDE_SENSING_H_*/ diff --git a/include/zephyr/sensing/sensing_datatypes.h b/include/zephyr/sensing/sensing_datatypes.h new file mode 100644 index 00000000000000..3549aab4bdb692 --- /dev/null +++ b/include/zephyr/sensing/sensing_datatypes.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2022-2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SENSING_DATATYPES_H_ +#define ZEPHYR_INCLUDE_SENSING_DATATYPES_H_ + +#include +#include + +/** + * @brief Data Types + * @addtogroup sensing_datatypes + * @{ + */ + +/** + * @struct sensing_sensor_value_header + * @brief sensor value header + * + * Each sensor value data structure should have this header + * + * Here use 'base_timestamp' (uint64_t) and 'timestamp_delta' (uint32_t) to + * save memory usage in batching mode. + * + * The 'base_timestamp' is for readings[0], the 'timestamp_delta' is relation + * to the previous 'readings'. So, + * timestamp of readings[0] is + * header.base_timestamp + readings[0].timestamp_delta. + * timestamp of readings[1] is + * timestamp of readings[0] + readings[1].timestamp_delta. + * + * Since timestamp unit is micro seconds, the max 'timestamp_delta' (uint32_t) + * is 4295 seconds. + * + * If a sensor has batched data where two consecutive readings differ by + * more than 4295 seconds, the sensor subsystem core will split them + * across multiple instances of the readings structure, and send multiple + * events. + * + * This concept is borrowed from CHRE: + * https://cs.android.com/android/platform/superproject/+/master:\ + * system/chre/chre_api/include/chre_api/chre/sensor_types.h + */ +struct sensing_sensor_value_header { + /** base timestamp of this data readings, unit is micro seconds */ + uint64_t base_timestamp; + /** count of this data readings */ + uint16_t reading_count; +}; + +/** + * @brief Sensor value data structure types based on common data types. + * Suitable for common sensors, such as IMU, Light sensors and orientation sensors. + */ + +/** + * @brief Sensor value data structure for 3-axis sensors. + * struct sensing_sensor_value_3d_q31 can be used by 3D IMU sensors like: + * SENSING_SENSOR_TYPE_MOTION_ACCELEROMETER_3D, + * SENSING_SENSOR_TYPE_MOTION_UNCALIB_ACCELEROMETER_3D, + * SENSING_SENSOR_TYPE_MOTION_GYROMETER_3D, + * q31 version + */ +struct sensing_sensor_value_3d_q31 { + struct sensing_sensor_value_header header; + int8_t shift; + struct { + uint32_t timestamp_delta; + union { + q31_t v[3]; + struct { + q31_t x; + q31_t y; + q31_t z; + }; + }; + } readings[1]; +}; + +/** + * @brief Sensor value data structure for single 1-axis value. + * struct sensing_sensor_value_uint32 can be used by SENSING_SENSOR_TYPE_LIGHT_AMBIENTLIGHT sensor + * uint32_t version + */ +struct sensing_sensor_value_uint32 { + struct sensing_sensor_value_header header; + struct { + uint32_t timestamp_delta; + uint32_t v; + } readings[1]; +}; + +/** + * @brief Sensor value data structure for single 1-axis value. + * struct sensing_sensor_value_q31 can be used by SENSING_SENSOR_TYPE_MOTION_HINGE_ANGLE sensor + * q31 version + */ +struct sensing_sensor_value_q31 { + int8_t shift; + struct sensing_sensor_value_header header; + struct { + uint32_t timestamp_delta; + q31_t v; + } readings[1]; +}; + + +/** + * @} + */ + +#endif /*ZEPHYR_INCLUDE_SENSING_DATATYPES_H_*/ diff --git a/include/zephyr/sensing/sensing_sensor.h b/include/zephyr/sensing/sensing_sensor.h new file mode 100644 index 00000000000000..e7716a317d8e81 --- /dev/null +++ b/include/zephyr/sensing/sensing_sensor.h @@ -0,0 +1,594 @@ +/* + * Copyright (c) 2022-2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SENSING_SENSOR_H_ +#define ZEPHYR_INCLUDE_SENSING_SENSOR_H_ + +#include +#include +#include +#include + +/** + * @defgroup sensing_sensor Sensing Sensor API + * @ingroup sensing + * @defgroup sensing_sensor_callbacks Sensor Callbacks + * @ingroup sensing_sensor + */ + +/** + * @brief Sensing Sensor API + * @addtogroup sensing_sensor + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Sensor registration information + * + */ +struct sensing_sensor_register_info { + + /** + * Sensor flags + */ + uint16_t flags; + + /** + * Sample size in bytes for a single sample of the registered sensor. + * sensing runtime need this information for internal buffer allocation. + */ + uint16_t sample_size; + + /** + * The number of sensor sensitivities + */ + uint8_t sensitivity_count; + + /** + * Sensor version. + * Version can be used to identify different versions of sensor implementation. + */ + struct sensing_sensor_version version; +}; + +/** + * @brief Sensor context data structure + * + */ +struct sensing_sensor_ctx { + + /** + * For sensing runtime internal private data, sensor should not see and touch + */ + void *priv_ptr; + + /** + * Pointer to the sensor register information. + */ + const struct sensing_sensor_register_info *register_info; + + /** + * For sensor private context data, registered by sensor with \ref SENSING_SENSOR_DT_DEFINE. + * Sensor could use \ref sensing_sensor_get_ctx_data to fetch out this filed with + * struct device. + */ + void *const sensor_ctx_ptr; +}; + +static inline int sensing_sensor_dev_init( + const struct device *dev) +{ + /** + * Nothing need to do in system auto initialization. + * Sensor subsystem runtime will call each sensor instance's initialization + * function via API callback according sensor reporting dependency sequences. + * Sensor subsystem can make sure the depends sensor instances always initialized before + * client sensors. + */ + return 0; +} + +/** + * @brief Macro for define a sensor instance from device tree node id + * + * This macro also defined a struct device for this sensor instance, and registered sensors' + * private context data, configuration data structure and API. + * + * sensing_init will enumerate all sensor instances from device tree, and initialize each sensor + * instance defined by this macro. + * + */ + +#define SENSING_SENSOR_DT_DEFINE(node_id, reg_ptr, ctx_ptr, api_ptr) \ + static struct sensing_sensor_ctx \ + _CONCAT(__sensing_sensor_ctx_, Z_DEVICE_DT_DEV_ID(node_id)) = { \ + .register_info = reg_ptr, \ + .sensor_ctx_ptr = ctx_ptr, \ + }; \ + DEVICE_DT_DEFINE(node_id, sensing_sensor_dev_init, NULL, \ + &_CONCAT(__sensing_sensor_ctx_, Z_DEVICE_DT_DEV_ID(node_id)), \ + NULL, APPLICATION, 10, api_ptr) + +/** + * @brief Get registered context data pointer for a sensor instance. + * + * Used by a sensor instance to get its registered context data pointer with its struct device. + * + * @param dev The sensor instance device structure. + */ +static inline void *sensing_sensor_get_ctx_data( + const struct device *dev) +{ + struct sensing_sensor_ctx *data = dev->data; + + return data->sensor_ctx_ptr; +} + +/** + * @brief Post sensor data, sensor subsystem runtime will deliver to it's + * clients. + * + * Unblocked function, returned immediately. + * + * Used by a virtual sensor to post data to it's clients. + * + * A reporter sensor can use this API to post data to it's clients. + * For example, when a virtual sensor computed a data, then can use this API + * to deliver the data to it's clients. + * Please note, this API just for reporter post data to the sensor subsystem + * runtime, the runtime will help delivered the data to it's all clients + * according clients' configurations such as reporter interval, data change sensitivity. + * + * @param dev The sensor instance device structure. + * + * @param buf The data buffer. + * + * @param size The buffer size in bytes. + * + * @return 0 on success or negative error value on failure. + */ +int sensing_sensor_post_data( + const struct device *dev, + void *buf, int size); + +/** + * @brief Get reporter handles of a given sensor instance by sensor type. + * + * @param dev The sensor instance device structure. + * + * @param type The given type, \ref SENSING_SENSOR_TYPE_ALL to get reporters + * with all types. + * + * @param max_handles The max count of the \p reporter_handles array input. Can + * get real count number via \ref sensing_sensor_get_reporters_count + * + * @param *reporter_handles Input handles array for receiving found reporter + * sensor instances + * + * @return number of reporters found, 0 returned if not found. + */ +int sensing_sensor_get_reporters( + const struct device *dev, int type, + const int *reporter_handles, int max_handles); + +/** + * @brief Get reporters count of a given sensor instance by sensor type. + * + * @param dev The sensor instance device structure. + * + * @param type The sensor type for checking, \ref SENSING_SENSOR_TYPE_ALL + * + * @return Count of reporters by \p type, 0 returned if no reporters by \p type. + */ +int sensing_sensor_get_reporters_count( + const struct device *dev, int type); + +/** + * @brief Get this sensor's state + * + * @param dev The sensor instance device structure. + * + * @param state Returned sensor state value + * + * @return 0 on success or negative error value on failure. + */ +int sensing_sensor_get_state( + const struct device *dev, + enum sensing_sensor_state *state); + +/** + * @brief Trigger the data ready event to sensing + * + * @param dev Pointer to the sensor device + * + * @return 0 on success or negative error value on failure. + */ +int sensing_sensor_notify_data_ready( + const struct device *dev); + +/** + * @brief Set the data ready mode of the sensor + * + * @param dev Pointer to the sensor device + * + * @param data_ready Enable/disable the data ready mode. Default:disabled + * + * @return 0 on success or negative error value on failure. + */ +int sensing_sensor_set_data_ready( + const struct device *dev, bool data_ready); + +/** + * @} + */ + +/** + * @brief Sensor Callbacks + * @addtogroup sensing_sensor_callbacks + * \{ + */ + +/** + * @brief Sensor initialize. + * + * Sensor can initialize it's runtime context in this callback. + * + * @param dev The sensor instance device structure. + * + * @param info The sensor instance's constant information. + * + * @param reporter_handles The reporters handles for this sensor, NULL for physical sensors. + * + * @param reporters_count The number of reporters, zero for physical sensors. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_init_t)( + const struct device *dev, const struct sensing_sensor_info *info, + const sensing_sensor_handle_t *reporter_handles, int reporters_count); + +/** + * @brief Sensor's de-initialize. + * + * Sensor can release it's runtime context in this callback. + * + * @param dev The sensor instance device structure. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_deinit_t)( + const struct device *dev); + +/** + * @brief Sensor reset. + * + * Sensor can reset its runtime context in this callback to default values without resources + * release and re-allocation. + * + * Its very useful for a virtual sensor to quickly reset its runtime context to a default state. + * + * @param dev The sensor instance device structure. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_reset_t)( + const struct device *dev); + +/** + * @brief Sensor read sample. + * + * Only physical sensor need implement this callback. + * Physical sensor can fetch sample data from sensor device in this callback + * + * @param dev The sensor instance device structure. + * + * @param buf Sensor subsystem runtime allocated buffer, and passed its pointer + * to this sensor for store fetched sample. + * + * @param size The size of the buffer in bytes. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_read_sample_t)( + const struct device *dev, + void *buf, int size); + +/** + * @brief Sensor process data. + * + * Only virtual sensor need implement this callback. + * Virtual sensor can receive reporter's data and do fusion computing + * in this callback. + * + * @param dev The sensor instance device structure. + * + * @param reporter The reporter handle who delivered this sensor data + * + * @param buf The buffer stored the reporter's sensor data. + * + * @param size The size of the buffer in bytes. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_process_t)( + const struct device *dev, + int reporter, + void *buf, int size); + +/** + * @brief Trigger a sensor to do self calibration + * + * If not support self calibration, can not implement this callback. + * + * @param dev The sensor instance device structure. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_self_calibration_t)( + const struct device *dev); + +/** + * @brief Sensitivity arbitration. + * + * This callback API provides a chance for sensor to do customized arbitration on data change + * sensitivity. + * The sensor can check two sequential samples with client's sensitivity value (passed with + * parameters in this callback) and decide if can pass the sensor sample to its client. + * + * @param dev The sensor instance device structure. + * + * @param index The value fields index to be set, -1 for all fields (global). + * + * @param sensitivity The sensitivity value. + * + * @param last_sample_buf The buffer stored last sample data. + * + * @param last_sample_size The size of last sample's data buffer in bytes + * + * @param current_sample_buf The buffer stored current sample data. + * + * @param current_sample_size The size of current sample's data buffer in bytes + * + * @return 0 on test passed or negative error value on failure. + * + */ +typedef int (*sensing_sensor_sensitivity_test_t)( + const struct device *dev, + int index, uint32_t sensitivity, + void *last_sample_buf, int last_sample_size, + void *current_sample_buf, int current_sample_size); + +/** + * @brief Set current report interval. + * + * @param dev The sensor instance device structure. + * + * @param value The value to be set. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_set_interval_t)( + const struct device *dev, + uint32_t value); + +/** + * @brief Get current report interval. + * + * @param dev The sensor instance device structure. + * + * @param value The data buffer to receive value. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_get_interval_t)( + const struct device *dev, + uint32_t *value); + +/** + * @brief Set data change sensitivity. + * + * Since each sensor type may have multiple data fields for it's value, this + * API support set separated sensitivity for each data field, or global + * sensitivity for all data fields. + * + * @param dev The sensor instance device structure. + * + * @param index The value fields index to be set, -1 for all fields (global). + * + * @param value The value to be set. + * + * @return 0 on success or negative error value on failure. + * + */ +typedef int (*sensing_sensor_set_sensitivity_t)( + const struct device *dev, + int index, uint32_t value); + +/** + * @brief Get current data change sensitivity. + * + * Since each sensor type may have multiple data fields for it's value, this + * API support get separated sensitivity for each data field, or global + * sensitivity for all data fields. + * + * @param dev The sensor instance device structure. + * + * @param index The value fields index to be set, -1 for all fields (global). + * + * @param value The data buffer to receive value. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +typedef int (*sensing_sensor_get_sensitivity_t)( + const struct device *dev, + int index, uint32_t *value); + +/** + * @brief Set data range. + * + * Some sensors especially for physical sensors, support data range + * configuration, this may change data resolution. + * + * Since each sensor type may have multiple data fields for it's value, this + * API support set separated range for each data field, or global range for + * all data fields. + * + * @param dev The sensor instance device structure. + * + * @param index The value fields index to be set, -1 for all fields (global). + * + * @param value The value to be set. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +typedef int (*sensing_sensor_set_range_t)( + const struct device *dev, + int index, uint32_t value); + +/** + * @brief Get current data range. + * + * Some sensors especially for physical sensors, support data range + * configuration, this may change data resolution. + * + * Since each sensor type may have multiple data fields for it's value, this + * API support get separated range for each data field, or global range for + * all data fields. + * + * @param dev The sensor instance device structure. + * + * @param index The value fields index to be set, -1 for all fields (global). + * + * @param value The data buffer to receive value. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +typedef int (*sensing_sensor_get_range_t)( + const struct device *dev, + int index, uint32_t *value); + +/** + * @brief Set current sensor's hardware fifo size + * + * Some sensors especially for physical sensors, support hardware fifo, this API can + * configure the current fifo size. + * + * @param dev The sensor instance device structure. + * + * @param samples The sample number to set for fifo. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +typedef int (*sensing_sensor_set_fifo_t)( + const struct device *dev, + uint32_t samples); + +/** + * @brief Get current sensor's hardware fifo size + * + * Some sensors especially for physical sensors, support fifo, this API can + * get the current fifo size. + * + * @param dev The sensor instance device structure. + * + * @param samples The data buffer to receive the fifo sample number. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +typedef int (*sensing_sensor_get_fifo_t)( + const struct device *dev, + uint32_t *samples); + +/** + * @brief Set current sensor data offset + * + * Some sensors especially for physical sensors, such as accelerometer senors, + * as data drift, need configure offset calibration. + * + * Since each sensor type may have multiple data fields for it's value, this + * API support set separated offset for each data field, or global offset for + * all data fields. + * + * @param dev The sensor instance device structure. + * + * @param index The value fields index to be set, -1 for all fields (global). + * + * @param value The offset value to be set. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +typedef int (*sensing_sensor_set_offset_t)( + const struct device *dev, + int index, int32_t value); + +/** + * @brief Get current sensor data offset + * + * Some sensors especially for physical sensors, such as accelerometer senors, + * as data drift, need configure offset calibration. + * + * Since each sensor type may have multiple data fields for it's value, this + * API support get separated offset for each data field, or global offset for + * all data fields. + * + * @param dev The sensor instance device structure. + * + * @param index The value fields index to be set, -1 for all fields (global). + * + * @param value The data buffer to receive the offset value. + * + * @return 0 on success or negative error value on failure, not support etc. + */ +typedef int (*sensing_sensor_get_offset_t)( + const struct device *dev, + int index, int32_t *value); +/** + * @struct sensing_sensor_api + * @brief Sensor callback api + * + * A sensor must register this callback API during sensor registration. + */ +struct sensing_sensor_api { + sensing_sensor_init_t init; + sensing_sensor_reset_t reset; + sensing_sensor_deinit_t deinit; + sensing_sensor_set_interval_t set_interval; + sensing_sensor_get_interval_t get_interval; + sensing_sensor_set_range_t set_range; + sensing_sensor_get_range_t get_range; + sensing_sensor_set_offset_t set_offset; + sensing_sensor_get_offset_t get_offset; + sensing_sensor_get_fifo_t get_fifo; + sensing_sensor_set_fifo_t set_fifo; + sensing_sensor_set_sensitivity_t set_sensitivity; + sensing_sensor_get_sensitivity_t get_sensitivity; + sensing_sensor_read_sample_t read_sample; + sensing_sensor_process_t process; + sensing_sensor_sensitivity_test_t sensitivity_test; + sensing_sensor_self_calibration_t self_calibration; +}; + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*ZEPHYR_INCLUDE_SENSING_SENSOR_H_*/ diff --git a/include/zephyr/sensing/sensing_sensor_types.h b/include/zephyr/sensing/sensing_sensor_types.h new file mode 100644 index 00000000000000..db15af1e279f7a --- /dev/null +++ b/include/zephyr/sensing/sensing_sensor_types.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022-2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SENSING_SENSOR_TYPES_H_ +#define ZEPHYR_INCLUDE_SENSING_SENSOR_TYPES_H_ + +/** + * @brief Sensor Types Definition + * + * Sensor types definition followed HID standard. + * https://usb.org/sites/default/files/hutrr39b_0.pdf + * + * TODO: will add more types + * + * @addtogroup sensing_sensor_types + * @{ + */ + +/** + * sensor category light + */ +#define SENSING_SENSOR_TYPE_LIGHT_AMBIENTLIGHT 0x41 + +/** + * sensor category motion + */ +#define SENSING_SENSOR_TYPE_MOTION_ACCELEROMETER_3D 0x73 +#define SENSING_SENSOR_TYPE_MOTION_GYROMETER_3D 0x76 +#define SENSING_SENSOR_TYPE_MOTION_MOTION_DETECTOR 0x77 + + +/** + * sensor category other + */ +#define SENSING_SENSOR_TYPE_OTHER_CUSTOM 0xE1 + +#define SENSING_SENSOR_TYPE_MOTION_UNCALIB_ACCELEROMETER_3D 0x240 + +#define SENSING_SENSOR_TYPE_MOTION_HINGE_ANGLE 0x20B + +#define SENSING_SENSOR_TYPE_ALL 0xFFFF + +/** + * @} + */ + +#endif /*ZEPHYR_INCLUDE_SENSING_SENSOR_TYPES_H_*/ diff --git a/samples/subsys/sensing/sensing.rst b/samples/subsys/sensing/sensing.rst new file mode 100644 index 00000000000000..99fdf509c082f2 --- /dev/null +++ b/samples/subsys/sensing/sensing.rst @@ -0,0 +1,10 @@ +.. _sensing-subsystem-samples: + +Sensing Subsystem Samples +######################### + +.. toctree:: + :maxdepth: 1 + :glob: + + **/* diff --git a/samples/subsys/sensing/simple/CMakeLists.txt b/samples/subsys/sensing/simple/CMakeLists.txt new file mode 100644 index 00000000000000..4f35ea3e342068 --- /dev/null +++ b/samples/subsys/sensing/simple/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sensing) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/subsys/sensing/simple/README.rst b/samples/subsys/sensing/simple/README.rst new file mode 100644 index 00000000000000..11a72341fcb48e --- /dev/null +++ b/samples/subsys/sensing/simple/README.rst @@ -0,0 +1,38 @@ +.. _sensing subsytem-sample: + +Sensing subsystem sample +######################## + +Overview +******** + +A simple sample that shows how to use the sensors with sensing subsystem APIs. It defines +two sensors, with the underlying device bmi160 emulator, and gets the sensor +data in defined interval. + +The program runs in the following sequence: + +#. Define the sensor in the dts + +#. Open the sensor + +#. Register call back. + +#. Set sample interval + +#. Run forever and get the sensor data. + +Building and Running +******************** + +This application can be built and executed on native_posix as follows: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/sensing/simple + :host-os: unix + :board: native_posix + :goals: run + :compact: + +To build for another board, change "native_posix" above to that board's name. +At the current stage, it only support native posix diff --git a/samples/subsys/sensing/simple/app.overlay b/samples/subsys/sensing/simple/app.overlay new file mode 100644 index 00000000000000..f9892f42a56a18 --- /dev/null +++ b/samples/subsys/sensing/simple/app.overlay @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + * Application overlay for testing the devicetree.h API. + * + * Names in this file should be chosen in a way that won't conflict + * with real-world devicetree nodes, to allow these tests to run on + * (and be extended to test) real hardware. + */ + + +/ { + sensing: sensing-node { + compatible = "zephyr,sensing"; + status = "okay"; + }; +}; diff --git a/samples/subsys/sensing/simple/boards/native_posix.conf b/samples/subsys/sensing/simple/boards/native_posix.conf new file mode 100644 index 00000000000000..b5a8eb94dc8e3c --- /dev/null +++ b/samples/subsys/sensing/simple/boards/native_posix.conf @@ -0,0 +1,5 @@ +#Enable BMI160 and BMI160 Emulator +CONFIG_BMI160_TRIGGER_NONE=y +CONFIG_EMUL_BMI160=y +CONFIG_SENSOR=y +CONFIG_SENSOR_INFO=y diff --git a/samples/subsys/sensing/simple/boards/native_posix.overlay b/samples/subsys/sensing/simple/boards/native_posix.overlay new file mode 100644 index 00000000000000..d11630cd5d98af --- /dev/null +++ b/samples/subsys/sensing/simple/boards/native_posix.overlay @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&i2c0 { + bmi160_i2c: bmi@68 { + compatible = "bosch,bmi160"; + reg = <0x68>; + }; +}; + +&spi0 { + bmi160_spi: bmi@3 { + compatible = "bosch,bmi160"; + spi-max-frequency = <50000000>; + reg = <0x3>; + }; +}; + +/ { + sensing: sensing-node { + compatible = "zephyr,sensing"; + status = "okay"; + + base_accel: base-accel { + compatible = "zephyr,sensing-phy-3d-sensor"; + status = "okay"; + sensor-type = <0x73>; + friendly-name = "Base Accelerometer Sensor"; + minimal-interval = <625>; + underlying-device = <&bmi160_i2c>; + }; + + lid_accel: lid-accel { + compatible = "zephyr,sensing-phy-3d-sensor"; + status = "okay"; + sensor-type = <0x73>; + friendly-name = "Lid Accelerometer Sensor"; + minimal-interval = <625>; + underlying-device = <&bmi160_spi>; + }; + }; +}; diff --git a/samples/subsys/sensing/simple/prj.conf b/samples/subsys/sensing/simple/prj.conf new file mode 100644 index 00000000000000..1e935e973c761b --- /dev/null +++ b/samples/subsys/sensing/simple/prj.conf @@ -0,0 +1 @@ +CONFIG_LOG=y diff --git a/samples/subsys/sensing/simple/sample.yaml b/samples/subsys/sensing/simple/sample.yaml new file mode 100644 index 00000000000000..b930aff274be83 --- /dev/null +++ b/samples/subsys/sensing/simple/sample.yaml @@ -0,0 +1,16 @@ +sample: + description: A sensor framework sample + name: sensing sample +common: + tags: sensing + integration_platforms: + - native_posix + harness: console + harness_config: + type: multi_line + regex: + - "sensing subsystem run successfully" +tests: + sample.subsys.sensing.simple: + platform_allow: native_posix + tags: sensing diff --git a/samples/subsys/sensing/simple/src/main.c b/samples/subsys/sensing/simple/src/main.c new file mode 100644 index 00000000000000..bc1b3bb8b916da --- /dev/null +++ b/samples/subsys/sensing/simple/src/main.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); + +static void acc_data_event_callback(sensing_sensor_handle_t handle, const void *buf) +{ + const struct sensing_sensor_info *info = sensing_get_sensor_info(handle); + struct sensing_sensor_value_3d_q31 *sample = (struct sensing_sensor_value_3d_q31 *)buf; + + LOG_INF("%s(%d), handle:%p, Sensor:%s data:(x:%d, y:%d, z:%d)", + __func__, __LINE__, handle, info->name, + sample->readings[0].x, + sample->readings[0].y, + sample->readings[0].z); +} + +void main(void) +{ + const struct sensing_callback_list base_acc_cb_list = { + .on_data_event = &acc_data_event_callback, + }; + const struct sensing_callback_list lid_acc_cb_list = { + .on_data_event = &acc_data_event_callback, + }; + const struct sensing_sensor_info *info; + sensing_sensor_handle_t base_acc; + sensing_sensor_handle_t lid_acc; + int ret, i, num = 0; + + ret = sensing_get_sensors(&num, &info); + if (ret) { + LOG_ERR("sensing_get_sensors error"); + return; + } + + for (i = 0; i < num; ++i) { + LOG_INF("Sensor %d: name: %s friendly_name: %s type: %d", + i, + info[i].name, + info[i].friendly_name, + info[i].type); + } + + LOG_INF("sensing subsystem run successfully"); + + ret = sensing_open_sensor(&info[0], &base_acc_cb_list, &base_acc); + if (ret) { + LOG_ERR("sensing_open_sensor, type:0x%x index:0 error:%d", + SENSING_SENSOR_TYPE_MOTION_ACCELEROMETER_3D, ret); + } + + ret = sensing_open_sensor_by_dt(DEVICE_DT_GET(DT_NODELABEL(lid_accel)), + &lid_acc_cb_list, + &lid_acc); + if (ret) { + LOG_ERR("sensing_open_sensor, type:0x%x index:1 error:%d", + SENSING_SENSOR_TYPE_MOTION_ACCELEROMETER_3D, ret); + } + + ret = sensing_close_sensor(&base_acc); + if (ret) { + LOG_ERR("sensing_close_sensor:%p error:%d", base_acc, ret); + } + + ret = sensing_close_sensor(&lid_acc); + if (ret) { + LOG_ERR("sensing_close_sensor:%p error:%d", lid_acc, ret); + } +} diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index 53ee9f96c81660..de5c85f51bb529 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory_ifdef(CONFIG_JWT jwt) add_subdirectory_ifdef(CONFIG_LORAWAN lorawan) add_subdirectory_ifdef(CONFIG_NET_BUF net) add_subdirectory_ifdef(CONFIG_RETENTION retention) +add_subdirectory_ifdef(CONFIG_SENSING sensing) add_subdirectory_ifdef(CONFIG_SETTINGS settings) add_subdirectory_ifdef(CONFIG_SHELL shell) add_subdirectory_ifdef(CONFIG_TIMING_FUNCTIONS timing) diff --git a/subsys/Kconfig b/subsys/Kconfig index 0bf699da96ce60..c2b4b914548dbc 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -31,6 +31,7 @@ source "subsys/random/Kconfig" source "subsys/retention/Kconfig" source "subsys/rtio/Kconfig" source "subsys/sd/Kconfig" +source "subsys/sensing/Kconfig" source "subsys/settings/Kconfig" source "subsys/shell/Kconfig" source "subsys/stats/Kconfig" diff --git a/subsys/sensing/CMakeLists.txt b/subsys/sensing/CMakeLists.txt new file mode 100644 index 00000000000000..59aa28514b03fe --- /dev/null +++ b/subsys/sensing/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_include_directories(include) + +zephyr_library_sources( + sensor_mgmt.c + sensing.c + sensing_sensor.c +) + +add_subdirectory_ifdef(CONFIG_SENSING_SENSOR_PHY_3D_SENSOR sensor/phy_3d_sensor) diff --git a/subsys/sensing/Kconfig b/subsys/sensing/Kconfig new file mode 100644 index 00000000000000..9101db074457a5 --- /dev/null +++ b/subsys/sensing/Kconfig @@ -0,0 +1,29 @@ +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SENSING + bool "Sensing Subsystem" + default y + depends on DT_HAS_ZEPHYR_SENSING_ENABLED + help + Enable Sensing Subsystem. + +if SENSING + +module = SENSING +module-str = sensing +source "subsys/logging/Kconfig.template.log_config" + +config SENSING_MAX_SENSITIVITY_COUNT + int "maximum sensitivity count one sensor could support" + depends on SENSING + default 6 + help + This is the maximum sensitivity count one sensor could support, + some sensors such as ALS sensor could define different sensitivity for each data filed, + So, maximum sensitivity count is needed for sensors + Typical values are 6 + +source "subsys/sensing/sensor/phy_3d_sensor/Kconfig" + +endif # SENSING diff --git a/subsys/sensing/sensing.c b/subsys/sensing/sensing.c new file mode 100644 index 00000000000000..6074925437923c --- /dev/null +++ b/subsys/sensing/sensing.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "sensor_mgmt.h" + +#include +LOG_MODULE_DECLARE(sensing, CONFIG_SENSING_LOG_LEVEL); + +/* sensing_open_sensor is normally called by applications: hid, chre, zephyr main, etc */ +int sensing_open_sensor(const struct sensing_sensor_info *sensor_info, + const struct sensing_callback_list *cb_list, + sensing_sensor_handle_t *handle) +{ + int ret = 0; + + if (handle == NULL) { + return -ENODEV; + } + + STRUCT_SECTION_FOREACH(sensing_sensor, sensor) { + if (sensor_info == sensor->info) { + ret = open_sensor(sensor, (struct sensing_connection **)handle); + if (ret) { + return -EINVAL; + } + break; + } + } + + return sensing_register_callback(*handle, cb_list); +} + +int sensing_open_sensor_by_dt(const struct device *dev, + const struct sensing_callback_list *cb_list, + sensing_sensor_handle_t *handle) +{ + int ret = 0; + struct sensing_sensor *sensor; + + if (handle == NULL) { + return -ENODEV; + } + + sensor = get_sensor_by_dev(dev); + if (sensor == NULL) { + LOG_ERR("cannot get sensor from dev:%p", dev); + return -ENODEV; + } + + ret = open_sensor(sensor, (struct sensing_connection **)handle); + if (ret) { + return -EINVAL; + } + + return sensing_register_callback(*handle, cb_list); +} + +/* sensing_close_sensor is normally called by applications: hid, chre, zephyr main, etc */ +int sensing_close_sensor(sensing_sensor_handle_t *handle) +{ + return close_sensor((struct sensing_connection **)handle); +} + +int sensing_set_config(sensing_sensor_handle_t handle, + struct sensing_sensor_config *configs, + int count) +{ + struct sensing_sensor_config *cfg; + int i, ret = 0; + + if (count <= 0 || count > SENSING_SENSOR_ATTRIBUTE_MAX) { + LOG_ERR("invalid config count:%d", count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + cfg = &configs[i]; + switch (cfg->attri) { + case SENSING_SENSOR_ATTRIBUTE_INTERVAL: + ret |= set_interval(handle, cfg->interval); + break; + + case SENSING_SENSOR_ATTRIBUTE_SENSITIVITY: + ret |= set_sensitivity(handle, cfg->data_field, cfg->sensitivity); + break; + + case SENSING_SENSOR_ATTRIBUTE_LATENCY: + break; + + default: + ret = -EINVAL; + LOG_ERR("invalid config attribute:%d\n", cfg->attri); + break; + } + } + + return ret; +} + +int sensing_get_config(sensing_sensor_handle_t handle, + struct sensing_sensor_config *configs, + int count) +{ + struct sensing_sensor_config *cfg; + int i, ret = 0; + + if (count <= 0 || count > SENSING_SENSOR_ATTRIBUTE_MAX) { + LOG_ERR("invalid config count:%d", count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + cfg = &configs[i]; + switch (cfg->attri) { + case SENSING_SENSOR_ATTRIBUTE_INTERVAL: + ret |= get_interval(handle, &cfg->interval); + break; + + case SENSING_SENSOR_ATTRIBUTE_SENSITIVITY: + ret |= get_sensitivity(handle, cfg->data_field, &cfg->sensitivity); + break; + + case SENSING_SENSOR_ATTRIBUTE_LATENCY: + break; + + default: + ret = -EINVAL; + LOG_ERR("invalid config attribute:%d\n", cfg->attri); + break; + } + } + + return ret; +} + +const struct sensing_sensor_info *sensing_get_sensor_info(sensing_sensor_handle_t handle) +{ + return get_sensor_info(handle); +} diff --git a/subsys/sensing/sensing_sensor.c b/subsys/sensing/sensing_sensor.c new file mode 100644 index 00000000000000..c305cbb35c4fac --- /dev/null +++ b/subsys/sensing/sensing_sensor.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +LOG_MODULE_DECLARE(sensing, CONFIG_SENSING_LOG_LEVEL); + +int sensing_sensor_notify_data_ready(const struct device *dev) +{ + return -ENOTSUP; +} + +int sensing_sensor_set_data_ready(const struct device *dev, bool data_ready) +{ + return -ENOTSUP; +} + +int sensing_sensor_post_data(const struct device *dev, void *buf, int size) +{ + return -ENOTSUP; +} diff --git a/subsys/sensing/sensor/phy_3d_sensor/CMakeLists.txt b/subsys/sensing/sensor/phy_3d_sensor/CMakeLists.txt new file mode 100644 index 00000000000000..69f0b70637c7c3 --- /dev/null +++ b/subsys/sensing/sensor/phy_3d_sensor/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources(phy_3d_sensor.c) diff --git a/subsys/sensing/sensor/phy_3d_sensor/Kconfig b/subsys/sensing/sensor/phy_3d_sensor/Kconfig new file mode 100644 index 00000000000000..3e8ca66bcb0ef4 --- /dev/null +++ b/subsys/sensing/sensor/phy_3d_sensor/Kconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SENSING_SENSOR_PHY_3D_SENSOR + bool "sensing subsystem physical 3d sensors(accel, gyro, mag)" + default y + depends on DT_HAS_ZEPHYR_SENSING_PHY_3D_SENSOR_ENABLED + help + Enable sensing subsystem physical 3d sensors(accel, gyro, mag). diff --git a/subsys/sensing/sensor/phy_3d_sensor/phy_3d_sensor.c b/subsys/sensing/sensor/phy_3d_sensor/phy_3d_sensor.c new file mode 100644 index 00000000000000..6ee2131bc3bdb0 --- /dev/null +++ b/subsys/sensing/sensor/phy_3d_sensor/phy_3d_sensor.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT zephyr_sensing_phy_3d_sensor + +#include +#include +#include +#include +#include +#include +#include + +#include "phy_3d_sensor.h" + +LOG_MODULE_REGISTER(phy_3d_sensor, CONFIG_SENSING_LOG_LEVEL); + +static int phy_3d_sensor_init(const struct device *dev, + const struct sensing_sensor_info *info, + const sensing_sensor_handle_t *reporter_handles, + int reporters_count) +{ + return 0; +} + +static int phy_3d_sensor_deinit(const struct device *dev) +{ + return 0; +} + +static int phy_3d_sensor_read_sample(const struct device *dev, + void *buf, int size) +{ + return 0; +} + +static int phy_3d_sensor_sensitivity_test(const struct device *dev, + int index, uint32_t sensitivity, + void *last_sample_buf, int last_sample_size, + void *current_sample_buf, int current_sample_size) +{ + return 0; +} + +static int phy_3d_sensor_set_interval(const struct device *dev, uint32_t value) +{ + return 0; +} + +static int phy_3d_sensor_get_interval(const struct device *dev, + uint32_t *value) +{ + return 0; +} + +static int phy_3d_sensor_set_sensitivity(const struct device *dev, + int index, uint32_t value) +{ + return 0; +} + +static int phy_3d_sensor_get_sensitivity(const struct device *dev, + int index, uint32_t *value) +{ + return 0; +} + +static const struct sensing_sensor_api phy_3d_sensor_api = { + .init = phy_3d_sensor_init, + .deinit = phy_3d_sensor_deinit, + .set_interval = phy_3d_sensor_set_interval, + .get_interval = phy_3d_sensor_get_interval, + .set_sensitivity = phy_3d_sensor_set_sensitivity, + .get_sensitivity = phy_3d_sensor_get_sensitivity, + .read_sample = phy_3d_sensor_read_sample, + .sensitivity_test = phy_3d_sensor_sensitivity_test, +}; + +static const struct sensing_sensor_register_info phy_3d_sensor_reg = { + .flags = SENSING_SENSOR_FLAG_REPORT_ON_CHANGE, + .sample_size = sizeof(struct sensing_sensor_value_3d_q31), + .sensitivity_count = PHY_3D_SENSOR_CHANNEL_NUM, + .version.value = SENSING_SENSOR_VERSION(0, 8, 0, 0), +}; + +#define SENSING_PHY_3D_SENSOR_DT_DEFINE(_inst) \ + static struct phy_3d_sensor_context _CONCAT(ctx, _inst) = { \ + .hw_dev = DEVICE_DT_GET( \ + DT_PHANDLE(DT_DRV_INST(_inst), \ + underlying_device)), \ + .sensor_type = DT_PROP(DT_DRV_INST(_inst), sensor_type),\ + }; \ + SENSING_SENSOR_DT_DEFINE(DT_DRV_INST(_inst), \ + &phy_3d_sensor_reg, &_CONCAT(ctx, _inst), \ + &phy_3d_sensor_api); + +DT_INST_FOREACH_STATUS_OKAY(SENSING_PHY_3D_SENSOR_DT_DEFINE); diff --git a/subsys/sensing/sensor/phy_3d_sensor/phy_3d_sensor.h b/subsys/sensing/sensor/phy_3d_sensor/phy_3d_sensor.h new file mode 100644 index 00000000000000..966a6f088c33b9 --- /dev/null +++ b/subsys/sensing/sensor/phy_3d_sensor/phy_3d_sensor.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_SPHY_3D_SENSOR_H_ +#define ZEPHYR_INCLUDE_SPHY_3D_SENSOR_H_ + +#include + +#define PHY_3D_SENSOR_CHANNEL_NUM 3 + +struct phy_3d_sensor_context { + const struct device *dev; + const struct device *hw_dev; + const int32_t sensor_type; +}; + +#endif diff --git a/subsys/sensing/sensor_mgmt.c b/subsys/sensing/sensor_mgmt.c new file mode 100644 index 00000000000000..9ecde551a02a5c --- /dev/null +++ b/subsys/sensing/sensor_mgmt.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sensor_mgmt.h" + +#define DT_DRV_COMPAT zephyr_sensing + +#define SENSING_SENSOR_NUM (sizeof((int []){ DT_FOREACH_CHILD_STATUS_OKAY_SEP( \ + DT_DRV_INST(0), DT_NODE_EXISTS, (,))}) / sizeof(int)) + +BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, + "only one 'zephyr_sensing' compatible node may be present"); + +LOG_MODULE_REGISTER(sensing, CONFIG_SENSING_LOG_LEVEL); + +DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(0), SENSING_SENSOR_INFO_DEFINE) +DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(0), SENSING_SENSOR_DEFINE) + + +/** + * @struct sensing_context + * @brief sensing subsystem context to include global variables + */ +struct sensing_context { + bool sensing_initialized; + int sensor_num; + struct sensing_sensor *sensors[SENSING_SENSOR_NUM]; +}; + +static struct sensing_context sensing_ctx = { + .sensor_num = SENSING_SENSOR_NUM, +}; + + +static int set_sensor_state(struct sensing_sensor *sensor, enum sensing_sensor_state state) +{ + __ASSERT(sensor, "set sensor state, sensing_sensor is NULL"); + + sensor->state = state; + + return 0; +} + +static void init_connection(struct sensing_connection *conn, + struct sensing_sensor *source, + struct sensing_sensor *sink) +{ + __ASSERT(conn, "init each connection, invalid connection"); + + conn->source = source; + conn->sink = sink; + conn->interval = 0; + memset(conn->sensitivity, 0x00, sizeof(conn->sensitivity)); + /* link connection to its reporter's client_list */ + sys_slist_append(&source->client_list, &conn->snode); +} + +static int init_sensor(struct sensing_sensor *sensor, int conns_num) +{ + const struct sensing_sensor_api *sensor_api; + struct sensing_sensor *reporter; + struct sensing_connection *conn; + void *tmp_conns[conns_num]; + int i; + + __ASSERT(sensor && sensor->dev, "init sensor, sensor or sensor device is NULL"); + sensor_api = sensor->dev->api; + __ASSERT(sensor_api, "init sensor, sensor device sensor_api is NULL"); + + if (sensor->data_buf == NULL) { + LOG_ERR("sensor:%s memory alloc failed", sensor->dev->name); + return -ENOMEM; + } + /* physical sensor has no reporters, conns_num is 0 */ + if (conns_num == 0) { + sensor->conns = NULL; + } + + for (i = 0; i < conns_num; i++) { + conn = &sensor->conns[i]; + reporter = get_reporter_sensor(sensor, i); + __ASSERT(reporter, "sensor's reporter should not be NULL"); + + init_connection(conn, reporter, sensor); + + LOG_DBG("init sensor, reporter:%s, client:%s, connection:%d", + reporter->dev->name, sensor->dev->name, i); + + tmp_conns[i] = conn; + } + + /* physical sensor is working at polling mode by default, + * virtual sensor working mode is inherited from its reporter + */ + if (is_phy_sensor(sensor)) { + sensor->mode = SENSOR_TRIGGER_MODE_POLLING; + } + + return sensor_api->init(sensor->dev, sensor->info, tmp_conns, conns_num); +} + +/* create struct sensing_sensor *sensor according to sensor device tree */ +static int pre_init_sensor(struct sensing_sensor *sensor) +{ + struct sensing_sensor_ctx *sensor_ctx; + uint16_t sample_size, total_size; + uint16_t conn_sample_size = 0; + int i = 0; + void *tmp_data; + + __ASSERT(sensor && sensor->dev, "sensor or sensor dev is invalid"); + sensor_ctx = sensor->dev->data; + __ASSERT(sensor_ctx, "sensing sensor context is invalid"); + + sample_size = sensor_ctx->register_info->sample_size; + for (i = 0; i < sensor->reporter_num; i++) { + conn_sample_size += get_reporter_sample_size(sensor, i); + } + + /* total memory to be allocated for a sensor according to sensor device tree: + * 1) sample data point to struct sensing_sensor->data_buf + * 2) size of struct sensing_connection* for sensor connection to its reporter + * 3) reporter sample size to be stored in connection data + */ + total_size = sample_size + sensor->reporter_num * sizeof(*sensor->conns) + + conn_sample_size; + + /* total size for different sensor maybe different, for example: + * there's no reporter for physical sensor, so no connection memory is needed + * reporter num of each virtual sensor may also different, so connection memory is also + * varied, so here malloc is a must for different sensor. + */ + tmp_data = malloc(total_size); + if (!tmp_data) { + LOG_ERR("malloc memory for sensing_sensor error"); + return -ENOMEM; + } + sensor->sample_size = sample_size; + sensor->data_buf = tmp_data; + sensor->conns = (struct sensing_connection *)((uint8_t *)sensor->data_buf + sample_size); + + tmp_data = sensor->conns + sensor->reporter_num; + for (i = 0; i < sensor->reporter_num; i++) { + sensor->conns[i].data = tmp_data; + tmp_data = (uint8_t *)tmp_data + get_reporter_sample_size(sensor, i); + } + + if (tmp_data != ((uint8_t *)sensor->data_buf + total_size)) { + LOG_ERR("sensor memory assign error, data_buf:%p, tmp_data:%p, size:%d", + sensor->data_buf, tmp_data, total_size); + free(sensor->data_buf); + sensor->data_buf = NULL; + return -EINVAL; + } + + LOG_INF("pre init sensor, sensor:%s, min_ri:%d(us)", + sensor->dev->name, sensor->info->minimal_interval); + + sensor->interval = 0; + sensor->sensitivity_count = sensor_ctx->register_info->sensitivity_count; + __ASSERT(sensor->sensitivity_count <= CONFIG_SENSING_MAX_SENSITIVITY_COUNT, + "sensitivity count:%d should not exceed MAX_SENSITIVITY_COUNT", + sensor->sensitivity_count); + memset(sensor->sensitivity, 0x00, sizeof(sensor->sensitivity)); + + sys_slist_init(&sensor->client_list); + + sensor_ctx->priv_ptr = sensor; + + return 0; +} + +static int sensing_init(void) +{ + struct sensing_context *ctx = &sensing_ctx; + struct sensing_sensor *sensor; + enum sensing_sensor_state state; + int ret = 0; + int i = 0; + + LOG_INF("sensing init begin..."); + + if (ctx->sensing_initialized) { + LOG_INF("sensing is already initialized"); + return 0; + } + + if (ctx->sensor_num == 0) { + LOG_WRN("no sensor created by device tree yet"); + return 0; + } + + STRUCT_SECTION_FOREACH(sensing_sensor, tmp_sensor) { + ret = pre_init_sensor(tmp_sensor); + if (ret) { + LOG_ERR("sensing init, pre init sensor error"); + } + ctx->sensors[i++] = tmp_sensor; + } + + for_each_sensor(ctx, i, sensor) { + ret = init_sensor(sensor, sensor->reporter_num); + if (ret) { + LOG_ERR("sensor:%s initial error", sensor->dev->name); + } + state = (ret ? SENSING_SENSOR_STATE_OFFLINE : SENSING_SENSOR_STATE_READY); + ret = set_sensor_state(sensor, state); + if (ret) { + LOG_ERR("set sensor:%s state:%d error", sensor->dev->name, state); + } + LOG_INF("sensing init, sensor:%s state:%d", sensor->dev->name, sensor->state); + } + + return ret; +} + +int open_sensor(struct sensing_sensor *sensor, struct sensing_connection **conn) +{ + struct sensing_connection *tmp_conn; + + if (sensor->state != SENSING_SENSOR_STATE_READY) + return -EINVAL; + + /* allocate struct sensing_connection *conn and conn data for application client */ + tmp_conn = malloc(sizeof(*tmp_conn) + sensor->sample_size); + if (!tmp_conn) { + LOG_ERR("malloc memory for struct sensing_connection error"); + return -ENOMEM; + } + tmp_conn->data = (uint8_t *)tmp_conn + sizeof(*tmp_conn); + + /* create connection from sensor to application(client = NULL) */ + init_connection(tmp_conn, sensor, NULL); + + *conn = tmp_conn; + + return 0; +} + +int close_sensor(struct sensing_connection **conn) +{ + struct sensing_connection *tmp_conn = *conn; + + if (tmp_conn == NULL) { + LOG_ERR("connection should not be NULL"); + return -EINVAL; + } + + __ASSERT(!tmp_conn->sink, "sensor derived from device tree cannot be closed"); + + sys_slist_find_and_remove(&tmp_conn->source->client_list, &tmp_conn->snode); + + *conn = NULL; + free(*conn); + + return 0; +} + +int sensing_register_callback(struct sensing_connection *conn, + const struct sensing_callback_list *cb_list) +{ + if (conn == NULL) { + LOG_ERR("register sensing callback list, connection not be NULL"); + return -ENODEV; + } + + __ASSERT(!conn->sink, "only connection to application could register sensing callback"); + + if (cb_list == NULL) { + LOG_ERR("callback should not be NULL"); + return -ENODEV; + } + conn->data_evt_cb = cb_list->on_data_event; + + return 0; +} + +int set_interval(struct sensing_connection *conn, uint32_t interval) +{ + return -ENOTSUP; +} + +int get_interval(struct sensing_connection *conn, uint32_t *interval) +{ + return -ENOTSUP; +} + +int set_sensitivity(struct sensing_connection *conn, int8_t index, uint32_t sensitivity) +{ + return -ENOTSUP; +} + +int get_sensitivity(struct sensing_connection *conn, int8_t index, uint32_t *sensitivity) +{ + return -ENOTSUP; +} + +int sensing_get_sensors(int *sensor_nums, const struct sensing_sensor_info **info) +{ + if (info == NULL) { + LOG_ERR("sensing_sensor_info should not be NULL"); + return -ENODEV; + } + + STRUCT_SECTION_COUNT(sensing_sensor_info, sensor_nums); + + STRUCT_SECTION_GET(sensing_sensor_info, 0, info); + + return 0; +} + + +SYS_INIT(sensing_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/subsys/sensing/sensor_mgmt.h b/subsys/sensing/sensor_mgmt.h new file mode 100644 index 00000000000000..38219305304d6e --- /dev/null +++ b/subsys/sensing/sensor_mgmt.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SENSOR_MGMT_H_ +#define SENSOR_MGMT_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PHANDLE_DEVICE_BY_IDX(idx, node, prop) \ + DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node, prop, idx)) + +#define PHANDLE_DEVICE_LIST(node, prop) \ +{ \ + LISTIFY(DT_PROP_LEN_OR(node, prop, 0), \ + PHANDLE_DEVICE_BY_IDX, \ + (,), \ + node, \ + prop) \ +} + +#define SENSING_SENSOR_INFO_NAME(node) \ + _CONCAT(__sensing_sensor_info_, DEVICE_DT_NAME_GET(node)) + +#define SENSING_SENSOR_INFO_DEFINE(node) \ + const static STRUCT_SECTION_ITERABLE(sensing_sensor_info, \ + SENSING_SENSOR_INFO_NAME(node)) = { \ + .type = DT_PROP(node, sensor_type), \ + .name = DT_NODE_FULL_NAME(node), \ + .friendly_name = DT_PROP(node, friendly_name), \ + .vendor = DT_NODE_VENDOR_OR(node, NULL), \ + .model = DT_NODE_MODEL_OR(node, NULL), \ + .minimal_interval = DT_PROP(node, minimal_interval), \ + }; + +#define SENSING_SENSOR_NAME(node) \ + _CONCAT(__sensing_sensor_, DEVICE_DT_NAME_GET(node)) + +#define SENSING_SENSOR_DEFINE(node) \ + static STRUCT_SECTION_ITERABLE(sensing_sensor, \ + SENSING_SENSOR_NAME(node)) = { \ + .dev = DEVICE_DT_GET(node), \ + .info = &SENSING_SENSOR_INFO_NAME(node), \ + .reporter_num = DT_PROP_LEN_OR(node, reporters, 0), \ + .reporters = PHANDLE_DEVICE_LIST(node, reporters), \ + }; + +#define for_each_sensor(ctx, i, sensor) \ + for (i = 0; i < ctx->sensor_num && (sensor = ctx->sensors[i]) != NULL; i++) + +enum sensor_trigger_mode { + SENSOR_TRIGGER_MODE_POLLING = 1, + SENSOR_TRIGGER_MODE_DATA_READY = 2, +}; + +/** + * @struct sensing_connection information + * @brief sensing_connection indicates connection from reporter(source) to client(sink) + */ +struct sensing_connection { + struct sensing_sensor *source; + struct sensing_sensor *sink; + /* interval and sensitivity set from client(sink) to reporter(source) */ + uint32_t interval; + int sensitivity[CONFIG_SENSING_MAX_SENSITIVITY_COUNT]; + /* copy sensor data to connection data buf from reporter */ + void *data; + /* client(sink) next consume time */ + sys_snode_t snode; + /* post data to application */ + sensing_data_event_t data_evt_cb; +}; + +/** + * @struct sensing_sensor + * @brief Internal sensor instance data structure. + * + * Each sensor instance will have its unique data structure for storing all + * it's related information. + * + * Sensor management will enumerate all these instance data structures, + * build report relationship model base on them, etc. + */ +struct sensing_sensor { + const struct device *dev; + const struct sensing_sensor_info *info; + const uint16_t reporter_num; + sys_slist_t client_list; + uint32_t interval; + uint8_t sensitivity_count; + int sensitivity[CONFIG_SENSING_MAX_SENSITIVITY_COUNT]; + enum sensing_sensor_state state; + enum sensor_trigger_mode mode; + /* runtime info */ + uint16_t sample_size; + void *data_buf; + struct sensing_connection *conns; + const struct device *reporters[]; +}; + +int open_sensor(struct sensing_sensor *sensor, struct sensing_connection **conn); +int close_sensor(struct sensing_connection **conn); +int sensing_register_callback(struct sensing_connection *conn, + const struct sensing_callback_list *cb_list); +int set_interval(struct sensing_connection *conn, uint32_t interval); +int get_interval(struct sensing_connection *con, uint32_t *sensitivity); +int set_sensitivity(struct sensing_connection *conn, int8_t index, uint32_t interval); +int get_sensitivity(struct sensing_connection *con, int8_t index, uint32_t *sensitivity); + + +static inline bool is_phy_sensor(struct sensing_sensor *sensor) +{ + return sensor->reporter_num == 0; +} + +static inline uint16_t get_reporter_sample_size(const struct sensing_sensor *sensor, int i) +{ + __ASSERT(i < sensor->reporter_num, "dt index should less than reporter num"); + + return ((struct sensing_sensor_ctx *) + sensor->reporters[i]->data)->register_info->sample_size; +} + +static inline struct sensing_sensor *get_sensor_by_dev(const struct device *dev) +{ + return dev ? + (struct sensing_sensor *)((struct sensing_sensor_ctx *)dev->data)->priv_ptr : NULL; +} + +static inline struct sensing_sensor *get_reporter_sensor(struct sensing_sensor *sensor, int index) +{ + if (!sensor || index >= sensor->reporter_num) { + return NULL; + } + + return get_sensor_by_dev(sensor->reporters[index]); +} + +static inline const struct sensing_sensor_info *get_sensor_info(struct sensing_connection *conn) +{ + __ASSERT(conn, "get sensor info, connection not be NULL"); + + __ASSERT(conn->source, "get sensor info, sensing_sensor is NULL"); + + return conn->source->info; +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif /* SENSOR_MGMT_H_ */