1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include "pw_bytes/span.h" 17 #include "pw_chrono/system_clock.h" 18 #include "pw_i2c/address.h" 19 #include "pw_i2c/initiator.h" 20 #include "pw_result/result.h" 21 #include "pw_status/status.h" 22 #include "pw_sync/lock_annotations.h" 23 #include "pw_sync/timed_mutex.h" 24 25 namespace pw::i2c { 26 27 /// Initiator interface implementation using the Linux userspace i2c-dev driver. 28 /// 29 /// Takes exclusive control of an I2C bus device (ex. "/dev/i2c-0"). The user is 30 /// responsible to open the device node prior to creating the initiator. The 31 /// file descriptor is closed when the initiator object is destroyed. 32 /// 33 /// The bus device must support the full I2C functionality. Users of the class 34 /// are encouraged to use the `OpenI2cBus` helper to ensure the bus is valid. 35 /// 36 /// Access to the bus is guarded by an internal mutex, so this initiator can be 37 /// safely used from multiple threads. 38 /// 39 class LinuxInitiator final : public Initiator { 40 public: 41 /// Open an I2C bus and validate that full I2C functionality is supported. 42 /// 43 /// @param[in] bus_path Path to the I2C bus device node. 44 /// 45 /// @retval OK The device node was opened successfully. 46 /// @retval InvalidArgument Failed to open the device node or to validate I2C 47 /// functionality. 48 /// 49 /// @return The open file descriptor on success. 50 /// 51 static Result<int> OpenI2cBus(const char* bus_path); 52 53 /// Construct an instantiator using an open file descriptor. 54 /// The file descriptor is closed during destruction. 55 /// 56 /// @param[in] fd Valid file descriptor for an I2C device node. 57 /// 58 LinuxInitiator(int fd); 59 60 LinuxInitiator(const LinuxInitiator&) = delete; 61 LinuxInitiator& operator=(const LinuxInitiator&) = delete; 62 63 ~LinuxInitiator() override; 64 65 private: 66 /// Implement pw::i2c::Initiator with the following additional requriements: 67 /// - Asserts that `device_address` is a 7-bit address. 68 /// - At least one of `tx_buffer` or `rx_buffer` must be not empty. 69 /// Otherwise, returns InvalidArgument. 70 /// 71 /// @note 72 /// The timeout is used both for getting an exclusive lock on the initiator 73 /// and for getting exclusive use of a multi-controller bus. If the timeout 74 /// is zero or negative, the transaction will only execute if there is no 75 /// contention at either level. 76 /// 77 Status DoWriteReadFor(Address device_address, 78 ConstByteSpan tx_buffer, 79 ByteSpan rx_buffer, 80 chrono::SystemClock::duration timeout) override 81 PW_LOCKS_EXCLUDED(mutex_); 82 83 Status DoWriteReadForLocked(uint8_t address, 84 ConstByteSpan tx_buffer, 85 ByteSpan rx_buffer, 86 chrono::SystemClock::duration timeout) 87 PW_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 88 89 /// The file descriptor for the i2c-dev device representing this bus. 90 const int fd_; 91 92 /// This mutex is used to synchronize access across multiple retries. 93 sync::TimedMutex mutex_; 94 }; 95 96 } // namespace pw::i2c 97