diff --git a/sensors/aidl/Android.bp b/sensors/aidl/Android.bp new file mode 100644 index 0000000..c3f681d --- /dev/null +++ b/sensors/aidl/Android.bp @@ -0,0 +1,118 @@ +/* + * 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. + */ + +soong_config_module_type { + name: "senPlugin_cc_default", + module_type: "cc_defaults", + config_namespace: "senPlugin", + bool_variables: ["SENSOR_LIST"], + properties: ["cflags", "srcs"], +} + +senPlugin_cc_default { + name: "senPlugin_default", + soong_config_variables: { + SENSOR_LIST: { cflags: ["-DSENSOR_LIST_ENABLED"],}, + }, +} + +cc_library_static { + name: "libsensorsaidlimpl", + vendor: true, + shared_libs: [ + "libbase", + "libfmq", + "libpower", + "libbinder_ndk", + "android.hardware.sensors-V1-ndk", + ], + export_include_dirs: ["include"], + srcs: [ + "Sensors.cpp", + "Sensor.cpp", + ], + visibility: [ + ":__subpackages__", + "//hardware/interfaces/tests/extension/sensors:__subpackages__", + ], +} + +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@aidl-service.intel", + defaults: ["senPlugin_default"], + relative_install_path: "hw", + init_rc: ["sensors-aidl.rc"], + vintf_fragments: ["sensors-aidl.xml"], + vendor: true, + local_include_dirs: [ + "libiio_client" + ], + shared_libs: [ + "libbase", + "libbinder_ndk", + "libfmq", + "libpower", + "libcutils", + "liblog", + "libutils", + "android.hardware.sensors-V1-ndk", + "libxml2", + ], + static_libs: [ + "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 new file mode 100644 index 0000000..16b036b --- /dev/null +++ b/sensors/aidl/Sensor.cpp @@ -0,0 +1,539 @@ +/* + * 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 +#include "utils/SystemClock.h" + +#include +#include +#undef DEBUG + +using ::ndk::ScopedAStatus; + +namespace aidl { +namespace android { +namespace hardware { +namespace sensors { + +static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000; + +Sensor::Sensor(ISensorsEventCallback* callback) + : mIsEnabled(false), + mSamplingPeriodNs(0), + mLastSampleTimeNs(0), + mCallback(callback), + mMode(OperationMode::NORMAL) { + iioc = iioClient::get_iioClient(); + 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(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(); + } +} + +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(); + } +} + +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_REALTIME, &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)); + 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); +#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); +#endif + 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 = "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.minDelayUs = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_DATA_INJECTION); + +} + +PressureSensor::PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Pressure Sensor"; + 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.minDelayUs = 100 * 1000; // microseconds + mSensorInfo.maxDelayUs = 100 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +} + +MagnetometerSensor::MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Magnetic Field Sensor"; + 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 = 10 * 1000; // microseconds + mSensorInfo.maxDelayUs = 10 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +} + +LightSensor::LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Light Sensor"; + 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.minDelayUs = 200 * 1000; // microseconds + 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); +} + +ProximitySensor::ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Proximity Sensor"; + 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.minDelayUs = 200 * 1000; // microseconds + 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); +} + +GyroSensor::GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback) : Sensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Gyro Sensor"; + mSensorInfo.vendor = "Intel"; + mSensorInfo.version = 1; + 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 = 10 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = 0; +} + +AmbientTempSensor::AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Ambient 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); +} + +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, + ISensorsEventCallback* callback) + : OnChangeSensor(callback) { + mSensorInfo.sensorHandle = sensorHandle; + mSensorInfo.name = "Relative Humidity Sensor"; + mSensorInfo.vendor = "Intel"; + 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 = 40 * 1000 * 10; // microseconds + mSensorInfo.fifoReservedEventCount = 0; + mSensorInfo.fifoMaxEventCount = 0; + mSensorInfo.requiredPermission = ""; + mSensorInfo.flags = static_cast(SensorInfo::SENSOR_FLAG_BITS_ON_CHANGE_MODE); +} + +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 = "Intel"; + 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); +} + + +} // 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/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 new file mode 100644 index 0000000..4c8d286 --- /dev/null +++ b/sensors/aidl/include/sensors-impl/Sensor.h @@ -0,0 +1,172 @@ +/* + * 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 "iioClient.h" +#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; + + iioClient *iioc; + + Sensor(ISensorsEventCallback* callback); + virtual ~Sensor(); + + const SensorInfo& getSensorInfo() const; + void batch(int32_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(); + 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); +}; + +class GyroSensor : public Sensor { + public: + GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class AmbientTempSensor : public OnChangeSensor { + public: + AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class DeviceTempSensor : public OnChangeSensor { + public: + DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class PressureSensor : public Sensor { + public: + PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class MagnetometerSensor : public Sensor { + public: + MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class LightSensor : public OnChangeSensor { + public: + LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class ProximitySensor : public OnChangeSensor { + public: + ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +class RelativeHumiditySensor : public OnChangeSensor { + public: + RelativeHumiditySensor(int32_t sensorHandle, ISensorsEventCallback* callback); +}; + +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); +}; + +} // namespace sensors +} // namespace hardware +} // namespace android +} // 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 new file mode 100644 index 0000000..3757608 --- /dev/null +++ b/sensors/aidl/include/sensors-impl/Sensors.h @@ -0,0 +1,218 @@ +/* + * 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) { +#if SENSOR_LIST_ENABLED + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); + AddSensor(); +#endif + } + + 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 \ 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/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-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-aidl.xml b/sensors/aidl/sensors-aidl.xml new file mode 100644 index 0000000..36b28ed --- /dev/null +++ b/sensors/aidl/sensors-aidl.xml @@ -0,0 +1,7 @@ + + + android.hardware.sensors + 2 + ISensors/default + +