blob: 2faa84951c5e03ffb17fe1a8ec04088ec8d14ac7 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DIAGNOSTICS_WILCO_DTC_SUPPORTD_TELEMETRY_EC_SERVICE_H_
#define DIAGNOSTICS_WILCO_DTC_SUPPORTD_TELEMETRY_EC_SERVICE_H_
#include <cstdint>
#include <memory>
#include <string>
#include <base/callback.h>
#include <base/files/file_path.h>
#include <base/files/scoped_file.h>
#include <base/macros.h>
#include <base/observer_list.h>
#include <base/observer_list_types.h>
#include <base/sequence_checker_impl.h>
#include <base/single_thread_task_runner.h>
#include <base/threading/simple_thread.h>
#include "diagnostics/wilco_dtc_supportd/ec_constants.h"
namespace diagnostics {
namespace internal {
class EcEventMonitoringThreadDelegate;
} // namespace internal
// Reads EC telemetry data and subscribes on EC events then distributes them to
// the list of observers.
class EcService {
public:
// A packet of data sent by the EC when it notices certain events have
// occurred, such as the battery, AC adapter, or USB-C state changing.
// The format of this packet is a variable length sequence of 16-bit words.
// Word 0 is the |size| word, representing the number of following
// words in the struct. Word 1 is the |type| word. The following |size|-1
// words are the |payload|. Depending on the value of |type|, the |payload|
// is interpreted in different ways. There are other possible values of |type|
// and other interpretations of |payload| than those listed here. There will
// be, at most, 6 words in the |payload|. See section 2.3 "ACPI EC Event
// notification" of the Wilco EC specification at go/wilco-ec-spec for more
// information.
struct alignas(2) EcEvent {
public:
// Derived value representing the reason/cause of the EC event.
//
// NOTE: This is a computed value and not sent by the EC.
enum Reason {
// |SYSTEM_NOTIFY| EC event types:
//
// When |SystemNotifySubType| is "AC_ADAPTER" and
// |AcAdapterFlags::Cause::NON_WILCO_CHARGER| is true.
kNonWilcoCharger,
// When |SystemNotifySubType| is "AC_ADAPTER" and
// |AcAdapterFlags::Cause::LOW_POWER_CHARGER| is true.
kLowPowerCharger,
// When |SystemNotifySubType| is "BATTERY" and
// |BatteryFlags::Cause::BATTERY_AUTH| is true.
kBatteryAuth,
// When |SystemNotifySubType| is "USB_C" and
// |UsbCFlags::Billboard::HDMI_USBC_CONFLICT| is true.
kDockDisplay,
// When |SystemNotifySubType| is "USB_C" and
// |UsbCFlags::Dock|::THUNDERBOLT_UNSUPPORTED_USING_USBC| is true.
kDockThunderbolt,
// When |SystemNotifySubType| is "USB_C" and
// |UsbCFlags::Dock::INCOMPATIBLE_DOCK| is true.
kIncompatibleDock,
// When |SystemNotifySubType| is "USB_C" and
// |UsbCFlags::Dock::OVERTEMP_ERROR| is true.
kDockError,
// |SYSTEM_NOTIFY| EC event type with no appropiate |SystemNotifySubType|
// or flags.
kSysNotification,
// Non |SYSTEM_NOTIFY| EC event type.
kNonSysNotification,
};
// The |type| member will be one of these.
enum Type : uint16_t {
// Interpret |payload| as SystemNotifyPayload.
SYSTEM_NOTIFY = 0x0012,
};
// Sub-types applicable for SystemNotifyPayload.
enum SystemNotifySubType : uint16_t {
AC_ADAPTER = 0x0000,
BATTERY = 0x0003,
USB_C = 0x0008,
};
// Flags used within |SystemNotifyPayload|.
struct alignas(2) AcAdapterFlags {
enum Cause : uint16_t {
// Barrel charger is incompatible and performance will be restricted.
NON_WILCO_CHARGER = 1 << 0,
// Attached charger does not supply enough power.
LOW_POWER_CHARGER = 1 << 1,
};
uint16_t reserved0;
Cause cause;
uint16_t reserved2;
uint16_t reserved3;
uint16_t reserved4;
};
// Flags used within |SystemNotifyPayload|.
struct alignas(2) BatteryFlags {
enum Cause : uint16_t {
// An incompatible battery is connected and battery will not charge.
BATTERY_AUTH = 1 << 0,
};
uint16_t reserved0;
Cause cause;
uint16_t reserved2;
uint16_t reserved3;
uint16_t reserved4;
};
// Flags used within |SystemNotifyPayload|
struct alignas(2) UsbCFlags {
// "Billboard" is the name taken directly from the EC spec. It's a weird
// name, but these can represent a variety of miscellaneous events.
enum Billboard : uint16_t {
// HDMI and USB Type-C ports on the dock cannot be used for
// displays at the same time. Only the first one connected will work.
HDMI_USBC_CONFLICT = 1 << 9,
};
enum Dock : uint16_t {
// Thunderbolt is not supported on Chromebooks, so the dock
// will fall back on using USB Type-C.
THUNDERBOLT_UNSUPPORTED_USING_USBC = 1 << 8,
// Attached dock is incompatible.
INCOMPATIBLE_DOCK = 1 << 12,
// Attached dock has overheated.
OVERTEMP_ERROR = 1 << 15,
};
Billboard billboard;
uint16_t reserved1;
Dock dock;
};
// Interpretation of |payload| applicable when |type|==Type::SYSTEM_NOTIFY.
struct alignas(2) SystemNotifyPayload {
SystemNotifySubType sub_type;
// Depending on |sub_type| we interpret the following data in different
// ways. Note that these flags aren't all the same size.
union SystemNotifyFlags {
AcAdapterFlags ac_adapter;
BatteryFlags battery;
UsbCFlags usb_c;
} flags;
};
EcEvent();
EcEvent(uint16_t num_words_in_payload,
Type type,
const uint16_t payload[6]);
bool operator==(const EcEvent& other) const {
return memcmp(this, &other, sizeof(*this)) == 0;
}
// Extracts the EC event's reason from the event's |Type|,
// |SystemNotifySubType| and flags.
Reason GetReason() const;
// Translates the |size| member into how many bytes of |payload| are used.
size_t PayloadSizeInBytes() const;
// |size| is the number of following 16-bit words in the event.
// Default is 1 to account for |type| word and empty |payload|.
uint16_t size = 1;
Type type = static_cast<Type>(0);
// Depending on |type| we interpret the following data in different ways.
union {
SystemNotifyPayload system_notify;
} payload = {};
};
class Observer : public base::CheckedObserver {
public:
virtual ~Observer() = default;
// Called when event from EC was received.
virtual void OnEcEvent(const EcEvent& ec_event) = 0;
};
struct GetEcTelemetryResponse {
enum Status {
STATUS_UNSET,
// The EC telemetry command was successfully completed.
STATUS_OK,
// The EC telemetry command was rejected due to the empty request payload.
STATUS_ERROR_INPUT_PAYLOAD_EMPTY,
// The EC telemetry command was rejected due to the request payload being
// too large.
STATUS_ERROR_INPUT_PAYLOAD_MAX_SIZE_EXCEEDED,
// The EC telemetry command was failed due to EC driver error.
STATUS_ERROR_ACCESSING_DRIVER,
};
Status status;
std::string payload;
};
EcService();
EcService(const EcService&) = delete;
EcService& operator=(const EcService&) = delete;
virtual ~EcService();
// Starts service.
bool Start();
// Shuts down service.
void ShutDown(base::Closure on_shutdown_callback);
// Reads the telemetry information.
GetEcTelemetryResponse GetEcTelemetry(const std::string& request_payload);
// Overrides the file system root directory for file operations in tests.
void set_root_dir_for_testing(const base::FilePath& root_dir) {
root_dir_ = root_dir;
}
// Overrides the |event_fd_events_| in tests.
void set_event_fd_events_for_testing(int16_t events) {
event_fd_events_ = events;
}
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
bool HasObserver(Observer* observer);
protected:
base::ObserverList<Observer> observers_;
private:
// Signal via writing to the |shutdown_fd_| that the monitoring thread should
// shut down. Once the monitoring thread handles this event and gets ready
// for shutting down, it will reply by scheduling an invocation of
// OnShutdown() on the foreground thread.
void ShutDownMonitoringThread();
// This is called on the |task_runner_| when new EC event
// was received by background monitoring thread.
void OnEventAvailable(const EcEvent& ec_event);
// This is called on the |task_runner_| when the background
// monitoring thread is shutting down.
void OnShutdown();
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// This callback will be invoked after current service shutdown.
base::Closure on_shutdown_callback_;
// The file system root directory. Can be overridden in tests.
base::FilePath root_dir_{"/"};
// EC event |event_fd_| and |event_fd_events_| are using for |poll()|
// function in |monitoring_thread_|. Both can be overridden in tests.
base::ScopedFD event_fd_;
int16_t event_fd_events_ = kEcEventFilePollEvents;
// Shutdown event fd. It is used to stop |poll()| immediately and shutdown
// |monitoring_thread_|.
base::ScopedFD shutdown_fd_;
// The delegate which will be executed on the |monitoring_thread_|.
std::unique_ptr<internal::EcEventMonitoringThreadDelegate>
monitoring_thread_delegate_;
// The backgroung thread monitoring the EC sysfs file for upcoming events.
std::unique_ptr<base::SimpleThread> monitoring_thread_;
base::SequenceCheckerImpl sequence_checker_;
};
} // namespace diagnostics
#endif // DIAGNOSTICS_WILCO_DTC_SUPPORTD_TELEMETRY_EC_SERVICE_H_