From 3ba866e86e3acd080c0d962e609be0a913b30b12 Mon Sep 17 00:00:00 2001 From: "Ranjan, Rajani" Date: Mon, 31 Jul 2023 15:13:16 +0530 Subject: [PATCH 1/5] google default implementation for Sensor aidl. to implement the Sensors AIDL HAL, an object must extend the ISensors interface and implement all functions defined in hardware/interfaces/sensors/aidl/android/hardware/sensors/ISensors.aidl Tracked-On: OAM-112804 Signed-off-by: Ranjan, Rajani --- sensors/aidl/Android.bp | 77 ++++ sensors/aidl/Sensor.cpp | 434 ++++++++++++++++++++ sensors/aidl/Sensors.cpp | 157 +++++++ sensors/aidl/include/sensors-impl/Sensor.h | 168 ++++++++ sensors/aidl/include/sensors-impl/Sensors.h | 216 ++++++++++ sensors/aidl/main.cpp | 37 ++ sensors/aidl/sensors-default.rc | 5 + sensors/aidl/sensors-default.xml | 7 + 8 files changed, 1101 insertions(+) create mode 100644 sensors/aidl/Android.bp create mode 100644 sensors/aidl/Sensor.cpp create mode 100644 sensors/aidl/Sensors.cpp create mode 100644 sensors/aidl/include/sensors-impl/Sensor.h create mode 100644 sensors/aidl/include/sensors-impl/Sensors.h create mode 100644 sensors/aidl/main.cpp create mode 100644 sensors/aidl/sensors-default.rc create mode 100644 sensors/aidl/sensors-default.xml diff --git a/sensors/aidl/Android.bp b/sensors/aidl/Android.bp new file mode 100644 index 0000000..16b4d35 --- /dev/null +++ b/sensors/aidl/Android.bp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +filegroup { + name: "sensors-default.rc", + srcs: ["sensors-default.rc"], +} + +filegroup { + name: "sensors-default.xml", + srcs: ["sensors-default.xml"], +} + +cc_library_static { + name: "libsensorsexampleimpl", + vendor: true, + shared_libs: [ + "libbase", + "libfmq", + "libpower", + "libbinder_ndk", + "android.hardware.sensors-V2-ndk", + ], + export_include_dirs: ["include"], + srcs: [ + "Sensors.cpp", + "Sensor.cpp", + ], + visibility: [ + ":__subpackages__", + "//hardware/interfaces/tests/extension/sensors:__subpackages__", + ], +} + +cc_binary { + name: "android.hardware.sensors-service.example", + relative_install_path: "hw", + init_rc: [":sensors-default.rc"], + vintf_fragments: [":sensors-default.xml"], + vendor: true, + shared_libs: [ + "libbase", + "libbinder_ndk", + "libfmq", + "libpower", + "libcutils", + "liblog", + "libutils", + "android.hardware.sensors-V2-ndk", + ], + static_libs: [ + "libsensorsexampleimpl", + ], + srcs: ["main.cpp"], +} diff --git a/sensors/aidl/Sensor.cpp b/sensors/aidl/Sensor.cpp new file mode 100644 index 0000000..3bdd8b6 --- /dev/null +++ b/sensors/aidl/Sensor.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sensors-impl/Sensor.h" + +#include "utils/SystemClock.h" + +#include + +using ::ndk::ScopedAStatus; + +namespace aidl { +namespace android { +namespace hardware { +namespace sensors { + +static constexpr int32_t kDefaultMaxDelayUs = 10 * 1000 * 1000; + +Sensor::Sensor(ISensorsEventCallback* callback) + : mIsEnabled(false), + mSamplingPeriodNs(0), + mLastSampleTimeNs(0), + mCallback(callback), + mMode(OperationMode::NORMAL) { + mRunThread = std::thread(startThread, this); +} + +Sensor::~Sensor() { + std::unique_lock lock(mRunMutex); + mStopThread = true; + mIsEnabled = false; + mWaitCV.notify_all(); + lock.release(); + mRunThread.join(); +} + +const SensorInfo& Sensor::getSensorInfo() const { + return mSensorInfo; +} + +void Sensor::batch(int64_t samplingPeriodNs) { + if (samplingPeriodNs < mSensorInfo.minDelayUs * 1000LL) { + samplingPeriodNs = mSensorInfo.minDelayUs * 1000LL; + } else if (samplingPeriodNs > mSensorInfo.maxDelayUs * 1000LL) { + samplingPeriodNs = mSensorInfo.maxDelayUs * 1000LL; + } + + if (mSamplingPeriodNs != samplingPeriodNs) { + mSamplingPeriodNs = samplingPeriodNs; + // Wake up the 'run' thread to check if a new event should be generated now + mWaitCV.notify_all(); + } +} + +void Sensor::activate(bool enable) { + if (mIsEnabled != enable) { + std::unique_lock lock(mRunMutex); + mIsEnabled = enable; + mWaitCV.notify_all(); + } +} + +ScopedAStatus Sensor::flush() { + // Only generate a flush complete event if the sensor is enabled and if the sensor is not a + // one-shot sensor. + if (!mIsEnabled || + (mSensorInfo.flags & static_cast(SensorInfo::SENSOR_FLAG_BITS_ONE_SHOT_MODE))) { + return ScopedAStatus::fromServiceSpecificError( + static_cast(BnSensors::ERROR_BAD_VALUE)); + } + + // Note: If a sensor supports batching, write all of the currently batched events for the sensor + // to the Event FMQ prior to writing the flush complete event. + Event ev; + ev.sensorHandle = mSensorInfo.sensorHandle; + ev.sensorType = SensorType::META_DATA; + EventPayload::MetaData meta = { + .what = MetaDataEventType::META_DATA_FLUSH_COMPLETE, + }; + ev.payload.set(meta); + std::vector evs{ev}; + mCallback->postEvents(evs, isWakeUpSensor()); + + return ScopedAStatus::ok(); +} + +void Sensor::startThread(Sensor* sensor) { + sensor->run(); +} + +void Sensor::run() { + std::unique_lock runLock(mRunMutex); + constexpr int64_t kNanosecondsInSeconds = 1000 * 1000 * 1000; + + while (!mStopThread) { + if (!mIsEnabled || mMode == OperationMode::DATA_INJECTION) { + mWaitCV.wait(runLock, [&] { + return ((mIsEnabled && mMode == OperationMode::NORMAL) || mStopThread); + }); + } else { + timespec curTime; + clock_gettime(CLOCK_BOOTTIME, &curTime); + int64_t now = (curTime.tv_sec * kNanosecondsInSeconds) + curTime.tv_nsec; + int64_t nextSampleTime = mLastSampleTimeNs + mSamplingPeriodNs; + + if (now >= nextSampleTime) { + mLastSampleTimeNs = now; + nextSampleTime = mLastSampleTimeNs + mSamplingPeriodNs; + mCallback->postEvents(readEvents(), isWakeUpSensor()); + } + + mWaitCV.wait_for(runLock, std::chrono::nanoseconds(nextSampleTime - now)); + } + } +} + +bool Sensor::isWakeUpSensor() { + return mSensorInfo.flags & static_cast(SensorInfo::SENSOR_FLAG_BITS_WAKE_UP); +} + +std::vector Sensor::readEvents() { + std::vector events; + Event event; + event.sensorHandle = mSensorInfo.sensorHandle; + event.sensorType = mSensorInfo.type; + event.timestamp = ::android::elapsedRealtimeNano(); + memset(&event.payload, 0, sizeof(event.payload)); + readEventPayload(event.payload); + events.push_back(event); + return events; +} + +void Sensor::setOperationMode(OperationMode mode) { + if (mMode != mode) { + std::unique_lock lock(mRunMutex); + mMode = mode; + mWaitCV.notify_all(); + } +} + +bool Sensor::supportsDataInjection() const { + return mSensorInfo.flags & static_cast(SensorInfo::SENSOR_FLAG_BITS_DATA_INJECTION); +} + +ScopedAStatus Sensor::injectEvent(const Event& event) { + if (event.sensorType == SensorType::ADDITIONAL_INFO) { + return ScopedAStatus::ok(); + // When in OperationMode::NORMAL, SensorType::ADDITIONAL_INFO is used to push operation + // environment data into the device. + } + + if (!supportsDataInjection()) { + return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + + if (mMode == OperationMode::DATA_INJECTION) { + mCallback->postEvents(std::vector{event}, isWakeUpSensor()); + return ScopedAStatus::ok(); + } + + return ScopedAStatus::fromServiceSpecificError( + static_cast(BnSensors::ERROR_BAD_VALUE)); +} + +OnChangeSensor::OnChangeSensor(ISensorsEventCallback* callback) + : Sensor(callback), mPreviousEventSet(false) {} + +void OnChangeSensor::activate(bool enable) { + Sensor::activate(enable); + if (!enable) { + mPreviousEventSet = false; + } +} + +std::vector OnChangeSensor::readEvents() { + std::vector events = Sensor::readEvents(); + std::vector outputEvents; + + for (auto iter = events.begin(); iter != events.end(); ++iter) { + Event ev = *iter; + if (!mPreviousEventSet || + memcmp(&mPreviousEvent.payload, &ev.payload, sizeof(ev.payload)) != 0) { + outputEvents.push_back(ev); + mPreviousEvent = ev; + mPreviousEventSet = true; + } + } + return outputEvents; +} + +AccelSensor::AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Accel Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::ACCELEROMETER; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 78.4f; // +/- 8g + mSensorInfo.resolution = 1.52e-5; + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_DATA_INJECTION); +}; + +void AccelSensor::readEventPayload(EventPayload& payload) { + EventPayload::Vec3 vec3 = { + .x = 0, + .y = 0, + .z = 9.8, + .status = SensorStatus::ACCURACY_HIGH, + }; + payload.set(vec3); +} + +PressureSensor::PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Pressure Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::PRESSURE; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 1100.0f; // hPa + mSensorInfo.resolution = 0.005f; // hPa + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelayUs = 100 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +}; + +void PressureSensor::readEventPayload(EventPayload& payload) { + payload.set(1013.25f); +} + +MagnetometerSensor::MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Magnetic Field Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::MAGNETIC_FIELD; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 1300.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelayUs = 20 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +}; + +void MagnetometerSensor::readEventPayload(EventPayload& payload) { + EventPayload::Vec3 vec3 = { + .x = 100.0, + .y = 0, + .z = 50.0, + .status = SensorStatus::ACCURACY_HIGH, + }; + payload.set(vec3); +} + +LightSensor::LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Light Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::LIGHT; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 43000.0f; + mSensorInfo.resolution = 10.0f; + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelayUs = 200 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE); +}; + +void LightSensor::readEventPayload(EventPayload& payload) { + payload.set(80.0f); +} + +ProximitySensor::ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Proximity Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::PROXIMITY; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 5.0f; + mSensorInfo.resolution = 1.0f; + mSensorInfo.power = 0.012f; // mA + mSensorInfo.minDelayUs = 200 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE | + SensorInfo::SENSOR_FLAG_BITS_WAKE_UP); +}; + +void ProximitySensor::readEventPayload(EventPayload& payload) { + payload.set(2.5f); +} + +GyroSensor::GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Gyro Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::GYROSCOPE; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 1000.0f * M_PI / 180.0f; + mSensorInfo.resolution = 1000.0f * M_PI / (180.0f * 32768.0f); + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +}; + +void GyroSensor::readEventPayload(EventPayload& payload) { + EventPayload::Vec3 vec3 = { + .x = 0, + .y = 0, + .z = 0, + .status = SensorStatus::ACCURACY_HIGH, + }; + payload.set(vec3); +} + +AmbientTempSensor::AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Ambient Temp Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::AMBIENT_TEMPERATURE; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 80.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 40 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE); +}; + +void AmbientTempSensor::readEventPayload(EventPayload& payload) { + payload.set(40.0f); +} + +RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle, + ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Relative Humidity Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::RELATIVE_HUMIDITY; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 100.0f; + mSensorInfo.resolution = 0.1f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 40 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE); +} + +void RelativeHumiditySensor::readEventPayload(EventPayload& payload) { + payload.set(50.0f); +} + +HingeAngleSensor::HingeAngleSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Hinge Angle Sensor"; + mSensorInfo.vendor = "Vendor String"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::HINGE_ANGLE; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 360.0f; + mSensorInfo.resolution = 1.0f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 40 * 1000; // microseconds + mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE | + SensorInfo::SENSOR_FLAG_BITS_WAKE_UP | + SensorInfo::SENSOR_FLAG_BITS_DATA_INJECTION); +} + +void HingeAngleSensor::readEventPayload(EventPayload& payload) { + payload.set(180.0f); +} + +} // namespace sensors +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/sensors/aidl/Sensors.cpp b/sensors/aidl/Sensors.cpp new file mode 100644 index 0000000..65dd304 --- /dev/null +++ b/sensors/aidl/Sensors.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sensors-impl/Sensors.h" + +#include + +using ::aidl::android::hardware::common::fmq::MQDescriptor; +using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite; +using ::aidl::android::hardware::sensors::Event; +using ::aidl::android::hardware::sensors::ISensors; +using ::aidl::android::hardware::sensors::ISensorsCallback; +using ::aidl::android::hardware::sensors::SensorInfo; +using ::ndk::ScopedAStatus; + +namespace aidl { +namespace android { +namespace hardware { +namespace sensors { + +ScopedAStatus Sensors::activate(int32_t in_sensorHandle, bool in_enabled) { + auto sensor = mSensors.find(in_sensorHandle); + if (sensor != mSensors.end()) { + sensor->second->activate(in_enabled); + return ScopedAStatus::ok(); + } + + return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + +ScopedAStatus Sensors::batch(int32_t in_sensorHandle, int64_t in_samplingPeriodNs, + int64_t /* in_maxReportLatencyNs */) { + auto sensor = mSensors.find(in_sensorHandle); + if (sensor != mSensors.end()) { + sensor->second->batch(in_samplingPeriodNs); + return ScopedAStatus::ok(); + } + + return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + +ScopedAStatus Sensors::configDirectReport(int32_t /* in_sensorHandle */, + int32_t /* in_channelHandle */, + ISensors::RateLevel /* in_rate */, + int32_t* _aidl_return) { + *_aidl_return = EX_UNSUPPORTED_OPERATION; + + return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ScopedAStatus Sensors::flush(int32_t in_sensorHandle) { + auto sensor = mSensors.find(in_sensorHandle); + if (sensor != mSensors.end()) { + return sensor->second->flush(); + } + + return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +} + +ScopedAStatus Sensors::getSensorsList(std::vector* _aidl_return) { + for (const auto& sensor : mSensors) { + _aidl_return->push_back(sensor.second->getSensorInfo()); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus Sensors::initialize( + const MQDescriptor& in_eventQueueDescriptor, + const MQDescriptor& in_wakeLockDescriptor, + const std::shared_ptr<::aidl::android::hardware::sensors::ISensorsCallback>& + in_sensorsCallback) { + ScopedAStatus result = ScopedAStatus::ok(); + + mEventQueue = std::make_unique>( + in_eventQueueDescriptor, true /* resetPointers */); + + // Ensure that all sensors are disabled. + for (auto sensor : mSensors) { + sensor.second->activate(false); + } + + // Stop the Wake Lock thread if it is currently running + if (mReadWakeLockQueueRun.load()) { + mReadWakeLockQueueRun = false; + mWakeLockThread.join(); + } + + // Save a reference to the callback + mCallback = in_sensorsCallback; + + // Ensure that any existing EventFlag is properly deleted + deleteEventFlag(); + + // Create the EventFlag that is used to signal to the framework that sensor events have been + // written to the Event FMQ + if (EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag) != OK) { + result = ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + // Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP + // events have been successfully read and handled by the framework. + mWakeLockQueue = std::make_unique>( + in_wakeLockDescriptor, true /* resetPointers */); + + if (!mCallback || !mEventQueue || !mWakeLockQueue || mEventQueueFlag == nullptr) { + result = ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + // Start the thread to read events from the Wake Lock FMQ + mReadWakeLockQueueRun = true; + mWakeLockThread = std::thread(startReadWakeLockThread, this); + return result; +} + +ScopedAStatus Sensors::injectSensorData(const Event& in_event) { + auto sensor = mSensors.find(in_event.sensorHandle); + if (sensor != mSensors.end()) { + return sensor->second->injectEvent(in_event); + } + return ScopedAStatus::fromServiceSpecificError(static_cast(ERROR_BAD_VALUE)); +} + +ScopedAStatus Sensors::registerDirectChannel(const ISensors::SharedMemInfo& /* in_mem */, + int32_t* _aidl_return) { + *_aidl_return = EX_UNSUPPORTED_OPERATION; + + return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ScopedAStatus Sensors::setOperationMode(OperationMode in_mode) { + for (auto sensor : mSensors) { + sensor.second->setOperationMode(in_mode); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus Sensors::unregisterDirectChannel(int32_t /* in_channelHandle */) { + return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +} // namespace sensors +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/sensors/aidl/include/sensors-impl/Sensor.h b/sensors/aidl/include/sensors-impl/Sensor.h new file mode 100644 index 0000000..e6cd3e6 --- /dev/null +++ b/sensors/aidl/include/sensors-impl/Sensor.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace sensors { + +class ISensorsEventCallback { + public: + using Event = ::aidl::android::hardware::sensors::Event; + + virtual ~ISensorsEventCallback(){}; + virtual void postEvents(const std::vector& events, bool wakeup) = 0; +}; + +class Sensor { + public: + using OperationMode = ::aidl::android::hardware::sensors::ISensors::OperationMode; + using Event = ::aidl::android::hardware::sensors::Event; + using EventPayload = ::aidl::android::hardware::sensors::Event::EventPayload; + using SensorInfo = ::aidl::android::hardware::sensors::SensorInfo; + using SensorType = ::aidl::android::hardware::sensors::SensorType; + using MetaDataEventType = + ::aidl::android::hardware::sensors::Event::EventPayload::MetaData::MetaDataEventType; + + Sensor(ISensorsEventCallback* callback); + virtual ~Sensor(); + + const SensorInfo& getSensorInfo() const; + void batch(int64_t samplingPeriodNs); + virtual void activate(bool enable); + ndk::ScopedAStatus flush(); + + void setOperationMode(OperationMode mode); + bool supportsDataInjection() const; + ndk::ScopedAStatus injectEvent(const Event& event); + + protected: + void run(); + virtual std::vector readEvents(); + virtual void readEventPayload(EventPayload&) = 0; + static void startThread(Sensor* sensor); + + bool isWakeUpSensor(); + + bool mIsEnabled; + int64_t mSamplingPeriodNs; + int64_t mLastSampleTimeNs; + SensorInfo mSensorInfo; + + std::atomic_bool mStopThread; + std::condition_variable mWaitCV; + std::mutex mRunMutex; + std::thread mRunThread; + + ISensorsEventCallback* mCallback; + + OperationMode mMode; +}; + +class OnChangeSensor : public Sensor { + public: + OnChangeSensor(ISensorsEventCallback* callback); + + virtual void activate(bool enable) override; + + protected: + virtual std::vector readEvents() override; + + protected: + Event mPreviousEvent; + bool mPreviousEventSet; +}; + +class AccelSensor : public Sensor { + public: + AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +class GyroSensor : public Sensor { + public: + GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +class AmbientTempSensor : public OnChangeSensor { + public: + AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +class PressureSensor : public Sensor { + public: + PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +class MagnetometerSensor : public Sensor { + public: + MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +class LightSensor : public OnChangeSensor { + public: + LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +class ProximitySensor : public OnChangeSensor { + public: + ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +class RelativeHumiditySensor : public OnChangeSensor { + public: + RelativeHumiditySensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +class HingeAngleSensor : public OnChangeSensor { + public: + HingeAngleSensor(int32_t sensorHandle, ISensorsEventCallback* callback); + + protected: + virtual void readEventPayload(EventPayload& payload) override; +}; + +} // namespace sensors +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/sensors/aidl/include/sensors-impl/Sensors.h b/sensors/aidl/include/sensors-impl/Sensors.h new file mode 100644 index 0000000..e270d96 --- /dev/null +++ b/sensors/aidl/include/sensors-impl/Sensors.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "Sensor.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace sensors { + +using aidl::android::hardware::common::fmq::SynchronizedReadWrite; +using ::android::AidlMessageQueue; +using ::android::OK; +using ::android::status_t; +using ::android::hardware::EventFlag; + +class Sensors : public BnSensors, public ISensorsEventCallback { + static constexpr const char* kWakeLockName = "SensorsHAL_WAKEUP"; + + public: + Sensors() + : mEventQueueFlag(nullptr), + mNextHandle(1), + mOutstandingWakeUpEvents(0), + mReadWakeLockQueueRun(false), + mAutoReleaseWakeLockTime(0), + mHasWakeLock(false) { + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + } + + virtual ~Sensors() { + deleteEventFlag(); + mReadWakeLockQueueRun = false; + mWakeLockThread.join(); + } + + ::ndk::ScopedAStatus activate(int32_t in_sensorHandle, bool in_enabled) override; + ::ndk::ScopedAStatus batch(int32_t in_sensorHandle, int64_t in_samplingPeriodNs, + int64_t in_maxReportLatencyNs) override; + ::ndk::ScopedAStatus configDirectReport( + int32_t in_sensorHandle, int32_t in_channelHandle, + ::aidl::android::hardware::sensors::ISensors::RateLevel in_rate, + int32_t* _aidl_return) override; + ::ndk::ScopedAStatus flush(int32_t in_sensorHandle) override; + ::ndk::ScopedAStatus getSensorsList( + std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override; + ::ndk::ScopedAStatus initialize( + const ::aidl::android::hardware::common::fmq::MQDescriptor< + ::aidl::android::hardware::sensors::Event, + ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>& + in_eventQueueDescriptor, + const ::aidl::android::hardware::common::fmq::MQDescriptor< + int32_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>& + in_wakeLockDescriptor, + const std::shared_ptr<::aidl::android::hardware::sensors::ISensorsCallback>& + in_sensorsCallback) override; + ::ndk::ScopedAStatus injectSensorData( + const ::aidl::android::hardware::sensors::Event& in_event) override; + ::ndk::ScopedAStatus registerDirectChannel( + const ::aidl::android::hardware::sensors::ISensors::SharedMemInfo& in_mem, + int32_t* _aidl_return) override; + ::ndk::ScopedAStatus setOperationMode( + ::aidl::android::hardware::sensors::ISensors::OperationMode in_mode) override; + ::ndk::ScopedAStatus unregisterDirectChannel(int32_t in_channelHandle) override; + + void postEvents(const std::vector& events, bool wakeup) override { + std::lock_guard lock(mWriteLock); + if (mEventQueue == nullptr) { + return; + } + if (mEventQueue->write(&events.front(), events.size())) { + mEventQueueFlag->wake( + static_cast(BnSensors::EVENT_QUEUE_FLAG_BITS_READ_AND_PROCESS)); + + if (wakeup) { + // Keep track of the number of outstanding WAKE_UP events in order to properly hold + // a wake lock until the framework has secured a wake lock + updateWakeLock(events.size(), 0 /* eventsHandled */); + } + } + } + + protected: + // Add a new sensor + template + void AddSensor() { + std::shared_ptr sensor = + std::make_shared(mNextHandle++ /* sensorHandle */, this /* callback */); + mSensors[sensor->getSensorInfo().sensorHandle] = sensor; + } + + // Utility function to delete the Event Flag + void deleteEventFlag() { + if (mEventQueueFlag != nullptr) { + status_t status = EventFlag::deleteEventFlag(&mEventQueueFlag); + if (status != OK) { + ALOGI("Failed to delete event flag: %d", status); + } + } + } + + static void startReadWakeLockThread(Sensors* sensors) { sensors->readWakeLockFMQ(); } + + // Function to read the Wake Lock FMQ and release the wake lock when appropriate + void readWakeLockFMQ() { + while (mReadWakeLockQueueRun.load()) { + constexpr int64_t kReadTimeoutNs = 500 * 1000 * 1000; // 500 ms + int32_t eventsHandled = 0; + + // Read events from the Wake Lock FMQ. Timeout after a reasonable amount of time to + // ensure that any held wake lock is able to be released if it is held for too long. + mWakeLockQueue->readBlocking( + &eventsHandled, 1 /* count */, 0 /* readNotification */, + static_cast(WAKE_LOCK_QUEUE_FLAG_BITS_DATA_WRITTEN), kReadTimeoutNs); + updateWakeLock(0 /* eventsWritten */, eventsHandled); + } + } + + /** + * Responsible for acquiring and releasing a wake lock when there are unhandled WAKE_UP events + */ + void updateWakeLock(int32_t eventsWritten, int32_t eventsHandled) { + std::lock_guard lock(mWakeLockLock); + int32_t newVal = mOutstandingWakeUpEvents + eventsWritten - eventsHandled; + if (newVal < 0) { + mOutstandingWakeUpEvents = 0; + } else { + mOutstandingWakeUpEvents = newVal; + } + + if (eventsWritten > 0) { + // Update the time at which the last WAKE_UP event was sent + mAutoReleaseWakeLockTime = ::android::uptimeMillis() + + static_cast(WAKE_LOCK_TIMEOUT_SECONDS) * 1000; + } + + if (!mHasWakeLock && mOutstandingWakeUpEvents > 0 && + acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLockName) == 0) { + mHasWakeLock = true; + } else if (mHasWakeLock) { + // Check if the wake lock should be released automatically if + // SensorTimeout::WAKE_LOCK_SECONDS has elapsed since the last WAKE_UP event was written + // to the Wake Lock FMQ. + if (::android::uptimeMillis() > mAutoReleaseWakeLockTime) { + ALOGD("No events read from wake lock FMQ for %d seconds, auto releasing wake lock", + WAKE_LOCK_TIMEOUT_SECONDS); + mOutstandingWakeUpEvents = 0; + } + + if (mOutstandingWakeUpEvents == 0 && release_wake_lock(kWakeLockName) == 0) { + mHasWakeLock = false; + } + } + } + + private: + // The Event FMQ where sensor events are written + std::unique_ptr> mEventQueue; + // The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events + std::unique_ptr> mWakeLockQueue; + // Event Flag to signal to the framework when sensor events are available to be read + EventFlag* mEventQueueFlag; + // Callback for asynchronous events, such as dynamic sensor connections. + std::shared_ptr<::aidl::android::hardware::sensors::ISensorsCallback> mCallback; + // A map of the available sensors. + std::map> mSensors; + // The next available sensor handle. + int32_t mNextHandle; + // Lock to protect writes to the FMQs. + std::mutex mWriteLock; + // Lock to protect acquiring and releasing the wake lock + std::mutex mWakeLockLock; + // Track the number of WAKE_UP events that have not been handled by the framework + uint32_t mOutstandingWakeUpEvents; + // A thread to read the Wake Lock FMQ + std::thread mWakeLockThread; + // Flag to indicate that the Wake Lock Thread should continue to run + std::atomic_bool mReadWakeLockQueueRun; + // Track the time when the wake lock should automatically be released + int64_t mAutoReleaseWakeLockTime; + // Flag to indicate if a wake lock has been acquired + bool mHasWakeLock; +}; + +} // namespace sensors +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/sensors/aidl/main.cpp b/sensors/aidl/main.cpp new file mode 100644 index 0000000..8a5a7de --- /dev/null +++ b/sensors/aidl/main.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sensors-impl/Sensors.h" + +#include +#include +#include + +using aidl::android::hardware::sensors::Sensors; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + + // Make a default sensors service + auto sensor = ndk::SharedRefBase::make(); + const std::string sensorName = std::string() + Sensors::descriptor + "/default"; + binder_status_t status = + AServiceManager_addService(sensor->asBinder().get(), sensorName.c_str()); + CHECK_EQ(status, STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/sensors/aidl/sensors-default.rc b/sensors/aidl/sensors-default.rc new file mode 100644 index 0000000..96da85d --- /dev/null +++ b/sensors/aidl/sensors-default.rc @@ -0,0 +1,5 @@ +service vendor.sensors-default /vendor/bin/hw/android.hardware.sensors-service.example + class hal + user system + group system + rlimit rtprio 10 10 diff --git a/sensors/aidl/sensors-default.xml b/sensors/aidl/sensors-default.xml new file mode 100644 index 0000000..36b28ed --- /dev/null +++ b/sensors/aidl/sensors-default.xml @@ -0,0 +1,7 @@ + + + android.hardware.sensors + 2 + ISensors/default + + From b72c2f21149cc6e834496fc38e86baaae6899d3f Mon Sep 17 00:00:00 2001 From: "Ranjan, Rajani" Date: Mon, 31 Jul 2023 16:05:33 +0530 Subject: [PATCH 2/5] Sensors HAL : Update to AIDL implementation. The minimum requirement for Sensors HAL in android 14 should be AIDL implementation to meet target-level 8, API level 34 requirement. upgrading to AIDL implementations. moving from Sensors2.0 HIDL service to Sensors AIDL service HAL, which is based on Sensors HAL 2.1, uses the AIDL HAL interface. Sensors AIDL HAL is available in Android 13 and higher for new and upgraded devices. Tracked-On: OAM-112804 Signed-off-by: Ranjan, Rajani --- sensors/aidl/Android.bp | 85 +- sensors/aidl/OWNERS | 5 + sensors/aidl/Sensor.cpp | 278 ++- sensors/aidl/iioClient.cpp | 331 +++ sensors/aidl/iioClient.h | 111 + sensors/aidl/include/sensors-impl/Sensor.h | 62 +- sensors/aidl/include/sensors-impl/Sensors.h | 14 +- sensors/aidl/libiio_client/backend.c | 84 + sensors/aidl/libiio_client/buffer.c | 324 +++ sensors/aidl/libiio_client/channel.c | 844 ++++++++ sensors/aidl/libiio_client/context.c | 470 +++++ sensors/aidl/libiio_client/debug.h | 96 + sensors/aidl/libiio_client/device.c | 1138 +++++++++++ sensors/aidl/libiio_client/iio-config.h | 39 + sensors/aidl/libiio_client/iio-lock.h | 30 + sensors/aidl/libiio_client/iio-private.h | 297 +++ sensors/aidl/libiio_client/iio.h | 1785 ++++++++++++++++ sensors/aidl/libiio_client/iiod-client.c | 736 +++++++ sensors/aidl/libiio_client/iiod-client.h | 74 + sensors/aidl/libiio_client/lock.c | 90 + sensors/aidl/libiio_client/network.c | 1796 +++++++++++++++++ sensors/aidl/libiio_client/sort.h | 28 + sensors/aidl/libiio_client/utilities.c | 214 ++ sensors/aidl/libiio_client/xml.c | 479 +++++ sensors/aidl/sensors-aidl.rc | 8 + .../{sensors-default.xml => sensors-aidl.xml} | 2 +- sensors/aidl/sensors-default.rc | 5 - 27 files changed, 9275 insertions(+), 150 deletions(-) create mode 100644 sensors/aidl/OWNERS create mode 100644 sensors/aidl/iioClient.cpp create mode 100644 sensors/aidl/iioClient.h create mode 100644 sensors/aidl/libiio_client/backend.c create mode 100644 sensors/aidl/libiio_client/buffer.c create mode 100644 sensors/aidl/libiio_client/channel.c create mode 100644 sensors/aidl/libiio_client/context.c create mode 100644 sensors/aidl/libiio_client/debug.h create mode 100644 sensors/aidl/libiio_client/device.c create mode 100644 sensors/aidl/libiio_client/iio-config.h create mode 100644 sensors/aidl/libiio_client/iio-lock.h create mode 100644 sensors/aidl/libiio_client/iio-private.h create mode 100644 sensors/aidl/libiio_client/iio.h create mode 100644 sensors/aidl/libiio_client/iiod-client.c create mode 100644 sensors/aidl/libiio_client/iiod-client.h create mode 100644 sensors/aidl/libiio_client/lock.c create mode 100644 sensors/aidl/libiio_client/network.c create mode 100644 sensors/aidl/libiio_client/sort.h create mode 100644 sensors/aidl/libiio_client/utilities.c create mode 100644 sensors/aidl/libiio_client/xml.c create mode 100644 sensors/aidl/sensors-aidl.rc rename sensors/aidl/{sensors-default.xml => sensors-aidl.xml} (85%) delete mode 100644 sensors/aidl/sensors-default.rc diff --git a/sensors/aidl/Android.bp b/sensors/aidl/Android.bp index 16b4d35..c3f681d 100644 --- a/sensors/aidl/Android.bp +++ b/sensors/aidl/Android.bp @@ -14,34 +14,30 @@ * limitations under the License. */ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "hardware_interfaces_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["hardware_interfaces_license"], +soong_config_module_type { + name: "senPlugin_cc_default", + module_type: "cc_defaults", + config_namespace: "senPlugin", + bool_variables: ["SENSOR_LIST"], + properties: ["cflags", "srcs"], } -filegroup { - name: "sensors-default.rc", - srcs: ["sensors-default.rc"], -} - -filegroup { - name: "sensors-default.xml", - srcs: ["sensors-default.xml"], +senPlugin_cc_default { + name: "senPlugin_default", + soong_config_variables: { + SENSOR_LIST: { cflags: ["-DSENSOR_LIST_ENABLED"],}, + }, } cc_library_static { - name: "libsensorsexampleimpl", + name: "libsensorsaidlimpl", vendor: true, shared_libs: [ "libbase", "libfmq", "libpower", "libbinder_ndk", - "android.hardware.sensors-V2-ndk", + "android.hardware.sensors-V1-ndk", ], export_include_dirs: ["include"], srcs: [ @@ -54,12 +50,55 @@ cc_library_static { ], } +cc_library_static { + name: "intel.android.iiod.client@aidl-impel", + vendor: true, + local_include_dirs: ["libiio_client"], + srcs: [ + "Sensor.cpp", + "iioClient.cpp", + "libiio_client/xml.c", + "libiio_client/buffer.c", + "libiio_client/context.c", + "libiio_client/iiod-client.c", + "libiio_client/lock.c", + "libiio_client/channel.c", + "libiio_client/backend.c", + "libiio_client/device.c", + "libiio_client/utilities.c", + "libiio_client/network.c", + ], + + shared_libs: [ + "libbase", + "libfmq", + "libpower", + "libbinder_ndk", + "android.hardware.sensors-V1-ndk", + "liblog", + "libdl", + "libxml2", + ], + export_include_dirs: ["include"], + cflags: [ + "-DLOG_TAG=\"SensorsHal_aidl\"", + "-Wall", + "-Wno-unused-variable", + "-Wno-unused-parameter", + "-Wno-unused-function", + ], +} + cc_binary { - name: "android.hardware.sensors-service.example", + name: "android.hardware.sensors@aidl-service.intel", + defaults: ["senPlugin_default"], relative_install_path: "hw", - init_rc: [":sensors-default.rc"], - vintf_fragments: [":sensors-default.xml"], + init_rc: ["sensors-aidl.rc"], + vintf_fragments: ["sensors-aidl.xml"], vendor: true, + local_include_dirs: [ + "libiio_client" + ], shared_libs: [ "libbase", "libbinder_ndk", @@ -68,10 +107,12 @@ cc_binary { "libcutils", "liblog", "libutils", - "android.hardware.sensors-V2-ndk", + "android.hardware.sensors-V1-ndk", + "libxml2", ], static_libs: [ - "libsensorsexampleimpl", + "libsensorsaidlimpl", + "intel.android.iiod.client@aidl-impel", ], srcs: ["main.cpp"], } diff --git a/sensors/aidl/OWNERS b/sensors/aidl/OWNERS new file mode 100644 index 0000000..5bdbef0 --- /dev/null +++ b/sensors/aidl/OWNERS @@ -0,0 +1,5 @@ +arthuri@google.com +bduddie@google.com +stange@google.com +rajani.ranjan@intel.com +vilas.r.k@intel.com diff --git a/sensors/aidl/Sensor.cpp b/sensors/aidl/Sensor.cpp index 3bdd8b6..735fea0 100644 --- a/sensors/aidl/Sensor.cpp +++ b/sensors/aidl/Sensor.cpp @@ -16,9 +16,11 @@ #include "sensors-impl/Sensor.h" +#include #include "utils/SystemClock.h" #include +#include using ::ndk::ScopedAStatus; @@ -27,7 +29,7 @@ namespace android { namespace hardware { namespace sensors { -static constexpr int32_t kDefaultMaxDelayUs = 10 * 1000 * 1000; +static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000; Sensor::Sensor(ISensorsEventCallback* callback) : mIsEnabled(false), @@ -35,6 +37,7 @@ Sensor::Sensor(ISensorsEventCallback* callback) mLastSampleTimeNs(0), mCallback(callback), mMode(OperationMode::NORMAL) { + iioc = iioClient::get_iioClient(); mRunThread = std::thread(startThread, this); } @@ -51,16 +54,19 @@ const SensorInfo& Sensor::getSensorInfo() const { return mSensorInfo; } -void Sensor::batch(int64_t samplingPeriodNs) { - if (samplingPeriodNs < mSensorInfo.minDelayUs * 1000LL) { - samplingPeriodNs = mSensorInfo.minDelayUs * 1000LL; - } else if (samplingPeriodNs > mSensorInfo.maxDelayUs * 1000LL) { - samplingPeriodNs = mSensorInfo.maxDelayUs * 1000LL; +void Sensor::batch(int32_t samplingPeriodNs) { + if (samplingPeriodNs < mSensorInfo.minDelayUs * 1000) { + samplingPeriodNs = mSensorInfo.minDelayUs * 1000; + } else if (samplingPeriodNs > mSensorInfo.maxDelayUs * 1000) { + samplingPeriodNs = mSensorInfo.maxDelayUs * 1000; } if (mSamplingPeriodNs != samplingPeriodNs) { mSamplingPeriodNs = samplingPeriodNs; // Wake up the 'run' thread to check if a new event should be generated now + + if (iioc) + iioc->batch(mSensorInfo.sensorHandle, mSamplingPeriodNs); mWaitCV.notify_all(); } } @@ -69,6 +75,9 @@ void Sensor::activate(bool enable) { if (mIsEnabled != enable) { std::unique_lock lock(mRunMutex); mIsEnabled = enable; + + if (iioc) + iioc->activate(mSensorInfo.sensorHandle, enable); mWaitCV.notify_all(); } } @@ -84,7 +93,7 @@ ScopedAStatus Sensor::flush() { // Note: If a sensor supports batching, write all of the currently batched events for the sensor // to the Event FMQ prior to writing the flush complete event. - Event ev; + Event ev{}; ev.sensorHandle = mSensorInfo.sensorHandle; ev.sensorType = SensorType::META_DATA; EventPayload::MetaData meta = { @@ -112,7 +121,7 @@ void Sensor::run() { }); } else { timespec curTime; - clock_gettime(CLOCK_BOOTTIME, &curTime); + clock_gettime(CLOCK_REALTIME, &curTime); int64_t now = (curTime.tv_sec * kNanosecondsInSeconds) + curTime.tv_nsec; int64_t nextSampleTime = mLastSampleTimeNs + mSamplingPeriodNs; @@ -138,8 +147,39 @@ std::vector Sensor::readEvents() { event.sensorType = mSensorInfo.type; event.timestamp = ::android::elapsedRealtimeNano(); memset(&event.payload, 0, sizeof(event.payload)); - readEventPayload(event.payload); + EventPayload::Vec3 vec3 ; + if (iioc && iioc->is_iioc_initialized) { + vec3 = { + .x = iioc->devlist[mSensorInfo.sensorHandle].data[0], + .y = iioc->devlist[mSensorInfo.sensorHandle].data[1], + .z = iioc->devlist[mSensorInfo.sensorHandle].data[2], + .status = SensorStatus::ACCURACY_HIGH, + }; + } else { + if (event.sensorHandle == 1) { + vec3 = { + .x = 0, + .y = 0, + .z = -9.8, + .status = SensorStatus::ACCURACY_HIGH, + }; + } + else { + vec3 = { + .x = 0, + .y = 0, + .z = 0, + .status = SensorStatus::ACCURACY_HIGH, + }; + } + } + event.payload.set(vec3); events.push_back(event); +#if 1 // Probing data to debug + ALOGD("readEvents: handle(%d) name -> %s data[%f, %f, %f]", + mSensorInfo.sensorHandle, mSensorInfo.name.c_str(), + vec3.x, vec3.y, vec3.z); +#endif return events; } @@ -192,7 +232,7 @@ std::vector OnChangeSensor::readEvents() { for (auto iter = events.begin(); iter != events.end(); ++iter) { Event ev = *iter; if (!mPreviousEventSet || - memcmp(&mPreviousEvent.payload, &ev.payload, sizeof(ev.payload)) != 0) { + memcmp(&mPreviousEvent.payload, &ev.payload, sizeof(ev.payload)) != 0) { outputEvents.push_back(ev); mPreviousEvent = ev; mPreviousEventSet = true; @@ -204,163 +244,122 @@ std::vector OnChangeSensor::readEvents() { AccelSensor::AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : Sensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Accel Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; mSensorInfo.type = SensorType::ACCELEROMETER; mSensorInfo.typeAsString = ""; mSensorInfo.maxRange = 78.4f; // +/- 8g mSensorInfo.resolution = 1.52e-5; - mSensorInfo.power = 0.001f; // mA + mSensorInfo.power = 0.001f; // mA mSensorInfo.minDelayUs = 10 * 1000; // microseconds - mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds mSensorInfo.fifoReservedEventCount = 0; mSensorInfo.fifoMaxEventCount = 0; mSensorInfo.requiredPermission = ""; mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_DATA_INJECTION); -}; - -void AccelSensor::readEventPayload(EventPayload& payload) { - EventPayload::Vec3 vec3 = { - .x = 0, - .y = 0, - .z = 9.8, - .status = SensorStatus::ACCURACY_HIGH, - }; - payload.set(vec3); + } PressureSensor::PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : Sensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Pressure Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; mSensorInfo.type = SensorType::PRESSURE; mSensorInfo.typeAsString = ""; - mSensorInfo.maxRange = 1100.0f; // hPa - mSensorInfo.resolution = 0.005f; // hPa - mSensorInfo.power = 0.001f; // mA + mSensorInfo.maxRange = 1100.0f; // hPa + mSensorInfo.resolution = 0.005f; // hPa + mSensorInfo.power = 0.001f; // mA mSensorInfo.minDelayUs = 100 * 1000; // microseconds - mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.maxDelayUs = 100 * 1000 * 10; // microseconds mSensorInfo.fifoReservedEventCount = 0; mSensorInfo.fifoMaxEventCount = 0; mSensorInfo.requiredPermission = ""; mSensorInfo.flags = 0; -}; - -void PressureSensor::readEventPayload(EventPayload& payload) { - payload.set(1013.25f); } MagnetometerSensor::MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : Sensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Magnetic Field Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; mSensorInfo.type = SensorType::MAGNETIC_FIELD; mSensorInfo.typeAsString = ""; mSensorInfo.maxRange = 1300.0f; mSensorInfo.resolution = 0.01f; - mSensorInfo.power = 0.001f; // mA - mSensorInfo.minDelayUs = 20 * 1000; // microseconds - mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds mSensorInfo.fifoReservedEventCount = 0; mSensorInfo.fifoMaxEventCount = 0; mSensorInfo.requiredPermission = ""; mSensorInfo.flags = 0; -}; - -void MagnetometerSensor::readEventPayload(EventPayload& payload) { - EventPayload::Vec3 vec3 = { - .x = 100.0, - .y = 0, - .z = 50.0, - .status = SensorStatus::ACCURACY_HIGH, - }; - payload.set(vec3); } LightSensor::LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : OnChangeSensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Light Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; mSensorInfo.type = SensorType::LIGHT; mSensorInfo.typeAsString = ""; mSensorInfo.maxRange = 43000.0f; mSensorInfo.resolution = 10.0f; - mSensorInfo.power = 0.001f; // mA + mSensorInfo.power = 0.001f; // mA mSensorInfo.minDelayUs = 200 * 1000; // microseconds - mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.maxDelayUs = 200 * 1000 * 10; // microseconds mSensorInfo.fifoReservedEventCount = 0; mSensorInfo.fifoMaxEventCount = 0; mSensorInfo.requiredPermission = ""; mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE); -}; - -void LightSensor::readEventPayload(EventPayload& payload) { - payload.set(80.0f); } ProximitySensor::ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback) : OnChangeSensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Proximity Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; mSensorInfo.type = SensorType::PROXIMITY; mSensorInfo.typeAsString = ""; mSensorInfo.maxRange = 5.0f; mSensorInfo.resolution = 1.0f; - mSensorInfo.power = 0.012f; // mA + mSensorInfo.power = 0.012f; // mA mSensorInfo.minDelayUs = 200 * 1000; // microseconds - mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.maxDelayUs = 200 * 1000 * 10; // microseconds mSensorInfo.fifoReservedEventCount = 0; mSensorInfo.fifoMaxEventCount = 0; mSensorInfo.requiredPermission = ""; mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE | SensorInfo::SENSOR_FLAG_BITS_WAKE_UP); -}; - -void ProximitySensor::readEventPayload(EventPayload& payload) { - payload.set(2.5f); } GyroSensor::GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : Sensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Gyro Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; - mSensorInfo.type = SensorType::GYROSCOPE; + mSensorInfo.type = SensorType::GYROSCOPE_UNCALIBRATED; mSensorInfo.typeAsString = ""; mSensorInfo.maxRange = 1000.0f * M_PI / 180.0f; mSensorInfo.resolution = 1000.0f * M_PI / (180.0f * 32768.0f); mSensorInfo.power = 0.001f; mSensorInfo.minDelayUs = 10 * 1000; // microseconds - mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds mSensorInfo.fifoReservedEventCount = 0; mSensorInfo.fifoMaxEventCount = 0; mSensorInfo.requiredPermission = ""; mSensorInfo.flags = 0; -}; - -void GyroSensor::readEventPayload(EventPayload& payload) { - EventPayload::Vec3 vec3 = { - .x = 0, - .y = 0, - .z = 0, - .status = SensorStatus::ACCURACY_HIGH, - }; - payload.set(vec3); } AmbientTempSensor::AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : OnChangeSensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Ambient Temp Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; mSensorInfo.type = SensorType::AMBIENT_TEMPERATURE; mSensorInfo.typeAsString = ""; @@ -368,15 +367,31 @@ AmbientTempSensor::AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback mSensorInfo.resolution = 0.01f; mSensorInfo.power = 0.001f; mSensorInfo.minDelayUs = 40 * 1000; // microseconds - mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.maxDelayUs = 40 * 1000 * 10; // microseconds mSensorInfo.fifoReservedEventCount = 0; mSensorInfo.fifoMaxEventCount = 0; mSensorInfo.requiredPermission = ""; mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE); -}; +} -void AmbientTempSensor::readEventPayload(EventPayload& payload) { - payload.set(40.0f); +DeviceTempSensor::DeviceTempSensor(int32_t sensorHandle, + ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Device Temp Sensor"; + mSensorInfo.vendor = "Intel"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::AMBIENT_TEMPERATURE; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 80.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 40 * 1000; // microseconds + mSensorInfo.maxDelayUs = 40 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE); } RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle, @@ -384,7 +399,7 @@ RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle, : OnChangeSensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Relative Humidity Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; mSensorInfo.type = SensorType::RELATIVE_HUMIDITY; mSensorInfo.typeAsString = ""; @@ -392,22 +407,114 @@ RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle, mSensorInfo.resolution = 0.1f; mSensorInfo.power = 0.001f; mSensorInfo.minDelayUs = 40 * 1000; // microseconds - mSensorInfo.maxDelayUs = kDefaultMaxDelayUs; + mSensorInfo.maxDelayUs = 40 * 1000 * 10; // microseconds mSensorInfo.fifoReservedEventCount = 0; mSensorInfo.fifoMaxEventCount = 0; mSensorInfo.requiredPermission = ""; mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE); } -void RelativeHumiditySensor::readEventPayload(EventPayload& payload) { - payload.set(50.0f); +GravitySensor::GravitySensor(int32_t sensorHandle, + ISensorsEventCallback* callback) : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Gravity Sensor"; + mSensorInfo.vendor = "Intel"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::GRAVITY; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 1300.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +} + +RotationVector::RotationVector(int32_t sensorHandle, + ISensorsEventCallback* callback) : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Rotation Vector"; + mSensorInfo.vendor = "Intel"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::ROTATION_VECTOR; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 1300.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +} + +GeomagnaticRotationVector::GeomagnaticRotationVector(int32_t sensorHandle, + ISensorsEventCallback* callback) : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Geomagnatic Rotation Vector"; + mSensorInfo.vendor = "Intel"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::GEOMAGNETIC_ROTATION_VECTOR; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 1300.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +} + +OrientationSensor::OrientationSensor(int32_t sensorHandle, + ISensorsEventCallback* callback) : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Orientation Sensor"; + mSensorInfo.vendor = "Intel"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::ORIENTATION; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 1300.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; + mSensorInfo.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +} + +InclinometerSensor::InclinometerSensor(int32_t sensorHandle, + ISensorsEventCallback* callback) + : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Inclinometer 3D Sensor"; + mSensorInfo.vendor = "Intel"; + mSensorInfo.version = 1; + mSensorInfo.type = SensorType::ORIENTATION; + mSensorInfo.typeAsString = ""; + mSensorInfo.maxRange = 1300.0f; + mSensorInfo.resolution = 0.01f; + mSensorInfo.power = 0.001f; // mA + mSensorInfo.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; } HingeAngleSensor::HingeAngleSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : OnChangeSensor(callback) { mSensorInfo.sensorHandle = sensorHandle; mSensorInfo.name = "Hinge Angle Sensor"; - mSensorInfo.vendor = "Vendor String"; + mSensorInfo.vendor = "Intel"; mSensorInfo.version = 1; mSensorInfo.type = SensorType::HINGE_ANGLE; mSensorInfo.typeAsString = ""; @@ -424,11 +531,8 @@ HingeAngleSensor::HingeAngleSensor(int32_t sensorHandle, ISensorsEventCallback* SensorInfo::SENSOR_FLAG_BITS_DATA_INJECTION); } -void HingeAngleSensor::readEventPayload(EventPayload& payload) { - payload.set(180.0f); -} } // namespace sensors } // namespace hardware } // namespace android -} // namespace aidl +} // namespace aidl \ No newline at end of file diff --git a/sensors/aidl/iioClient.cpp b/sensors/aidl/iioClient.cpp new file mode 100644 index 0000000..45235ab --- /dev/null +++ b/sensors/aidl/iioClient.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2020 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include +#include "iioClient.h" + +struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_cid = VMADDR_CID_HOST, + .svm_port = 30431, +}; + +iioClient *iioClient::iioc = NULL; +/** + * sensor_map, holds list of sensor information to map during iio initalization. + * name@sensor_map should be matched with iio sensor name and returns sensor-id. + * Where stuct iio_sensor_map -> {name, id}; + */ +struct iio_sensor_map sensor_map[10] = { + {"unknown", 0}, + {"accel_3d", 1}, + {"gyro_3d", 2}, + {"magn_3d", 3}, + {"als", 4}, + {"gravity", 5}, + {"dev_rotation", 6}, + {"geomagnetic_orientation", 7}, + {"relative_orientation", 8}, + {"incli_3d", 9}, +}; + +/** + * get_android_sensor_id_by_name is an helper function to map + * iio sensor with android sensor list. return -1 when sensor + * not found otherwise return sensor handle. + */ +int iioClient::get_sensorid_by_name(const char *name) { + for (int index = 1; index <= MAX_SENSOR; index++) { + if (!strcmp(name, sensor_map[index].name)) { + return sensor_map[index].id; + } + } + + /* in this case appropriate sensor not found */ + return -1; +} + +int socket_fd; + +void iioClient::iioThread(struct iioclient_device *devlist) { + /** + * Incase sensor not initialized initialize + * once before use + */ + iioClient *iioc = iioClient::get_iioClient(); + + if (iioc == NULL) + return; + + while (!iioc->is_iioc_initialized && !iioc->iioInit()) + sleep(5); + + /* post initialization of iio-devices to align with + * android sensor fwk.*/ + for (int id = 1; id < MAX_SENSOR; id++) { + if (!devlist[id].is_initialized) + continue; + + if (iioc->devlist[id].sampling_period_ns) + iioc->batch(id, iioc->devlist[id].sampling_period_ns); + + if (iioc->devlist[id].is_enabled) + iioc->activate(id, iioc->devlist[id].is_enabled); + } + + /** + * read_sensor_data thread is a deffered call to read sensor data. + * this api is implemented to collect active sensor data from iiod. + */ + while (iioc) { + for (int id = 1; id < MAX_SENSOR; id++) { + if (!(devlist[id].is_initialized && devlist[id].is_enabled)) + continue; + + for (int index = 0; index < devlist[id].raw_channel_count; index++) { + struct iio_channel *channel = devlist[id].channel_raw[index]; + char buf[1024] = {0}; + if (iio_channel_attr_read(channel, "raw", buf, sizeof(buf)) > 0) + { + if (strcmp(devlist[id].name, "magn_3d") == 0) + devlist[id].data[index] = strtof(buf, NULL) * devlist[id].scale * 100; + else + devlist[id].data[index] = strtof(buf, NULL) * devlist[id].scale; + } + else { + close(socket_fd); + int fd = socket(AF_VSOCK, SOCK_STREAM, 0); + if (fd < 0) { + ALOGW("Sensor HAL socket init failed\n"); + } + + if (connect(fd, (struct sockaddr*)&sa, sizeof(sa)) != 0) { + ALOGW("Sensor HAL connect failed\n"); + close(fd); + } + socket_fd = fd ; + } + } +#if 0 // data probing point for debug + sleep(1); + ALOGI("%s -> data[%d](%f, %f, %f)", + devlist[id].name, devlist[id].raw_channel_count, + devlist[id].data[0], devlist[id].data[1], devlist[id].data[2]); +#endif + } + usleep(1000); // 1millisec + } + + ALOGE("Error: exit iiClient::iioThread"); +} + +/** + * iioClient:iioInit, establish nw bakend connection with iiod server + * and initialize raw chanel, freq channel & read sensorscale. + * return true on success else fail + */ +bool iioClient::iioInit(void) { + sensorCount = 0; + ctx = NULL; + active_sensor_count = 0; + is_iioc_initialized = false; + + /* Read IP address from vendor property */ + char value[PROPERTY_VALUE_MAX] = {0}; + #ifdef USE_NETWORK_CONTEXT + property_get("vendor.intel.ipaddr", value, "invalid_ip_addr"); + + /* Create IIO context */ + ctx = iio_create_network_context(value); + if (!ctx) { + ALOGW("Retrying: Initialize IIO Client@%s with N/W backend.", value); + return false; + } + #endif + #ifdef USE_VM_CONTEXT + /* Create VM context */ + ctx = iio_create_vm_context(SENSOR_PORT); + if (!ctx) { + ALOGW("Retrying: Initialize IIO Client with VSOCK"); + return false; + } + #endif + unsigned int nb_iio_devices = iio_context_get_devices_count(ctx); + for (int i = 0; i < nb_iio_devices; i++) { + const struct iio_device *device = iio_context_get_device(ctx, i); + /* Skip device with null pointer */ + if (!device) + continue; + + const char *sensor_name = iio_device_get_name(device); + int handle = get_sensorid_by_name(sensor_name); + /* Skip device with invalid id/handle */ + if (handle < 0) + continue; + + bool scale_found = false; + unsigned int nb_channels = iio_device_get_channels_count(device); + /* Skip device with no channles */ + if (!nb_channels) + continue; + + for (int ch_index = 0; ch_index < nb_channels; ch_index++) { + struct iio_channel *channel = iio_device_get_channel(device, ch_index); + + if (!iio_channel_is_output(channel)) { + unsigned int attrs_count = iio_channel_get_attrs_count(channel); + for (int attr_index = 0; attr_index < attrs_count; attr_index++) { + const char* attr_name = iio_channel_get_attr(channel, attr_index); + if (attr_name && !strcmp(attr_name, "raw")) { + /* get raw data channels */ + devlist[handle].raw_channel_count = ch_index + 1; + devlist[handle].channel_raw[ch_index] = channel; + } else if (!scale_found && attr_name && !strcmp(attr_name, "scale")) { + char buf[1024] = {0}; + /* reading scale */ + if (iio_channel_attr_read(channel, "scale", buf, sizeof(buf)) > 0) { + devlist[handle].scale = strtod(buf, NULL); + scale_found = true; + } + } else if (!scale_found && attr_name && !strcmp(attr_name, "sampling_frequency")) { + /** + * channle name might be "sampling_frequency" or "frequency" + * find existing channel fir gressful operations + */ + devlist[handle].channel_frequency = channel; + } + } + } + } + + /* Initialize & Map IIO sensor devices with Android sensor devices */ + devlist[handle].dev = device; + devlist[handle].name = sensor_name; + devlist[handle].nb_channels = nb_channels; + devlist[handle].is_initialized = true; + + sensorCount++; + if (sensorCount > MAX_SENSOR) + break; + } + + /* Destroy iio context if sensor count = zero */ + if (!sensorCount) { + if (ctx) + iio_context_destroy(ctx); + return false; + } + + ALOGI("Success: Initialized IIO Client@%s with N/W backend. sensor_count(%u)", value, sensorCount); + is_iioc_initialized = true; + return true; +} + + +/** + * Activate/de-activate one sensor. + * sensor_handle is the handle of the sensor to change. + * enabled set to 1 to enable, or 0 to disable the sensor. + */ +int iioClient::activate(int handle, bool enabled) { + if ((handle < 0) || (handle >= MAX_SENSOR)) { + ALOGE("ERROR: activate(%d) Sensor hadle(%d) is out of range", enabled, handle); + return 0; + } + + const char *sensor_name = sensor_map[handle].name; + + /* store the state*/ + devlist[handle].is_enabled = enabled; + + /* skip, if device not initialized*/ + if (!devlist[handle].is_initialized) + return 0; + + active_sensor_count = 0; + for (int id = 1; id < MAX_SENSOR; id++) + if (devlist[id].is_initialized && devlist[id].is_enabled) + active_sensor_count++; + + ALOGI("Success: activate -> Sensor(%s): %s -> active_sensor_count(%d)", + sensor_name, enabled?"enabled":"disabled", active_sensor_count); + + /* Activate or Deactivate all sensor */ + for (int index = 0; index < devlist[handle].nb_channels; index++) { + const struct iio_device *device = devlist[handle].dev; + struct iio_channel *channel = iio_device_get_channel(device, index); + + if(channel == NULL) + continue; + + /* skip output channels */ + if (iio_channel_is_output(channel)) + continue; + + /* enable/disable input channels only */ + if (enabled) + iio_channel_enable(channel); + else + iio_channel_disable(channel); + + ALOGI("%s channel(%d)", enabled?"Activated":"Deactivated", index); + } + + return 0; +} + +/** + * Sets a sensor's parameters, including sampling frequency and maximum + * report latency. This function can be called while the sensor is + * activated. + */ +int iioClient::batch(int handle, int32_t sampling_period_ns) { + if ((handle < 0) || (handle >= MAX_SENSOR)) { + ALOGE("Warning: batch invalid handle sampling_time(%d) sensor handle(%d) is out of range", + sampling_period_ns, handle); + return 0; + } + + /* store the sample period*/ + devlist[handle].sampling_period_ns = sampling_period_ns; + + /* Skip, if device not initialized */ + if (!devlist[handle].is_initialized) + return 0; + + const char *sensor_name = sensor_map[handle].name; + struct iio_channel *channel = devlist[handle].channel_frequency; + + /* Calculate frequency from sampling time(ns) */ + double write_freq = static_cast (1000000000.00/sampling_period_ns); + if (iio_channel_attr_write_double(channel, "sampling_frequency", write_freq)) { + ALOGD("iio-write error: batch -> Sensor(%s) sampling_period_ns(%d) freq(%f)", + sensor_name, sampling_period_ns, write_freq); + } + + /* Read confirmation of frequency value */ + double read_freq = 0; + if (iio_channel_attr_read_double(channel, "sampling_frequency", &read_freq)) { + ALOGD("iio-read error: batch -> Sensor(%s) sampling_period_ns(%d) freq(%f)", + sensor_name, sampling_period_ns, read_freq); + } + + ALOGD("Success: batch -> Sensor(%s), sampling_period_ns(%d) freq(%f %f)", + sensor_name, sampling_period_ns, write_freq, read_freq); + + return 0; +} diff --git a/sensors/aidl/iioClient.h b/sensors/aidl/iioClient.h new file mode 100644 index 0000000..b84557d --- /dev/null +++ b/sensors/aidl/iioClient.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2020 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "libiio_client/iio.h" + +#define MAX_SENSOR 9 +#define MAX_CHANNEL 3 +#define SENSOR_PORT "30431" +#define USE_VM_CONTEXT + +struct iio_sensor_map { + const char *name; + int id; +}; + +struct iioclient_device { + const char *name; + const struct iio_device *dev; + double scale; + int raw_channel_count; + struct iio_channel *channel_raw[10]; + struct iio_channel *channel_frequency;; + float data[16]; + + unsigned int nb_channels; + const char *frequency_channel; + + bool is_initialized; + bool is_enabled; + int32_t sampling_period_ns; +}; + +class iioClient { + private: + static iioClient *iioc; + /** + * controctor called when iioclient object created. + * initialize member variables to default state. + */ + iioClient() { + sensorCount = 0; + active_sensor_count = 0; + ctx = NULL; + is_iioc_initialized = false; + static std::thread thread_object(iioThread, devlist); + } + + public: + static iioClient *get_iioClient() { + if (!iioc) + iioc = new iioClient; + + return iioc; + } + /** + * distructor called when iioclient object deleted. + * and destroy iio client if already initialized. + */ + ~iioClient() { + if (ctx) + iio_context_destroy(ctx); + iioc = NULL; + } + + /* member veriables*/ + volatile int sensorCount; + bool is_iioc_initialized; + int active_sensor_count; + struct iio_context *ctx; + struct iioclient_device devlist[MAX_SENSOR]{}; + + /* member funcions*/ + static void iioThread(struct iioclient_device *devlist); + bool iioInit(void); + int get_sensorid_by_name(const char *name); + int activate(int handle, bool enabled); + int batch(int handle, int32_t sampling_period_ns); +}; diff --git a/sensors/aidl/include/sensors-impl/Sensor.h b/sensors/aidl/include/sensors-impl/Sensor.h index e6cd3e6..4c8d286 100644 --- a/sensors/aidl/include/sensors-impl/Sensor.h +++ b/sensors/aidl/include/sensors-impl/Sensor.h @@ -15,7 +15,7 @@ */ #include - +#include "iioClient.h" #include namespace aidl { @@ -40,12 +40,14 @@ class Sensor { using SensorType = ::aidl::android::hardware::sensors::SensorType; using MetaDataEventType = ::aidl::android::hardware::sensors::Event::EventPayload::MetaData::MetaDataEventType; + + iioClient *iioc; Sensor(ISensorsEventCallback* callback); virtual ~Sensor(); const SensorInfo& getSensorInfo() const; - void batch(int64_t samplingPeriodNs); + void batch(int32_t samplingPeriodNs); virtual void activate(bool enable); ndk::ScopedAStatus flush(); @@ -56,7 +58,6 @@ class Sensor { protected: void run(); virtual std::vector readEvents(); - virtual void readEventPayload(EventPayload&) = 0; static void startThread(Sensor* sensor); bool isWakeUpSensor(); @@ -93,76 +94,79 @@ class OnChangeSensor : public Sensor { class AccelSensor : public Sensor { public: AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback); - - protected: - virtual void readEventPayload(EventPayload& payload) override; }; class GyroSensor : public Sensor { public: GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback); - - protected: - virtual void readEventPayload(EventPayload& payload) override; }; class AmbientTempSensor : public OnChangeSensor { public: AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; - protected: - virtual void readEventPayload(EventPayload& payload) override; +class DeviceTempSensor : public OnChangeSensor { + public: + DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback); }; class PressureSensor : public Sensor { public: PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback); - - protected: - virtual void readEventPayload(EventPayload& payload) override; }; class MagnetometerSensor : public Sensor { public: MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback); - - protected: - virtual void readEventPayload(EventPayload& payload) override; }; class LightSensor : public OnChangeSensor { public: LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback); - - protected: - virtual void readEventPayload(EventPayload& payload) override; }; class ProximitySensor : public OnChangeSensor { public: ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback); - - protected: - virtual void readEventPayload(EventPayload& payload) override; }; class RelativeHumiditySensor : public OnChangeSensor { public: RelativeHumiditySensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; - protected: - virtual void readEventPayload(EventPayload& payload) override; +class GravitySensor : public Sensor { + public: + GravitySensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class RotationVector : public Sensor { + public: + RotationVector(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class GeomagnaticRotationVector : public Sensor { + public: + GeomagnaticRotationVector(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class OrientationSensor : public Sensor { + public: + OrientationSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class InclinometerSensor : public Sensor { + public: + InclinometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback); }; class HingeAngleSensor : public OnChangeSensor { public: HingeAngleSensor(int32_t sensorHandle, ISensorsEventCallback* callback); - - protected: - virtual void readEventPayload(EventPayload& payload) override; }; } // namespace sensors } // namespace hardware } // namespace android -} // namespace aidl +} // namespace aidl \ No newline at end of file diff --git a/sensors/aidl/include/sensors-impl/Sensors.h b/sensors/aidl/include/sensors-impl/Sensors.h index e270d96..3757608 100644 --- a/sensors/aidl/include/sensors-impl/Sensors.h +++ b/sensors/aidl/include/sensors-impl/Sensors.h @@ -45,15 +45,17 @@ class Sensors : public BnSensors, public ISensorsEventCallback { mReadWakeLockQueueRun(false), mAutoReleaseWakeLockTime(0), mHasWakeLock(false) { +#if SENSOR_LIST_ENABLED AddSensor(); AddSensor(); - AddSensor(); - AddSensor(); AddSensor(); AddSensor(); - AddSensor(); - AddSensor(); - AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); +#endif } virtual ~Sensors() { @@ -213,4 +215,4 @@ class Sensors : public BnSensors, public ISensorsEventCallback { } // namespace sensors } // namespace hardware } // namespace android -} // namespace aidl +} // namespace aidl \ No newline at end of file diff --git a/sensors/aidl/libiio_client/backend.c b/sensors/aidl/libiio_client/backend.c new file mode 100644 index 0000000..d7353f5 --- /dev/null +++ b/sensors/aidl/libiio_client/backend.c @@ -0,0 +1,84 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2017 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "iio-config.h" +#include "iio-private.h" + +#include + +unsigned int iio_get_backends_count(void) +{ + unsigned int count = 0; + +#ifdef WITH_LOCAL_BACKEND + count++; +#endif +#ifdef WITH_XML_BACKEND + count++; +#endif +#ifdef WITH_NETWORK_BACKEND + count++; +#endif +#ifdef WITH_USB_BACKEND + count++; +#endif +#ifdef WITH_SERIAL_BACKEND + count++; +#endif + + return count; +} + +const char * iio_get_backend(unsigned int index) +{ +#ifdef WITH_LOCAL_BACKEND + if (index == 0) + return "local"; + index--; +#endif +#ifdef WITH_XML_BACKEND + if (index == 0) + return "xml"; + index--; +#endif +#ifdef WITH_NETWORK_BACKEND + if (index == 0) + return "ip"; + index--; +#endif +#ifdef WITH_USB_BACKEND + if (index == 0) + return "usb"; + index--; +#endif +#ifdef WITH_SERIAL_BACKEND + if (index == 0) + return "serial"; +#endif + return NULL; +} + +bool iio_has_backend(const char *backend) +{ + unsigned int i; + + for (i = 0; i < iio_get_backends_count(); i++) + if (strcmp(backend, iio_get_backend(i)) == 0) + return true; + + return false; +} diff --git a/sensors/aidl/libiio_client/buffer.c b/sensors/aidl/libiio_client/buffer.c new file mode 100644 index 0000000..5878da0 --- /dev/null +++ b/sensors/aidl/libiio_client/buffer.c @@ -0,0 +1,324 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014-2015 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#include "iio-config.h" +#include "iio-private.h" + +#include +#include + +struct callback_wrapper_data { + ssize_t (*callback)(const struct iio_channel *, void *, size_t, void *); + void *data; + uint32_t *mask; +}; + +static bool device_is_high_speed(const struct iio_device *dev) +{ + /* Little trick: We call the backend's get_buffer() function, which is + * for now only implemented in the Local backend, with a NULL pointer. + * It will return -ENOSYS if the device is not high speed, and either + * -EBADF or -EINVAL otherwise. */ + const struct iio_backend_ops *ops = dev->ctx->ops; + return !!ops->get_buffer && + (ops->get_buffer(dev, NULL, 0, NULL, 0) != -ENOSYS); +} + +struct iio_buffer * iio_device_create_buffer(const struct iio_device *dev, + size_t samples_count, bool cyclic) +{ + int ret = -EINVAL; + struct iio_buffer *buf; + unsigned int sample_size = iio_device_get_sample_size(dev); + + if (!sample_size || !samples_count) + goto err_set_errno; + + buf = malloc(sizeof(*buf)); + if (!buf) { + ret = -ENOMEM; + goto err_set_errno; + } + + buf->dev_sample_size = sample_size; + buf->length = sample_size * samples_count; + buf->dev = dev; + buf->mask = calloc(dev->words, sizeof(*buf->mask)); + if (!buf->mask) { + ret = -ENOMEM; + goto err_free_buf; + } + + /* Set the default channel mask to the one used by the device. + * While input buffers will erase this as soon as the refill function + * is used, it is useful for output buffers, as it permits + * iio_buffer_foreach_sample to be used. */ + memcpy(buf->mask, dev->mask, dev->words * sizeof(*buf->mask)); + + ret = iio_device_open(dev, samples_count, cyclic); + if (ret < 0) + goto err_free_mask; + + buf->dev_is_high_speed = device_is_high_speed(dev); + if (buf->dev_is_high_speed) { + /* Dequeue the first buffer, so that buf->buffer is correctly + * initialized */ + buf->buffer = NULL; + if (iio_device_is_tx(dev)) { + ret = dev->ctx->ops->get_buffer(dev, &buf->buffer, + buf->length, buf->mask, dev->words); + if (ret < 0) + goto err_close_device; + } + } else { + buf->buffer = malloc(buf->length); + if (!buf->buffer) { + ret = -ENOMEM; + goto err_close_device; + } + } + + buf->sample_size = iio_device_get_sample_size_mask(dev, + buf->mask, dev->words); + buf->data_length = buf->length; + return buf; + +err_close_device: + iio_device_close(dev); +err_free_mask: + free(buf->mask); +err_free_buf: + free(buf); +err_set_errno: + errno = -ret; + return NULL; +} + +void iio_buffer_destroy(struct iio_buffer *buffer) +{ + iio_device_close(buffer->dev); + if (!buffer->dev_is_high_speed) + free(buffer->buffer); + free(buffer->mask); + free(buffer); +} + +int iio_buffer_get_poll_fd(struct iio_buffer *buffer) +{ + return iio_device_get_poll_fd(buffer->dev); +} + +int iio_buffer_set_blocking_mode(struct iio_buffer *buffer, bool blocking) +{ + return iio_device_set_blocking_mode(buffer->dev, blocking); +} + +ssize_t iio_buffer_refill(struct iio_buffer *buffer) +{ + ssize_t read; + const struct iio_device *dev = buffer->dev; + + if (buffer->dev_is_high_speed) { + read = dev->ctx->ops->get_buffer(dev, &buffer->buffer, + buffer->length, buffer->mask, dev->words); + } else { + read = iio_device_read_raw(dev, buffer->buffer, buffer->length, + buffer->mask, dev->words); + } + + if (read >= 0) { + buffer->data_length = read; + buffer->sample_size = iio_device_get_sample_size_mask(dev, + buffer->mask, dev->words); + } + return read; +} + +ssize_t iio_buffer_push(struct iio_buffer *buffer) +{ + const struct iio_device *dev = buffer->dev; + ssize_t ret; + + if (buffer->dev_is_high_speed) { + void *buf; + ret = dev->ctx->ops->get_buffer(dev, &buf, + buffer->data_length, buffer->mask, dev->words); + if (ret >= 0) { + buffer->buffer = buf; + ret = (ssize_t) buffer->data_length; + } + } else { + void *ptr = buffer->buffer; + size_t tmp_len; + + /* iio_device_write_raw doesn't guarantee that all bytes are + * written */ + for (tmp_len = buffer->data_length; tmp_len; ) { + ret = iio_device_write_raw(dev, ptr, tmp_len); + if (ret < 0) + goto out_reset_data_length; + + tmp_len -= ret; + ptr = (void *) ((uintptr_t) ptr + ret); + } + + ret = (ssize_t) buffer->data_length; + } + +out_reset_data_length: + buffer->data_length = buffer->length; + return ret; +} + +ssize_t iio_buffer_push_partial(struct iio_buffer *buffer, size_t samples_count) +{ + size_t new_len = samples_count * buffer->dev_sample_size; + + if (new_len == 0 || new_len > buffer->length) + return -EINVAL; + + buffer->data_length = new_len; + return iio_buffer_push(buffer); +} + +ssize_t iio_buffer_foreach_sample(struct iio_buffer *buffer, + ssize_t (*callback)(const struct iio_channel *, + void *, size_t, void *), void *d) +{ + uintptr_t ptr = (uintptr_t) buffer->buffer, + start = ptr, + end = ptr + buffer->data_length; + const struct iio_device *dev = buffer->dev; + ssize_t processed = 0; + + if (buffer->sample_size == 0) + return -EINVAL; + + if (buffer->data_length < buffer->dev_sample_size) + return 0; + + while (end - ptr >= (size_t) buffer->sample_size) { + unsigned int i; + + for (i = 0; i < dev->nb_channels; i++) { + const struct iio_channel *chn = dev->channels[i]; + unsigned int length = chn->format.length / 8; + + if (chn->index < 0) + break; + + /* Test if the buffer has samples for this channel */ + if (!TEST_BIT(buffer->mask, chn->number)) + continue; + + if ((ptr - start) % length) + ptr += length - ((ptr - start) % length); + + /* Test if the client wants samples from this channel */ + if (TEST_BIT(dev->mask, chn->number)) { + ssize_t ret = callback(chn, + (void *) ptr, length, d); + if (ret < 0) + return ret; + else + processed += ret; + } + + if (i == dev->nb_channels - 1 || dev->channels[ + i + 1]->index != chn->index) + ptr += length * chn->format.repeat; + } + } + return processed; +} + +void * iio_buffer_start(const struct iio_buffer *buffer) +{ + return buffer->buffer; +} + +void * iio_buffer_first(const struct iio_buffer *buffer, + const struct iio_channel *chn) +{ + size_t len; + unsigned int i; + uintptr_t ptr = (uintptr_t) buffer->buffer, + start = ptr; + + if (!iio_channel_is_enabled(chn)) + return iio_buffer_end(buffer); + + for (i = 0; i < buffer->dev->nb_channels; i++) { + struct iio_channel *cur = buffer->dev->channels[i]; + len = cur->format.length / 8 * cur->format.repeat; + + /* NOTE: dev->channels are ordered by index */ + if (cur->index < 0 || cur->index == chn->index) + break; + + /* Test if the buffer has samples for this channel */ + if (!TEST_BIT(buffer->mask, cur->number)) + continue; + + /* Two channels with the same index use the same samples */ + if (i > 0 && cur->index == buffer->dev->channels[i - 1]->index) + continue; + + if ((ptr - start) % len) + ptr += len - ((ptr - start) % len); + ptr += len; + } + + len = chn->format.length / 8; + if ((ptr - start) % len) + ptr += len - ((ptr - start) % len); + return (void *) ptr; +} + +ptrdiff_t iio_buffer_step(const struct iio_buffer *buffer) +{ + return (ptrdiff_t) buffer->sample_size; +} + +void * iio_buffer_end(const struct iio_buffer *buffer) +{ + return (void *) ((uintptr_t) buffer->buffer + buffer->data_length); +} + +void iio_buffer_set_data(struct iio_buffer *buf, void *data) +{ + buf->userdata = data; +} + +void * iio_buffer_get_data(const struct iio_buffer *buf) +{ + return buf->userdata; +} + +const struct iio_device * iio_buffer_get_device(const struct iio_buffer *buf) +{ + return buf->dev; +} + +void iio_buffer_cancel(struct iio_buffer *buf) +{ + const struct iio_backend_ops *ops = buf->dev->ctx->ops; + + if (ops->cancel) + ops->cancel(buf->dev); +} diff --git a/sensors/aidl/libiio_client/channel.c b/sensors/aidl/libiio_client/channel.c new file mode 100644 index 0000000..a90ae13 --- /dev/null +++ b/sensors/aidl/libiio_client/channel.c @@ -0,0 +1,844 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#include "debug.h" +#include "iio-private.h" + +#include +#include +#include + +static const char * const iio_chan_type_name_spec[] = { + [IIO_VOLTAGE] = "voltage", + [IIO_CURRENT] = "current", + [IIO_POWER] = "power", + [IIO_ACCEL] = "accel", + [IIO_ANGL_VEL] = "anglvel", + [IIO_MAGN] = "magn", + [IIO_LIGHT] = "illuminance", + [IIO_INTENSITY] = "intensity", + [IIO_PROXIMITY] = "proximity", + [IIO_TEMP] = "temp", + [IIO_INCLI] = "incli", + [IIO_ROT] = "rot", + [IIO_ANGL] = "angl", + [IIO_TIMESTAMP] = "timestamp", + [IIO_CAPACITANCE] = "capacitance", + [IIO_ALTVOLTAGE] = "altvoltage", + [IIO_CCT] = "cct", + [IIO_PRESSURE] = "pressure", + [IIO_HUMIDITYRELATIVE] = "humidityrelative", + [IIO_ACTIVITY] = "activity", + [IIO_STEPS] = "steps", + [IIO_ENERGY] = "energy", + [IIO_DISTANCE] = "distance", + [IIO_VELOCITY] = "velocity", + [IIO_CONCENTRATION] = "concentration", + [IIO_RESISTANCE] = "resistance", + [IIO_PH] = "ph", + [IIO_UVINDEX] = "uvindex", + [IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity", + [IIO_COUNT] = "count", + [IIO_INDEX] = "index", + [IIO_GRAVITY] = "gravity", +}; + +static const char * const modifier_names[] = { + [IIO_MOD_X] = "x", + [IIO_MOD_Y] = "y", + [IIO_MOD_Z] = "z", + [IIO_MOD_X_AND_Y] = "x&y", + [IIO_MOD_X_AND_Z] = "x&z", + [IIO_MOD_Y_AND_Z] = "y&z", + [IIO_MOD_X_AND_Y_AND_Z] = "x&y&z", + [IIO_MOD_X_OR_Y] = "x|y", + [IIO_MOD_X_OR_Z] = "x|z", + [IIO_MOD_Y_OR_Z] = "y|z", + [IIO_MOD_X_OR_Y_OR_Z] = "x|y|z", + [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)", + [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2", + [IIO_MOD_LIGHT_BOTH] = "both", + [IIO_MOD_LIGHT_IR] = "ir", + [IIO_MOD_LIGHT_CLEAR] = "clear", + [IIO_MOD_LIGHT_RED] = "red", + [IIO_MOD_LIGHT_GREEN] = "green", + [IIO_MOD_LIGHT_BLUE] = "blue", + [IIO_MOD_LIGHT_UV] = "uv", + [IIO_MOD_QUATERNION] = "quaternion", + [IIO_MOD_TEMP_AMBIENT] = "ambient", + [IIO_MOD_TEMP_OBJECT] = "object", + [IIO_MOD_NORTH_MAGN] = "from_north_magnetic", + [IIO_MOD_NORTH_TRUE] = "from_north_true", + [IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp", + [IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp", + [IIO_MOD_RUNNING] = "running", + [IIO_MOD_JOGGING] = "jogging", + [IIO_MOD_WALKING] = "walking", + [IIO_MOD_STILL] = "still", + [IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)", + [IIO_MOD_I] = "i", + [IIO_MOD_Q] = "q", + [IIO_MOD_CO2] = "co2", + [IIO_MOD_VOC] = "voc", +}; + +/* + * Looks for a IIO channel modifier at the beginning of the string s. If a + * modifier was found the symbolic constant (IIO_MOD_*) is returned, otherwise + * IIO_NO_MOD is returned. If a modifier was found len_p will be updated with + * the length of the modifier. + */ +unsigned int find_channel_modifier(const char *s, size_t *len_p) +{ + unsigned int i; + size_t len; + + for (i = 0; i < ARRAY_SIZE(modifier_names); i++) { + if (!modifier_names[i]) + continue; + len = strlen(modifier_names[i]); + if (strncmp(s, modifier_names[i], len) == 0 && + (s[len] == '\0' || s[len] == '_')) { + if (len_p) + *len_p = len; + return i; + } + } + + return IIO_NO_MOD; +} + +/* + * Initializes all auto-detected fields of the channel struct. Must be called + * after the channel has been otherwise fully initialized. + */ +void iio_channel_init_finalize(struct iio_channel *chn) +{ + unsigned int i; + size_t len; + char *mod; + + chn->type = IIO_CHAN_TYPE_UNKNOWN; + chn->modifier = IIO_NO_MOD; + + for (i = 0; i < ARRAY_SIZE(iio_chan_type_name_spec); i++) { + len = strlen(iio_chan_type_name_spec[i]); + if (strncmp(iio_chan_type_name_spec[i], chn->id, len) != 0) + continue; + /* Type must be followed by either a '_' or a digit */ + if (chn->id[len] != '_' && (chn->id[len] < '0' || chn->id[len] > '9')) + continue; + + chn->type = (enum iio_chan_type) i; + } + + mod = strchr(chn->id, '_'); + if (!mod) + return; + + mod++; + + for (i = 0; i < ARRAY_SIZE(modifier_names); i++) { + if (!modifier_names[i]) + continue; + len = strlen(modifier_names[i]); + if (strncmp(modifier_names[i], mod, len) != 0) + continue; + + chn->modifier = (enum iio_modifier) i; + break; + } +} + +static char *get_attr_xml(struct iio_channel_attr *attr, size_t *length) +{ + char *str; + size_t len = strlen(attr->name) + sizeof(""); + if (attr->filename) + len += strlen(attr->filename) + sizeof("filename=\"\""); + + str = malloc(len); + if (!str) + return NULL; + + *length = len - 1; /* Skip the \0 */ + if (attr->filename) + iio_snprintf(str, len, "", + attr->name, attr->filename); + else + iio_snprintf(str, len, "", attr->name); + return str; +} + +static char * get_scan_element(const struct iio_channel *chn, size_t *length) +{ + char buf[1024], repeat[12] = "", *str; + char processed = (chn->format.is_fully_defined ? 'A' - 'a' : 0); + + if (chn->format.repeat > 1) + iio_snprintf(repeat, sizeof(repeat), "X%u", chn->format.repeat); + + iio_snprintf(buf, sizeof(buf), "", + chn->index, chn->format.is_be ? 'b' : 'l', + chn->format.is_signed ? 's' + processed : 'u' + processed, + chn->format.bits, chn->format.length, repeat, + chn->format.shift); + + if (chn->format.with_scale) { + char *ptr = strrchr(buf, '\0'); + iio_snprintf(ptr - 2, buf + sizeof(buf) - ptr + 2, + "scale=\"%f\" />", chn->format.scale); + } + + str = iio_strdup(buf); + if (str) + *length = strlen(str); + return str; +} + +/* Returns a string containing the XML representation of this channel */ +char * iio_channel_get_xml(const struct iio_channel *chn, size_t *length) +{ + size_t len = sizeof("") + + strlen(chn->id) + (chn->name ? strlen(chn->name) : 0); + char *ptr, *str, **attrs, *scan_element = NULL; + size_t *attrs_len, scan_element_len = 0; + unsigned int i; + + if (chn->is_scan_element) { + scan_element = get_scan_element(chn, &scan_element_len); + if (!scan_element) + return NULL; + else + len += scan_element_len; + } + + attrs_len = malloc(chn->nb_attrs * sizeof(*attrs_len)); + if (!attrs_len) + goto err_free_scan_element; + + attrs = malloc(chn->nb_attrs * sizeof(*attrs)); + if (!attrs) + goto err_free_attrs_len; + + for (i = 0; i < chn->nb_attrs; i++) { + char *xml = get_attr_xml(&chn->attrs[i], &attrs_len[i]); + if (!xml) + goto err_free_attrs; + attrs[i] = xml; + len += attrs_len[i]; + } + + str = malloc(len); + if (!str) + goto err_free_attrs; + + iio_snprintf(str, len, "id); + ptr = strrchr(str, '\0'); + + if (chn->name) { + sprintf(ptr, " name=\"%s\"", chn->name); + ptr = strrchr(ptr, '\0'); + } + + sprintf(ptr, " type=\"%s\" >", chn->is_output ? "output" : "input"); + ptr = strrchr(ptr, '\0'); + + if (chn->is_scan_element) { + strcpy(ptr, scan_element); + ptr += scan_element_len; + } + + for (i = 0; i < chn->nb_attrs; i++) { + strcpy(ptr, attrs[i]); + ptr += attrs_len[i]; + free(attrs[i]); + } + + free(scan_element); + free(attrs); + free(attrs_len); + + strcpy(ptr, ""); + *length = ptr - str + sizeof("") - 1; + return str; + +err_free_attrs: + while (i--) + free(attrs[i]); + free(attrs); +err_free_attrs_len: + free(attrs_len); +err_free_scan_element: + if (chn->is_scan_element) + free(scan_element); + return NULL; +} + +const char * iio_channel_get_id(const struct iio_channel *chn) +{ + return chn->id; +} + +const char * iio_channel_get_name(const struct iio_channel *chn) +{ + return chn->name; +} + +bool iio_channel_is_output(const struct iio_channel *chn) +{ + return chn->is_output; +} + +bool iio_channel_is_scan_element(const struct iio_channel *chn) +{ + return chn->is_scan_element; +} + +enum iio_modifier iio_channel_get_modifier(const struct iio_channel *chn) +{ + return chn->modifier; +} + +enum iio_chan_type iio_channel_get_type(const struct iio_channel *chn) +{ + return chn->type; +} + +unsigned int iio_channel_get_attrs_count(const struct iio_channel *chn) +{ + return chn->nb_attrs; +} + +const char * iio_channel_get_attr(const struct iio_channel *chn, + unsigned int index) +{ + if (index >= chn->nb_attrs) + return NULL; + else + return chn->attrs[index].name; +} + +const char * iio_channel_find_attr(const struct iio_channel *chn, + const char *name) +{ + unsigned int i; + for (i = 0; i < chn->nb_attrs; i++) { + const char *attr = chn->attrs[i].name; + if (!strcmp(attr, name)) + return attr; + } + return NULL; +} + +ssize_t iio_channel_attr_read(const struct iio_channel *chn, + const char *attr, char *dst, size_t len) +{ + if (chn->dev->ctx->ops->read_channel_attr) + return chn->dev->ctx->ops->read_channel_attr(chn, + attr, dst, len); + else + return -ENOSYS; +} + +ssize_t iio_channel_attr_write_raw(const struct iio_channel *chn, + const char *attr, const void *src, size_t len) +{ + if (chn->dev->ctx->ops->write_channel_attr) + return chn->dev->ctx->ops->write_channel_attr(chn, + attr, src, len); + else + return -ENOSYS; +} + +ssize_t iio_channel_attr_write(const struct iio_channel *chn, + const char *attr, const char *src) +{ + return iio_channel_attr_write_raw(chn, attr, src, strlen(src) + 1); +} + +void iio_channel_set_data(struct iio_channel *chn, void *data) +{ + chn->userdata = data; +} + +void * iio_channel_get_data(const struct iio_channel *chn) +{ + return chn->userdata; +} + +long iio_channel_get_index(const struct iio_channel *chn) +{ + return chn->index; +} + +const struct iio_data_format * iio_channel_get_data_format( + const struct iio_channel *chn) +{ + return &chn->format; +} + +bool iio_channel_is_enabled(const struct iio_channel *chn) +{ + return chn->index >= 0 && chn->dev->mask && + TEST_BIT(chn->dev->mask, chn->number); +} + +void iio_channel_enable(struct iio_channel *chn) +{ + if (chn->is_scan_element && chn->index >= 0 && chn->dev->mask) + SET_BIT(chn->dev->mask, chn->number); +} + +void iio_channel_disable(struct iio_channel *chn) +{ + if (chn->index >= 0 && chn->dev->mask) + CLEAR_BIT(chn->dev->mask, chn->number); +} + +void free_channel(struct iio_channel *chn) +{ + size_t i; + for (i = 0; i < chn->nb_attrs; i++) { + free(chn->attrs[i].name); + free(chn->attrs[i].filename); + } + if (chn->nb_attrs) + free(chn->attrs); + if (chn->name) + free(chn->name); + if (chn->id) + free(chn->id); + free(chn); +} + +static void byte_swap(uint8_t *dst, const uint8_t *src, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + dst[i] = src[len - i - 1]; +} + +static void shift_bits(uint8_t *dst, size_t shift, size_t len, bool left) +{ + size_t i, shift_bytes = shift / 8; + shift %= 8; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + if (!left) +#else + if (left) +#endif + { + if (shift_bytes) { + memmove(dst, dst + shift_bytes, len - shift_bytes); + memset(dst + len - shift_bytes, 0, shift_bytes); + } + if (shift) { + for (i = 0; i < len; i++) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + dst[i] >>= shift; + if (i < len - 1) + dst[i] |= dst[i + 1] << (8 - shift); +#else + dst[i] <<= shift; + if (i < len - 1) + dst[i] |= dst[i + 1] >> (8 - shift); +#endif + } + } + } else { + if (shift_bytes) { + memmove(dst + shift_bytes, dst, len - shift_bytes); + memset(dst, 0, shift_bytes); + } + if (shift) { + for (i = len; i > 0; i--) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + dst[i - 1] <<= shift; + if (i > 1) + dst[i - 1] |= dst[i - 2] >> (8 - shift); +#else + dst[i - 1] >>= shift; + if (i > 1) + dst[i - 1] |= dst[i - 2] << (8 - shift); +#endif + } + } + } +} + +static void sign_extend(uint8_t *dst, size_t bits, size_t len) +{ + size_t upper_bytes = ((len * 8 - bits) / 8); + uint8_t msb, msb_bit = 1 << ((bits - 1) % 8); + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + msb = dst[len - 1 - upper_bytes] & msb_bit; + if (upper_bytes) + memset(dst + len - upper_bytes, msb ? 0xff : 0x00, upper_bytes); + if (msb) + dst[len - 1 - upper_bytes] |= ~(msb_bit - 1); + else + dst[len - 1 - upper_bytes] &= (msb_bit - 1); +#else + /* XXX: untested */ + msb = dst[upper_bytes] & msb_bit; + if (upper_bytes) + memset(dst, msb ? 0xff : 0x00, upper_bytes); + if (msb) + dst[upper_bytes] |= ~(msb_bit - 1); +#endif +} + +static void mask_upper_bits(uint8_t *dst, size_t bits, size_t len) +{ + size_t i; + + /* Clear upper bits */ + if (bits % 8) + dst[bits / 8] &= (1 << (bits % 8)) - 1; + + /* Clear upper bytes */ + for (i = (bits + 7) / 8; i < len; i++) + dst[i] = 0; +} + + +void iio_channel_convert(const struct iio_channel *chn, + void *dst, const void *src) +{ + uintptr_t src_ptr = (uintptr_t) src, dst_ptr = (uintptr_t) dst; + unsigned int len = chn->format.length / 8; + ptrdiff_t end = len * chn->format.repeat; + uintptr_t end_ptr = src_ptr + end; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + bool swap = chn->format.is_be; +#else + bool swap = !chn->format.is_be; +#endif + + for (src_ptr = (uintptr_t) src; src_ptr < end_ptr; + src_ptr += len, dst_ptr += len) { + if (len == 1 || !swap) + memcpy((void *) dst_ptr, (const void *) src_ptr, len); + else + byte_swap((void *) dst_ptr, (const void *) src_ptr, + len); + + if (chn->format.shift) + shift_bits((void *) dst_ptr, chn->format.shift, len, + false); + + if (!chn->format.is_fully_defined) { + if (chn->format.is_signed) + sign_extend((void *) dst_ptr, + chn->format.bits, len); + else + mask_upper_bits((void *) dst_ptr, + chn->format.bits, len); + } + } +} + +void iio_channel_convert_inverse(const struct iio_channel *chn, + void *dst, const void *src) +{ + uintptr_t src_ptr = (uintptr_t) src, dst_ptr = (uintptr_t) dst; + unsigned int len = chn->format.length / 8; + ptrdiff_t end = len * chn->format.repeat; + uintptr_t end_ptr = dst_ptr + end; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + bool swap = chn->format.is_be; +#else + bool swap = !chn->format.is_be; +#endif + uint8_t buf[1024]; + + /* Somehow I doubt we will have samples of 8192 bits each. */ + if (len > sizeof(buf)) + return; + + for (dst_ptr = (uintptr_t) dst; dst_ptr < end_ptr; + src_ptr += len, dst_ptr += len) { + memcpy(buf, (const void *) src_ptr, len); + mask_upper_bits(buf, chn->format.bits, len); + + if (chn->format.shift) + shift_bits(buf, chn->format.shift, len, true); + + if (len == 1 || !swap) + memcpy((void *) dst_ptr, buf, len); + else + byte_swap((void *) dst_ptr, buf, len); + } +} + +size_t iio_channel_read_raw(const struct iio_channel *chn, + struct iio_buffer *buf, void *dst, size_t len) +{ + uintptr_t src_ptr, dst_ptr = (uintptr_t) dst, end = dst_ptr + len; + unsigned int length = chn->format.length / 8 * chn->format.repeat; + uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf); + ptrdiff_t buf_step = iio_buffer_step(buf); + + for (src_ptr = (uintptr_t) iio_buffer_first(buf, chn); + src_ptr < buf_end && dst_ptr + length <= end; + src_ptr += buf_step, dst_ptr += length) + memcpy((void *) dst_ptr, (const void *) src_ptr, length); + return dst_ptr - (uintptr_t) dst; +} + +size_t iio_channel_read(const struct iio_channel *chn, + struct iio_buffer *buf, void *dst, size_t len) +{ + uintptr_t src_ptr, dst_ptr = (uintptr_t) dst, end = dst_ptr + len; + unsigned int length = chn->format.length / 8 * chn->format.repeat; + uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf); + ptrdiff_t buf_step = iio_buffer_step(buf); + + for (src_ptr = (uintptr_t) iio_buffer_first(buf, chn); + src_ptr < buf_end && dst_ptr + length <= end; + src_ptr += buf_step, dst_ptr += length) + iio_channel_convert(chn, + (void *) dst_ptr, (const void *) src_ptr); + return dst_ptr - (uintptr_t) dst; +} + +size_t iio_channel_write_raw(const struct iio_channel *chn, + struct iio_buffer *buf, const void *src, size_t len) +{ + uintptr_t dst_ptr, src_ptr = (uintptr_t) src, end = src_ptr + len; + unsigned int length = chn->format.length / 8 * chn->format.repeat; + uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf); + ptrdiff_t buf_step = iio_buffer_step(buf); + + for (dst_ptr = (uintptr_t) iio_buffer_first(buf, chn); + dst_ptr < buf_end && src_ptr + length <= end; + dst_ptr += buf_step, src_ptr += length) + memcpy((void *) dst_ptr, (const void *) src_ptr, length); + return src_ptr - (uintptr_t) src; +} + +size_t iio_channel_write(const struct iio_channel *chn, + struct iio_buffer *buf, const void *src, size_t len) +{ + uintptr_t dst_ptr, src_ptr = (uintptr_t) src, end = src_ptr + len; + unsigned int length = chn->format.length / 8 * chn->format.repeat; + uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf); + ptrdiff_t buf_step = iio_buffer_step(buf); + + for (dst_ptr = (uintptr_t) iio_buffer_first(buf, chn); + dst_ptr < buf_end && src_ptr + length <= end; + dst_ptr += buf_step, src_ptr += length) + iio_channel_convert_inverse(chn, + (void *) dst_ptr, (const void *) src_ptr); + return src_ptr - (uintptr_t) src; +} + +int iio_channel_attr_read_longlong(const struct iio_channel *chn, + const char *attr, long long *val) +{ + char *end, buf[1024]; + long long value; + ssize_t ret = iio_channel_attr_read(chn, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + + value = strtoll(buf, &end, 0); + if (end == buf) + return -EINVAL; + *val = value; + return 0; +} + +int iio_channel_attr_read_bool(const struct iio_channel *chn, + const char *attr, bool *val) +{ + long long value; + int ret = iio_channel_attr_read_longlong(chn, attr, &value); + if (ret < 0) + return ret; + + *val = !!value; + return 0; +} + +int iio_channel_attr_read_double(const struct iio_channel *chn, + const char *attr, double *val) +{ + char buf[1024]; + ssize_t ret = iio_channel_attr_read(chn, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + else + return read_double(buf, val); +} + +int iio_channel_attr_write_longlong(const struct iio_channel *chn, + const char *attr, long long val) +{ + ssize_t ret; + char buf[1024]; + iio_snprintf(buf, sizeof(buf), "%lld", val); + ret = iio_channel_attr_write(chn, attr, buf); + return ret < 0 ? ret : 0; +} + +int iio_channel_attr_write_double(const struct iio_channel *chn, + const char *attr, double val) +{ + ssize_t ret; + char buf[1024]; + + ret = (ssize_t) write_double(buf, sizeof(buf), val); + if (!ret) + ret = iio_channel_attr_write(chn, attr, buf); + return ret < 0 ? ret : 0; +} + +int iio_channel_attr_write_bool(const struct iio_channel *chn, + const char *attr, bool val) +{ + ssize_t ret; + if (val) + ret = iio_channel_attr_write_raw(chn, attr, "1", 2); + else + ret = iio_channel_attr_write_raw(chn, attr, "0", 2); + return ret < 0 ? ret : 0; +} + +const char * iio_channel_attr_get_filename( + const struct iio_channel *chn, const char *attr) +{ + unsigned int i; + for (i = 0; i < chn->nb_attrs; i++) { + if (!strcmp(chn->attrs[i].name, attr)) + return chn->attrs[i].filename; + } + return NULL; +} + +int iio_channel_attr_read_all(struct iio_channel *chn, + int (*cb)(struct iio_channel *chn, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + int ret, buf_size; + char *buf, *ptr; + unsigned int i; + + /* We need a big buffer here; 1 MiB should be enough */ + buf = malloc(0x100000); + if (!buf) + return -ENOMEM; + + ret = (int) iio_channel_attr_read(chn, NULL, buf, 0x100000); + if (ret < 0) + goto err_free_buf; + + ptr = buf; + buf_size = ret; + + for (i = 0; i < iio_channel_get_attrs_count(chn); i++) { + const char *attr = iio_channel_get_attr(chn, i); + int32_t len; + + if (buf_size < 4) { + ret = -EPROTO; + break; + } + + len = (int32_t) iio_be32toh(*(uint32_t *) ptr); + ptr += 4; + buf_size -= 4; + + if (len > 0 && buf_size < len) { + ret = -EPROTO; + break; + } + + if (len > 0) { + ret = cb(chn, attr, ptr, (size_t) len, data); + if (ret < 0) + goto err_free_buf; + + if (len & 0x3) + len = ((len >> 2) + 1) << 2; + ptr += len; + if (len >= buf_size) + buf_size = 0; + else + buf_size -= len; + } + } + +err_free_buf: + free(buf); + return ret < 0 ? ret : 0; +} + +int iio_channel_attr_write_all(struct iio_channel *chn, + ssize_t (*cb)(struct iio_channel *chn, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + char *buf, *ptr; + unsigned int i; + size_t len = 0x100000; + int ret; + + /* We need a big buffer here; 1 MiB should be enough */ + buf = malloc(len); + if (!buf) + return -ENOMEM; + + ptr = buf; + + for (i = 0; i < iio_channel_get_attrs_count(chn); i++) { + const char *attr = iio_channel_get_attr(chn, i); + + ret = (int) cb(chn, attr, ptr + 4, len - 4, data); + if (ret < 0) + goto err_free_buf; + + *(int32_t *) ptr = (int32_t) iio_htobe32((uint32_t) ret); + ptr += 4; + len -= 4; + + if (ret > 0) { + if (ret & 0x3) + ret = ((ret >> 2) + 1) << 2; + ptr += ret; + len -= ret; + } + } + + ret = (int) iio_channel_attr_write_raw(chn, NULL, buf, ptr - buf); + +err_free_buf: + free(buf); + return ret < 0 ? ret : 0; +} + +const struct iio_device * iio_channel_get_device(const struct iio_channel *chn) +{ + return chn->dev; +} diff --git a/sensors/aidl/libiio_client/context.c b/sensors/aidl/libiio_client/context.c new file mode 100644 index 0000000..cdaa189 --- /dev/null +++ b/sensors/aidl/libiio_client/context.c @@ -0,0 +1,470 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ +#include +#include +#include +#include "debug.h" +#include "iio-config.h" +#include "iio-private.h" +#include "sort.h" + +#include +#include +#include + +#ifdef _WIN32 +#define LOCAL_BACKEND 0 +#define NETWORK_BACKEND 1 +#endif + +static const char xml_header[] = "" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"]>"; + +/* Returns a string containing the XML representation of this context */ +char * iio_context_create_xml(const struct iio_context *ctx) +{ + size_t len, *devices_len = NULL; + char *str, *ptr, **devices = NULL; + unsigned int i; + + len = strlen(ctx->name) + sizeof(xml_header) - 1 + + sizeof(""); + if (ctx->description) + len += strlen(ctx->description) + + sizeof(" description=\"\"") - 1; + + for (i = 0; i < ctx->nb_attrs; i++) + len += strlen(ctx->attrs[i]) + + strlen(ctx->values[i]) + + sizeof(""); + + if (ctx->nb_devices) { + devices_len = malloc(ctx->nb_devices * sizeof(*devices_len)); + if (!devices_len) { + errno = ENOMEM; + return NULL; + } + + devices = calloc(ctx->nb_devices, sizeof(*devices)); + if (!devices) + goto err_free_devices_len; + + for (i = 0; i < ctx->nb_devices; i++) { + char *xml = iio_device_get_xml(ctx->devices[i], + &devices_len[i]); + if (!xml) + goto err_free_devices; + devices[i] = xml; + len += devices_len[i]; + } + } + + str = malloc(len); + if (!str) { + errno = ENOMEM; + goto err_free_devices; + } + + if (ctx->description) { + iio_snprintf(str, len, "%s", + xml_header, ctx->name, ctx->description); + } else { + iio_snprintf(str, len, "%s", + xml_header, ctx->name); + } + + ptr = strrchr(str, '\0'); + + for (i = 0; i < ctx->nb_attrs; i++) + ptr += sprintf(ptr, "", + ctx->attrs[i], ctx->values[i]); + + + for (i = 0; i < ctx->nb_devices; i++) { + strcpy(ptr, devices[i]); + ptr += devices_len[i]; + free(devices[i]); + } + + free(devices); + free(devices_len); + strcpy(ptr, ""); + return str; + +err_free_devices: + for (i = 0; i < ctx->nb_devices; i++) + free(devices[i]); + free(devices); +err_free_devices_len: + free(devices_len); + return NULL; +} + +const char * iio_context_get_xml(const struct iio_context *ctx) +{ + return ctx->xml; +} + +const char * iio_context_get_name(const struct iio_context *ctx) +{ + return ctx->name; +} + +const char * iio_context_get_description(const struct iio_context *ctx) +{ + if (ctx->description) + return ctx->description; + else + return ""; +} + +void iio_context_destroy(struct iio_context *ctx) +{ + unsigned int i; + if (ctx->ops->shutdown) + ctx->ops->shutdown(ctx); + + for (i = 0; i < ctx->nb_attrs; i++) { + free(ctx->attrs[i]); + free(ctx->values[i]); + } + if (ctx->nb_attrs) { + free(ctx->attrs); + free(ctx->values); + } + for (i = 0; i < ctx->nb_devices; i++) + free_device(ctx->devices[i]); + if (ctx->nb_devices) + free(ctx->devices); + if (ctx->xml) + free(ctx->xml); + if (ctx->description) + free(ctx->description); + free(ctx); +} + +unsigned int iio_context_get_devices_count(const struct iio_context *ctx) +{ + return ctx->nb_devices; +} + +struct iio_device * iio_context_get_device(const struct iio_context *ctx, + unsigned int index) +{ + if (index >= ctx->nb_devices) + return NULL; + else + return ctx->devices[index]; +} + +struct iio_device * iio_context_find_device(const struct iio_context *ctx, + const char *name) +{ + unsigned int i; + for (i = 0; i < ctx->nb_devices; i++) { + struct iio_device *dev = ctx->devices[i]; + if (!strcmp(dev->id, name) || + (dev->name && !strcmp(dev->name, name))) + return dev; + } + return NULL; +} + +static void reorder_channels(struct iio_device *dev) +{ + bool found; + unsigned int i; + + /* Reorder channels by index */ + do { + found = false; + for (i = 1; i < dev->nb_channels; i++) { + struct iio_channel **channels = dev->channels; + long ch1 = channels[i - 1]->index; + long ch2 = channels[i]->index; + + if (ch1 == ch2 && ch1 >= 0) { + ch1 = channels[i - 1]->format.shift; + ch2 = channels[i]->format.shift; + } + + if (ch2 >= 0 && ((ch1 > ch2) || ch1 < 0)) { + struct iio_channel *bak = channels[i]; + channels[i] = channels[i - 1]; + channels[i - 1] = bak; + found = true; + } + } + } while (found); + + for (i = 0; i < dev->nb_channels; i++) + dev->channels[i]->number = i; +} + +int iio_context_init(struct iio_context *ctx) +{ + unsigned int i; + + for (i = 0; i < ctx->nb_devices; i++) + reorder_channels(ctx->devices[i]); + + if (!ctx->xml) { + ctx->xml = iio_context_create_xml(ctx); + if (!ctx->xml) + return -ENOMEM; + } + + return 0; +} + +int iio_context_get_version(const struct iio_context *ctx, + unsigned int *major, unsigned int *minor, char git_tag[8]) +{ + if (ctx->ops->get_version) + return ctx->ops->get_version(ctx, major, minor, git_tag); + + iio_library_get_version(major, minor, git_tag); + return 0; +} + +int iio_context_set_timeout(struct iio_context *ctx, unsigned int timeout) +{ + if (ctx->ops->set_timeout) + return ctx->ops->set_timeout(ctx, timeout); + else + return -ENOSYS; +} + +struct iio_context * iio_context_clone(const struct iio_context *ctx) +{ + if (ctx->ops->clone) { + return ctx->ops->clone(ctx); + } else { + errno = ENOSYS; + return NULL; + } +} + +struct iio_context * iio_create_context_from_uri(const char *uri) +{ + int prefix_size; + +#ifdef WITH_LOCAL_BACKEND + prefix_size = strlen("local:"); + if (strncmp(uri, "local:", prefix_size) == 0) /* No address part */ + return iio_create_local_context(); +#endif + +#ifdef WITH_XML_BACKEND + prefix_size = strlen("xml:"); + if (strncmp(uri, "xml:", prefix_size) == 0) + return iio_create_xml_context(uri + prefix_size); +#endif + +#ifdef WITH_NETWORK_BACKEND + prefix_size = strlen("ip:"); + if (strncmp(uri, "ip:", prefix_size) == 0) + return iio_create_network_context(uri + prefix_size); + prefix_size = strlen("vsock:"); + if (strncmp(uri, "vsock:", prefix_size) == 0) { + return iio_create_vm_context(uri + prefix_size); + } +#endif + +#ifdef WITH_USB_BACKEND + prefix_size = strlen("usb:"); + if (strncmp(uri, "usb:", prefix_size) == 0) + return usb_create_context_from_uri(uri); +#endif + +#ifdef WITH_SERIAL_BACKEND + prefix_size = strlen("serial:"); + if (strncmp(uri, "serial:", prefix_size) == 0) + return serial_create_context_from_uri(uri); +#endif + + errno = ENOSYS; + return NULL; +} + +struct iio_context * iio_create_default_context(void) +{ + char *hostname = getenv("IIOD_REMOTE"); + + if (hostname) { + struct iio_context *ctx; + + ctx = iio_create_context_from_uri(hostname); + if (ctx) + return ctx; + +#ifdef WITH_NETWORK_BACKEND + /* If the environment variable is an empty string, we will + * discover the server using ZeroConf */ + if (strlen(hostname) == 0) + hostname = NULL; + + return iio_create_network_context(hostname); +#endif + } + + return iio_create_local_context(); +} + +struct iio_context * iio_create_local_context(void) +{ +#ifdef WITH_LOCAL_BACKEND + return local_create_context(); +#else + errno = ENOSYS; + return NULL; +#endif +} + +struct iio_context * iio_create_network_context(const char *hostname) +{ +#ifdef WITH_NETWORK_BACKEND + return network_create_context(hostname); +#else + errno = ENOSYS; + return NULL; +#endif +} + +struct iio_context * iio_create_vm_context(const char *port) +{ +#ifdef WITH_NETWORK_BACKEND + char *end = NULL; + unsigned int port_num = strtol(port, &end, 10); + + return vm_create_context(port_num); +#else + errno = ENOSYS; + return NULL; +#endif +} + +//reserve socket file descriptor +int get_reserve_fd_context() +{ + return get_reserve_fd(); +} + +struct iio_context * iio_create_xml_context_mem(const char *xml, size_t len) +{ +#ifdef WITH_XML_BACKEND + return xml_create_context_mem(xml, len); +#else + errno = ENOSYS; + return NULL; +#endif +} + +struct iio_context * iio_create_xml_context(const char *xml_file) +{ +#ifdef WITH_XML_BACKEND + return xml_create_context(xml_file); +#else + errno = ENOSYS; + return NULL; +#endif +} + +unsigned int iio_context_get_attrs_count(const struct iio_context *ctx) +{ + return ctx->nb_attrs; +} + +int iio_context_get_attr(const struct iio_context *ctx, unsigned int index, + const char **name, const char **value) +{ + if (index >= ctx->nb_attrs) + return -EINVAL; + + if (name) + *name = ctx->attrs[index]; + if (value) + *value = ctx->values[index]; + return 0; +} + +const char * iio_context_get_attr_value( + const struct iio_context *ctx, const char *name) +{ + unsigned int i; + + for (i = 0; i < ctx->nb_attrs; i++) { + if (!strcmp(name, ctx->attrs[i])) + return ctx->values[i]; + } + + return NULL; +} + +int iio_context_add_attr(struct iio_context *ctx, + const char *key, const char *value) +{ + char **attrs, **values, *new_key, *new_val; + + attrs = realloc(ctx->attrs, + (ctx->nb_attrs + 1) * sizeof(*ctx->attrs)); + if (!attrs) + return -ENOMEM; + + ctx->attrs = attrs; + + values = realloc(ctx->values, + (ctx->nb_attrs + 1) * sizeof(*ctx->values)); + if (!values) + return -ENOMEM; + + ctx->values = values; + + new_key = iio_strdup(key); + if (!new_key) + return -ENOMEM; + + new_val = iio_strdup(value); + if (!new_val) { + free(new_key); + return -ENOMEM; + } + + ctx->attrs[ctx->nb_attrs] = new_key; + ctx->values[ctx->nb_attrs] = new_val; + ctx->nb_attrs++; + return 0; +} diff --git a/sensors/aidl/libiio_client/debug.h b/sensors/aidl/libiio_client/debug.h new file mode 100644 index 0000000..cb84ad6 --- /dev/null +++ b/sensors/aidl/libiio_client/debug.h @@ -0,0 +1,96 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include "iio-config.h" + +#include + +#define NoLog_L 0 +#define Error_L 1 +#define Warning_L 2 +#define Info_L 3 +#define Debug_L 4 + +/* -------------------- */ + +#ifdef WITH_COLOR_DEBUG +#ifndef COLOR_DEBUG +#define COLOR_DEBUG "\e[0;32m" +#endif +#ifndef COLOR_WARNING +#define COLOR_WARNING "\e[01;35m" +#endif +#ifndef COLOR_ERROR +#define COLOR_ERROR "\e[01;31m" +#endif + +#define COLOR_END "\e[0m" +#endif + +#if (LOG_LEVEL >= Debug_L) +# ifdef COLOR_DEBUG +# define DEBUG(str, ...) \ + fprintf(stdout, COLOR_DEBUG "DEBUG: " str COLOR_END, ##__VA_ARGS__) +# else +# define DEBUG(...) \ + fprintf(stdout, "DEBUG: " __VA_ARGS__) +# endif +#else +#define DEBUG(...) do { } while (0) +#endif + +#if (LOG_LEVEL >= Info_L) +# ifdef COLOR_INFO +# define INFO(str, ...) \ + fprintf(stdout, COLOR_INFO str COLOR_END, ##__VA_ARGS__) +# else +# define INFO(...) \ + fprintf(stdout, __VA_ARGS__) +# endif +#else +#define INFO(...) do { } while (0) +#endif + +#if (LOG_LEVEL >= Warning_L) +# ifdef COLOR_WARNING +# define WARNING(str, ...) \ + fprintf(stderr, COLOR_WARNING "WARNING: " str COLOR_END, ##__VA_ARGS__) +# else +# define WARNING(...) \ + fprintf(stderr, "WARNING: " __VA_ARGS__) +# endif +#else +#define WARNING(...) do { } while (0) +#endif + +#if (LOG_LEVEL >= Error_L) +# ifdef COLOR_ERROR +# define ERROR(str, ...) \ + fprintf(stderr, COLOR_ERROR "ERROR: " str COLOR_END, ##__VA_ARGS__) +# else +# define ERROR(...) \ + fprintf(stderr, "ERROR: " __VA_ARGS__) +# endif +#else +#define ERROR(...) do { } while (0) +#endif + +#endif diff --git a/sensors/aidl/libiio_client/device.c b/sensors/aidl/libiio_client/device.c new file mode 100644 index 0000000..0f0c2cc --- /dev/null +++ b/sensors/aidl/libiio_client/device.c @@ -0,0 +1,1138 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#include "debug.h" +#include "iio-private.h" + +#include +#include +#include +#include + +static char *get_attr_xml(const char *attr, size_t *length, enum iio_attr_type type) +{ + size_t len = sizeof("") + strlen(attr); + char *str; + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + break; + case IIO_ATTR_TYPE_DEBUG: + len += (sizeof("debug-") - 1); + break; + case IIO_ATTR_TYPE_BUFFER: + len += (sizeof("buffer-") - 1); + break; + default: + return NULL; + } + + str = malloc(len); + if (!str) + return NULL; + + *length = len - 1; /* Skip the \0 */ + switch (type) { + case IIO_ATTR_TYPE_DEVICE: + iio_snprintf(str, len, "", attr); + break; + case IIO_ATTR_TYPE_DEBUG: + iio_snprintf(str, len, "", attr); + break; + case IIO_ATTR_TYPE_BUFFER: + iio_snprintf(str, len, "", attr); + break; + } + + return str; +} + +/* Returns a string containing the XML representation of this device */ +char * iio_device_get_xml(const struct iio_device *dev, size_t *length) +{ + size_t len = sizeof("") + + strlen(dev->id) + (dev->name ? strlen(dev->name) : 0); + char *ptr, *str, **attrs, **channels, **buffer_attrs, **debug_attrs; + size_t *attrs_len, *channels_len, *buffer_attrs_len, *debug_attrs_len; + unsigned int i, j, k; + + attrs_len = malloc(dev->nb_attrs * sizeof(*attrs_len)); + if (!attrs_len) + return NULL; + + attrs = malloc(dev->nb_attrs * sizeof(*attrs)); + if (!attrs) + goto err_free_attrs_len; + + for (i = 0; i < dev->nb_attrs; i++) { + char *xml = get_attr_xml(dev->attrs[i], &attrs_len[i], IIO_ATTR_TYPE_DEVICE); + if (!xml) + goto err_free_attrs; + attrs[i] = xml; + len += attrs_len[i]; + } + + channels_len = malloc(dev->nb_channels * sizeof(*channels_len)); + if (!channels_len) + goto err_free_attrs; + + channels = malloc(dev->nb_channels * sizeof(*channels)); + if (!channels) + goto err_free_channels_len; + + for (j = 0; j < dev->nb_channels; j++) { + char *xml = iio_channel_get_xml(dev->channels[j], + &channels_len[j]); + if (!xml) + goto err_free_channels; + channels[j] = xml; + len += channels_len[j]; + } + + buffer_attrs_len = malloc(dev->nb_buffer_attrs * + sizeof(*buffer_attrs_len)); + if (!buffer_attrs_len) + goto err_free_channels; + + buffer_attrs = malloc(dev->nb_buffer_attrs * sizeof(*buffer_attrs)); + if (!buffer_attrs) + goto err_free_buffer_attrs_len; + + for (k = 0; k < dev->nb_buffer_attrs; k++) { + char *xml = get_attr_xml(dev->buffer_attrs[k], + &buffer_attrs_len[k], IIO_ATTR_TYPE_BUFFER); + if (!xml) + goto err_free_buffer_attrs; + buffer_attrs[k] = xml; + len += buffer_attrs_len[k]; + } + + debug_attrs_len = malloc(dev->nb_debug_attrs * + sizeof(*debug_attrs_len)); + if (!debug_attrs_len) + goto err_free_buffer_attrs; + + debug_attrs = malloc(dev->nb_debug_attrs * sizeof(*debug_attrs)); + if (!debug_attrs) + goto err_free_debug_attrs_len; + + for (k = 0; k < dev->nb_debug_attrs; k++) { + char *xml = get_attr_xml(dev->debug_attrs[k], + &debug_attrs_len[k], IIO_ATTR_TYPE_DEBUG); + if (!xml) + goto err_free_debug_attrs; + debug_attrs[k] = xml; + len += debug_attrs_len[k]; + } + + str = malloc(len); + if (!str) + goto err_free_debug_attrs; + + iio_snprintf(str, len, "id); + ptr = strrchr(str, '\0'); + + if (dev->name) { + sprintf(ptr, " name=\"%s\"", dev->name); + ptr = strrchr(ptr, '\0'); + } + + strcpy(ptr, " >"); + ptr += 2; + + for (i = 0; i < dev->nb_channels; i++) { + strcpy(ptr, channels[i]); + ptr += channels_len[i]; + free(channels[i]); + } + + free(channels); + free(channels_len); + + for (i = 0; i < dev->nb_attrs; i++) { + strcpy(ptr, attrs[i]); + ptr += attrs_len[i]; + free(attrs[i]); + } + + free(attrs); + free(attrs_len); + + for (i = 0; i < dev->nb_buffer_attrs; i++) { + strcpy(ptr, buffer_attrs[i]); + ptr += buffer_attrs_len[i]; + free(buffer_attrs[i]); + } + + free(buffer_attrs); + free(buffer_attrs_len); + + for (i = 0; i < dev->nb_debug_attrs; i++) { + strcpy(ptr, debug_attrs[i]); + ptr += debug_attrs_len[i]; + free(debug_attrs[i]); + } + + free(debug_attrs); + free(debug_attrs_len); + + strcpy(ptr, ""); + *length = ptr - str + sizeof("") - 1; + return str; + +err_free_debug_attrs: + while (k--) + free(debug_attrs[k]); + free(debug_attrs); +err_free_debug_attrs_len: + free(debug_attrs_len); +err_free_buffer_attrs: + while (k--) + free(buffer_attrs[k]); + free(buffer_attrs); +err_free_buffer_attrs_len: + free(buffer_attrs_len); +err_free_channels: + while (j--) + free(channels[j]); + free(channels); +err_free_channels_len: + free(channels_len); +err_free_attrs: + while (i--) + free(attrs[i]); + free(attrs); +err_free_attrs_len: + free(attrs_len); + return NULL; +} + +const char * iio_device_get_id(const struct iio_device *dev) +{ + return dev->id; +} + +const char * iio_device_get_name(const struct iio_device *dev) +{ + return dev->name; +} + +unsigned int iio_device_get_channels_count(const struct iio_device *dev) +{ + return dev->nb_channels; +} + +struct iio_channel * iio_device_get_channel(const struct iio_device *dev, + unsigned int index) +{ + if (index >= dev->nb_channels) + return NULL; + else + return dev->channels[index]; +} + +struct iio_channel * iio_device_find_channel(const struct iio_device *dev, + const char *name, bool output) +{ + unsigned int i; + for (i = 0; i < dev->nb_channels; i++) { + struct iio_channel *chn = dev->channels[i]; + if (iio_channel_is_output(chn) != output) + continue; + + if (!strcmp(chn->id, name) || + (chn->name && !strcmp(chn->name, name))) + return chn; + } + return NULL; +} + +unsigned int iio_device_get_attrs_count(const struct iio_device *dev) +{ + return dev->nb_attrs; +} + +const char * iio_device_get_attr(const struct iio_device *dev, + unsigned int index) +{ + if (index >= dev->nb_attrs) + return NULL; + else + return dev->attrs[index]; +} + +const char * iio_device_find_attr(const struct iio_device *dev, + const char *name) +{ + unsigned int i; + for (i = 0; i < dev->nb_attrs; i++) { + const char *attr = dev->attrs[i]; + if (!strcmp(attr, name)) + return attr; + } + return NULL; +} + +unsigned int iio_device_get_buffer_attrs_count(const struct iio_device *dev) +{ + return dev->nb_buffer_attrs; +} + +const char * iio_device_get_buffer_attr(const struct iio_device *dev, + unsigned int index) +{ + if (index >= dev->nb_buffer_attrs) + return NULL; + else + return dev->buffer_attrs[index]; +} + +const char * iio_device_find_buffer_attr(const struct iio_device *dev, + const char *name) +{ + unsigned int i; + for (i = 0; i < dev->nb_buffer_attrs; i++) { + const char *attr = dev->buffer_attrs[i]; + if (!strcmp(attr, name)) + return attr; + } + return NULL; +} + +const char * iio_device_find_debug_attr(const struct iio_device *dev, + const char *name) +{ + unsigned int i; + for (i = 0; i < dev->nb_debug_attrs; i++) { + const char *attr = dev->debug_attrs[i]; + if (!strcmp(attr, name)) + return attr; + } + return NULL; +} + +bool iio_device_is_tx(const struct iio_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->nb_channels; i++) { + struct iio_channel *ch = dev->channels[i]; + if (iio_channel_is_output(ch) && iio_channel_is_enabled(ch)) + return true; + } + + return false; +} + +int iio_device_open(const struct iio_device *dev, + size_t samples_count, bool cyclic) +{ + unsigned int i; + bool has_channels = false; + + for (i = 0; !has_channels && i < dev->words; i++) + has_channels = !!dev->mask[i]; + if (!has_channels) + return -EINVAL; + + if (dev->ctx->ops->open) + return dev->ctx->ops->open(dev, samples_count, cyclic); + else + return -ENOSYS; +} + +int iio_device_close(const struct iio_device *dev) +{ + if (dev->ctx->ops->close) + return dev->ctx->ops->close(dev); + else + return -ENOSYS; +} + +int iio_device_get_poll_fd(const struct iio_device *dev) +{ + if (dev->ctx->ops->get_fd) + return dev->ctx->ops->get_fd(dev); + else + return -ENOSYS; +} + +int iio_device_set_blocking_mode(const struct iio_device *dev, bool blocking) +{ + if (dev->ctx->ops->set_blocking_mode) + return dev->ctx->ops->set_blocking_mode(dev, blocking); + else + return -ENOSYS; +} + +ssize_t iio_device_read_raw(const struct iio_device *dev, + void *dst, size_t len, uint32_t *mask, size_t words) +{ + if (dev->ctx->ops->read) + return dev->ctx->ops->read(dev, dst, len, mask, words); + else + return -ENOSYS; +} + +ssize_t iio_device_write_raw(const struct iio_device *dev, + const void *src, size_t len) +{ + if (dev->ctx->ops->write) + return dev->ctx->ops->write(dev, src, len); + else + return -ENOSYS; +} + +ssize_t iio_device_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len) +{ + if (dev->ctx->ops->read_device_attr) + return dev->ctx->ops->read_device_attr(dev, + attr, dst, len, IIO_ATTR_TYPE_DEVICE); + else + return -ENOSYS; +} + +ssize_t iio_device_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len) +{ + if (dev->ctx->ops->write_device_attr) + return dev->ctx->ops->write_device_attr(dev, + attr, src, len, IIO_ATTR_TYPE_DEVICE); + else + return -ENOSYS; +} + +ssize_t iio_device_attr_write(const struct iio_device *dev, + const char *attr, const char *src) +{ + return iio_device_attr_write_raw(dev, attr, src, strlen(src) + 1); +} + +ssize_t iio_device_buffer_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len) +{ + if (dev->ctx->ops->read_device_attr) + return dev->ctx->ops->read_device_attr(dev, + attr, dst, len, IIO_ATTR_TYPE_BUFFER); + else + return -ENOSYS; +} + +ssize_t iio_device_buffer_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len) +{ + if (dev->ctx->ops->write_device_attr) + return dev->ctx->ops->write_device_attr(dev, + attr, src, len, IIO_ATTR_TYPE_BUFFER); + else + return -ENOSYS; +} + +ssize_t iio_device_buffer_attr_write(const struct iio_device *dev, + const char *attr, const char *src) +{ + return iio_device_buffer_attr_write_raw(dev, attr, src, strlen(src) + 1); +} + +void iio_device_set_data(struct iio_device *dev, void *data) +{ + dev->userdata = data; +} + +void * iio_device_get_data(const struct iio_device *dev) +{ + return dev->userdata; +} + +bool iio_device_is_trigger(const struct iio_device *dev) +{ + /* A trigger has a name, an id which starts by "trigger", + * and zero channels. */ + + unsigned int nb = iio_device_get_channels_count(dev); + const char *name = iio_device_get_name(dev), + *id = iio_device_get_id(dev); + return ((nb == 0) && !!name && + !strncmp(id, "trigger", sizeof("trigger") - 1)); +} + +int iio_device_set_kernel_buffers_count(const struct iio_device *dev, + unsigned int nb_buffers) +{ + if (nb_buffers == 0) + return -EINVAL; + else if (dev->ctx->ops->set_kernel_buffers_count) + return dev->ctx->ops->set_kernel_buffers_count(dev, nb_buffers); + else + return -ENOSYS; +} + +int iio_device_get_trigger(const struct iio_device *dev, + const struct iio_device **trigger) +{ + if (!trigger) + return -EINVAL; + else if (dev->ctx->ops->get_trigger) + return dev->ctx->ops->get_trigger(dev, trigger); + else + return -ENOSYS; +} + +int iio_device_set_trigger(const struct iio_device *dev, + const struct iio_device *trigger) +{ + if (trigger && !iio_device_is_trigger(trigger)) + return -EINVAL; + else if (dev->ctx->ops->set_trigger) + return dev->ctx->ops->set_trigger(dev, trigger); + else + return -ENOSYS; +} + +void free_device(struct iio_device *dev) +{ + unsigned int i; + for (i = 0; i < dev->nb_attrs; i++) + free(dev->attrs[i]); + if (dev->nb_attrs) + free(dev->attrs); + for (i = 0; i < dev->nb_buffer_attrs; i++) + free(dev->buffer_attrs[i]); + if (dev->nb_buffer_attrs) + free(dev->buffer_attrs); + for (i = 0; i < dev->nb_debug_attrs; i++) + free(dev->debug_attrs[i]); + if (dev->nb_debug_attrs) + free(dev->debug_attrs); + for (i = 0; i < dev->nb_channels; i++) + free_channel(dev->channels[i]); + if (dev->nb_channels) + free(dev->channels); + if (dev->mask) + free(dev->mask); + if (dev->name) + free(dev->name); + if (dev->id) + free(dev->id); + free(dev); +} + +ssize_t iio_device_get_sample_size_mask(const struct iio_device *dev, + const uint32_t *mask, size_t words) +{ + ssize_t size = 0; + unsigned int i; + const struct iio_channel *prev = NULL; + + if (words != (dev->nb_channels + 31) / 32) + return -EINVAL; + + for (i = 0; i < dev->nb_channels; i++) { + const struct iio_channel *chn = dev->channels[i]; + unsigned int length = chn->format.length / 8 * + chn->format.repeat; + + if (chn->index < 0) + break; + if (!TEST_BIT(mask, chn->number)) + continue; + + if (prev && chn->index == prev->index) { + prev = chn; + continue; + } + + if (size % length) + size += 2 * length - (size % length); + else + size += length; + + prev = chn; + } + return size; +} + +ssize_t iio_device_get_sample_size(const struct iio_device *dev) +{ + return iio_device_get_sample_size_mask(dev, dev->mask, dev->words); +} + +int iio_device_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val) +{ + char *end, buf[1024]; + long long value; + ssize_t ret = iio_device_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + + value = strtoll(buf, &end, 0); + if (end == buf) + return -EINVAL; + *val = value; + return 0; +} + +int iio_device_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val) +{ + long long value; + int ret = iio_device_attr_read_longlong(dev, attr, &value); + if (ret < 0) + return ret; + + *val = !!value; + return 0; +} + +int iio_device_attr_read_double(const struct iio_device *dev, + const char *attr, double *val) +{ + char buf[1024]; + ssize_t ret = iio_device_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + else + return read_double(buf, val); +} + +int iio_device_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val) +{ + ssize_t ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "%lld", val); + ret = iio_device_attr_write(dev, attr, buf); + + return ret < 0 ? ret : 0; +} + +int iio_device_attr_write_double(const struct iio_device *dev, + const char *attr, double val) +{ + ssize_t ret; + char buf[1024]; + + ret = (ssize_t) write_double(buf, sizeof(buf), val); + if (!ret) + ret = iio_device_attr_write(dev, attr, buf); + return ret < 0 ? ret : 0; +} + +int iio_device_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val) +{ + ssize_t ret; + + if (val) + ret = iio_device_attr_write(dev, attr, "1"); + else + ret = iio_device_attr_write(dev, attr, "0"); + + return ret < 0 ? ret : 0; +} + +int iio_device_buffer_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val) +{ + char *end, buf[1024]; + long long value; + ssize_t ret = iio_device_buffer_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + + value = strtoll(buf, &end, 0); + if (end == buf) + return -EINVAL; + *val = value; + return 0; +} + +int iio_device_buffer_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val) +{ + long long value; + int ret = iio_device_buffer_attr_read_longlong(dev, attr, &value); + if (ret < 0) + return ret; + + *val = !!value; + return 0; +} + +int iio_device_buffer_attr_read_double(const struct iio_device *dev, + const char *attr, double *val) +{ + char buf[1024]; + ssize_t ret = iio_device_buffer_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + else + return read_double(buf, val); +} + +int iio_device_buffer_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val) +{ + ssize_t ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "%lld", val); + ret = iio_device_buffer_attr_write(dev, attr, buf); + + return ret < 0 ? ret : 0; +} + +int iio_device_buffer_attr_write_double(const struct iio_device *dev, + const char *attr, double val) +{ + ssize_t ret; + char buf[1024]; + + ret = (ssize_t) write_double(buf, sizeof(buf), val); + if (!ret) + ret = iio_device_buffer_attr_write(dev, attr, buf); + return ret < 0 ? ret : 0; +} + +int iio_device_buffer_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val) +{ + ssize_t ret; + + if (val) + ret = iio_device_buffer_attr_write(dev, attr, "1"); + else + ret = iio_device_buffer_attr_write(dev, attr, "0"); + + return ret < 0 ? ret : 0; +} + +ssize_t iio_device_debug_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len) +{ + if (dev->ctx->ops->read_device_attr) + return dev->ctx->ops->read_device_attr(dev, + attr, dst, len, IIO_ATTR_TYPE_DEBUG); + else + return -ENOSYS; +} + +ssize_t iio_device_debug_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len) +{ + if (dev->ctx->ops->write_device_attr) + return dev->ctx->ops->write_device_attr(dev, + attr, src, len, IIO_ATTR_TYPE_DEBUG); + else + return -ENOSYS; +} + +ssize_t iio_device_debug_attr_write(const struct iio_device *dev, + const char *attr, const char *src) +{ + return iio_device_debug_attr_write_raw(dev, attr, src, strlen(src) + 1); +} + +unsigned int iio_device_get_debug_attrs_count(const struct iio_device *dev) +{ + return dev->nb_debug_attrs; +} + +const char * iio_device_get_debug_attr(const struct iio_device *dev, + unsigned int index) +{ + if (index >= dev->nb_debug_attrs) + return NULL; + else + return dev->debug_attrs[index]; +} + +int iio_device_debug_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val) +{ + char *end, buf[1024]; + long long value; + ssize_t ret = iio_device_debug_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + + value = strtoll(buf, &end, 0); + if (end == buf) + return -EINVAL; + *val = value; + return 0; +} + +int iio_device_debug_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val) +{ + long long value; + int ret = iio_device_debug_attr_read_longlong(dev, attr, &value); + if (ret < 0) + return ret; + + *val = !!value; + return 0; +} + +int iio_device_debug_attr_read_double(const struct iio_device *dev, + const char *attr, double *val) +{ + char buf[1024]; + ssize_t ret = iio_device_debug_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + else + return read_double(buf, val); +} + +int iio_device_debug_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val) +{ + ssize_t ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "%lld", val); + ret = iio_device_debug_attr_write(dev, attr, buf); + + return ret < 0 ? ret : 0; +} + +int iio_device_debug_attr_write_double(const struct iio_device *dev, + const char *attr, double val) +{ + ssize_t ret; + char buf[1024]; + + ret = (ssize_t) write_double(buf, sizeof(buf), val); + if (!ret) + ret = iio_device_debug_attr_write(dev, attr, buf); + return ret < 0 ? ret : 0; +} + +int iio_device_debug_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val) +{ + ssize_t ret; + + if (val) + ret = iio_device_debug_attr_write_raw(dev, attr, "1", 2); + else + ret = iio_device_debug_attr_write_raw(dev, attr, "0", 2); + + return ret < 0 ? ret : 0; +} + +int iio_device_identify_filename(const struct iio_device *dev, + const char *filename, struct iio_channel **chn, + const char **attr) +{ + unsigned int i; + + for (i = 0; i < dev->nb_channels; i++) { + struct iio_channel *ch = dev->channels[i]; + unsigned int j; + + for (j = 0; j < ch->nb_attrs; j++) { + if (!strcmp(ch->attrs[j].filename, filename)) { + *attr = ch->attrs[j].name; + *chn = ch; + return 0; + } + } + } + + for (i = 0; i < dev->nb_attrs; i++) { + /* Devices attributes are named after their filename */ + if (!strcmp(dev->attrs[i], filename)) { + *attr = dev->attrs[i]; + *chn = NULL; + return 0; + } + } + + for (i = 0; i < dev->nb_debug_attrs; i++) { + if (!strcmp(dev->debug_attrs[i], filename)) { + *attr = dev->debug_attrs[i]; + *chn = NULL; + return 0; + } + } + + return -EINVAL; +} + +int iio_device_reg_write(struct iio_device *dev, + uint32_t address, uint32_t value) +{ + ssize_t ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "0x%" PRIx32 " 0x%" PRIx32, + address, value); + ret = iio_device_debug_attr_write(dev, "direct_reg_access", buf); + + return ret < 0 ? ret : 0; +} + +int iio_device_reg_read(struct iio_device *dev, + uint32_t address, uint32_t *value) +{ + /* NOTE: There is a race condition here. But it is extremely unlikely to + * happen, and as this is a debug function, it shouldn't be used for + * something else than debug. */ + + long long val; + int ret = iio_device_debug_attr_write_longlong(dev, + "direct_reg_access", (long long) address); + if (ret < 0) + return ret; + + ret = iio_device_debug_attr_read_longlong(dev, + "direct_reg_access", &val); + if (!ret) + *value = (uint32_t) val; + return ret; +} + +static int read_each_attr(struct iio_device *dev, enum iio_attr_type type, + int (*cb)(struct iio_device *dev, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + int ret, buf_size; + char *buf, *ptr; + unsigned int i, count; + + /* We need a big buffer here; 1 MiB should be enough */ + buf = malloc(0x100000); + if (!buf) + return -ENOMEM; + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + count = iio_device_get_attrs_count(dev); + ret = (int) iio_device_attr_read(dev, + NULL, buf, 0x100000); + break; + case IIO_ATTR_TYPE_DEBUG: + count = iio_device_get_debug_attrs_count(dev); + ret = (int) iio_device_debug_attr_read(dev, + NULL, buf, 0x100000); + break; + case IIO_ATTR_TYPE_BUFFER: + count = iio_device_get_buffer_attrs_count(dev); + ret = (int) iio_device_buffer_attr_read(dev, + NULL, buf, 0x100000); + break; + } + + if (ret < 0) + goto err_free_buf; + + ptr = buf; + buf_size = ret; + + for (i = 0; i < count; i++) { + const char *attr; + int32_t len; + + if (buf_size < 4) { + ret = -EPROTO; + break; + } + + len = (int32_t) iio_be32toh(*(uint32_t *) ptr); + ptr += 4; + buf_size -= 4; + + if (len > 0 && buf_size < len) { + ret = -EPROTO; + break; + } + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + attr = iio_device_get_attr(dev, i); + break; + case IIO_ATTR_TYPE_DEBUG: + attr = iio_device_get_debug_attr(dev, i); + break; + case IIO_ATTR_TYPE_BUFFER: + attr = iio_device_get_buffer_attr(dev, i); + break; + default: + attr = NULL; + break; + } + + if (len > 0) { + ret = cb(dev, attr, ptr, (size_t) len, data); + if (ret < 0) + goto err_free_buf; + + if (len & 0x3) + len = ((len >> 2) + 1) << 2; + ptr += len; + if (len >= buf_size) + buf_size = 0; + else + buf_size -= len; + } + } + +err_free_buf: + free(buf); + return ret < 0 ? ret : 0; +} + +static int write_each_attr(struct iio_device *dev, enum iio_attr_type type, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + char *buf, *ptr; + unsigned int i, count; + size_t len = 0x100000; + int ret; + + /* We need a big buffer here; 1 MiB should be enough */ + buf = malloc(len); + if (!buf) + return -ENOMEM; + + ptr = buf; + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + count = iio_device_get_attrs_count(dev); + break; + case IIO_ATTR_TYPE_DEBUG: + count = iio_device_get_debug_attrs_count(dev); + break; + case IIO_ATTR_TYPE_BUFFER: + count = iio_device_get_buffer_attrs_count(dev); + break; + } + + for (i = 0; i < count; i++) { + const char *attr; + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + attr = iio_device_get_attr(dev, i); + break; + case IIO_ATTR_TYPE_DEBUG: + attr = iio_device_get_debug_attr(dev, i); + break; + case IIO_ATTR_TYPE_BUFFER: + attr = iio_device_get_buffer_attr(dev, i); + break; + } + + ret = (int) cb(dev, attr, ptr + 4, len - 4, data); + if (ret < 0) + goto err_free_buf; + + *(int32_t *) ptr = (int32_t) iio_htobe32((uint32_t) ret); + ptr += 4; + len -= 4; + + if (ret > 0) { + if (ret & 0x3) + ret = ((ret >> 2) + 1) << 2; + ptr += ret; + len -= ret; + } + } + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + ret = (int) iio_device_attr_write_raw(dev, + NULL, buf, ptr - buf); + break; + case IIO_ATTR_TYPE_DEBUG: + ret = (int) iio_device_debug_attr_write_raw(dev, + NULL, buf, ptr - buf); + break; + case IIO_ATTR_TYPE_BUFFER: + ret = (int) iio_device_buffer_attr_write_raw(dev, + NULL, buf, ptr - buf); + break; + } + +err_free_buf: + free(buf); + return ret < 0 ? ret : 0; +} + +int iio_device_debug_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + return read_each_attr(dev, IIO_ATTR_TYPE_DEBUG, cb, data); +} + +int iio_device_buffer_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + return read_each_attr(dev, IIO_ATTR_TYPE_BUFFER, cb, data); +} + +int iio_device_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + return read_each_attr(dev, IIO_ATTR_TYPE_DEVICE ,cb, data); +} + +int iio_device_debug_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + return write_each_attr(dev, IIO_ATTR_TYPE_DEBUG, cb, data); +} + +int iio_device_buffer_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + return write_each_attr(dev, IIO_ATTR_TYPE_BUFFER, cb, data); +} + +int iio_device_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + return write_each_attr(dev, IIO_ATTR_TYPE_DEVICE, cb, data); +} + +const struct iio_context * iio_device_get_context(const struct iio_device *dev) +{ + return dev->ctx; +} diff --git a/sensors/aidl/libiio_client/iio-config.h b/sensors/aidl/libiio_client/iio-config.h new file mode 100644 index 0000000..1be8e9d --- /dev/null +++ b/sensors/aidl/libiio_client/iio-config.h @@ -0,0 +1,39 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#ifndef IIO_CONFIG_H +#define IIO_CONFIG_H + +#define LIBIIO_VERSION_MAJOR 0 +#define LIBIIO_VERSION_MINOR 18 +#define LIBIIO_VERSION_GIT "c0012d0" + +#define LOG_LEVEL Info_L + +#define WITH_XML_BACKEND +#define WITH_NETWORK_BACKEND + +/* #undef WITH_NETWORK_GET_BUFFER */ +#define WITH_NETWORK_EVENTFD +#define HAS_PIPE2 +#define HAS_STRDUP +#define HAS_STRERROR_R +#define HAS_NEWLOCALE +#define HAS_PTHREAD_SETNAME_NP + +#endif /* IIO_CONFIG_H */ diff --git a/sensors/aidl/libiio_client/iio-lock.h b/sensors/aidl/libiio_client/iio-lock.h new file mode 100644 index 0000000..612a925 --- /dev/null +++ b/sensors/aidl/libiio_client/iio-lock.h @@ -0,0 +1,30 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2015 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifndef _IIO_LOCK_H +#define _IIO_LOCK_H + +struct iio_mutex; + +struct iio_mutex * iio_mutex_create(void); +void iio_mutex_destroy(struct iio_mutex *lock); + +void iio_mutex_lock(struct iio_mutex *lock); +void iio_mutex_unlock(struct iio_mutex *lock); + +#endif /* _IIO_LOCK_H */ diff --git a/sensors/aidl/libiio_client/iio-private.h b/sensors/aidl/libiio_client/iio-private.h new file mode 100644 index 0000000..4062d02 --- /dev/null +++ b/sensors/aidl/libiio_client/iio-private.h @@ -0,0 +1,297 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#ifndef __IIO_PRIVATE_H__ +#define __IIO_PRIVATE_H__ + +/* Include public interface */ +#include "iio.h" + +#include "iio-config.h" + +#include + +#ifdef _MSC_BUILD +#define inline __inline +#define iio_snprintf sprintf_s +#else +#define iio_snprintf snprintf +#endif + +#ifdef _WIN32 +# ifdef LIBIIO_EXPORTS +# define __api __declspec(dllexport) +# else +# define __api __declspec(dllimport) +# endif +#elif __GNUC__ >= 4 +# define __api __attribute__((visibility ("default"))) +#else +# define __api +#endif + +#ifdef WITH_MATLAB_BINDINGS_API +#include "bindings/matlab/iio-wrapper.h" +#endif + +#define ARRAY_SIZE(x) (sizeof(x) ? sizeof(x) / sizeof((x)[0]) : 0) +#define BIT(x) (1 << (x)) +#define BIT_MASK(bit) BIT((bit) % 32) +#define BIT_WORD(bit) ((bit) / 32) +#define TEST_BIT(addr, bit) (!!(*(((uint32_t *) addr) + BIT_WORD(bit)) \ + & BIT_MASK(bit))) +#define SET_BIT(addr, bit) \ + *(((uint32_t *) addr) + BIT_WORD(bit)) |= BIT_MASK(bit) +#define CLEAR_BIT(addr, bit) \ + *(((uint32_t *) addr) + BIT_WORD(bit)) &= ~BIT_MASK(bit) + + +/* ntohl/htonl are a nightmare to use in cross-platform applications, + * since they are defined in different headers on different platforms. + * iio_be32toh/iio_htobe32 are just clones of ntohl/htonl. */ +static inline uint32_t iio_be32toh(uint32_t word) +{ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#ifdef __GNUC__ + return __builtin_bswap32(word); +#else + return ((word & 0xff) << 24) | ((word & 0xff00) << 8) | + ((word >> 8) & 0xff00) | ((word >> 24) & 0xff); +#endif +#else + return word; +#endif +} + +static inline uint32_t iio_htobe32(uint32_t word) +{ + return iio_be32toh(word); +} + +/* Allocate zeroed out memory */ +static inline void *zalloc(size_t size) +{ + return calloc(1, size); +} + +enum iio_attr_type { + IIO_ATTR_TYPE_DEVICE = 0, + IIO_ATTR_TYPE_DEBUG, + IIO_ATTR_TYPE_BUFFER, +}; + +struct iio_backend_ops { + struct iio_context * (*clone)(const struct iio_context *ctx); + ssize_t (*read)(const struct iio_device *dev, void *dst, size_t len, + uint32_t *mask, size_t words); + ssize_t (*write)(const struct iio_device *dev, + const void *src, size_t len); + int (*open)(const struct iio_device *dev, + size_t samples_count, bool cyclic); + int (*close)(const struct iio_device *dev); + int (*get_fd)(const struct iio_device *dev); + int (*set_blocking_mode)(const struct iio_device *dev, bool blocking); + + void (*cancel)(const struct iio_device *dev); + + int (*set_kernel_buffers_count)(const struct iio_device *dev, + unsigned int nb_blocks); + ssize_t (*get_buffer)(const struct iio_device *dev, + void **addr_ptr, size_t bytes_used, + uint32_t *mask, size_t words); + + ssize_t (*read_device_attr)(const struct iio_device *dev, + const char *attr, char *dst, size_t len, enum iio_attr_type); + ssize_t (*write_device_attr)(const struct iio_device *dev, + const char *attr, const char *src, + size_t len, enum iio_attr_type); + ssize_t (*read_channel_attr)(const struct iio_channel *chn, + const char *attr, char *dst, size_t len); + ssize_t (*write_channel_attr)(const struct iio_channel *chn, + const char *attr, const char *src, size_t len); + + int (*get_trigger)(const struct iio_device *dev, + const struct iio_device **trigger); + int (*set_trigger)(const struct iio_device *dev, + const struct iio_device *trigger); + + void (*shutdown)(struct iio_context *ctx); + + int (*request_client_id)(const struct iio_context *ctx); + int (*register_client_id)(const struct iio_device *dev); + int (*get_version)(const struct iio_context *ctx, unsigned int *major, + unsigned int *minor, char git_tag[8]); + + int (*set_timeout)(struct iio_context *ctx, unsigned int timeout); +}; + +/* + * If these structures are updated, the qsort functions defined in sort.c + * may need to be updated. + */ + +struct iio_context_pdata; +struct iio_device_pdata; +struct iio_channel_pdata; +struct iio_scan_backend_context; + +struct iio_channel_attr { + char *name; + char *filename; +}; + +struct iio_context { + struct iio_context_pdata *pdata; + const struct iio_backend_ops *ops; + const char *name; + char *description; + + struct iio_device **devices; + unsigned int nb_devices; + + char *xml; + + char **attrs; + char **values; + unsigned int nb_attrs; + int client_id; +}; + +struct iio_channel { + struct iio_device *dev; + struct iio_channel_pdata *pdata; + void *userdata; + + bool is_output; + bool is_scan_element; + struct iio_data_format format; + char *name, *id; + long index; + enum iio_modifier modifier; + enum iio_chan_type type; + + struct iio_channel_attr *attrs; + unsigned int nb_attrs; + + unsigned int number; +}; + +struct iio_device { + const struct iio_context *ctx; + struct iio_device_pdata *pdata; + void *userdata; + + char *name, *id; + + char **attrs; + unsigned int nb_attrs; + + char **buffer_attrs; + unsigned int nb_buffer_attrs; + + char **debug_attrs; + unsigned int nb_debug_attrs; + + struct iio_channel **channels; + unsigned int nb_channels; + + uint32_t *mask; + size_t words; +}; + +struct iio_buffer { + const struct iio_device *dev; + void *buffer, *userdata; + size_t length, data_length; + + uint32_t *mask; + unsigned int dev_sample_size; + unsigned int sample_size; + bool is_output, dev_is_high_speed; +}; + +struct iio_context_info { + char *description; + char *uri; +}; + +struct iio_scan_result { + size_t size; + struct iio_context_info **info; +}; + +struct iio_context_info ** iio_scan_result_add( + struct iio_scan_result *scan_result, size_t num); + +void free_channel(struct iio_channel *chn); +void free_device(struct iio_device *dev); + +char *iio_channel_get_xml(const struct iio_channel *chn, size_t *len); +char *iio_device_get_xml(const struct iio_device *dev, size_t *len); + +char *iio_context_create_xml(const struct iio_context *ctx); +int iio_context_init(struct iio_context *ctx); + +bool iio_device_is_tx(const struct iio_device *dev); +int iio_device_open(const struct iio_device *dev, + size_t samples_count, bool cyclic); +int iio_device_close(const struct iio_device *dev); +int iio_device_set_blocking_mode(const struct iio_device *dev, bool blocking); +ssize_t iio_device_read_raw(const struct iio_device *dev, + void *dst, size_t len, uint32_t *mask, size_t words); +ssize_t iio_device_write_raw(const struct iio_device *dev, + const void *src, size_t len); +int iio_device_get_poll_fd(const struct iio_device *dev); + +int read_double(const char *str, double *val); +int write_double(char *buf, size_t len, double val); + +struct iio_context * local_create_context(void); +struct iio_context * network_create_context(const char *hostname); +struct iio_context * vm_create_context(unsigned int port); +struct iio_context * xml_create_context_mem(const char *xml, size_t len); +struct iio_context * xml_create_context(const char *xml_file); +struct iio_context * usb_create_context(unsigned int bus, unsigned int address, + unsigned int interface); +struct iio_context * usb_create_context_from_uri(const char *uri); +struct iio_context * serial_create_context_from_uri(const char *uri); + +int local_context_scan(struct iio_scan_result *scan_result); + +struct iio_scan_backend_context * usb_context_scan_init(void); +void usb_context_scan_free(struct iio_scan_backend_context *ctx); + +int usb_context_scan(struct iio_scan_backend_context *ctx, + struct iio_scan_result *scan_result); + +/* This function is not part of the API, but is used by the IIO daemon */ +__api ssize_t iio_device_get_sample_size_mask(const struct iio_device *dev, + const uint32_t *mask, size_t words); + +void iio_channel_init_finalize(struct iio_channel *chn); +unsigned int find_channel_modifier(const char *s, size_t *len_p); + +char *iio_strdup(const char *str); + +int iio_context_add_attr(struct iio_context *ctx, + const char *key, const char *value); + +int get_reserve_fd(); +#undef __api + +#endif /* __IIO_PRIVATE_H__ */ diff --git a/sensors/aidl/libiio_client/iio.h b/sensors/aidl/libiio_client/iio.h new file mode 100644 index 0000000..ed39e15 --- /dev/null +++ b/sensors/aidl/libiio_client/iio.h @@ -0,0 +1,1785 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +/** @file iio.h + * @brief Public interface */ + +#ifndef __IIO_H__ +#define __IIO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#if (defined(_WIN32) || defined(__MBED__)) +#ifndef _SSIZE_T_DEFINED +typedef ptrdiff_t ssize_t; +#define _SSIZE_T_DEFINED +#endif +#else +#include +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1800) && !defined(__BOOL_DEFINED) +#undef bool +#undef false +#undef true +#define bool char +#define false 0 +#define true 1 +#else +#include +#endif + +#if defined(__GNUC__) && !defined(MATLAB_MEX_FILE) && !defined(MATLAB_LOADLIBRARY) +#ifndef __cnst +#define __cnst __attribute__((const)) +#endif +#ifndef __pure +#define __pure __attribute__((pure)) +#endif +#define __notused __attribute__((unused)) +#else +#define __cnst +#define __pure +#define __notused +#endif + +#ifdef _WIN32 +# ifdef LIBIIO_EXPORTS +# define __api __declspec(dllexport) +# else +# define __api __declspec(dllimport) +# endif +#elif __GNUC__ >= 4 && !defined(MATLAB_MEX_FILE) && !defined(MATLAB_LOADLIBRARY) +# define __api __attribute__((visibility ("default"))) +#else +# define __api +#endif + +struct iio_context; +struct iio_device; +struct iio_channel; +struct iio_buffer; + +struct iio_context_info; +struct iio_scan_context; + +/** + * @enum iio_chan_type + * @brief IIO channel type + * + * A IIO channel has a type specifying the type of data associated with the + * channel. + */ +enum iio_chan_type { + IIO_VOLTAGE, + IIO_CURRENT, + IIO_POWER, + IIO_ACCEL, + IIO_ANGL_VEL, + IIO_MAGN, + IIO_LIGHT, + IIO_INTENSITY, + IIO_PROXIMITY, + IIO_TEMP, + IIO_INCLI, + IIO_ROT, + IIO_ANGL, + IIO_TIMESTAMP, + IIO_CAPACITANCE, + IIO_ALTVOLTAGE, + IIO_CCT, + IIO_PRESSURE, + IIO_HUMIDITYRELATIVE, + IIO_ACTIVITY, + IIO_STEPS, + IIO_ENERGY, + IIO_DISTANCE, + IIO_VELOCITY, + IIO_CONCENTRATION, + IIO_RESISTANCE, + IIO_PH, + IIO_UVINDEX, + IIO_ELECTRICALCONDUCTIVITY, + IIO_COUNT, + IIO_INDEX, + IIO_GRAVITY, + IIO_CHAN_TYPE_UNKNOWN = INT_MAX +}; + +/** + * @enum iio_modifier + * @brief IIO channel modifier + * + * In a addition to a type a IIO channel can optionally have a channel modifier + * further specifying the data type of of the channel. + */ +enum iio_modifier { + IIO_NO_MOD, + IIO_MOD_X, + IIO_MOD_Y, + IIO_MOD_Z, + IIO_MOD_X_AND_Y, + IIO_MOD_X_AND_Z, + IIO_MOD_Y_AND_Z, + IIO_MOD_X_AND_Y_AND_Z, + IIO_MOD_X_OR_Y, + IIO_MOD_X_OR_Z, + IIO_MOD_Y_OR_Z, + IIO_MOD_X_OR_Y_OR_Z, + IIO_MOD_LIGHT_BOTH, + IIO_MOD_LIGHT_IR, + IIO_MOD_ROOT_SUM_SQUARED_X_Y, + IIO_MOD_SUM_SQUARED_X_Y_Z, + IIO_MOD_LIGHT_CLEAR, + IIO_MOD_LIGHT_RED, + IIO_MOD_LIGHT_GREEN, + IIO_MOD_LIGHT_BLUE, + IIO_MOD_QUATERNION, + IIO_MOD_TEMP_AMBIENT, + IIO_MOD_TEMP_OBJECT, + IIO_MOD_NORTH_MAGN, + IIO_MOD_NORTH_TRUE, + IIO_MOD_NORTH_MAGN_TILT_COMP, + IIO_MOD_NORTH_TRUE_TILT_COMP, + IIO_MOD_RUNNING, + IIO_MOD_JOGGING, + IIO_MOD_WALKING, + IIO_MOD_STILL, + IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z, + IIO_MOD_I, + IIO_MOD_Q, + IIO_MOD_CO2, + IIO_MOD_VOC, + IIO_MOD_LIGHT_UV, +}; + +/* ---------------------------------------------------------------------------*/ +/* ------------------------- Scan functions ----------------------------------*/ +/** @defgroup Scan Functions for scanning available contexts + * @{ + * @struct iio_scan_context + * @brief The scanning context + * + * @struct iio_context_info + * @brief The information related to a discovered context + */ + + +/** @brief Create a scan context + * @param backend A NULL-terminated string containing the backend to use for + * scanning. If NULL, all the available backends are used. + * @param flags Unused for now. Set to 0. + * @return on success, a pointer to a iio_scan_context structure + * @return On failure, NULL is returned and errno is set appropriately */ +__api struct iio_scan_context * iio_create_scan_context( + const char *backend, unsigned int flags); + + +/** @brief Destroy the given scan context + * @param ctx A pointer to an iio_scan_context structure + * + * NOTE: After that function, the iio_scan_context pointer shall be invalid. */ +__api void iio_scan_context_destroy(struct iio_scan_context *ctx); + + +/** @brief Enumerate available contexts + * @param ctx A pointer to an iio_scan_context structure + * @param info A pointer to a 'const struct iio_context_info **' typed variable. + * The pointed variable will be initialized on success. + * @returns On success, the number of contexts found. + * @returns On failure, a negative error number. + */ +__api ssize_t iio_scan_context_get_info_list(struct iio_scan_context *ctx, + struct iio_context_info ***info); + + +/** @brief Free a context info list + * @param info A pointer to a 'const struct iio_context_info *' typed variable + */ +__api void iio_context_info_list_free(struct iio_context_info **info); + + +/** @brief Get a description of a discovered context + * @param info A pointer to an iio_context_info structure + * @return A pointer to a static NULL-terminated string + */ +__api __pure const char * iio_context_info_get_description( + const struct iio_context_info *info); + + +/** @brief Get the URI of a discovered context + * @param info A pointer to an iio_context_info structure + * @return A pointer to a static NULL-terminated string + */ +__api __pure const char * iio_context_info_get_uri( + const struct iio_context_info *info); + + +/** @} *//* ------------------------------------------------------------------*/ +/* ------------------------- Top-level functions -----------------------------*/ +/** @defgroup TopLevel Top-level functions + * @{ */ + + +/** @brief Get the version of the libiio library + * @param major A pointer to an unsigned integer (NULL accepted) + * @param minor A pointer to an unsigned integer (NULL accepted) + * @param git_tag A pointer to a 8-characters buffer (NULL accepted) */ +__api void iio_library_get_version(unsigned int *major, + unsigned int *minor, char git_tag[8]); + + +/** @brief Get a string description of an error code + * @param err The error code + * @param dst A pointer to the memory area where the NULL-terminated string + * corresponding to the error message will be stored + * @param len The available length of the memory area, in bytes */ +__api void iio_strerror(int err, char *dst, size_t len); + + +/** @brief Check if the specified backend is available + * @param backend The name of the backend to query + * @return True if the backend is available, false otherwise + * + * Introduced in version 0.9. */ +__api __cnst bool iio_has_backend(const char *backend); + + +/** @brief Get the number of available backends + * @return The number of available backends + * + * Introduced in version 0.9. */ +__api __cnst unsigned int iio_get_backends_count(void); + + +/** @brief Retrieve the name of a given backend + * @param index The index corresponding to the attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the index is invalid, NULL is returned + * + * Introduced in version 0.9. */ +__api __cnst const char * iio_get_backend(unsigned int index); + + +/** @} *//* ------------------------------------------------------------------*/ +/* ------------------------- Context functions -------------------------------*/ +/** @defgroup Context Context + * @{ + * @struct iio_context + * @brief Contains the representation of an IIO context */ + + +/** @brief Create a context from local or remote IIO devices + * @return On success, A pointer to an iio_context structure + * @return On failure, NULL is returned and errno is set appropriately + * + * NOTE: This function will create a network context if the IIOD_REMOTE + * environment variable is set to the hostname where the IIOD server runs. If + * set to an empty string, the server will be discovered using ZeroConf. + * If the environment variable is not set, a local context will be created + * instead. */ +__api struct iio_context * iio_create_default_context(void); + + +/** @brief Create a context from local IIO devices (Linux only) + * @return On success, A pointer to an iio_context structure + * @return On failure, NULL is returned and errno is set appropriately */ +__api struct iio_context * iio_create_local_context(void); + + +/** @brief Create a context from a XML file + * @param xml_file Path to the XML file to open + * @return On success, A pointer to an iio_context structure + * @return On failure, NULL is returned and errno is set appropriately + * + * NOTE: The format of the XML must comply to the one returned by + * iio_context_get_xml. */ +__api struct iio_context * iio_create_xml_context(const char *xml_file); + + +/** @brief Create a context from XML data in memory + * @param xml Pointer to the XML data in memory + * @param len Length of the XML string in memory (excluding the final \0) + * @return On success, A pointer to an iio_context structure + * @return On failure, NULL is returned and errno is set appropriately + * + * NOTE: The format of the XML must comply to the one returned by + * iio_context_get_xml */ +__api struct iio_context * iio_create_xml_context_mem( + const char *xml, size_t len); + + +/** @brief Create a context from the network + * @param host Hostname, IPv4 or IPv6 address where the IIO Daemon is running + * @return On success, a pointer to an iio_context structure + * @return On failure, NULL is returned and errno is set appropriately */ +__api struct iio_context * iio_create_network_context(const char *host); + +/** @brief Create a context from a VSOCK + * @param port The port number to a listening VSOCK + * @return On success, a pointer to an iio_context structure + * @return On failure, NULL is returned and errno is set appropriately */ +__api struct iio_context * iio_create_vm_context(const char *port); + +/*function declaration for reserve socket file descriptor*/ +int get_reserve_fd_context(); + +/** @brief Create a context from a URI description + * @param uri A URI describing the context location + * @return On success, a pointer to a iio_context structure + * @return On failure, NULL is returned and errno is set appropriately */ +__api struct iio_context * iio_create_context_from_uri(const char *uri); + + +/** @brief Duplicate a pre-existing IIO context + * @param ctx A pointer to an iio_context structure + * @return On success, A pointer to an iio_context structure + * @return On failure, NULL is returned and errno is set appropriately */ +__api struct iio_context * iio_context_clone(const struct iio_context *ctx); + + +/** @brief Destroy the given context + * @param ctx A pointer to an iio_context structure + * + * NOTE: After that function, the iio_context pointer shall be invalid. */ +__api void iio_context_destroy(struct iio_context *ctx); + + +/** @brief Get the version of the backend in use + * @param ctx A pointer to an iio_context structure + * @param major A pointer to an unsigned integer (NULL accepted) + * @param minor A pointer to an unsigned integer (NULL accepted) + * @param git_tag A pointer to a 8-characters buffer (NULL accepted) + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_context_get_version(const struct iio_context *ctx, + unsigned int *major, unsigned int *minor, char git_tag[8]); + + +/** @brief Obtain a XML representation of the given context + * @param ctx A pointer to an iio_context structure + * @return A pointer to a static NULL-terminated string */ +__api __pure const char * iio_context_get_xml(const struct iio_context *ctx); + + +/** @brief Get the name of the given context + * @param ctx A pointer to an iio_context structure + * @return A pointer to a static NULL-terminated string + * + * NOTE:The returned string will be local, + * xml or network when the context has been + * created with the local, xml and network backends respectively.*/ +__api __pure const char * iio_context_get_name(const struct iio_context *ctx); + + +/** @brief Get a description of the given context + * @param ctx A pointer to an iio_context structure + * @return A pointer to a static NULL-terminated string + * + * NOTE:The returned string will contain human-readable information about + * the current context. */ +__api __pure const char * iio_context_get_description( + const struct iio_context *ctx); + + +/** @brief Get the number of context-specific attributes + * @param ctx A pointer to an iio_context structure + * @return The number of context-specific attributes + * + * Introduced in version 0.9. */ +__api __pure unsigned int iio_context_get_attrs_count( + const struct iio_context *ctx); + + +/** @brief Retrieve the name and value of a context-specific attribute + * @param ctx A pointer to an iio_context structure + * @param index The index corresponding to the attribute + * @param name A pointer to a const char * pointer (NULL accepted) + * @param value A pointer to a const char * pointer (NULL accepted) + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * Introduced in version 0.9. */ +__api int iio_context_get_attr( + const struct iio_context *ctx, unsigned int index, + const char **name, const char **value); + + +/** @brief Retrieve the value of a context-specific attribute + * @param ctx A pointer to an iio_context structure + * @param name The name of the context attribute to read + * @return On success, a pointer to a static NULL-terminated string + * @return If the name does not correspond to any attribute, NULL is + * returned + * + * Introduced in version 0.9. */ +__api const char * iio_context_get_attr_value( + const struct iio_context *ctx, const char *name); + + +/** @brief Enumerate the devices found in the given context + * @param ctx A pointer to an iio_context structure + * @return The number of devices found */ +__api __pure unsigned int iio_context_get_devices_count( + const struct iio_context *ctx); + + +/** @brief Get the device present at the given index + * @param ctx A pointer to an iio_context structure + * @param index The index corresponding to the device + * @return On success, a pointer to an iio_device structure + * @return If the index is invalid, NULL is returned */ +__api __pure struct iio_device * iio_context_get_device( + const struct iio_context *ctx, unsigned int index); + + +/** @brief Try to find a device structure by its name of ID + * @param ctx A pointer to an iio_context structure + * @param name A NULL-terminated string corresponding to the name or the ID of + * the device to search for + * @return On success, a pointer to an iio_device structure + * @return If the name or ID does not correspond to any known device, NULL is + * returned */ +__api __pure struct iio_device * iio_context_find_device( + const struct iio_context *ctx, const char *name); + + +/** @brief Set a timeout for I/O operations + * @param ctx A pointer to an iio_context structure + * @param timeout_ms A positive integer representing the time in milliseconds + * after which a timeout occurs. A value of 0 is used to specify that no + * timeout should occur. + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_context_set_timeout( + struct iio_context *ctx, unsigned int timeout_ms); + + +/** @} *//* ------------------------------------------------------------------*/ +/* ------------------------- Device functions --------------------------------*/ +/** @defgroup Device Device + * @{ + * @struct iio_device + * @brief Represents a device in the IIO context */ + + +/** @brief Retrieve a pointer to the iio_context structure + * @param dev A pointer to an iio_device structure + * @return A pointer to an iio_context structure */ +__api __pure const struct iio_context * iio_device_get_context( + const struct iio_device *dev); + + +/** @brief Retrieve the device ID (e.g. iio:device0) + * @param dev A pointer to an iio_device structure + * @return A pointer to a static NULL-terminated string */ +__api __pure const char * iio_device_get_id(const struct iio_device *dev); + + +/** @brief Retrieve the device name (e.g. xadc) + * @param dev A pointer to an iio_device structure + * @return A pointer to a static NULL-terminated string + * + * NOTE: if the device has no name, NULL is returned. */ +__api __pure const char * iio_device_get_name(const struct iio_device *dev); + + +/** @brief Enumerate the channels of the given device + * @param dev A pointer to an iio_device structure + * @return The number of channels found */ +__api __pure unsigned int iio_device_get_channels_count( + const struct iio_device *dev); + + +/** @brief Enumerate the device-specific attributes of the given device + * @param dev A pointer to an iio_device structure + * @return The number of device-specific attributes found */ +__api __pure unsigned int iio_device_get_attrs_count( + const struct iio_device *dev); + +/** @brief Enumerate the buffer-specific attributes of the given device + * @param dev A pointer to an iio_device structure + * @return The number of buffer-specific attributes found */ +__api __pure unsigned int iio_device_get_buffer_attrs_count( + const struct iio_device *dev); + +/** @brief Get the channel present at the given index + * @param dev A pointer to an iio_device structure + * @param index The index corresponding to the channel + * @return On success, a pointer to an iio_channel structure + * @return If the index is invalid, NULL is returned */ +__api __pure struct iio_channel * iio_device_get_channel( + const struct iio_device *dev, unsigned int index); + + +/** @brief Get the device-specific attribute present at the given index + * @param dev A pointer to an iio_device structure + * @param index The index corresponding to the attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the index is invalid, NULL is returned */ +__api __pure const char * iio_device_get_attr( + const struct iio_device *dev, unsigned int index); + +/** @brief Get the buffer-specific attribute present at the given index + * @param dev A pointer to an iio_device structure + * @param index The index corresponding to the attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the index is invalid, NULL is returned */ +__api __pure const char * iio_device_get_buffer_attr( + const struct iio_device *dev, unsigned int index); + +/** @brief Try to find a channel structure by its name of ID + * @param dev A pointer to an iio_device structure + * @param name A NULL-terminated string corresponding to the name or the ID of + * the channel to search for + * @param output True if the searched channel is output, False otherwise + * @return On success, a pointer to an iio_channel structure + * @return If the name or ID does not correspond to any known channel of the + * given device, NULL is returned */ +__api __pure struct iio_channel * iio_device_find_channel( + const struct iio_device *dev, const char *name, bool output); + + +/** @brief Try to find a device-specific attribute by its name + * @param dev A pointer to an iio_device structure + * @param name A NULL-terminated string corresponding to the name of the + * attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the name does not correspond to any known attribute of the given + * device, NULL is returned + * + * NOTE: This function is useful to detect the presence of an attribute. + * It can also be used to retrieve the name of an attribute as a pointer to a + * static string from a dynamically allocated string. */ +__api __pure const char * iio_device_find_attr( + const struct iio_device *dev, const char *name); + +/** @brief Try to find a buffer-specific attribute by its name + * @param dev A pointer to an iio_device structure + * @param name A NULL-terminated string corresponding to the name of the + * attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the name does not correspond to any known attribute of the given + * device, NULL is returned + * + * NOTE: This function is useful to detect the presence of an attribute. + * It can also be used to retrieve the name of an attribute as a pointer to a + * static string from a dynamically allocated string. */ +__api __pure const char * iio_device_find_buffer_attr( + const struct iio_device *dev, const char *name); + +/** @brief Read the content of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param dst A pointer to the memory area where the NULL-terminated string + * corresponding to the value read will be stored + * @param len The available length of the memory area, in bytes + * @return On success, the number of bytes written to the buffer + * @return On error, a negative errno code is returned + * + * NOTE:By passing NULL as the "attr" argument to iio_device_attr_read, + * it is now possible to read all of the attributes of a device. + * + * The buffer is filled with one block of data per attribute of the device, + * by the order they appear in the iio_device structure. + * + * The first four bytes of one block correspond to a 32-bit signed value in + * network order. If negative, it corresponds to the errno code that were + * returned when reading the attribute; if positive, it corresponds to the + * length of the data read. In that case, the rest of the block contains + * the data. */ + __api ssize_t iio_device_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len); + + +/** @brief Read the content of all device-specific attributes + * @param dev A pointer to an iio_device structure + * @param cb A pointer to a callback function + * @param data A pointer that will be passed to the callback function + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * NOTE: This function is especially useful when used with the network + * backend, as all the device-specific attributes are read in one single + * command. */ +__api int iio_device_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, const char *attr, + const char *value, size_t len, void *d), + void *data); + + +/** @brief Read the content of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a bool variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val); + + +/** @brief Read the content of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a long long variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val); + + +/** @brief Read the content of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a double variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_attr_read_double(const struct iio_device *dev, + const char *attr, double *val); + + +/** @brief Set the value of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param src A NULL-terminated string to set the attribute to + * @return On success, the number of bytes written + * @return On error, a negative errno code is returned + * + * NOTE:By passing NULL as the "attr" argument to iio_device_attr_write, + * it is now possible to write all of the attributes of a device. + * + * The buffer must contain one block of data per attribute of the device, + * by the order they appear in the iio_device structure. + * + * The first four bytes of one block correspond to a 32-bit signed value in + * network order. If negative, the attribute is not written; if positive, + * it corresponds to the length of the data to write. In that case, the rest + * of the block must contain the data. */ +__api ssize_t iio_device_attr_write(const struct iio_device *dev, + const char *attr, const char *src); + + +/** @brief Set the value of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param src A pointer to the data to be written + * @param len The number of bytes that should be written + * @return On success, the number of bytes written + * @return On error, a negative errno code is returned */ +__api ssize_t iio_device_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len); + + +/** @brief Set the values of all device-specific attributes + * @param dev A pointer to an iio_device structure + * @param cb A pointer to a callback function + * @param data A pointer that will be passed to the callback function + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * NOTE: This function is especially useful when used with the network + * backend, as all the device-specific attributes are written in one single + * command. */ +__api int iio_device_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data); + + +/** @brief Set the value of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A bool value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val); + + +/** @brief Set the value of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A long long value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val); + + +/** @brief Set the value of the given device-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A double value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_attr_write_double(const struct iio_device *dev, + const char *attr, double val); + +/** @brief Read the content of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param dst A pointer to the memory area where the NULL-terminated string + * corresponding to the value read will be stored + * @param len The available length of the memory area, in bytes + * @return On success, the number of bytes written to the buffer + * @return On error, a negative errno code is returned + * + * NOTE:By passing NULL as the "attr" argument to + * iio_device_buffer_attr_read, it is now possible to read all of the attributes + * of a device. + * + * The buffer is filled with one block of data per attribute of the buffer, + * by the order they appear in the iio_device structure. + * + * The first four bytes of one block correspond to a 32-bit signed value in + * network order. If negative, it corresponds to the errno code that were + * returned when reading the attribute; if positive, it corresponds to the + * length of the data read. In that case, the rest of the block contains + * the data. */ + __api ssize_t iio_device_buffer_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len); + +/** @brief Read the content of all buffer-specific attributes + * @param dev A pointer to an iio_device structure + * @param cb A pointer to a callback function + * @param data A pointer that will be passed to the callback function + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * NOTE: This function is especially useful when used with the network + * backend, as all the buffer-specific attributes are read in one single + * command. */ +__api int iio_device_buffer_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, const char *attr, + const char *value, size_t len, void *d), + void *data); + + +/** @brief Read the content of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a bool variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_buffer_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val); + + +/** @brief Read the content of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a long long variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_buffer_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val); + + +/** @brief Read the content of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a double variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_buffer_attr_read_double(const struct iio_device *dev, + const char *attr, double *val); + + +/** @brief Set the value of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param src A NULL-terminated string to set the attribute to + * @return On success, the number of bytes written + * @return On error, a negative errno code is returned + * + * NOTE:By passing NULL as the "attr" argument to + * iio_device_buffer_attr_write, it is now possible to write all of the + * attributes of a device. + * + * The buffer must contain one block of data per attribute of the buffer, + * by the order they appear in the iio_device structure. + * + * The first four bytes of one block correspond to a 32-bit signed value in + * network order. If negative, the attribute is not written; if positive, + * it corresponds to the length of the data to write. In that case, the rest + * of the block must contain the data. */ +__api ssize_t iio_device_buffer_attr_write(const struct iio_device *dev, + const char *attr, const char *src); + + +/** @brief Set the value of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param src A pointer to the data to be written + * @param len The number of bytes that should be written + * @return On success, the number of bytes written + * @return On error, a negative errno code is returned */ +__api ssize_t iio_device_buffer_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len); + + +/** @brief Set the values of all buffer-specific attributes + * @param dev A pointer to an iio_device structure + * @param cb A pointer to a callback function + * @param data A pointer that will be passed to the callback function + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * NOTE: This function is especially useful when used with the network + * backend, as all the buffer-specific attributes are written in one single + * command. */ +__api int iio_device_buffer_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data); + + +/** @brief Set the value of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A bool value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_buffer_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val); + + +/** @brief Set the value of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A long long value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_buffer_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val); + + +/** @brief Set the value of the given buffer-specific attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A double value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_buffer_attr_write_double(const struct iio_device *dev, + const char *attr, double val); + + +/** @brief Associate a pointer to an iio_device structure + * @param dev A pointer to an iio_device structure + * @param data The pointer to be associated */ +__api void iio_device_set_data(struct iio_device *dev, void *data); + + +/** @brief Retrieve a previously associated pointer of an iio_device structure + * @param dev A pointer to an iio_device structure + * @return The pointer previously associated if present, or NULL */ +__api void * iio_device_get_data(const struct iio_device *dev); + + +/** @brief Retrieve the trigger of a given device + * @param dev A pointer to an iio_device structure + * @param trigger a pointer to a pointer of an iio_device structure. The pointed + * pointer will be set to the address of the iio_device structure corresponding + * to the associated trigger device. + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_get_trigger(const struct iio_device *dev, + const struct iio_device **trigger); + + +/** @brief Associate a trigger to a given device + * @param dev A pointer to an iio_device structure + * @param trigger a pointer to the iio_device structure corresponding to the + * trigger that should be associated. + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_set_trigger(const struct iio_device *dev, + const struct iio_device *trigger); + + +/** @brief Return True if the given device is a trigger + * @param dev A pointer to an iio_device structure + * @return True if the device is a trigger, False otherwise */ +__api __pure bool iio_device_is_trigger(const struct iio_device *dev); + +/** + * @brief Configure the number of kernel buffers for a device + * + * This function allows to change the number of buffers on kernel side. + * @param dev A pointer to an iio_device structure + * @param nb_buffers The number of buffers + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_set_kernel_buffers_count(const struct iio_device *dev, + unsigned int nb_buffers); + +/** @} *//* ------------------------------------------------------------------*/ +/* ------------------------- Channel functions -------------------------------*/ +/** @defgroup Channel Channel + * @{ + * @struct iio_channel + * @brief Represents an input or output channel of a device */ + + +/** @brief Retrieve a pointer to the iio_device structure + * @param chn A pointer to an iio_channel structure + * @return A pointer to an iio_device structure */ +__api __pure const struct iio_device * iio_channel_get_device( + const struct iio_channel *chn); + + +/** @brief Retrieve the channel ID (e.g. voltage0) + * @param chn A pointer to an iio_channel structure + * @return A pointer to a static NULL-terminated string */ +__api __pure const char * iio_channel_get_id(const struct iio_channel *chn); + + +/** @brief Retrieve the channel name (e.g. vccint) + * @param chn A pointer to an iio_channel structure + * @return A pointer to a static NULL-terminated string + * + * NOTE: if the channel has no name, NULL is returned. */ +__api __pure const char * iio_channel_get_name(const struct iio_channel *chn); + + +/** @brief Return True if the given channel is an output channel + * @param chn A pointer to an iio_channel structure + * @return True if the channel is an output channel, False otherwise */ +__api __pure bool iio_channel_is_output(const struct iio_channel *chn); + + +/** @brief Return True if the given channel is a scan element + * @param chn A pointer to an iio_channel structure + * @return True if the channel is a scan element, False otherwise + * + * NOTE: a channel that is a scan element is a channel that can + * generate samples (for an input channel) or receive samples (for an output + * channel) after being enabled. */ +__api __pure bool iio_channel_is_scan_element(const struct iio_channel *chn); + + +/** @brief Enumerate the channel-specific attributes of the given channel + * @param chn A pointer to an iio_channel structure + * @return The number of channel-specific attributes found */ +__api __pure unsigned int iio_channel_get_attrs_count( + const struct iio_channel *chn); + + +/** @brief Get the channel-specific attribute present at the given index + * @param chn A pointer to an iio_channel structure + * @param index The index corresponding to the attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the index is invalid, NULL is returned */ +__api __pure const char * iio_channel_get_attr( + const struct iio_channel *chn, unsigned int index); + + +/** @brief Try to find a channel-specific attribute by its name + * @param chn A pointer to an iio_channel structure + * @param name A NULL-terminated string corresponding to the name of the + * attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the name does not correspond to any known attribute of the given + * channel, NULL is returned + * + * NOTE: This function is useful to detect the presence of an attribute. + * It can also be used to retrieve the name of an attribute as a pointer to a + * static string from a dynamically allocated string. */ +__api __pure const char * iio_channel_find_attr( + const struct iio_channel *chn, const char *name); + + +/** @brief Retrieve the filename of an attribute + * @param chn A pointer to an iio_channel structure + * @param attr a NULL-terminated string corresponding to the name of the + * attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the attribute name is unknown, NULL is returned */ +__api __pure const char * iio_channel_attr_get_filename( + const struct iio_channel *chn, const char *attr); + + +/** @brief Read the content of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param dst A pointer to the memory area where the NULL-terminated string + * corresponding to the value read will be stored + * @param len The available length of the memory area, in bytes + * @return On success, the number of bytes written to the buffer + * @return On error, a negative errno code is returned + * + * NOTE:By passing NULL as the "attr" argument to iio_channel_attr_read, + * it is now possible to read all of the attributes of a channel. + * + * The buffer is filled with one block of data per attribute of the channel, + * by the order they appear in the iio_channel structure. + * + * The first four bytes of one block correspond to a 32-bit signed value in + * network order. If negative, it corresponds to the errno code that were + * returned when reading the attribute; if positive, it corresponds to the + * length of the data read. In that case, the rest of the block contains + * the data. */ +__api ssize_t iio_channel_attr_read(const struct iio_channel *chn, + const char *attr, char *dst, size_t len); + + +/** @brief Read the content of all channel-specific attributes + * @param chn A pointer to an iio_channel structure + * @param cb A pointer to a callback function + * @param data A pointer that will be passed to the callback function + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * NOTE: This function is especially useful when used with the network + * backend, as all the channel-specific attributes are read in one single + * command. */ +__api int iio_channel_attr_read_all(struct iio_channel *chn, + int (*cb)(struct iio_channel *chn, + const char *attr, const char *val, size_t len, void *d), + void *data); + + +/** @brief Read the content of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a bool variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_channel_attr_read_bool(const struct iio_channel *chn, + const char *attr, bool *val); + + +/** @brief Read the content of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a long long variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_channel_attr_read_longlong(const struct iio_channel *chn, + const char *attr, long long *val); + + +/** @brief Read the content of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A pointer to a double variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_channel_attr_read_double(const struct iio_channel *chn, + const char *attr, double *val); + + +/** @brief Set the value of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param src A NULL-terminated string to set the attribute to + * @return On success, the number of bytes written + * @return On error, a negative errno code is returned + * + * NOTE:By passing NULL as the "attr" argument to iio_channel_attr_write, + * it is now possible to write all of the attributes of a channel. + * + * The buffer must contain one block of data per attribute of the channel, + * by the order they appear in the iio_channel structure. + * + * The first four bytes of one block correspond to a 32-bit signed value in + * network order. If negative, the attribute is not written; if positive, + * it corresponds to the length of the data to write. In that case, the rest + * of the block must contain the data. */ +__api ssize_t iio_channel_attr_write(const struct iio_channel *chn, + const char *attr, const char *src); + + +/** @brief Set the value of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param src A pointer to the data to be written + * @param len The number of bytes that should be written + * @return On success, the number of bytes written + * @return On error, a negative errno code is returned */ +__api ssize_t iio_channel_attr_write_raw(const struct iio_channel *chn, + const char *attr, const void *src, size_t len); + + +/** @brief Set the values of all channel-specific attributes + * @param chn A pointer to an iio_channel structure + * @param cb A pointer to a callback function + * @param data A pointer that will be passed to the callback function + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * NOTE: This function is especially useful when used with the network + * backend, as all the channel-specific attributes are written in one single + * command. */ +__api int iio_channel_attr_write_all(struct iio_channel *chn, + ssize_t (*cb)(struct iio_channel *chn, + const char *attr, void *buf, size_t len, void *d), + void *data); + + +/** @brief Set the value of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A bool value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_channel_attr_write_bool(const struct iio_channel *chn, + const char *attr, bool val); + + +/** @brief Set the value of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A long long value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_channel_attr_write_longlong(const struct iio_channel *chn, + const char *attr, long long val); + + +/** @brief Set the value of the given channel-specific attribute + * @param chn A pointer to an iio_channel structure + * @param attr A NULL-terminated string corresponding to the name of the + * attribute + * @param val A double value to set the attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_channel_attr_write_double(const struct iio_channel *chn, + const char *attr, double val); + + +/** @brief Enable the given channel + * @param chn A pointer to an iio_channel structure + * + * NOTE:Before creating an iio_buffer structure with + * iio_device_create_buffer, it is required to enable at least one channel of + * the device to read from. */ +__api void iio_channel_enable(struct iio_channel *chn); + + +/** @brief Disable the given channel + * @param chn A pointer to an iio_channel structure */ +__api void iio_channel_disable(struct iio_channel *chn); + + +/** @brief Returns True if the channel is enabled + * @param chn A pointer to an iio_channel structure + * @return True if the channel is enabled, False otherwise */ +__api bool iio_channel_is_enabled(const struct iio_channel *chn); + + +/** Demultiplex the samples of a given channel + * @param chn A pointer to an iio_channel structure + * @param buffer A pointer to an iio_buffer structure + * @param dst A pointer to the memory area where the demultiplexed data will be + * stored + * @param len The available length of the memory area, in bytes + * @return The size of the demultiplexed data, in bytes */ +__api size_t iio_channel_read_raw(const struct iio_channel *chn, + struct iio_buffer *buffer, void *dst, size_t len); + + +/** Demultiplex and convert the samples of a given channel + * @param chn A pointer to an iio_channel structure + * @param buffer A pointer to an iio_buffer structure + * @param dst A pointer to the memory area where the converted data will be + * stored + * @param len The available length of the memory area, in bytes + * @return The size of the converted data, in bytes */ +__api size_t iio_channel_read(const struct iio_channel *chn, + struct iio_buffer *buffer, void *dst, size_t len); + + +/** Multiplex the samples of a given channel + * @param chn A pointer to an iio_channel structure + * @param buffer A pointer to an iio_buffer structure + * @param src A pointer to the memory area where the sequential data will + * be read from + * @param len The length of the memory area, in bytes + * @return The number of bytes actually multiplexed */ +__api size_t iio_channel_write_raw(const struct iio_channel *chn, + struct iio_buffer *buffer, const void *src, size_t len); + + +/** Convert and multiplex the samples of a given channel + * @param chn A pointer to an iio_channel structure + * @param buffer A pointer to an iio_buffer structure + * @param src A pointer to the memory area where the sequential data will + * be read from + * @param len The length of the memory area, in bytes + * @return The number of bytes actually converted and multiplexed */ +__api size_t iio_channel_write(const struct iio_channel *chn, + struct iio_buffer *buffer, const void *src, size_t len); + + +/** @brief Associate a pointer to an iio_channel structure + * @param chn A pointer to an iio_channel structure + * @param data The pointer to be associated */ +__api void iio_channel_set_data(struct iio_channel *chn, void *data); + + +/** @brief Retrieve a previously associated pointer of an iio_channel structure + * @param chn A pointer to an iio_channel structure + * @return The pointer previously associated if present, or NULL */ +__api void * iio_channel_get_data(const struct iio_channel *chn); + + +/** @brief Get the type of the given channel + * @param chn A pointer to an iio_channel structure + * @return The type of the channel */ +__api __pure enum iio_chan_type iio_channel_get_type( + const struct iio_channel *chn); + + +/** @brief Get the modifier type of the given channel + * @param chn A pointer to an iio_channel structure + * @return The modifier type of the channel */ +__api __pure enum iio_modifier iio_channel_get_modifier( + const struct iio_channel *chn); + + +/** @} *//* ------------------------------------------------------------------*/ +/* ------------------------- Buffer functions --------------------------------*/ +/** @defgroup Buffer Buffer + * @{ + * @struct iio_buffer + * @brief An input or output buffer, used to read or write samples */ + + +/** @brief Retrieve a pointer to the iio_device structure + * @param buf A pointer to an iio_buffer structure + * @return A pointer to an iio_device structure */ +__api __pure const struct iio_device * iio_buffer_get_device( + const struct iio_buffer *buf); + + +/** @brief Create an input or output buffer associated to the given device + * @param dev A pointer to an iio_device structure + * @param samples_count The number of samples that the buffer should contain + * @param cyclic If True, enable cyclic mode + * @return On success, a pointer to an iio_buffer structure + * @return On error, NULL is returned, and errno is set to the error code + * + * NOTE: Channels that have to be written to / read from must be enabled + * before creating the buffer. */ +__api struct iio_buffer * iio_device_create_buffer(const struct iio_device *dev, + size_t samples_count, bool cyclic); + + +/** @brief Destroy the given buffer + * @param buf A pointer to an iio_buffer structure + * + * NOTE: After that function, the iio_buffer pointer shall be invalid. */ +__api void iio_buffer_destroy(struct iio_buffer *buf); + +/** @brief Get a pollable file descriptor + * + * Can be used to know when iio_buffer_refill() or iio_buffer_push() can be + * called + * @param buf A pointer to an iio_buffer structure + * @return On success, valid file descriptor + * @return On error, a negative errno code is returned + */ +__api int iio_buffer_get_poll_fd(struct iio_buffer *buf); + +/** @brief Make iio_buffer_refill() and iio_buffer_push() blocking or not + * + * After this function has been called with blocking == false, + * iio_buffer_refill() and iio_buffer_push() will return -EAGAIN if no data is + * ready. + * A device is blocking by default. + * @param buf A pointer to an iio_buffer structure + * @param blocking true if the buffer API should be blocking, else false + * @return On success, 0 + * @return On error, a negative errno code is returned + */ +__api int iio_buffer_set_blocking_mode(struct iio_buffer *buf, bool blocking); + + +/** @brief Fetch more samples from the hardware + * @param buf A pointer to an iio_buffer structure + * @return On success, the number of bytes read is returned + * @return On error, a negative errno code is returned + * + * NOTE: Only valid for input buffers */ +__api ssize_t iio_buffer_refill(struct iio_buffer *buf); + + +/** @brief Send the samples to the hardware + * @param buf A pointer to an iio_buffer structure + * @return On success, the number of bytes written is returned + * @return On error, a negative errno code is returned + * + * NOTE: Only valid for output buffers */ +__api ssize_t iio_buffer_push(struct iio_buffer *buf); + + +/** @brief Send a given number of samples to the hardware + * @param buf A pointer to an iio_buffer structure + * @param samples_count The number of samples to submit + * @return On success, the number of bytes written is returned + * @return On error, a negative errno code is returned + * + * NOTE: Only valid for output buffers */ +__api ssize_t iio_buffer_push_partial(struct iio_buffer *buf, + size_t samples_count); + +/** @brief Cancel all buffer operations + * @param buf The buffer for which operations should be canceled + * + * This function cancels all outstanding buffer operations previously scheduled. + * This means any pending iio_buffer_push() or iio_buffer_refill() operation + * will abort and return immediately, any further invocations of these functions + * on the same buffer will return immediately with an error. + * + * Usually iio_buffer_push() and iio_buffer_refill() will block until either all + * data has been transferred or a timeout occurs. This can depending on the + * configuration take a significant amount of time. iio_buffer_cancel() is + * useful to bypass these conditions if the buffer operation is supposed to be + * stopped in response to an external event (e.g. user input). + * + * To be able to capture additional data after calling this function the buffer + * should be destroyed and then re-created. + * + * This function can be called multiple times for the same buffer, but all but + * the first invocation will be without additional effect. + * + * This function is thread-safe, but not signal-safe, i.e. it must not be called + * from a signal handler. + */ +__api void iio_buffer_cancel(struct iio_buffer *buf); + + +/** @brief Get the start address of the buffer + * @param buf A pointer to an iio_buffer structure + * @return A pointer corresponding to the start address of the buffer */ +__api void * iio_buffer_start(const struct iio_buffer *buf); + + +/** @brief Find the first sample of a channel in a buffer + * @param buf A pointer to an iio_buffer structure + * @param chn A pointer to an iio_channel structure + * @return A pointer to the first sample found, or to the end of the buffer if + * no sample for the given channel is present in the buffer + * + * NOTE: This function, coupled with iio_buffer_step and iio_buffer_end, + * can be used to iterate on all the samples of a given channel present in the + * buffer, doing the following: + * + * @verbatim + for (void *ptr = iio_buffer_first(buffer, chn); ptr < iio_buffer_end(buffer); ptr += iio_buffer_step(buffer)) { + .... + } + @endverbatim */ +__api void * iio_buffer_first(const struct iio_buffer *buf, + const struct iio_channel *chn); + + +/** @brief Get the step size between two samples of one channel + * @param buf A pointer to an iio_buffer structure + * @return the difference between the addresses of two consecutive samples of + * one same channel */ +__api ptrdiff_t iio_buffer_step(const struct iio_buffer *buf); + + +/** @brief Get the address that follows the last sample in a buffer + * @param buf A pointer to an iio_buffer structure + * @return A pointer corresponding to the address that follows the last sample + * present in the buffer */ +__api void * iio_buffer_end(const struct iio_buffer *buf); + + +/** @brief Call the supplied callback for each sample found in a buffer + * @param buf A pointer to an iio_buffer structure + * @param callback A pointer to a function to call for each sample found + * @param data A user-specified pointer that will be passed to the callback + * @return number of bytes processed. + * + * NOTE: The callback receives four arguments: + * * A pointer to the iio_channel structure corresponding to the sample, + * * A pointer to the sample itself, + * * The length of the sample in bytes, + * * The user-specified pointer passed to iio_buffer_foreach_sample. */ +__api ssize_t iio_buffer_foreach_sample(struct iio_buffer *buf, + ssize_t (*callback)(const struct iio_channel *chn, + void *src, size_t bytes, void *d), void *data); + + +/** @brief Associate a pointer to an iio_buffer structure + * @param buf A pointer to an iio_buffer structure + * @param data The pointer to be associated */ +__api void iio_buffer_set_data(struct iio_buffer *buf, void *data); + + +/** @brief Retrieve a previously associated pointer of an iio_buffer structure + * @param buf A pointer to an iio_buffer structure + * @return The pointer previously associated if present, or NULL */ +__api void * iio_buffer_get_data(const struct iio_buffer *buf); + + +/** @} *//* ------------------------------------------------------------------*/ +/* ------------------------- Low-level functions -----------------------------*/ +/** @defgroup Debug Debug and low-level functions + * @{ + * @struct iio_data_format + * @brief Contains the format of a data sample. + * + * The different fields inform about the correct way to convert one sample from + * its raw format (as read from / generated by the hardware) to its real-world + * value. + */ +struct iio_data_format { + /** @brief Total length of the sample, in bits */ + unsigned int length; + + /** @brief Length of valuable data in the sample, in bits */ + unsigned int bits; + + /** @brief Right-shift to apply when converting sample */ + unsigned int shift; + + /** @brief Contains True if the sample is signed */ + bool is_signed; + + /** @brief Contains True if the sample is fully defined, sign extended, etc. */ + bool is_fully_defined; + + /** @brief Contains True if the sample is in big-endian format */ + bool is_be; + + /** @brief Contains True if the sample should be scaled when converted */ + bool with_scale; + + /** @brief Contains the scale to apply if with_scale is set */ + double scale; + + /** @brief Number of times length repeats (added in v0.8) */ + unsigned int repeat; +}; + + +/** @brief Get the current sample size + * @param dev A pointer to an iio_device structure + * @return On success, the sample size in bytes + * @return On error, a negative errno code is returned + * + * NOTE: The sample size is not constant and will change when channels + * get enabled or disabled. */ +__api ssize_t iio_device_get_sample_size(const struct iio_device *dev); + + +/** @brief Get the index of the given channel + * @param chn A pointer to an iio_channel structure + * @return On success, the index of the specified channel + * @return On error, a negative errno code is returned */ +__api __pure long iio_channel_get_index(const struct iio_channel *chn); + + +/** @brief Get a pointer to a channel's data format structure + * @param chn A pointer to an iio_channel structure + * @return A pointer to the channel's iio_data_format structure */ +__api __cnst const struct iio_data_format * iio_channel_get_data_format( + const struct iio_channel *chn); + + +/** @brief Convert the sample from hardware format to host format + * @param chn A pointer to an iio_channel structure + * @param dst A pointer to the destination buffer where the converted sample + * should be written + * @param src A pointer to the source buffer containing the sample */ +__api void iio_channel_convert(const struct iio_channel *chn, + void *dst, const void *src); + + +/** @brief Convert the sample from host format to hardware format + * @param chn A pointer to an iio_channel structure + * @param dst A pointer to the destination buffer where the converted sample + * should be written + * @param src A pointer to the source buffer containing the sample */ +__api void iio_channel_convert_inverse(const struct iio_channel *chn, + void *dst, const void *src); + + +/** @brief Enumerate the debug attributes of the given device + * @param dev A pointer to an iio_device structure + * @return The number of debug attributes found */ +__api __pure unsigned int iio_device_get_debug_attrs_count( + const struct iio_device *dev); + + +/** @brief Get the debug attribute present at the given index + * @param dev A pointer to an iio_device structure + * @param index The index corresponding to the debug attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the index is invalid, NULL is returned */ +__api __pure const char * iio_device_get_debug_attr( + const struct iio_device *dev, unsigned int index); + + +/** @brief Try to find a debug attribute by its name + * @param dev A pointer to an iio_device structure + * @param name A NULL-terminated string corresponding to the name of the + * debug attribute + * @return On success, a pointer to a static NULL-terminated string + * @return If the name does not correspond to any known debug attribute of the + * given device, NULL is returned + * + * NOTE: This function is useful to detect the presence of a debug + * attribute. + * It can also be used to retrieve the name of a debug attribute as a pointer + * to a static string from a dynamically allocated string. */ +__api __pure const char * iio_device_find_debug_attr( + const struct iio_device *dev, const char *name); + + +/** @brief Read the content of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param dst A pointer to the memory area where the NULL-terminated string + * corresponding to the value read will be stored + * @param len The available length of the memory area, in bytes + * @return On success, the number of bytes written to the buffer + * @return On error, a negative errno code is returned + * + * NOTE:By passing NULL as the "attr" argument to + * iio_device_debug_attr_read, it is now possible to read all of the debug + * attributes of a device. + * + * The buffer is filled with one block of data per debug attribute of the + * device, by the order they appear in the iio_device structure. + * + * The first four bytes of one block correspond to a 32-bit signed value in + * network order. If negative, it corresponds to the errno code that were + * returned when reading the debug attribute; if positive, it corresponds + * to the length of the data read. In that case, the rest of the block contains + * the data. */ +__api ssize_t iio_device_debug_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len); + + +/** @brief Read the content of all debug attributes + * @param dev A pointer to an iio_device structure + * @param cb A pointer to a callback function + * @param data A pointer that will be passed to the callback function + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * NOTE: This function is especially useful when used with the network + * backend, as all the debug attributes are read in one single command. */ +__api int iio_device_debug_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, const char *attr, + const char *value, size_t len, void *d), + void *data); + + +/** @brief Set the value of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param src A NULL-terminated string to set the debug attribute to + * @return On success, the number of bytes written + * @return On error, a negative errno code is returned + * + * NOTE:By passing NULL as the "attr" argument to + * iio_device_debug_attr_write, it is now possible to write all of the + * debug attributes of a device. + * + * The buffer must contain one block of data per debug attribute of the device, + * by the order they appear in the iio_device structure. + * + * The first four bytes of one block correspond to a 32-bit signed value in + * network order. If negative, the debug attribute is not written; if positive, + * it corresponds to the length of the data to write. In that case, the rest + * of the block must contain the data. */ +__api ssize_t iio_device_debug_attr_write(const struct iio_device *dev, + const char *attr, const char *src); + + +/** @brief Set the value of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param src A pointer to the data to be written + * @param len The number of bytes that should be written + * @return On success, the number of bytes written + * @return On error, a negative errno code is returned */ +__api ssize_t iio_device_debug_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len); + + +/** @brief Set the values of all debug attributes + * @param dev A pointer to an iio_device structure + * @param cb A pointer to a callback function + * @param data A pointer that will be passed to the callback function + * @return On success, 0 is returned + * @return On error, a negative errno code is returned + * + * NOTE: This function is especially useful when used with the network + * backend, as all the debug attributes are written in one single command. */ +__api int iio_device_debug_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data); + + +/** @brief Read the content of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param val A pointer to a bool variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_debug_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val); + + +/** @brief Read the content of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param val A pointer to a long long variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_debug_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val); + + +/** @brief Read the content of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param val A pointer to a double variable where the value should be stored + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_debug_attr_read_double(const struct iio_device *dev, + const char *attr, double *val); + + +/** @brief Set the value of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param val A bool value to set the debug attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_debug_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val); + + +/** @brief Set the value of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param val A long long value to set the debug attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_debug_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val); + + +/** @brief Set the value of the given debug attribute + * @param dev A pointer to an iio_device structure + * @param attr A NULL-terminated string corresponding to the name of the + * debug attribute + * @param val A double value to set the debug attribute to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_debug_attr_write_double(const struct iio_device *dev, + const char *attr, double val); + + +/** @brief Identify the channel or debug attribute corresponding to a filename + * @param dev A pointer to an iio_device structure + * @param filename A NULL-terminated string corresponding to the filename + * @param chn A pointer to a pointer of an iio_channel structure. The pointed + * pointer will be set to the address of the iio_channel structure if the + * filename correspond to the attribute of a channel, or NULL otherwise. + * @param attr A pointer to a NULL-terminated string. The pointer + * pointer will be set to point to the name of the attribute corresponding to + * the filename. + * @return On success, 0 is returned, and *chn and *attr are modified. + * @return On error, a negative errno code is returned. *chn and *attr are not + * modified. */ +__api int iio_device_identify_filename(const struct iio_device *dev, + const char *filename, struct iio_channel **chn, + const char **attr); + + +/** @brief Set the value of a hardware register + * @param dev A pointer to an iio_device structure + * @param address The address of the register + * @param value The value to set the register to + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_reg_write(struct iio_device *dev, + uint32_t address, uint32_t value); + + +/** @brief Get the value of a hardware register + * @param dev A pointer to an iio_device structure + * @param address The address of the register + * @param value A pointer to the variable where the value will be written + * @return On success, 0 is returned + * @return On error, a negative errno code is returned */ +__api int iio_device_reg_read(struct iio_device *dev, + uint32_t address, uint32_t *value); + + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#undef __api + +#endif /* __IIO_H__ */ diff --git a/sensors/aidl/libiio_client/iiod-client.c b/sensors/aidl/libiio_client/iiod-client.c new file mode 100644 index 0000000..46a7e6b --- /dev/null +++ b/sensors/aidl/libiio_client/iiod-client.c @@ -0,0 +1,736 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014-2016 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#include "debug.h" +#include "iiod-client.h" +#include "iio-lock.h" +#include "iio-private.h" + +#include +#include +#include +#include + +#define REQUEST_CLIENT_ID "REQUEST_CLIENT_ID\r\n" + +struct iiod_client { + struct iio_context_pdata *pdata; + const struct iiod_client_ops *ops; + struct iio_mutex *lock; +}; + +static ssize_t iiod_client_read_integer(struct iiod_client *client, + void *desc, int *val) +{ + unsigned int i; + char *ptr = NULL, *end; + char buf[1024] = ""; + ssize_t ret; + int value; + + do { + ret = client->ops->read_line(client->pdata, + desc, buf, sizeof(buf)); + if (ret < 0) + return ret; + + for (i = 0; i < (unsigned int) ret; i++) { + if (buf[i] != '\n') { + if (!ptr) + ptr = &buf[i]; + } else if (!!ptr) { + break; + } + } + } while (!ptr); + + buf[i] = '\0'; + + value = (int) strtol(ptr, &end, 10); + if (ptr == end) + return -EINVAL; + + *val = value; + return 0; +} + +static int iiod_client_exec_command(struct iiod_client *client, + void *desc, const char *cmd) +{ + int resp; + ssize_t ret; + + ret = client->ops->write(client->pdata, desc, cmd, strlen(cmd)); + if (ret < 0) + return (int) ret; + + ret = iiod_client_read_integer(client, desc, &resp); + return ret < 0 ? (int) ret : resp; +} + +static ssize_t iiod_client_write_all(struct iiod_client *client, + void *desc, const void *src, size_t len) +{ + struct iio_context_pdata *pdata = client->pdata; + const struct iiod_client_ops *ops = client->ops; + uintptr_t ptr = (uintptr_t) src; + + while (len) { + ssize_t ret = ops->write(pdata, desc, (const void *) ptr, len); + + if (ret < 0) { + if (ret == -EINTR) + continue; + else + return ret; + } + + if (ret == 0) + return -EPIPE; + + ptr += ret; + len -= ret; + } + + return (ssize_t) (ptr - (uintptr_t) src); +} + +static ssize_t iiod_client_read_all(struct iiod_client *client, + void *desc, void *dst, size_t len) +{ + struct iio_context_pdata *pdata = client->pdata; + const struct iiod_client_ops *ops = client->ops; + uintptr_t ptr = (uintptr_t) dst; + + while (len) { + ssize_t ret = ops->read(pdata, desc, (void *) ptr, len); + + if (ret < 0) { + if (ret == -EINTR) + continue; + else + return ret; + } + + if (ret == 0) + return -EPIPE; + + ptr += ret; + len -= ret; + } + + return (ssize_t) (ptr - (uintptr_t) dst); +} + +struct iiod_client * iiod_client_new(struct iio_context_pdata *pdata, + struct iio_mutex *lock, const struct iiod_client_ops *ops) +{ + struct iiod_client *client; + + client = malloc(sizeof(*client)); + if (!client) { + errno = ENOMEM; + return NULL; + } + + client->lock = lock; + client->pdata = pdata; + client->ops = ops; + return client; +} + +void iiod_client_destroy(struct iiod_client *client) +{ + free(client); +} + +int iiod_client_request_client_id(struct iiod_client *client, void *desc) +{ + struct iio_context_pdata *pdata = client->pdata; + const struct iiod_client_ops *ops = client->ops; + char buf[256], *ptr = buf, *end; + int ret, to_read; + + iio_mutex_lock(client->lock); + + ret = ops->write(pdata, desc, REQUEST_CLIENT_ID, strlen(REQUEST_CLIENT_ID)); + if (ret < 0) { + iio_mutex_unlock(client->lock); + return ret; + } + + ret = iiod_client_read_integer(client, desc, &to_read); + + iio_mutex_unlock(client->lock); + if (ret < 0) + return ret; + + return to_read; +} + +int iiod_client_get_version(struct iiod_client *client, void *desc, + unsigned int *major, unsigned int *minor, char *git_tag) +{ + struct iio_context_pdata *pdata = client->pdata; + const struct iiod_client_ops *ops = client->ops; + char buf[256] = ""; + char *ptr = buf, *end; + long maj, min; + int ret; + + iio_mutex_lock(client->lock); + + ret = ops->write(pdata, desc, "VERSION\r\n", sizeof("VERSION\r\n") - 1); + if (ret < 0) { + iio_mutex_unlock(client->lock); + return ret; + } + + ret = ops->read_line(pdata, desc, buf, sizeof(buf)); + iio_mutex_unlock(client->lock); + + if (ret < 0) + return ret; + + maj = strtol(ptr, &end, 10); + if (ptr == end) + return -EIO; + + ptr = end + 1; + min = strtol(ptr, &end, 10); + if (ptr == end) + return -EIO; + + ptr = end + 1; + if (buf + ret < ptr + 8) + return -EIO; + + /* Strip the \n */ + ptr[buf + ret - ptr - 1] = '\0'; + + if (major) + *major = (unsigned int) maj; + if (minor) + *minor = (unsigned int) min; + if (git_tag) + strncpy(git_tag, ptr, 8); + return 0; +} + +int iiod_client_get_trigger(struct iiod_client *client, void *desc, + const struct iio_device *dev, const struct iio_device **trigger) +{ + const struct iio_context *ctx = iio_device_get_context(dev); + unsigned int i, nb_devices = iio_context_get_devices_count(ctx); + char buf[1024]; + unsigned int name_len; + int ret; + + iio_snprintf(buf, sizeof(buf), "GETTRIG %s\r\n", + iio_device_get_id(dev)); + + iio_mutex_lock(client->lock); + ret = iiod_client_exec_command(client, desc, buf); + + if (ret == 0) + *trigger = NULL; + if (ret <= 0) + goto out_unlock; + + if ((unsigned int) ret > sizeof(buf) - 1) { + ret = -EIO; + goto out_unlock; + } + + name_len = ret; + + ret = (int) iiod_client_read_all(client, desc, buf, name_len + 1); + if (ret < 0) + goto out_unlock; + + ret = -ENXIO; + + for (i = 0; i < nb_devices; i++) { + struct iio_device *cur = iio_context_get_device(ctx, i); + + if (cur == NULL) + continue; + if (iio_device_is_trigger(cur)) { + const char *name = iio_device_get_name(cur); + + if (!name) + continue; + + if (!strncmp(name, buf, name_len)) { + *trigger = cur; + ret = 0; + goto out_unlock; + } + } + } + +out_unlock: + iio_mutex_unlock(client->lock); + return ret; +} + +int iiod_client_set_trigger(struct iiod_client *client, void *desc, + const struct iio_device *dev, const struct iio_device *trigger) +{ + char buf[1024]; + int ret; + + if (trigger) { + iio_snprintf(buf, sizeof(buf), "SETTRIG %s %s\r\n", + iio_device_get_id(dev), + iio_device_get_id(trigger)); + } else { + iio_snprintf(buf, sizeof(buf), "SETTRIG %s\r\n", + iio_device_get_id(dev)); + } + + iio_mutex_lock(client->lock); + ret = iiod_client_exec_command(client, desc, buf); + iio_mutex_unlock(client->lock); + return ret; +} + +int iiod_client_set_kernel_buffers_count(struct iiod_client *client, void *desc, + const struct iio_device *dev, unsigned int nb_blocks) +{ + int ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "SET %s BUFFERS_COUNT %u\r\n", + iio_device_get_id(dev), nb_blocks); + + iio_mutex_lock(client->lock); + ret = iiod_client_exec_command(client, desc, buf); + iio_mutex_unlock(client->lock); + return ret; +} + +int iiod_client_set_timeout(struct iiod_client *client, + void *desc, unsigned int timeout) +{ + int ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "TIMEOUT %u\r\n", timeout); + + iio_mutex_lock(client->lock); + ret = iiod_client_exec_command(client, desc, buf); + iio_mutex_unlock(client->lock); + return ret; +} + +static int iiod_client_discard(struct iiod_client *client, void *desc, + char *buf, size_t buf_len, size_t to_discard) +{ + do { + size_t read_len; + ssize_t ret; + + if (to_discard > buf_len) + read_len = buf_len; + else + read_len = to_discard; + + ret = iiod_client_read_all(client, desc, buf, read_len); + if (ret < 0) + return ret; + + to_discard -= (size_t) ret; + } while (to_discard); + + return 0; +} + +ssize_t iiod_client_read_attr(struct iiod_client *client, void *desc, + const struct iio_device *dev, const struct iio_channel *chn, + const char *attr, char *dest, size_t len, enum iio_attr_type type) +{ + const char *id = iio_device_get_id(dev); + char buf[1024]; + ssize_t ret; + + if (attr) { + if (chn) { + if (!iio_channel_find_attr(chn, attr)) + return -ENOENT; + } else { + switch (type) { + case IIO_ATTR_TYPE_DEVICE: + if (!iio_device_find_attr(dev, attr)) + return -ENOENT; + break; + case IIO_ATTR_TYPE_DEBUG: + if (!iio_device_find_debug_attr(dev, attr)) + return -ENOENT; + break; + case IIO_ATTR_TYPE_BUFFER: + if (!iio_device_find_buffer_attr(dev, attr)) + return -ENOENT; + break; + default: + return -EINVAL; + } + } + } + + if (chn) { + iio_snprintf(buf, sizeof(buf), "READ %s %s %s %s\r\n", id, + iio_channel_is_output(chn) ? "OUTPUT" : "INPUT", + iio_channel_get_id(chn), attr ? attr : ""); + } else { + switch (type) { + case IIO_ATTR_TYPE_DEVICE: + iio_snprintf(buf, sizeof(buf), "READ %s %s\r\n", + id, attr ? attr : ""); + break; + case IIO_ATTR_TYPE_DEBUG: + iio_snprintf(buf, sizeof(buf), "READ %s DEBUG %s\r\n", + id, attr ? attr : ""); + break; + case IIO_ATTR_TYPE_BUFFER: + iio_snprintf(buf, sizeof(buf), "READ %s BUFFER %s\r\n", + id, attr ? attr : ""); + break; + } + } + + iio_mutex_lock(client->lock); + + ret = (ssize_t) iiod_client_exec_command(client, desc, buf); + if (ret < 0) + goto out_unlock; + + if ((size_t) ret + 1 > len) { + iiod_client_discard(client, desc, dest, len, ret + 1); + ret = -EIO; + goto out_unlock; + } + + /* +1: Also read the trailing \n */ + ret = iiod_client_read_all(client, desc, dest, ret + 1); + + if (ret > 0) { + /* Discard the trailing \n */ + ret--; + + /* Replace it with a \0 just in case */ + dest[ret] = '\0'; + } + +out_unlock: + iio_mutex_unlock(client->lock); + return ret; +} + +ssize_t iiod_client_write_attr(struct iiod_client *client, void *desc, + const struct iio_device *dev, const struct iio_channel *chn, + const char *attr, const char *src, size_t len, enum iio_attr_type type) +{ + struct iio_context_pdata *pdata = client->pdata; + const struct iiod_client_ops *ops = client->ops; + const char *id = iio_device_get_id(dev); + char buf[1024]; + ssize_t ret; + int resp; + + if (attr) { + if (chn) { + if (!iio_channel_find_attr(chn, attr)) + return -ENOENT; + } else { + switch (type) { + case IIO_ATTR_TYPE_DEVICE: + if (!iio_device_find_attr(dev, attr)) + return -ENOENT; + break; + case IIO_ATTR_TYPE_DEBUG: + if (!iio_device_find_debug_attr(dev, attr)) + return -ENOENT; + break; + case IIO_ATTR_TYPE_BUFFER: + if (!iio_device_find_buffer_attr(dev, attr)) + return -ENOENT; + break; + default: + return -EINVAL; + } + } + } + + if (chn) { + iio_snprintf(buf, sizeof(buf), "WRITE %s %s %s %s %lu\r\n", id, + iio_channel_is_output(chn) ? "OUTPUT" : "INPUT", + iio_channel_get_id(chn), attr ? attr : "", + (unsigned long) len); + } else { + switch (type) { + case IIO_ATTR_TYPE_DEVICE: + iio_snprintf(buf, sizeof(buf), "WRITE %s %s %lu\r\n", + id, attr ? attr : "", (unsigned long) len); + break; + case IIO_ATTR_TYPE_DEBUG: + iio_snprintf(buf, sizeof(buf), "WRITE %s DEBUG %s %lu\r\n", + id, attr ? attr : "", (unsigned long) len); + break; + case IIO_ATTR_TYPE_BUFFER: + iio_snprintf(buf, sizeof(buf), "WRITE %s BUFFER %s %lu\r\n", + id, attr ? attr : "", (unsigned long) len); + break; + } + } + + iio_mutex_lock(client->lock); + ret = ops->write(pdata, desc, buf, strlen(buf)); + if (ret < 0) + goto out_unlock; + + ret = iiod_client_write_all(client, desc, src, len); + if (ret < 0) + goto out_unlock; + + ret = iiod_client_read_integer(client, desc, &resp); + if (ret < 0) + goto out_unlock; + + ret = (ssize_t) resp; + +out_unlock: + iio_mutex_unlock(client->lock); + return ret; +} + +struct iio_context * iiod_client_create_context( + struct iiod_client *client, void *desc) +{ + struct iio_context *ctx = NULL; + size_t xml_len; + char *xml; + int ret; + + int client_id = iiod_client_request_client_id(client, desc); + + iio_mutex_lock(client->lock); + ret = iiod_client_exec_command(client, desc, "PRINT\r\n"); + if (ret < 0) + goto out_unlock; + + xml_len = (size_t) ret; + xml = malloc(xml_len + 1); + if (!xml) { + ret = -ENOMEM; + goto out_unlock; + } + + /* +1: Also read the trailing \n */ + ret = (int) iiod_client_read_all(client, desc, xml, xml_len + 1); + if (ret < 0) + goto out_free_xml; + + ctx = iio_create_xml_context_mem(xml, xml_len); + if (!ctx) { + ret = -errno; + goto out_free_xml; + } + if (client_id >= 0) + ctx->client_id = client_id; + +out_free_xml: + free(xml); +out_unlock: + iio_mutex_unlock(client->lock); + if (!ctx) + errno = -ret; + return ctx; +} + +int iiod_client_open_unlocked(struct iiod_client *client, void *desc, + const struct iio_device *dev, size_t samples_count, bool cyclic) +{ + char buf[1024], *ptr; + size_t i; + + iio_snprintf(buf, sizeof(buf), "OPEN %s %lu ", + iio_device_get_id(dev), (unsigned long) samples_count); + ptr = buf + strlen(buf); + + for (i = dev->words; i > 0; i--, ptr += 8) { + iio_snprintf(ptr, (ptr - buf) + i * 8, "%08" PRIx32, + dev->mask[i - 1]); + } + + strcpy(ptr, cyclic ? " CYCLIC\r\n" : "\r\n"); + + return iiod_client_exec_command(client, desc, buf); +} + +int iiod_client_close_unlocked(struct iiod_client *client, void *desc, + const struct iio_device *dev) +{ + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "CLOSE %s\r\n", iio_device_get_id(dev)); + return iiod_client_exec_command(client, desc, buf); +} + +static int iiod_client_read_mask(struct iiod_client *client, + void *desc, uint32_t *mask, size_t words) +{ + size_t i; + ssize_t ret; + char *buf, *ptr; + + buf = malloc(words * 8 + 1); + if (!buf) + return -ENOMEM; + + ret = iiod_client_read_all(client, desc, buf, words * 8 + 1); + if (ret < 0) { + ERROR("READ ALL: %zu\n", ret); + goto out_buf_free; + } else + ret = 0; + + buf[words*8] = '\0'; + + DEBUG("Reading mask\n"); + + for (i = words, ptr = buf; i > 0; i--) { + sscanf(ptr, "%08" PRIx32, &mask[i - 1]); + DEBUG("mask[%lu] = 0x%08" PRIx32 "\n", + (unsigned long)(i - 1), mask[i - 1]); + + ptr = (char *) ((uintptr_t) ptr + 8); + } + +out_buf_free: + free(buf); + return (int) ret; +} + +int iiod_client_register_client_id(struct iiod_client *client, void *desc, + int client_id) +{ + char buf[1024]; + ssize_t ret; + int to_read; + + iio_snprintf(buf, sizeof(buf), "REGISTER_CLIENT_ID %i\r\n", client_id); + + ret = iiod_client_write_all(client, desc, buf, strlen(buf)); + if (ret < 0) + return ret; + + ret = iiod_client_read_integer(client, desc, &to_read); + if (ret < 0) + return ret; + + return to_read; +} + +ssize_t iiod_client_read_unlocked(struct iiod_client *client, void *desc, + const struct iio_device *dev, void *dst, size_t len, + uint32_t *mask, size_t words) +{ + unsigned int nb_channels = iio_device_get_channels_count(dev); + uintptr_t ptr = (uintptr_t) dst; + char buf[1024]; + ssize_t ret, read = 0; + + if (!len || words != (nb_channels + 31) / 32) + return -EINVAL; + + iio_snprintf(buf, sizeof(buf), "READBUF %s %lu\r\n", + iio_device_get_id(dev), (unsigned long) len); + + ret = iiod_client_write_all(client, desc, buf, strlen(buf)); + if (ret < 0) { + ERROR("WRITE ALL: %zu\n", ret); + return ret; + } + + do { + int to_read; + + ret = iiod_client_read_integer(client, desc, &to_read); + if (ret < 0) { + ERROR("READ INTEGER: %zu\n", ret); + return ret; + } + if (to_read < 0) + return (ssize_t) to_read; + if (!to_read) + break; + + if (mask) { + ret = iiod_client_read_mask(client, desc, mask, words); + if (ret < 0) { + return ret; + } + + mask = NULL; /* We read the mask only once */ + } + + ret = iiod_client_read_all(client, desc, (char *) ptr, to_read); + if (ret < 0) + return ret; + + ptr += ret; + read += ret; + len -= ret; + } while (len); + + return read; +} + +ssize_t iiod_client_write_unlocked(struct iiod_client *client, void *desc, + const struct iio_device *dev, const void *src, size_t len) +{ + ssize_t ret; + char buf[1024]; + int val; + + iio_snprintf(buf, sizeof(buf), "WRITEBUF %s %lu\r\n", + dev->id, (unsigned long) len); + + ret = iiod_client_write_all(client, desc, buf, strlen(buf)); + if (ret < 0) + return ret; + + ret = iiod_client_read_integer(client, desc, &val); + if (ret < 0) + return ret; + if (val < 0) + return (ssize_t) val; + + ret = iiod_client_write_all(client, desc, src, len); + if (ret < 0) + return ret; + + ret = iiod_client_read_integer(client, desc, &val); + if (ret < 0) + return ret; + if (val < 0) + return (ssize_t) val; + + return (ssize_t) len; +} diff --git a/sensors/aidl/libiio_client/iiod-client.h b/sensors/aidl/libiio_client/iiod-client.h new file mode 100644 index 0000000..bcf7f00 --- /dev/null +++ b/sensors/aidl/libiio_client/iiod-client.h @@ -0,0 +1,74 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2015 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifndef _IIOD_CLIENT_H +#define _IIOD_CLIENT_H + +#include "iio-private.h" + +struct iio_mutex; +struct iiod_client; +struct iio_context_pdata; + +struct iiod_client_ops { + ssize_t (*write)(struct iio_context_pdata *pdata, + void *desc, const char *src, size_t len); + ssize_t (*read)(struct iio_context_pdata *pdata, + void *desc, char *dst, size_t len); + ssize_t (*read_line)(struct iio_context_pdata *pdata, + void *desc, char *dst, size_t len); +}; + +struct iiod_client * iiod_client_new(struct iio_context_pdata *pdata, + struct iio_mutex *lock, const struct iiod_client_ops *ops); +void iiod_client_destroy(struct iiod_client *client); + +int iiod_client_request_client_id(struct iiod_client *client, void *desc); +int iiod_client_get_version(struct iiod_client *client, void *desc, + unsigned int *major, unsigned int *minor, char *git_tag); +int iiod_client_get_trigger(struct iiod_client *client, void *desc, + const struct iio_device *dev, + const struct iio_device **trigger); +int iiod_client_set_trigger(struct iiod_client *client, void *desc, + const struct iio_device *dev, const struct iio_device *trigger); +int iiod_client_set_kernel_buffers_count(struct iiod_client *client, + void *desc, const struct iio_device *dev, unsigned int nb_blocks); +int iiod_client_set_timeout(struct iiod_client *client, + void *desc, unsigned int timeout); +ssize_t iiod_client_read_attr(struct iiod_client *client, void *desc, + const struct iio_device *dev, const struct iio_channel *chn, + const char *attr, char *dest, size_t len, enum iio_attr_type type); +ssize_t iiod_client_write_attr(struct iiod_client *client, void *desc, + const struct iio_device *dev, const struct iio_channel *chn, + const char *attr, const char *src, size_t len, enum iio_attr_type type); +int iiod_client_open_unlocked(struct iiod_client *client, void *desc, + const struct iio_device *dev, size_t samples_count, + bool cyclic); +int iiod_client_close_unlocked(struct iiod_client *client, void *desc, + const struct iio_device *dev); +int iiod_client_register_client_id(struct iiod_client *client, void *desc, + int client_id); +ssize_t iiod_client_read_unlocked(struct iiod_client *client, void *desc, + const struct iio_device *dev, void *dst, size_t len, + uint32_t *mask, size_t words); +ssize_t iiod_client_write_unlocked(struct iiod_client *client, void *desc, + const struct iio_device *dev, const void *src, size_t len); +struct iio_context * iiod_client_create_context( + struct iiod_client *client, void *desc); + +#endif /* _IIOD_CLIENT_H */ diff --git a/sensors/aidl/libiio_client/lock.c b/sensors/aidl/libiio_client/lock.c new file mode 100644 index 0000000..bb3c011 --- /dev/null +++ b/sensors/aidl/libiio_client/lock.c @@ -0,0 +1,90 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2015 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#include "iio-config.h" + +#ifdef _WIN32 +#include +#elif !defined(NO_THREADS) +#include +#endif + +#include + +struct iio_mutex { +#ifdef NO_THREADS + int foo; /* avoid complaints about empty structure */ +#else +#ifdef _WIN32 + CRITICAL_SECTION lock; +#else + pthread_mutex_t lock; +#endif +#endif +}; + +struct iio_mutex * iio_mutex_create(void) +{ + struct iio_mutex *lock = malloc(sizeof(*lock)); + + if (!lock) + return NULL; + +#ifndef NO_THREADS +#ifdef _WIN32 + InitializeCriticalSection(&lock->lock); +#else + pthread_mutex_init(&lock->lock, NULL); +#endif +#endif + return lock; +} + +void iio_mutex_destroy(struct iio_mutex *lock) +{ +#ifndef NO_THREADS +#ifdef _WIN32 + DeleteCriticalSection(&lock->lock); +#else + pthread_mutex_destroy(&lock->lock); +#endif +#endif + free(lock); +} + +void iio_mutex_lock(struct iio_mutex *lock) +{ +#ifndef NO_THREADS +#ifdef _WIN32 + EnterCriticalSection(&lock->lock); +#else + pthread_mutex_lock(&lock->lock); +#endif +#endif +} + +void iio_mutex_unlock(struct iio_mutex *lock) +{ +#ifndef NO_THREADS +#ifdef _WIN32 + LeaveCriticalSection(&lock->lock); +#else + pthread_mutex_unlock(&lock->lock); +#endif +#endif +} diff --git a/sensors/aidl/libiio_client/network.c b/sensors/aidl/libiio_client/network.c new file mode 100644 index 0000000..076ba14 --- /dev/null +++ b/sensors/aidl/libiio_client/network.c @@ -0,0 +1,1796 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014-2015 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#include "iio-config.h" +#include "iio-private.h" +#include "iio-lock.h" +#include "iiod-client.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#define close(s) closesocket(s) + +/* winsock2.h defines ERROR, we don't want that */ +#undef ERROR + +#else /* _WIN32 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* _WIN32 */ + +#ifdef HAVE_AVAHI +#include +#include +#include +#include +#endif +#include +#include "debug.h" + +static int vsock_fd; + +#define DEFAULT_TIMEOUT_MS 5000 + +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) + +#define IIOD_PORT 30431 +#define IIOD_PORT_STR STRINGIFY(IIOD_PORT) + +struct iio_network_io_context { + int fd; + + /* Only buffer IO contexts can be cancelled. */ + bool cancellable; + bool cancelled; +#if defined(_WIN32) + WSAEVENT events[2]; +#elif defined(WITH_NETWORK_EVENTFD) + int cancel_fd[1]; /* eventfd */ +#else + int cancel_fd[2]; /* pipe */ +#endif + unsigned int timeout_ms; +}; + +struct iio_context_pdata { + struct iio_network_io_context io_ctx; + struct addrinfo *addrinfo; + struct iio_mutex *lock; + struct iiod_client *iiod_client; + bool msg_trunc_supported; + bool peek_supported; +}; + +struct iio_device_pdata { + struct iio_network_io_context io_ctx; +#ifdef WITH_NETWORK_GET_BUFFER + int memfd; + void *mmap_addr; + size_t mmap_len; +#endif + bool wait_for_err_code, is_cyclic, is_tx; + struct iio_mutex *lock; +}; + +#ifdef _WIN32 + +static int set_blocking_mode(int s, bool blocking) +{ + unsigned long nonblock; + int ret; + + nonblock = blocking ? 0 : 1; + + ret = ioctlsocket(s, FIONBIO, &nonblock); + if (ret == SOCKET_ERROR) { + ret = -WSAGetLastError(); + return ret; + } + + return 0; +} + +static int setup_cancel(struct iio_network_io_context *io_ctx) +{ + io_ctx->events[0] = WSACreateEvent(); + if (io_ctx->events[0] == WSA_INVALID_EVENT) + return -ENOMEM; /* Pretty much the only error that can happen */ + + io_ctx->events[1] = WSACreateEvent(); + if (io_ctx->events[1] == WSA_INVALID_EVENT) { + WSACloseEvent(io_ctx->events[0]); + return -ENOMEM; + } + + return 0; +} + +static void cleanup_cancel(struct iio_network_io_context *io_ctx) +{ + WSACloseEvent(io_ctx->events[0]); + WSACloseEvent(io_ctx->events[1]); +} + +static void do_cancel(struct iio_network_io_context *io_ctx) +{ + WSASetEvent(io_ctx->events[1]); +} + +static int wait_cancellable(struct iio_network_io_context *io_ctx, bool read) +{ + long wsa_events = FD_CLOSE; + DWORD ret; + + if (!io_ctx->cancellable) + return 0; + + if (read) + wsa_events |= FD_READ; + else + wsa_events |= FD_WRITE; + + WSAEventSelect(io_ctx->fd, NULL, 0); + WSAResetEvent(io_ctx->events[0]); + WSAEventSelect(io_ctx->fd, io_ctx->events[0], wsa_events); + + ret = WSAWaitForMultipleEvents(2, io_ctx->events, FALSE, + WSA_INFINITE, FALSE); + + if (ret == WSA_WAIT_EVENT_0 + 1) + return -EBADF; + + return 0; +} + +static int network_get_error(void) +{ + return -WSAGetLastError(); +} + +static bool network_should_retry(int err) +{ + return err == -WSAEWOULDBLOCK || err == -WSAETIMEDOUT; +} + +static bool network_is_interrupted(int err) +{ + return false; +} + +static bool network_connect_in_progress(int err) +{ + return err == -WSAEWOULDBLOCK; +} + +#define NETWORK_ERR_TIMEOUT WSAETIMEDOUT + +#else + +static int set_blocking_mode(int fd, bool blocking) +{ + int ret = fcntl(fd, F_GETFL, 0); + if (ret < 0) + return -errno; + + if (blocking) + ret &= ~O_NONBLOCK; + else + ret |= O_NONBLOCK; + + ret = fcntl(fd, F_SETFL, ret); + return ret < 0 ? -errno : 0; +} + +#include + +#if defined(WITH_NETWORK_EVENTFD) + +#include + +static int create_cancel_fd(struct iio_network_io_context *io_ctx) +{ + io_ctx->cancel_fd[0] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (io_ctx->cancel_fd[0] < 0) + return -errno; + return 0; +} + +static void cleanup_cancel(struct iio_network_io_context *io_ctx) +{ + close(io_ctx->cancel_fd[0]); +} + +#define CANCEL_WR_FD 0 + +#else + +static int create_cancel_fd(struct iio_network_io_context *io_ctx) +{ + int ret; + +#ifdef HAS_PIPE2 + ret = pipe2(io_ctx->cancel_fd, O_CLOEXEC | O_NONBLOCK); + if (ret < 0 && errno != ENOSYS) /* If ENOSYS try pipe() */ + return -errno; +#endif + ret = pipe(io_ctx->cancel_fd); + if (ret < 0) + return -errno; + ret = set_blocking_mode(io_ctx->cancel_fd[0], false); + if (ret < 0) + goto err_close; + ret = set_blocking_mode(io_ctx->cancel_fd[1], false); + if (ret < 0) + goto err_close; + + return 0; +err_close: + close(io_ctx->cancel_fd[0]); + close(io_ctx->cancel_fd[1]); + return ret; +} + +static void cleanup_cancel(struct iio_network_io_context *io_ctx) +{ + close(io_ctx->cancel_fd[0]); + close(io_ctx->cancel_fd[1]); +} + +#define CANCEL_WR_FD 1 + +#endif + +static int setup_cancel(struct iio_network_io_context *io_ctx) +{ + int ret; + + ret = set_blocking_mode(io_ctx->fd, false); + if (ret) + return ret; + + return create_cancel_fd(io_ctx); +} + +static void do_cancel(struct iio_network_io_context *io_ctx) +{ + uint64_t event = 1; + int ret; + + ret = write(io_ctx->cancel_fd[CANCEL_WR_FD], &event, sizeof(event)); + if (ret == -1) { + /* If this happens something went very seriously wrong */ + char err_str[1024]; + iio_strerror(errno, err_str, sizeof(err_str)); + ERROR("Unable to signal cancellation event: %s\n", err_str); + } +} + +static int wait_cancellable(struct iio_network_io_context *io_ctx, bool read) +{ + struct pollfd pfd[2]; + int ret; + + if (!io_ctx->cancellable) + return 0; + + memset(pfd, 0, sizeof(pfd)); + + pfd[0].fd = io_ctx->fd; + if (read) + pfd[0].events = POLLIN; + else + pfd[0].events = POLLOUT; + pfd[1].fd = io_ctx->cancel_fd[0]; + pfd[1].events = POLLIN; + + do { + int timeout_ms; + + if (io_ctx->timeout_ms > 0) + timeout_ms = (int) io_ctx->timeout_ms; + else + timeout_ms = -1; + + do { + ret = poll(pfd, 2, timeout_ms); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) + return -errno; + if (!ret) + return -EPIPE; + + if (pfd[1].revents & POLLIN) + return -EBADF; + } while (!(pfd[0].revents & (pfd[0].events | POLLERR | POLLHUP))); + + return 0; +} + +static int network_get_error(void) +{ + return -errno; +} + +static bool network_should_retry(int err) +{ + return err == -EAGAIN; +} + +static bool network_is_interrupted(int err) +{ + return err == -EINTR; +} + +static bool network_connect_in_progress(int err) +{ + return err == -EINPROGRESS; +} + +#define NETWORK_ERR_TIMEOUT ETIMEDOUT + +#endif + +#ifdef HAVE_AVAHI +struct avahi_discovery_data { + AvahiSimplePoll *poll; + AvahiAddress *address; + uint16_t *port; + bool found, resolved; +}; + +static void __avahi_resolver_cb(AvahiServiceResolver *resolver, + __notused AvahiIfIndex iface, __notused AvahiProtocol proto, + __notused AvahiResolverEvent event, __notused const char *name, + __notused const char *type, __notused const char *domain, + __notused const char *host_name, const AvahiAddress *address, + uint16_t port, __notused AvahiStringList *txt, + __notused AvahiLookupResultFlags flags, void *d) +{ + struct avahi_discovery_data *ddata = (struct avahi_discovery_data *) d; + + memcpy(ddata->address, address, sizeof(*address)); + *ddata->port = port; + ddata->resolved = true; + avahi_service_resolver_free(resolver); +} + +static void __avahi_browser_cb(AvahiServiceBrowser *browser, + AvahiIfIndex iface, AvahiProtocol proto, + AvahiBrowserEvent event, const char *name, + const char *type, const char *domain, + __notused AvahiLookupResultFlags flags, void *d) +{ + struct avahi_discovery_data *ddata = (struct avahi_discovery_data *) d; + struct AvahiClient *client = avahi_service_browser_get_client(browser); + + switch (event) { + default: + case AVAHI_BROWSER_NEW: + ddata->found = !!avahi_service_resolver_new(client, iface, + proto, name, type, domain, + AVAHI_PROTO_UNSPEC, 0, + __avahi_resolver_cb, d); + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + if (ddata->found) { + while (!ddata->resolved) { + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 4000000; + nanosleep(&ts, NULL); + } + } + /* fall-through */ + case AVAHI_BROWSER_FAILURE: + avahi_simple_poll_quit(ddata->poll); + /* fall-through */ + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } +} + +static int discover_host(AvahiAddress *addr, uint16_t *port) +{ + struct avahi_discovery_data ddata; + int ret = 0; + AvahiClient *client; + AvahiServiceBrowser *browser; + AvahiSimplePoll *poll = avahi_simple_poll_new(); + if (!poll) + return -ENOMEM; + + client = avahi_client_new(avahi_simple_poll_get(poll), + 0, NULL, NULL, &ret); + if (!client) { + ERROR("Unable to start ZeroConf client :%s\n", + avahi_strerror(ret)); + goto err_free_poll; + } + + memset(&ddata, 0, sizeof(ddata)); + ddata.poll = poll; + ddata.address = addr; + ddata.port = port; + + browser = avahi_service_browser_new(client, + AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + "_iio._tcp", NULL, 0, __avahi_browser_cb, &ddata); + if (!browser) { + ret = avahi_client_errno(client); + ERROR("Unable to create ZeroConf browser: %s\n", + avahi_strerror(ret)); + goto err_free_client; + } + + DEBUG("Trying to discover host\n"); + avahi_simple_poll_loop(poll); + + if (!ddata.found) + ret = ENXIO; + + avahi_service_browser_free(browser); +err_free_client: + avahi_client_free(client); +err_free_poll: + avahi_simple_poll_free(poll); + return -ret; /* we want a negative error code */ +} +#endif /* HAVE_AVAHI */ + +// coverity[ -taint_source : arg-1 ] +static ssize_t network_recv(struct iio_network_io_context *io_ctx, + void *data, size_t len, int flags) +{ + ssize_t ret; + int err; + + while (1) { + ret = wait_cancellable(io_ctx, true); + if (ret < 0) + return ret; + ret = recv(io_ctx->fd, data, (int) len, flags); + if (ret == 0) + return -EPIPE; + else if (ret > 0) + break; + + err = network_get_error(); + if (network_should_retry(err)) { + if (io_ctx->cancellable) + continue; + else + return -EPIPE; + } else if (!network_is_interrupted(err)) { + return (ssize_t) err; + } + } + return ret; +} + +static ssize_t network_send(struct iio_network_io_context *io_ctx, + const void *data, size_t len, int flags) +{ + ssize_t ret; + int err; + + while (1) { + ret = wait_cancellable(io_ctx, false); + if (ret < 0) + return ret; + + ret = send(io_ctx->fd, data, (int) len, flags); + if (ret == 0) + return -EPIPE; + else if (ret > 0) + break; + + err = network_get_error(); + if (network_should_retry(err)) { + if (io_ctx->cancellable) + continue; + else + return -EPIPE; + } else if (!network_is_interrupted(err)) { + return (ssize_t) err; + } + } + + return ret; +} + +static ssize_t write_all(struct iio_network_io_context *io_ctx, + const void *src, size_t len) +{ + uintptr_t ptr = (uintptr_t) src; + while (len) { + ssize_t ret = network_send(io_ctx, (const void *) ptr, len, 0); + if (ret < 0) + return ret; + ptr += ret; + len -= ret; + } + return (ssize_t)(ptr - (uintptr_t) src); +} + +static ssize_t write_command(struct iio_network_io_context *io_ctx, + const char *cmd) +{ + ssize_t ret; + + DEBUG("Writing command: %s\n", cmd); + ret = write_all(io_ctx, cmd, strlen(cmd)); + if (ret < 0) { + char buf[1024]; + iio_strerror(-ret, buf, sizeof(buf)); + ERROR("Unable to send command: %s\n", buf); + } + return ret; +} + +static void network_cancel(const struct iio_device *dev) +{ + struct iio_device_pdata *ppdata = dev->pdata; + + do_cancel(&ppdata->io_ctx); + + ppdata->io_ctx.cancelled = true; +} + +#ifndef _WIN32 + +/* Use it if available */ +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 0 +#endif + +int get_reserve_fd() +{ + return vsock_fd; +} + +static int do_create_socket(const struct addrinfo *addrinfo) +{ + int fd; + + fd = socket(addrinfo->ai_family, addrinfo->ai_socktype | SOCK_CLOEXEC, 0); + vsock_fd =fd; + if (fd < 0) + return -errno; + return fd; +} + +static int set_socket_timeout(int fd, unsigned int timeout) +{ + struct timeval tv; + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 || + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, + &tv, sizeof(tv)) < 0) + return -errno; + else + return 0; +} +#else + +/* Use it if available */ +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0 +#endif + +static int do_create_socket(const struct addrinfo *addrinfo) +{ + SOCKET s; + + s = WSASocketW(addrinfo->ai_family, addrinfo->ai_socktype, 0, NULL, 0, + WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED); + if (s == INVALID_SOCKET) + return -WSAGetLastError(); + + return (int) s; +} + +static int set_socket_timeout(int fd, unsigned int timeout) +{ + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, + (const char *) &timeout, sizeof(timeout)) < 0 || + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, + (const char *) &timeout, sizeof(timeout)) < 0) + return -WSAGetLastError(); + else + return 0; +} +#endif /* !_WIN32 */ + +/* The purpose of this function is to provide a version of connect() + * that does not ignore timeouts... */ +static int do_connect(int fd, const struct addrinfo *addrinfo, + unsigned int timeout) +{ + int ret, error; + socklen_t len; +#ifdef _WIN32 + struct timeval tv; + struct timeval *ptv; + fd_set set; +#else + struct pollfd pfd; +#endif + ret = set_blocking_mode(fd, false); + if (ret < 0) { + ERROR("set_blocking_mode failed\n"); + return ret; + } + + ret = connect(fd, addrinfo->ai_addr, (int) addrinfo->ai_addrlen); + if (ret < 0) { + ret = network_get_error(); + if (!network_connect_in_progress(ret)) + return ret; + } + +#ifdef _WIN32 + FD_ZERO(&set); + FD_SET(fd, &set); + + if (timeout != 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + ptv = &tv; + } else { + ptv = NULL; + } + + ret = select(fd + 1, NULL, &set, &set, ptv); +#else + pfd.fd = fd; + pfd.events = POLLOUT | POLLERR; + pfd.revents = 0; + + do { + ret = poll(&pfd, 1, timeout); + } while (ret == -1 && errno == EINTR); +#endif + if (ret < 0) + return network_get_error(); + + if (ret == 0) + return -NETWORK_ERR_TIMEOUT; + + /* Verify that we don't have an error */ + len = sizeof(error); + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len); + if(ret < 0) + return network_get_error(); + + if (error) + return -error; + + ret = set_blocking_mode(fd, true); + if (ret < 0) + return ret; + return 0; +} + +static int is_host_socket(int ai_family) +{ + switch (ai_family) { + case AF_UNIX: + case AF_VSOCK: + return true; + default: + return false; + } +} + +static int is_vm_socket(int ai_family) +{ + switch (ai_family) { + case AF_VSOCK: + return true; + default: + return false; + } +} + +static int is_inet_socket(int ai_family) +{ + switch (ai_family) { + case AF_INET: + case AF_INET6: + return true; + default: + return false; + } +} + +static int create_socket(const struct addrinfo *addrinfo, unsigned int timeout) +{ + int ret, fd, yes = 1; + + fd = do_create_socket(addrinfo); + if (fd < 0) + return fd; + + ret = do_connect(fd, addrinfo, timeout); + if (ret < 0) { + close(fd); + return ret; + } + + set_socket_timeout(fd, timeout); + + if (is_inet_socket(addrinfo->ai_family)) { + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, + (const char *) &yes, sizeof(yes)) < 0) { + ret = -errno; + close(fd); + return ret; + } + } + + return fd; +} + +static int network_open(const struct iio_device *dev, + size_t samples_count, bool cyclic) +{ + struct iio_context_pdata *pdata = dev->ctx->pdata; + struct iio_device_pdata *ppdata = dev->pdata; + int ret = -EBUSY; + + iio_mutex_lock(ppdata->lock); + if (ppdata->io_ctx.fd >= 0) + goto out_mutex_unlock; + + ret = create_socket(pdata->addrinfo, DEFAULT_TIMEOUT_MS); + if (ret < 0) { + ERROR("Create socket: %d\n", ret); + goto out_mutex_unlock; + } + + ppdata->io_ctx.fd = ret; + ppdata->io_ctx.cancelled = false; + ppdata->io_ctx.cancellable = false; + ppdata->io_ctx.timeout_ms = DEFAULT_TIMEOUT_MS; + + ret = iiod_client_register_client_id(dev->ctx->pdata->iiod_client, + &ppdata->io_ctx, dev->ctx->client_id); + if (ret < 0) + printf ("Failed to register client id: %i\n", ret); + + ret = iiod_client_open_unlocked(pdata->iiod_client, + &ppdata->io_ctx, dev, samples_count, cyclic); + if (ret < 0) { + ERROR("Open unlocked: %d\n", ret); + goto err_close_socket; + } + + ret = setup_cancel(&ppdata->io_ctx); + if (ret < 0) + goto err_close_socket; + + set_socket_timeout(ppdata->io_ctx.fd, pdata->io_ctx.timeout_ms); + + ppdata->io_ctx.timeout_ms = pdata->io_ctx.timeout_ms; + ppdata->io_ctx.cancellable = true; + ppdata->is_tx = iio_device_is_tx(dev); + ppdata->is_cyclic = cyclic; + ppdata->wait_for_err_code = false; +#ifdef WITH_NETWORK_GET_BUFFER + ppdata->mmap_len = samples_count * iio_device_get_sample_size(dev); +#endif + + iio_mutex_unlock(ppdata->lock); + + return 0; + +err_close_socket: + close(ppdata->io_ctx.fd); + ppdata->io_ctx.fd = -1; +out_mutex_unlock: + iio_mutex_unlock(ppdata->lock); + return ret; +} + +static int network_close(const struct iio_device *dev) +{ + struct iio_device_pdata *pdata = dev->pdata; + int ret = -EBADF; + + iio_mutex_lock(pdata->lock); + + if (pdata->io_ctx.fd >= 0) { + if (!pdata->io_ctx.cancelled) { + ret = iiod_client_close_unlocked( + dev->ctx->pdata->iiod_client, + &pdata->io_ctx, dev); + + write_command(&pdata->io_ctx, "\r\nEXIT\r\n"); + } else { + ret = 0; + } + + cleanup_cancel(&pdata->io_ctx); + close(pdata->io_ctx.fd); + pdata->io_ctx.fd = -1; + } + +#ifdef WITH_NETWORK_GET_BUFFER + if (pdata->memfd >= 0) + close(pdata->memfd); + pdata->memfd = -1; + + if (pdata->mmap_addr) { + munmap(pdata->mmap_addr, pdata->mmap_len); + pdata->mmap_addr = NULL; + } +#endif + + iio_mutex_unlock(pdata->lock); + return ret; +} + +static ssize_t network_read(const struct iio_device *dev, void *dst, size_t len, + uint32_t *mask, size_t words) +{ + struct iio_device_pdata *pdata = dev->pdata; + ssize_t ret; + + iio_mutex_lock(pdata->lock); + ret = iiod_client_read_unlocked(dev->ctx->pdata->iiod_client, + &pdata->io_ctx, dev, dst, len, mask, words); + iio_mutex_unlock(pdata->lock); + + return ret; +} + +static ssize_t network_write(const struct iio_device *dev, + const void *src, size_t len) +{ + struct iio_device_pdata *pdata = dev->pdata; + ssize_t ret; + + iio_mutex_lock(pdata->lock); + ret = iiod_client_write_unlocked(dev->ctx->pdata->iiod_client, + &pdata->io_ctx, dev, src, len); + iio_mutex_unlock(pdata->lock); + + return ret; +} + +#ifdef WITH_NETWORK_GET_BUFFER + +static ssize_t read_all(struct iio_network_io_context *io_ctx, + void *dst, size_t len) +{ + uintptr_t ptr = (uintptr_t) dst; + while (len) { + ssize_t ret = network_recv(io_ctx, (void *) ptr, len, 0); + if (ret < 0) { + ERROR("NETWORK RECV: %zu\n", ret); + return ret; + } + ptr += ret; + len -= ret; + } + return (ssize_t)(ptr - (uintptr_t) dst); +} + +static int read_integer(struct iio_network_io_context *io_ctx, long *val) +{ + unsigned int i; + char buf[1024], *ptr; + ssize_t ret; + bool found = false; + + for (i = 0; i < sizeof(buf) - 1; i++) { + ret = read_all(io_ctx, buf + i, 1); + if (ret < 0) + return (int) ret; + + /* Skip the eventual first few carriage returns. + * Also stop when a dot is found (for parsing floats) */ + if (buf[i] != '\n' && buf[i] != '.') + found = true; + else if (found) + break; + } + + buf[i] = '\0'; + ret = (ssize_t) strtol(buf, &ptr, 10); + if (ptr == buf) + return -EINVAL; + *val = (long) ret; + return 0; +} + +static ssize_t network_read_mask(struct iio_network_io_context *io_ctx, + uint32_t *mask, size_t words) +{ + long read_len; + ssize_t ret; + + ret = read_integer(io_ctx, &read_len); + if (ret < 0) { + ERROR("READ INTEGER: %zu\n", ret); + return ret; + } + + if (read_len > 0 && mask) { + size_t i; + char buf[9]; + + buf[8] = '\0'; + DEBUG("Reading mask\n"); + + for (i = words; i > 0; i--) { + ret = read_all(io_ctx, buf, 8); + if (ret < 0) + return ret; + + sscanf(buf, "%08x", &mask[i - 1]); + DEBUG("mask[%lu] = 0x%x\n", + (unsigned long)(i - 1), mask[i - 1]); + } + } + + if (read_len > 0) { + char c; + ssize_t nb = read_all(io_ctx, &c, 1); + if (nb > 0 && c != '\n') + read_len = -EIO; + } + + return (ssize_t) read_len; +} + +static ssize_t read_error_code(struct iio_network_io_context *io_ctx) +{ + /* + * The server returns two integer codes. + * The first one is returned right after the WRITEBUF command is issued, + * and corresponds to the error code returned when the server attempted + * to open the device. + * If zero, a second error code is returned, that corresponds (if positive) + * to the number of bytes written. + * + * To speed up things, we delay error reporting. We just send out the + * data without reading the error code that the server gives us, because + * the answer will take too much time. If an error occured, it will be + * reported by the next call to iio_buffer_push(). + */ + + unsigned int i; + long resp = 0; + + for (i = 0; i < 2; i++) { + ssize_t ret = read_integer(io_ctx, &resp); + if (ret < 0) + return ret; + if (resp < 0) + return (ssize_t) resp; + } + + return (ssize_t) resp; +} + +static ssize_t write_rwbuf_command(const struct iio_device *dev, + const char *cmd) +{ + struct iio_device_pdata *pdata = dev->pdata; + + if (pdata->wait_for_err_code) { + ssize_t ret = read_error_code(&pdata->io_ctx); + + pdata->wait_for_err_code = false; + if (ret < 0) + return ret; + } + + return write_command(&pdata->io_ctx, cmd); +} + +static ssize_t network_do_splice(struct iio_device_pdata *pdata, size_t len, + bool read) +{ + int pipefd[2]; + int fd_in, fd_out; + ssize_t ret, read_len = len, write_len = 0; + + ret = (ssize_t) pipe2(pipefd, O_CLOEXEC); + if (ret < 0) + return -errno; + + if (read) { + fd_in = pdata->io_ctx.fd; + fd_out = pdata->memfd; + } else { + fd_in = pdata->memfd; + fd_out = pdata->io_ctx.fd; + } + + do { + ret = wait_cancellable(&pdata->io_ctx, read); + if (ret < 0) + goto err_close_pipe; + + if (read_len) { + /* + * SPLICE_F_NONBLOCK is just here to avoid a deadlock when + * splicing from a socket. As the socket is not in + * non-blocking mode, it should never return -EAGAIN. + * TODO(pcercuei): Find why it locks... + * */ + ret = splice(fd_in, NULL, pipefd[1], NULL, read_len, + SPLICE_F_MOVE | SPLICE_F_NONBLOCK); + if (!ret) + ret = -EIO; + if (ret < 0 && errno != EAGAIN) { + ret = -errno; + goto err_close_pipe; + } else if (ret > 0) { + write_len += ret; + read_len -= ret; + } + } + + if (write_len) { + ret = splice(pipefd[0], NULL, fd_out, NULL, write_len, + SPLICE_F_MOVE | SPLICE_F_NONBLOCK); + if (!ret) + ret = -EIO; + if (ret < 0 && errno != EAGAIN) { + ret = -errno; + goto err_close_pipe; + } else if (ret > 0) { + write_len -= ret; + } + } + + } while (write_len || read_len); + +err_close_pipe: + close(pipefd[0]); + close(pipefd[1]); + return ret < 0 ? ret : len; +} + +static ssize_t network_get_buffer(const struct iio_device *dev, + void **addr_ptr, size_t bytes_used, + uint32_t *mask, size_t words) +{ + struct iio_device_pdata *pdata = dev->pdata; + ssize_t ret, read = 0; + int memfd; + + if (pdata->is_cyclic) + return -ENOSYS; + + /* We check early that the temporary file can be created, so that we can + * return -ENOSYS in case it fails, which will indicate that the + * high-speed interface is not available. + * + * O_TMPFILE -> Linux 3.11. + * TODO: use memfd_create (Linux 3.17) */ + memfd = open(P_tmpdir, O_RDWR | O_TMPFILE | O_EXCL | O_CLOEXEC, S_IRWXU); + if (memfd < 0) + return -ENOSYS; + + if (!addr_ptr || words != (dev->nb_channels + 31) / 32) { + close(memfd); + return -EINVAL; + } + + if (pdata->mmap_addr) + munmap(pdata->mmap_addr, pdata->mmap_len); + + if (pdata->mmap_addr && pdata->is_tx) { + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "WRITEBUF %s %lu\r\n", + dev->id, (unsigned long) bytes_used); + + iio_mutex_lock(pdata->lock); + + ret = write_rwbuf_command(dev, buf); + if (ret < 0) + goto err_close_memfd; + + ret = network_do_splice(pdata, bytes_used, false); + if (ret < 0) + goto err_close_memfd; + + pdata->wait_for_err_code = true; + iio_mutex_unlock(pdata->lock); + } + + if (pdata->memfd >= 0) + close(pdata->memfd); + + pdata->memfd = memfd; + + ret = (ssize_t) ftruncate(pdata->memfd, pdata->mmap_len); + if (ret < 0) { + ret = -errno; + ERROR("Unable to truncate temp file: %zi\n", -ret); + return ret; + } + + if (!pdata->is_tx) { + char buf[1024]; + size_t len = pdata->mmap_len; + + iio_snprintf(buf, sizeof(buf), "READBUF %s %lu\r\n", + dev->id, (unsigned long) len); + + iio_mutex_lock(pdata->lock); + ret = write_rwbuf_command(dev, buf); + if (ret < 0) + goto err_unlock; + + do { + ret = network_read_mask(&pdata->io_ctx, mask, words); + if (!ret) + break; + if (ret < 0) + goto err_unlock; + + mask = NULL; /* We read the mask only once */ + + ret = network_do_splice(pdata, ret, true); + if (ret < 0) + goto err_unlock; + + read += ret; + len -= ret; + } while (len); + + iio_mutex_unlock(pdata->lock); + } + + pdata->mmap_addr = mmap(NULL, pdata->mmap_len, + PROT_READ | PROT_WRITE, MAP_SHARED, pdata->memfd, 0); + if (pdata->mmap_addr == MAP_FAILED) { + pdata->mmap_addr = NULL; + ret = -errno; + ERROR("Unable to mmap: %zi\n", -ret); + return ret; + } + + *addr_ptr = pdata->mmap_addr; + return read ? read : (ssize_t) bytes_used; + +err_close_memfd: + close(memfd); +err_unlock: + iio_mutex_unlock(pdata->lock); + return ret; +} +#endif + +static ssize_t network_read_dev_attr(const struct iio_device *dev, + const char *attr, char *dst, size_t len, enum iio_attr_type type) +{ + struct iio_context_pdata *pdata = dev->ctx->pdata; + + return iiod_client_read_attr(pdata->iiod_client, + &pdata->io_ctx, dev, NULL, attr, dst, len, type); +} + +static ssize_t network_write_dev_attr(const struct iio_device *dev, + const char *attr, const char *src, size_t len, enum iio_attr_type type) +{ + struct iio_context_pdata *pdata = dev->ctx->pdata; + + return iiod_client_write_attr(pdata->iiod_client, + &pdata->io_ctx, dev, NULL, attr, src, len, type); +} + +static ssize_t network_read_chn_attr(const struct iio_channel *chn, + const char *attr, char *dst, size_t len) +{ + struct iio_context_pdata *pdata = chn->dev->ctx->pdata; + + return iiod_client_read_attr(pdata->iiod_client, + &pdata->io_ctx, chn->dev, chn, attr, dst, len, false); +} + +static ssize_t network_write_chn_attr(const struct iio_channel *chn, + const char *attr, const char *src, size_t len) +{ + struct iio_context_pdata *pdata = chn->dev->ctx->pdata; + + return iiod_client_write_attr(pdata->iiod_client, + &pdata->io_ctx, chn->dev, chn, attr, src, len, false); +} + +static int network_get_trigger(const struct iio_device *dev, + const struct iio_device **trigger) +{ + struct iio_context_pdata *pdata = dev->ctx->pdata; + + return iiod_client_get_trigger(pdata->iiod_client, + &pdata->io_ctx, dev, trigger); +} + +static int network_set_trigger(const struct iio_device *dev, + const struct iio_device *trigger) +{ + struct iio_context_pdata *pdata = dev->ctx->pdata; + + return iiod_client_set_trigger(pdata->iiod_client, + &pdata->io_ctx, dev, trigger); +} + +static void network_shutdown(struct iio_context *ctx) +{ + struct iio_context_pdata *pdata = ctx->pdata; + unsigned int i; + + iio_mutex_lock(pdata->lock); + write_command(&pdata->io_ctx, "\r\nEXIT\r\n"); + close(pdata->io_ctx.fd); + iio_mutex_unlock(pdata->lock); + + for (i = 0; i < ctx->nb_devices; i++) { + struct iio_device *dev = ctx->devices[i]; + struct iio_device_pdata *dpdata = dev->pdata; + + if (dpdata) { + network_close(dev); + iio_mutex_destroy(dpdata->lock); + free(dpdata); + } + } + + iiod_client_destroy(pdata->iiod_client); + iio_mutex_destroy(pdata->lock); + if (pdata->addrinfo->ai_family == AF_UNIX || + is_vm_socket(pdata->addrinfo->ai_family)) + free(pdata->addrinfo->ai_addr); + else + freeaddrinfo(pdata->addrinfo); + free(pdata); +} + +static int network_get_version(const struct iio_context *ctx, + unsigned int *major, unsigned int *minor, char git_tag[8]) +{ + return iiod_client_get_version(ctx->pdata->iiod_client, + &ctx->pdata->io_ctx, major, minor, git_tag); +} + +static unsigned int calculate_remote_timeout(unsigned int timeout) +{ + /* XXX(pcercuei): We currently hardcode timeout / 2 for the backend used + * by the remote. Is there something better to do here? */ + return timeout / 2; +} + +static int network_set_timeout(struct iio_context *ctx, unsigned int timeout) +{ + struct iio_context_pdata *pdata = ctx->pdata; + int ret, fd = pdata->io_ctx.fd; + + ret = set_socket_timeout(fd, timeout); + if (!ret) { + unsigned int remote_timeout = calculate_remote_timeout(timeout); + + ret = iiod_client_set_timeout(pdata->iiod_client, + &pdata->io_ctx, remote_timeout); + if (!ret) + pdata->io_ctx.timeout_ms = timeout; + } + if (ret < 0) { + char buf[1024]; + iio_strerror(-ret, buf, sizeof(buf)); + WARNING("Unable to set R/W timeout: %s\n", buf); + } + return ret; +} + +static int network_set_kernel_buffers_count(const struct iio_device *dev, + unsigned int nb_blocks) +{ + struct iio_context_pdata *pdata = dev->ctx->pdata; + + return iiod_client_set_kernel_buffers_count(pdata->iiod_client, + &pdata->io_ctx, dev, nb_blocks); +} + +static struct iio_context * network_clone(const struct iio_context *ctx) +{ + const char *addr = iio_context_get_attr_value(ctx, "ip,ip-addr"); + + return iio_create_network_context(addr); +} + +static const struct iio_backend_ops network_ops = { + .clone = network_clone, + .open = network_open, + .close = network_close, + .read = network_read, + .write = network_write, +#ifdef WITH_NETWORK_GET_BUFFER + .get_buffer = network_get_buffer, +#endif + .read_device_attr = network_read_dev_attr, + .write_device_attr = network_write_dev_attr, + .read_channel_attr = network_read_chn_attr, + .write_channel_attr = network_write_chn_attr, + .get_trigger = network_get_trigger, + .set_trigger = network_set_trigger, + .shutdown = network_shutdown, + .get_version = network_get_version, + .set_timeout = network_set_timeout, + .set_kernel_buffers_count = network_set_kernel_buffers_count, + + .cancel = network_cancel, +}; + +static ssize_t network_write_data(struct iio_context_pdata *pdata, + void *io_data, const char *src, size_t len) +{ + struct iio_network_io_context *io_ctx = io_data; + + return network_send(io_ctx, src, len, 0); +} + +static ssize_t network_read_data(struct iio_context_pdata *pdata, + void *io_data, char *dst, size_t len) +{ + struct iio_network_io_context *io_ctx = io_data; + + return network_recv(io_ctx, dst, len, 0); +} + +static ssize_t network_read_line(struct iio_context_pdata *pdata, + void *io_data, char *dst, size_t len) +{ + bool found = false; + size_t i; +#ifdef __linux__ + struct iio_network_io_context *io_ctx = io_data; + ssize_t ret; + size_t bytes_read = 0; + + do { + size_t to_trunc; + + if (pdata->peek_supported) + ret = network_recv(io_ctx, dst, len, MSG_PEEK); + else + ret = network_recv(io_ctx, dst, 1, 0); + if (ret < 0) + return ret; + + /* Lookup for the trailing \n */ + for (i = 0; i < (size_t) ret && dst[i] != '\n'; i++); + found = i < (size_t) ret; + + len -= ret; + dst += ret; + + if (found) + to_trunc = i + 1; + else + to_trunc = (size_t) ret; + + /* Advance the read offset to the byte following the \n if + * found, or after the last charater read otherwise */ + if (pdata->peek_supported) { + if (pdata->msg_trunc_supported) + ret = network_recv(io_ctx, NULL, to_trunc, MSG_TRUNC); + else + ret = network_recv(io_ctx, dst - ret, to_trunc, 0); + if (ret < 0) { + ERROR("NETWORK RECV: %zu\n", ret); + return ret; + } + } + + bytes_read += to_trunc; + } while (!found && len); + + if (!found) { + ERROR("EIO: %zu\n", ret); + return -EIO; + } else + return bytes_read; +#else + for (i = 0; i < len - 1; i++) { + ssize_t ret = network_read_data(pdata, io_data, dst + i, 1); + + if (ret < 0) + return ret; + + if (dst[i] != '\n') + found = true; + else if (found) + break; + } + + if (!found || i == len - 1) + return -EIO; + + return (ssize_t) i + 1; +#endif +} + +static const struct iiod_client_ops network_iiod_client_ops = { + .write = network_write_data, + .read = network_read_data, + .read_line = network_read_line, +}; + +#ifdef __linux__ +/* + * As of build 16299, Windows Subsystem for Linux presents a Linux API but + * without support for MSG_TRUNC. Since WSL allows running native Linux + * applications this is not something that can be detected at compile time. If + * we want to support WSL we have to have a runtime workaround. + */ +static bool msg_trunc_supported(struct iio_network_io_context *io_ctx, + int ai_family) +{ + int ret; + + if (is_host_socket(ai_family)) + return false; + + ret = network_recv(io_ctx, + NULL, + 0, + MSG_TRUNC | MSG_DONTWAIT); + + return ret != -EFAULT && ret != -EINVAL; +} + +static bool peek_supported(int ai_family) +{ + return !is_vm_socket(ai_family); +} + +#else +static bool msg_trunc_supported(struct iio_network_io_context *io_ctx, + int ai_family) +{ + return false; +} + +static bool peek_supported(int ai_family) +{ + return false; +} +#endif + +struct iio_context * do_network_create_context(struct addrinfo* ai) +{ + struct iio_context *ctx; + struct iio_context_pdata *pdata; + char *description; + size_t i, len; + int ret; + int fd = create_socket(ai, DEFAULT_TIMEOUT_MS); + if (fd < 0) { + ERROR("I could not create socket: %d\n", fd); + errno = -fd; + goto err_exit; + } + pdata = zalloc(sizeof(*pdata)); + if (!pdata) { + errno = ENOMEM; + goto err_close_socket; + } + + pdata->io_ctx.fd = fd; + pdata->addrinfo = ai; + pdata->io_ctx.timeout_ms = DEFAULT_TIMEOUT_MS; + + pdata->lock = iio_mutex_create(); + if (!pdata->lock) { + errno = ENOMEM; + goto err_free_pdata; + } + pdata->iiod_client = iiod_client_new(pdata, pdata->lock, + &network_iiod_client_ops); + + pdata->msg_trunc_supported = msg_trunc_supported(&pdata->io_ctx, + ai->ai_family); + pdata->peek_supported = peek_supported(ai->ai_family); + + if (pdata->msg_trunc_supported) + DEBUG("MSG_TRUNC is supported\n"); + else + DEBUG("MSG_TRUNC is NOT supported\n"); + + if (!pdata->iiod_client) + goto err_destroy_mutex; + + DEBUG("Creating context...\n"); + ctx = iiod_client_create_context(pdata->iiod_client, &pdata->io_ctx); + if (!ctx) + goto err_destroy_iiod_client; + + /* Override the name and low-level functions of the XML context + * with those corresponding to the network context */ + ctx->name = "network"; + ctx->ops = &network_ops; + ctx->pdata = pdata; + + if (is_host_socket(ai->ai_family)) + len = PATH_MAX; + else +#ifdef HAVE_IPV6 + len = INET6_ADDRSTRLEN + IF_NAMESIZE + 2; +#else + len = INET_ADDRSTRLEN + 1; +#endif + + description = malloc(len); + if (!description) { + ret = -ENOMEM; + goto err_network_shutdown; + } + + description[0] = '\0'; + + if (ai->ai_family == AF_UNIX) { + struct sockaddr_un* un = (struct sockaddr_un*)ai->ai_addr; + strncpy(description, un->sun_path, len); + } + if (is_vm_socket(ai->ai_family)) { + struct sockaddr_vm* vm = (struct sockaddr_vm*)ai->ai_addr; + snprintf(description, len, "%d:%d", vm->svm_cid, vm->svm_port); + } +#ifdef HAVE_IPV6 + if (ai->ai_family == AF_INET6) { + struct sockaddr_in6 *in = (struct sockaddr_in6 *) ai->ai_addr; + char *ptr; + inet_ntop(AF_INET6, &in->sin6_addr, + description, INET6_ADDRSTRLEN); + + if (IN6_IS_ADDR_LINKLOCAL(&in->sin6_addr)) { + ptr = if_indextoname(in->sin6_scope_id, description + + strlen(description) + 1); + if (!ptr) { + ret = -errno; + ERROR("Unable to lookup interface of IPv6 address\n"); + goto err_free_description; + } + + *(ptr - 1) = '%'; + } + } +#endif + if (ai->ai_family == AF_INET) { + struct sockaddr_in *in = (struct sockaddr_in *) ai->ai_addr; +#if (!_WIN32 || _WIN32_WINNT >= 0x600) + inet_ntop(AF_INET, &in->sin_addr, description, INET_ADDRSTRLEN); +#else + char *tmp = inet_ntoa(in->sin_addr); + strncpy(description, tmp, len); +#endif + } + + ret = iio_context_add_attr(ctx, "ip,ip-addr", description); + if (ret < 0) + goto err_free_description; + + for (i = 0; i < ctx->nb_devices; i++) { + struct iio_device *dev = ctx->devices[i]; + + dev->pdata = zalloc(sizeof(*dev->pdata)); + if (!dev->pdata) { + ret = -ENOMEM; + goto err_free_description; + } + + dev->pdata->io_ctx.fd = -1; + dev->pdata->io_ctx.timeout_ms = DEFAULT_TIMEOUT_MS; +#ifdef WITH_NETWORK_GET_BUFFER + dev->pdata->memfd = -1; +#endif + + dev->pdata->lock = iio_mutex_create(); + if (!dev->pdata->lock) { + ret = -ENOMEM; + goto err_free_description; + } + } + + if (ctx->description) { + size_t desc_len = strlen(description); + size_t new_size = desc_len + strlen(ctx->description) + 2; + char *ptr, *new_description = realloc(description, new_size); + if (!new_description) { + ret = -ENOMEM; + goto err_free_description; + } + + ptr = strrchr(new_description, '\0'); + iio_snprintf(ptr, new_size - desc_len, " %s", ctx->description); + free(ctx->description); + + ctx->description = new_description; + } else { + ctx->description = description; + } + + iiod_client_set_timeout(pdata->iiod_client, &pdata->io_ctx, + calculate_remote_timeout(DEFAULT_TIMEOUT_MS)); + return ctx; + +err_free_description: + free(description); +err_network_shutdown: + iio_context_destroy(ctx); + errno = -ret; + return NULL; + +err_destroy_iiod_client: + iiod_client_destroy(pdata->iiod_client); +err_destroy_mutex: + iio_mutex_destroy(pdata->lock); +err_free_pdata: + free(pdata); +err_close_socket: + close(fd); +err_exit: + return NULL; +} + +struct iio_context * network_create_context(const char *host) +{ + struct addrinfo hints, *res; + int ret; + struct iio_context *ctx; +#ifdef _WIN32 + WSADATA wsaData; + + ret = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (ret < 0) { + ERROR("WSAStartup failed with error %i\n", ret); + errno = -ret; + return NULL; + } +#endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + +#ifdef HAVE_AVAHI + if (!host) { + char addr_str[AVAHI_ADDRESS_STR_MAX]; + char port_str[6]; + AvahiAddress address; + uint16_t port = IIOD_PORT; + + memset(&address, 0, sizeof(address)); + + ret = discover_host(&address, &port); + if (ret < 0) { + char buf[1024]; + iio_strerror(-ret, buf, sizeof(buf)); + DEBUG("Unable to find host: %s\n", buf); + errno = -ret; + return NULL; + } + + avahi_address_snprint(addr_str, sizeof(addr_str), &address); + iio_snprintf(port_str, sizeof(port_str), "%hu", port); + ret = getaddrinfo(addr_str, port_str, &hints, &res); + } else +#endif + { + ret = getaddrinfo(host, IIOD_PORT_STR, &hints, &res); + } + + if (ret) { + ERROR("Unable to find host: %s\n", gai_strerror(ret)); +#ifndef _WIN32 + if (ret != EAI_SYSTEM) + errno = -ret; +#endif + return NULL; + } + + ctx = do_network_create_context(res); + if (ctx == NULL) + goto err_free_addrinfo; + else + return ctx; + +err_free_addrinfo: + freeaddrinfo(res); + return NULL; +} + +static struct iio_context * sock_create_context(sa_family_t family, + struct sockaddr* addr, + size_t addrlen) +{ + struct addrinfo *ai = zalloc(sizeof(*ai)); + struct iio_context *network_context; + + if (!ai) { + errno = ENOMEM; + return NULL; + } + + ai->ai_family = addr->sa_family = family; + ai->ai_socktype = SOCK_STREAM; + + ai->ai_addr = addr; + ai->ai_addrlen = addrlen; + + network_context = do_network_create_context(ai); + if (network_context == NULL) + free(ai); + + return network_context; +} + +struct iio_context * vm_create_context(unsigned int port) +{ + struct iio_context *ctx; + struct sockaddr_vm *addr = zalloc(sizeof(*addr)); + + if (!addr) { + errno = ENOMEM; + goto err_exit; + } + addr->svm_port = port; + addr->svm_cid = VMADDR_CID_HOST; + + ctx = sock_create_context(AF_VSOCK, (struct sockaddr*)addr, + sizeof(*addr)); + if (ctx == NULL) { + goto err_free; + } + else + return ctx; + +err_free: + free(addr); +err_exit: + return NULL; +} diff --git a/sensors/aidl/libiio_client/sort.h b/sensors/aidl/libiio_client/sort.h new file mode 100644 index 0000000..9f7cf33 --- /dev/null +++ b/sensors/aidl/libiio_client/sort.h @@ -0,0 +1,28 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2018 Analog Devices, Inc. + * Author: Robin Getz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#ifndef __IIO_QSORT_H__ +#define __IIO_QSORT_H__ + +int iio_channel_compare(const void *p1, const void *p2); +int iio_channel_attr_compare(const void *p1, const void *p2); +int iio_device_compare(const void *p1, const void *p2); +int iio_device_attr_compare(const void *p1, const void *p2); +int iio_buffer_attr_compare(const void *p1, const void *p2); + +#endif /* __IIO_QSORT_H__ */ diff --git a/sensors/aidl/libiio_client/utilities.c b/sensors/aidl/libiio_client/utilities.c new file mode 100644 index 0000000..b9dd42e --- /dev/null +++ b/sensors/aidl/libiio_client/utilities.c @@ -0,0 +1,214 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +/* Force the XSI version of strerror_r */ +#undef _GNU_SOURCE + +#include "iio-config.h" +#include "iio-private.h" + +#include +#include +#include +#include +#include + +#if defined(_WIN32) || (defined(__USE_XOPEN2K8) && \ + (!defined(__UCLIBC__) || defined(__UCLIBC_HAS_LOCALE__))) +#define LOCALE_SUPPORT +#endif + +#ifdef LOCALE_SUPPORT +#if defined(__MINGW32__) || (!defined(_WIN32) && !defined(HAS_NEWLOCALE)) +static int read_double_locale(const char *str, double *val) +{ + char *end, *old_locale; + double value; + + /* XXX: This is not thread-safe, but it's the only way we have to + * support locales under MinGW without linking with Visual Studio + * libraries. */ + old_locale = iio_strdup(setlocale(LC_NUMERIC, NULL)); + if (!old_locale) + return -ENOMEM; + + setlocale(LC_NUMERIC, "C"); + value = strtod(str, &end); + setlocale(LC_NUMERIC, old_locale); + free(old_locale); + + if (end == str) + return -EINVAL; + + *val = value; + return 0; +} + +static int write_double_locale(char *buf, size_t len, double val) +{ + /* XXX: Not thread-safe, see above */ + char *old_locale = iio_strdup(setlocale(LC_NUMERIC, NULL)); + if (!old_locale) + return -ENOMEM; + + setlocale(LC_NUMERIC, "C"); + iio_snprintf(buf, len, "%f", val); + setlocale(LC_NUMERIC, old_locale); + free(old_locale); + return 0; +} +#elif defined(_WIN32) +static int read_double_locale(const char *str, double *val) +{ + char *end; + double value; + _locale_t locale = _create_locale(LC_NUMERIC, "C"); + if (!locale) + return -ENOMEM; + + value = _strtod_l(str, &end, locale); + _free_locale(locale); + + if (end == str) + return -EINVAL; + + *val = value; + return 0; +} + +static int write_double_locale(char *buf, size_t len, double val) +{ + _locale_t locale = _create_locale(LC_NUMERIC, "C"); + if (!locale) + return -ENOMEM; + + _snprintf_l(buf, len, "%f", locale, val); + _free_locale(locale); + return 0; +} +#else +static int read_double_locale(const char *str, double *val) +{ + char *end; + double value; + locale_t old_locale, new_locale; + + new_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); + if (!new_locale) + return -errno; + + old_locale = uselocale(new_locale); + + value = strtod(str, &end); + uselocale(old_locale); + freelocale(new_locale); + + if (end == str) + return -EINVAL; + + *val = value; + return 0; +} + +static int write_double_locale(char *buf, size_t len, double val) +{ + locale_t old_locale, new_locale; + + new_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); + if (!new_locale) + return -errno; + + old_locale = uselocale(new_locale); + + iio_snprintf(buf, len, "%f", val); + + uselocale(old_locale); + freelocale(new_locale); + return 0; +} +#endif +#endif + +int read_double(const char *str, double *val) +{ +#ifdef LOCALE_SUPPORT + return read_double_locale(str, val); +#else + char *end; + double value = strtod(str, &end); + + if (end == str) + return -EINVAL; + + *val = value; + return 0; +#endif +} + +int write_double(char *buf, size_t len, double val) +{ +#ifdef LOCALE_SUPPORT + return write_double_locale(buf, len, val); +#else + iio_snprintf(buf, len, "%f", val); + return 0; +#endif +} + +void iio_library_get_version(unsigned int *major, + unsigned int *minor, char git_tag[8]) +{ + if (major) + *major = LIBIIO_VERSION_MAJOR; + if (minor) + *minor = LIBIIO_VERSION_MINOR; + if (git_tag) { + strncpy(git_tag, LIBIIO_VERSION_GIT, 8); + git_tag[7] = '\0'; + } +} + +void iio_strerror(int err, char *buf, size_t len) +{ +#if defined(_WIN32) + int ret = strerror_s(buf, len, err); +#elif defined(HAS_STRERROR_R) + int ret = strerror_r(err, buf, len); +#else + /* no strerror_s, no strerror_r... just use the default message */ + int ret = 0xf7de; +#endif + if (ret != 0) + iio_snprintf(buf, len, "Unknown error %i", err); +} + +char *iio_strdup(const char *str) +{ +#if defined(_WIN32) + return _strdup(str); +#elif defined(HAS_STRDUP) + return strdup(str); +#else + size_t len = strlen(str); + char *buf = malloc(len + 1); + + if (buf) + memcpy(buf, str, len + 1); + return buf; +#endif +} diff --git a/sensors/aidl/libiio_client/xml.c b/sensors/aidl/libiio_client/xml.c new file mode 100644 index 0000000..d2fe3b0 --- /dev/null +++ b/sensors/aidl/libiio_client/xml.c @@ -0,0 +1,479 @@ +/* + * libiio - Library for interfacing industrial I/O (IIO) devices + * + * Copyright (C) 2014 Analog Devices, Inc. + * Author: Paul Cercueil + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * */ + +#include "debug.h" +#include "iio-private.h" + +#include +#include +#include + +static int add_attr_to_channel(struct iio_channel *chn, xmlNode *n) +{ + xmlAttr *attr; + char *name = NULL, *filename = NULL; + struct iio_channel_attr *attrs; + + for (attr = n->properties; attr; attr = attr->next) { + if (!strcmp((char *) attr->name, "name")) { + name = iio_strdup((char *) attr->children->content); + // name = (char *) attr->children->content; + // if (!name) + // goto err_free; + } else if (!strcmp((char *) attr->name, "filename")) { + filename = iio_strdup((char *) attr->children->content); + // filename = (char *) attr->children->content; + // if (!filename) + // goto err_free; + } else { + WARNING("Unknown field \'%s\' in channel %s\n", + attr->name, chn->id); + } + } + + if (!name) { + ERROR("Incomplete attribute in channel %s\n", chn->id); + goto err_free; + } + + if (!filename) { + filename = iio_strdup(name); + if (!filename) + goto err_free; + } + + attrs = realloc(chn->attrs, (1 + chn->nb_attrs) * + sizeof(struct iio_channel_attr)); + if (!attrs) + goto err_free; + + attrs[chn->nb_attrs].filename = filename; + attrs[chn->nb_attrs++].name = name; + chn->attrs = attrs; + return 0; + +err_free: + if (name) + free(name); + if (filename) + free(filename); + return -1; +} + +static int add_attr_to_device(struct iio_device *dev, xmlNode *n, enum iio_attr_type type) +{ + xmlAttr *attr; + char **attrs, *name = NULL; + + for (attr = n->properties; attr; attr = attr->next) { + if (!strcmp((char *) attr->name, "name")) { + name = iio_strdup((char *) attr->children->content); + // name = (char *) attr->children->content; + } else { + WARNING("Unknown field \'%s\' in device %s\n", + attr->name, dev->id); + } + } + + if (!name) { + ERROR("Incomplete attribute in device %s\n", dev->id); + goto err_free; + } + + switch(type) { + case IIO_ATTR_TYPE_DEBUG: + attrs = realloc(dev->debug_attrs, + (1 + dev->nb_debug_attrs) * sizeof(char *)); + break; + case IIO_ATTR_TYPE_DEVICE: + attrs = realloc(dev->attrs, + (1 + dev->nb_attrs) * sizeof(char *)); + break; + case IIO_ATTR_TYPE_BUFFER: + attrs = realloc(dev->buffer_attrs, + (1 + dev->nb_buffer_attrs) * sizeof(char *)); + break; + default: + attrs = NULL; + break; + } + if (!attrs) + goto err_free; + + switch(type) { + case IIO_ATTR_TYPE_DEBUG: + attrs[dev->nb_debug_attrs++] = name; + dev->debug_attrs = attrs; + break; + case IIO_ATTR_TYPE_DEVICE: + attrs[dev->nb_attrs++] = name; + dev->attrs = attrs; + break; + case IIO_ATTR_TYPE_BUFFER: + attrs[dev->nb_buffer_attrs++] = name; + dev->buffer_attrs = attrs; + break; + } + + return 0; + +err_free: + if (name) + free(name); + return -1; +} + +static void setup_scan_element(struct iio_channel *chn, xmlNode *n) +{ + xmlAttr *attr; + + for (attr = n->properties; attr; attr = attr->next) { + const char *name = (const char *) attr->name, + *content = (const char *) attr->children->content; + if (!strcmp(name, "index")) { + chn->index = atol(content); + } else if (!strcmp(name, "format")) { + char e, s; + if (strchr(content, 'X')) { + sscanf(content, "%ce:%c%u/%uX%u>>%u", &e, &s, + &chn->format.bits, + &chn->format.length, + &chn->format.repeat, + &chn->format.shift); + } else { + chn->format.repeat = 1; + sscanf(content, "%ce:%c%u/%u>>%u", &e, &s, + &chn->format.bits, + &chn->format.length, + &chn->format.shift); + } + chn->format.is_be = e == 'b'; + chn->format.is_signed = (s == 's' || s == 'S'); + chn->format.is_fully_defined = (s == 'S' || s == 'U' || + chn->format.bits == chn->format.length); + } else if (!strcmp(name, "scale")) { + chn->format.with_scale = true; + chn->format.scale = atof(content); + } else { + WARNING("Unknown attribute \'%s\' in \n", + name); + } + } +} + +static struct iio_channel * create_channel(struct iio_device *dev, xmlNode *n) +{ + xmlAttr *attr; + struct iio_channel *chn = zalloc(sizeof(*chn)); + if (!chn) + return NULL; + + chn->dev = dev; + + /* Set the default index value < 0 (== no index) */ + chn->index = -ENOENT; + + for (attr = n->properties; attr; attr = attr->next) { + const char *name = (const char *) attr->name, + *content = (const char *) attr->children->content; + if (!strcmp(name, "name")) { + chn->name = iio_strdup(content); + } else if (!strcmp(name, "id")) { + chn->id = iio_strdup(content); + } else if (!strcmp(name, "type")) { + if (!strcmp(content, "output")) + chn->is_output = true; + else if (strcmp(content, "input")) + WARNING("Unknown channel type %s\n", content); + } else { + WARNING("Unknown attribute \'%s\' in \n", + name); + } + } + + if (!chn->id) { + ERROR("Incomplete \n"); + goto err_free_channel; + } + + for (n = n->children; n; n = n->next) { + if (!strcmp((char *) n->name, "attribute")) { + if (add_attr_to_channel(chn, n) < 0) + goto err_free_channel; + } else if (!strcmp((char *) n->name, "scan-element")) { + chn->is_scan_element = true; + setup_scan_element(chn, n); + } else if (strcmp((char *) n->name, "text")) { + WARNING("Unknown children \'%s\' in \n", + n->name); + continue; + } + } + + iio_channel_init_finalize(chn); + + return chn; + +err_free_channel: + free_channel(chn); + return NULL; +} + +static struct iio_device * create_device(struct iio_context *ctx, xmlNode *n) +{ + xmlAttr *attr; + struct iio_device *dev = zalloc(sizeof(*dev)); + if (!dev) + return NULL; + + dev->ctx = ctx; + + for (attr = n->properties; attr; attr = attr->next) { + if (!strcmp((char *) attr->name, "name")) { + dev->name = iio_strdup( + (char *) attr->children->content); + } else if (!strcmp((char *) attr->name, "id")) { + dev->id = iio_strdup((char *) attr->children->content); + } else { + WARNING("Unknown attribute \'%s\' in \n", + attr->name); + } + } + + if (!dev->id) { + ERROR("Unable to read device ID\n"); + goto err_free_device; + } + + for (n = n->children; n; n = n->next) { + if (!strcmp((char *) n->name, "channel")) { + struct iio_channel **chns, + *chn = create_channel(dev, n); + if (!chn) { + ERROR("Unable to create channel\n"); + goto err_free_device; + } + + chns = realloc(dev->channels, (1 + dev->nb_channels) * + sizeof(struct iio_channel *)); + if (!chns) { + ERROR("Unable to allocate memory\n"); + if (chn->name) + free(chn->name); + if (chn->id) + free(chn->id); + free(chn); + goto err_free_device; + } + + chns[dev->nb_channels++] = chn; + dev->channels = chns; + } else if (!strcmp((char *) n->name, "attribute")) { + if (add_attr_to_device(dev, n, IIO_ATTR_TYPE_DEVICE) < 0) + goto err_free_device; + } else if (!strcmp((char *) n->name, "debug-attribute")) { + if (add_attr_to_device(dev, n, IIO_ATTR_TYPE_DEBUG) < 0) + goto err_free_device; + } else if (!strcmp((char *) n->name, "buffer-attribute")) { + if (add_attr_to_device(dev, n, IIO_ATTR_TYPE_BUFFER) < 0) + goto err_free_device; + } else if (strcmp((char *) n->name, "text")) { + WARNING("Unknown children \'%s\' in \n", + n->name); + continue; + } + } + + dev->words = (dev->nb_channels + 31) / 32; + if (dev->words) { + dev->mask = calloc(dev->words, sizeof(*dev->mask)); + if (!dev->mask) { + errno = ENOMEM; + goto err_free_device; + } + } + + return dev; + +err_free_device: + free_device(dev); + return NULL; +} + +static struct iio_context * xml_clone(const struct iio_context *ctx) +{ + return xml_create_context_mem(ctx->xml, strlen(ctx->xml)); +} + +static const struct iio_backend_ops xml_ops = { + .clone = xml_clone, +}; + +static int parse_context_attr(struct iio_context *ctx, xmlNode *n) +{ + xmlAttr *attr; + const char *name = NULL, *value = NULL; + + for (attr = n->properties; attr; attr = attr->next) { + if (!strcmp((const char *) attr->name, "name")) { + name = (const char *) attr->children->content; + } else if (!strcmp((const char *) attr->name, "value")) { + value = (const char *) attr->children->content; + } + } + + if (!name || !value) + return -EINVAL; + else + return iio_context_add_attr(ctx, name, value); +} + +static struct iio_context * iio_create_xml_context_helper(xmlDoc *doc) +{ + unsigned int i; + xmlNode *root, *n; + xmlAttr *attr; + int err = -ENOMEM; + struct iio_context *ctx = zalloc(sizeof(*ctx)); + if (!ctx) + goto err_set_errno; + + ctx->name = "xml"; + ctx->ops = &xml_ops; + + root = xmlDocGetRootElement(doc); + + if (!root) { + err = -EINVAL; + goto err_free_ctx; + } + + if (strcmp((char *) root->name, "context")) { + ERROR("Unrecognized XML file\n"); + err = -EINVAL; + goto err_free_ctx; + } + + for (attr = root->properties; attr; attr = attr->next) { + if (!strcmp((char *) attr->name, "description")) + ctx->description = iio_strdup( + (char *) attr->children->content); + else if (strcmp((char *) attr->name, "name")) + WARNING("Unknown parameter \'%s\' in \n", + (char *) attr->children->content); + } + + for (n = root->children; n; n = n->next) { + struct iio_device **devs, *dev; + + if (!strcmp((char *) n->name, "context-attribute")) { + err = parse_context_attr(ctx, n); + if (err) + goto err_free_devices; + else + continue; + } else if (strcmp((char *) n->name, "device")) { + if (strcmp((char *) n->name, "text")) + WARNING("Unknown children \'%s\' in " + "\n", n->name); + continue; + } + + dev = create_device(ctx, n); + if (!dev) { + ERROR("Unable to create device\n"); + goto err_free_devices; + } + + devs = realloc(ctx->devices, (1 + ctx->nb_devices) * + sizeof(struct iio_device *)); + if (!devs) { + ERROR("Unable to allocate memory\n"); + if (dev->name) + free(dev->name); + if (dev->id) + free(dev->id); + free(dev); + goto err_free_devices; + } + + devs[ctx->nb_devices++] = dev; + ctx->devices = devs; + } + + err = iio_context_init(ctx); + if (err) + goto err_free_devices; + + return ctx; + +err_free_devices: + for (i = 0; i < ctx->nb_devices; i++) + free_device(ctx->devices[i]); + if (ctx->nb_devices) + free(ctx->devices); + for (i = 0; i < ctx->nb_attrs; i++) { + free(ctx->attrs[i]); + free(ctx->values[i]); + } + free(ctx->attrs); + free(ctx->values); +err_free_ctx: + free(ctx); +err_set_errno: + errno = -err; + return NULL; +} + +struct iio_context * xml_create_context(const char *xml_file) +{ + struct iio_context *ctx; + xmlDoc *doc; + + LIBXML_TEST_VERSION; + + doc = xmlReadFile(xml_file, NULL, XML_PARSE_DTDVALID); + if (!doc) { + ERROR("Unable to parse XML file\n"); + errno = EINVAL; + return NULL; + } + + ctx = iio_create_xml_context_helper(doc); + xmlFreeDoc(doc); + return ctx; +} + +struct iio_context * xml_create_context_mem(const char *xml, size_t len) +{ + struct iio_context *ctx; + xmlDoc *doc; + + LIBXML_TEST_VERSION; + + doc = xmlReadMemory(xml, (int) len, NULL, NULL, XML_PARSE_DTDVALID); + if (!doc) { + ERROR("Unable to parse XML file\n"); + errno = EINVAL; + return NULL; + } + + ctx = iio_create_xml_context_helper(doc); + xmlFreeDoc(doc); + return ctx; +} diff --git a/sensors/aidl/sensors-aidl.rc b/sensors/aidl/sensors-aidl.rc new file mode 100644 index 0000000..37a65b5 --- /dev/null +++ b/sensors/aidl/sensors-aidl.rc @@ -0,0 +1,8 @@ +service vendor.sensors-default /vendor/bin/hw/android.hardware.sensors@aidl-service.intel + interface aidl android.hardware.sensors.ISensors/default + class hal + user system + group system + group system wakelock uhid context_hub + capabilities BLOCK_SUSPEND + rlimit rtprio 10 10 diff --git a/sensors/aidl/sensors-default.xml b/sensors/aidl/sensors-aidl.xml similarity index 85% rename from sensors/aidl/sensors-default.xml rename to sensors/aidl/sensors-aidl.xml index 36b28ed..7898a6b 100644 --- a/sensors/aidl/sensors-default.xml +++ b/sensors/aidl/sensors-aidl.xml @@ -1,7 +1,7 @@ android.hardware.sensors - 2 + 1 ISensors/default diff --git a/sensors/aidl/sensors-default.rc b/sensors/aidl/sensors-default.rc deleted file mode 100644 index 96da85d..0000000 --- a/sensors/aidl/sensors-default.rc +++ /dev/null @@ -1,5 +0,0 @@ -service vendor.sensors-default /vendor/bin/hw/android.hardware.sensors-service.example - class hal - user system - group system - rlimit rtprio 10 10 From 7055c301691bb8cb298db1947728dc35657069c2 Mon Sep 17 00:00:00 2001 From: "Ranjan, Rajani" Date: Wed, 9 Aug 2023 12:05:52 +0530 Subject: [PATCH 3/5] changing sensors@aidl to version_2 in manifest.xml to inline with compatibilty matrix. google compatibility matrix for android u requires sensors@aidl version 2. android.hardware.sensors@2.0::ISensors/default is deprecated in compatibility matrix at FCM Version 8. The minimum requirement for Sensors HAL in android 14 should be AIDL implementation to meet target-level 8, API level 34 requirement. Tracked-On: OAM-112804 Signed-off-by: Ranjan, Rajani --- sensors/aidl/sensors-aidl.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/aidl/sensors-aidl.xml b/sensors/aidl/sensors-aidl.xml index 7898a6b..36b28ed 100644 --- a/sensors/aidl/sensors-aidl.xml +++ b/sensors/aidl/sensors-aidl.xml @@ -1,7 +1,7 @@ android.hardware.sensors - 1 + 2 ISensors/default From 3f2748e6317f0d8d5dbd48aa1b3d214626897729 Mon Sep 17 00:00:00 2001 From: "Ranjan, Rajani" Date: Mon, 4 Sep 2023 13:33:42 +0530 Subject: [PATCH 4/5] adding debug logs for AIDL sensors HAL. Tracked-On: OAM-112804 Signed-off-by: Ranjan, Rajani --- sensors/aidl/Sensor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sensors/aidl/Sensor.cpp b/sensors/aidl/Sensor.cpp index 735fea0..62732d3 100644 --- a/sensors/aidl/Sensor.cpp +++ b/sensors/aidl/Sensor.cpp @@ -21,6 +21,7 @@ #include #include +#define DEBUG 0 using ::ndk::ScopedAStatus; @@ -175,7 +176,7 @@ std::vector Sensor::readEvents() { } event.payload.set(vec3); events.push_back(event); -#if 1 // Probing data to debug +#ifdef DEBUG // Probing data to debug ALOGD("readEvents: handle(%d) name -> %s data[%f, %f, %f]", mSensorInfo.sensorHandle, mSensorInfo.name.c_str(), vec3.x, vec3.y, vec3.z); @@ -535,4 +536,4 @@ HingeAngleSensor::HingeAngleSensor(int32_t sensorHandle, ISensorsEventCallback* } // namespace sensors } // namespace hardware } // namespace android -} // namespace aidl \ No newline at end of file +} // namespace aidl From 5c08f61744a055cb46caa9ecb2876e143d8e64c7 Mon Sep 17 00:00:00 2001 From: "Ranjan, Rajani" Date: Wed, 11 Oct 2023 15:06:03 +0530 Subject: [PATCH 5/5] fixing DEBUG variable value to enable/disable logs Tracked-On: OAM-112804 Signed-off-by: Ranjan, Rajani --- sensors/aidl/Sensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/aidl/Sensor.cpp b/sensors/aidl/Sensor.cpp index 62732d3..16b036b 100644 --- a/sensors/aidl/Sensor.cpp +++ b/sensors/aidl/Sensor.cpp @@ -21,7 +21,7 @@ #include #include -#define DEBUG 0 +#undef DEBUG using ::ndk::ScopedAStatus;