blob: e6be81670becbe3b78aecf709adb36f3c106ceec [file] [log] [blame]
/*
* Copyright 2020 Google LLC
*
* 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.
*/
// Basic read/write routines for SMBus devices.
#ifndef THIRD_PARTY_ECCLESIA_LIB_IO_SMBUS_SMBUS_H_
#define THIRD_PARTY_ECCLESIA_LIB_IO_SMBUS_SMBUS_H_
#include <cstddef>
#include <cstdint>
#include <iosfwd>
#include <optional>
#include <tuple>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "types/fixed_range_int.h"
namespace ecclesia {
// An indentifier representing an I2C/SMBus. These are identifiers used and
// allocated by the kernel; they have no meaning at the protocol level.
class SmbusBus : public FixedRangeInteger<SmbusBus, int, 0, 255> {
public:
explicit constexpr SmbusBus(BaseType value) : BaseType(value) {}
};
// An I2C/SMBus address. This corresponds to the actual address on the wire.
// Uses the 7-bit form of the address.
class SmbusAddress : public FixedRangeInteger<SmbusAddress, int, 0x00, 0x7f> {
public:
explicit constexpr SmbusAddress(BaseType value) : BaseType(value) {}
};
// Device location information for an I2C/SMBus device.
class SmbusLocation {
public:
constexpr SmbusLocation(SmbusBus bus, SmbusAddress address)
: bus_(bus), address_(address) {}
SmbusLocation(const SmbusLocation &) = default;
SmbusLocation &operator=(const SmbusLocation &) = default;
// Create an SmbusLocation whose range is statically checked at compile time.
template <int bus, int address>
static constexpr SmbusLocation Make() {
return SmbusLocation(SmbusBus::Make<bus>(), SmbusAddress::Make<address>());
}
// Create an SmbusLocation whose range is checked at run time.
static std::optional<SmbusLocation> TryMake(int bus, int address) {
auto maybe_bus = SmbusBus::TryMake(bus);
auto maybe_address = SmbusAddress::TryMake(address);
if (!maybe_bus || !maybe_address) {
return std::nullopt;
}
return SmbusLocation(*maybe_bus, *maybe_address);
}
// Try to create SmbusLocation from bus+address string representation
static std::optional<SmbusLocation> FromString(
absl::string_view smbus_location);
// Accessors for reading the bus and address.
SmbusBus bus() const { return bus_; }
SmbusAddress address() const { return address_; }
// Relational operators. Order is equivalent to <bus, address> tuple.
friend bool operator==(const SmbusLocation &lhs, const SmbusLocation &rhs) {
return std::tie(lhs.bus_, lhs.address_) == std::tie(rhs.bus_, rhs.address_);
}
friend bool operator!=(const SmbusLocation &lhs, const SmbusLocation &rhs) {
return !(lhs == rhs);
}
friend bool operator<(const SmbusLocation &lhs, const SmbusLocation &rhs) {
return std::tie(lhs.bus_, lhs.address_) < std::tie(rhs.bus_, rhs.address_);
}
friend bool operator>(const SmbusLocation &lhs, const SmbusLocation &rhs) {
return rhs < lhs;
}
friend bool operator<=(const SmbusLocation &lhs, const SmbusLocation &rhs) {
return !(rhs < lhs);
}
friend bool operator>=(const SmbusLocation &lhs, const SmbusLocation &rhs) {
return !(lhs < rhs);
}
// Support hashing of locations.
template <typename H>
friend H AbslHashValue(H h, const SmbusLocation &loc) {
return H::combine(std::move(h), loc.bus_, loc.address_);
}
// String conversion. This deliberately follows the bus+address format that
// the kernel uses in sysfs.
friend std::ostream &operator<<(std::ostream &os,
const SmbusLocation &location) {
return os << absl::StreamFormat("%d-%04x", location.bus_.value(),
location.address_.value());
}
private:
SmbusBus bus_;
SmbusAddress address_;
};
// An interface to access SMBus devices.
class SmbusAccessInterface {
public:
SmbusAccessInterface() {}
virtual ~SmbusAccessInterface() = default;
// Probe for presence of a device at the requested location.
// NOTE: This is *NOT* guaranteed to detect all types of devices.
virtual absl::Status ProbeDevice(const SmbusLocation &loc) const = 0;
// Sends/Receives a byte of data to/from a SMBus device.
virtual absl::Status WriteQuick(const SmbusLocation &loc,
uint8_t data) const = 0;
virtual absl::Status SendByte(const SmbusLocation &loc,
uint8_t data) const = 0;
virtual absl::Status ReceiveByte(const SmbusLocation &loc,
uint8_t *data) const = 0;
// Reads/Writes data to a SMBus device specifying the command code
// (or register location).
virtual absl::Status Write8(const SmbusLocation &loc, int command,
uint8_t data) const = 0;
virtual absl::Status Read8(const SmbusLocation &loc, int command,
uint8_t *data) const = 0;
virtual absl::Status Write16(const SmbusLocation &loc, int command,
uint16_t data) const = 0;
virtual absl::Status Read16(const SmbusLocation &loc, int command,
uint16_t *data) const = 0;
// Reads/Writes a block of data to a SMBus device specifying the
// command code (or register location).
// Args:
// command: command code or register location
// data: buffer to put/receive data
// len: output parameter for number of bytes read
virtual absl::Status WriteBlockI2C(
const SmbusLocation &loc, int command,
absl::Span<const unsigned char> data) const = 0;
virtual absl::Status ReadBlockI2C(const SmbusLocation &loc, int command,
absl::Span<unsigned char> data,
size_t *len) const = 0;
virtual bool SupportBlockRead(const SmbusLocation &loc) const = 0;
};
// A class to encapsulate a single Smbus device at a specified location.
class SmbusDevice {
public:
// Create an SMBus device using the provided AccessInterface for all device
// accesses. The AccessInterface must not be NULL.
SmbusDevice(SmbusLocation location, const SmbusAccessInterface *access)
: location_(std::move(location)), access_(access) {}
// Get a pointer to this Device's AccessInterface.
const SmbusAccessInterface *access() const { return access_; }
// Get this Device's address. Subclasses might generate this dynamically.
const SmbusLocation &location() const { return location_; }
// Using SmbusAccessInterface to access a SMBus device
// specifying the command code (or register location).
absl::Status WriteQuick(uint8_t data) const {
return access()->WriteQuick(location(), data);
}
absl::Status SendByte(uint8_t data) const {
return access()->SendByte(location(), data);
}
absl::Status ReceiveByte(uint8_t *data) const {
return access()->ReceiveByte(location(), data);
}
absl::Status Write8(int command, uint8_t data) const {
return access()->Write8(location(), command, data);
}
absl::Status Read8(int command, uint8_t *data) const {
return access()->Read8(location(), command, data);
}
absl::Status Write16(int command, uint16_t data) const {
return access()->Write16(location(), command, data);
}
absl::Status Read16(int command, uint16_t *data) const {
return access()->Read16(location(), command, data);
}
bool SupportBlockRead() const {
return access()->SupportBlockRead(location());
}
absl::Status WriteBlockI2C(int command,
absl::Span<const unsigned char> data) const {
return access()->WriteBlockI2C(location(), command, data);
}
absl::Status ReadBlockI2C(int command, absl::Span<unsigned char> data,
size_t *len) const {
return access()->ReadBlockI2C(location(), command, data, len);
}
private:
SmbusLocation location_;
const SmbusAccessInterface *access_;
};
} // namespace ecclesia
#endif // THIRD_PARTY_ECCLESIA_LIB_IO_SMBUS_SMBUS_H_