blob: 8fced88e6e66c25645473954c380bc396693dd66 [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 SRC_LIB_DDK_INCLUDE_LIB_DDK_DRIVER_H_
#define SRC_LIB_DDK_INCLUDE_LIB_DDK_DRIVER_H_
#include <lib/async/dispatcher.h>
#include <stdint.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
__BEGIN_CDECLS
typedef struct zx_device zx_device_t;
typedef struct zx_driver zx_driver_t;
typedef struct zx_protocol_device zx_protocol_device_t;
typedef struct zx_device_prop zx_device_prop_t;
typedef struct zx_device_str_prop zx_device_str_prop_t;
typedef struct zx_device_str_prop_val zx_device_str_prop_val_t;
typedef struct zx_driver_rec zx_driver_rec_t;
typedef struct zx_bind_inst zx_bind_inst_t;
typedef struct zx_driver_binding zx_driver_binding_t;
// echo -n "zx_driver_ops_v0.5" | sha256sum | cut -c1-16
#define DRIVER_OPS_VERSION 0x2b3490fa40d9f452
typedef struct zx_driver_ops {
uint64_t version; // DRIVER_OPS_VERSION
// Opportunity to do on-load work. Called ony once, before any other ops are called. The driver
// may optionally return a context pointer to be passed to the other driver ops.
//
// This hook will only be executed on the devhost's main thread.
zx_status_t (*init)(void** out_ctx);
// Requests that the driver bind to the provided device, initialize it, and publish any
// children.
//
// This hook will only be executed on the devhost's main thread.
zx_status_t (*bind)(void* ctx, zx_device_t* device);
// Only provided by bus manager drivers, create() is invoked to instantiate a bus device
// instance in a new device host process
//
// This hook will only be executed on the devhost's main thread.
zx_status_t (*create)(void* ctx, zx_device_t* parent, const char* name, zx_handle_t rpc_channel);
// Last call before driver is unloaded.
//
// This hook will only be executed on the devhost's main thread.
void (*release)(void* ctx);
// Allows the driver to run its hardware unit tests. If tests are enabled for the driver, and
// run_unit_tests() is implemented, then it will be called after init(). If run_unit_tests()
// returns true, indicating that the tests passed, then driver operation continues as normal
// and the driver should be prepared to accept calls to bind(). The tests may write output to
// |channel| in the form of fuchsia.driver.test.Logger messages. The driver-unit-test library
// may be used to assist with the implementation of the tests, including output via |channel|.
//
// This hook will only be executed on the devhost's main thread.
bool (*run_unit_tests)(void* ctx, zx_device_t* parent, zx_handle_t channel);
} zx_driver_ops_t;
// echo -n "device_add_args_v0.5" | sha256sum | cut -c1-16
#define DEVICE_ADD_ARGS_VERSION 0x96a64134d56e88e3
enum {
// Do not attempt to bind drivers to this device automatically
DEVICE_ADD_NON_BINDABLE = (1 << 0),
// Children of this device will be loaded in their own devhost process,
// behind a proxy of this device
DEVICE_ADD_MUST_ISOLATE = (1 << 1),
// This device is allowed to be bindable in multiple composite devices
DEVICE_ADD_ALLOW_MULTI_COMPOSITE = (1 << 2),
};
// Device Manager API
// One of DEV_POWER_STATE_*
typedef uint8_t device_power_state_t;
typedef struct device_power_state_info {
device_power_state_t state_id;
// Restore time for coming out of this state to working D0 state.
zx_duration_t restore_latency;
// Is this device wakeup_capable?
bool wakeup_capable;
// Deepest system system sleep state that the device can wake the system from.
int32_t system_wake_state;
} device_power_state_info_t;
// One of DEV_PERFORMANCE_STATE_*
typedef uint32_t device_performance_state_t;
typedef struct device_performance_state_info {
device_performance_state_t state_id;
// Restore time for coming out of this state to fully performant state.
zx_duration_t restore_latency;
// TODO(ravoorir): Figure out how best can a device have metadata that is
// specific to a performant state of a specific device. For ex: The power
// manager wants to know what a cpu device's operating point is for a
// particular performant state.
} device_performance_state_info_t;
//
typedef struct device_metadata {
uint32_t type;
const void* data;
size_t length;
} device_metadata_t;
typedef struct device_add_args {
// DEVICE_ADD_ARGS_VERSION
uint64_t version;
// Driver name is copied to internal structure
// max length is ZX_DEVICE_NAME_MAX
const char* name;
// Context pointer for use by the driver
// and passed to driver in all zx_protocol_device_t callbacks
void* ctx;
// Pointer to device's device protocol operations
const zx_protocol_device_t* ops;
// Optional list of device properties.
const zx_device_prop_t* props;
// Number of device properties
uint32_t prop_count;
// Optional list of device string properties.
const zx_device_str_prop_t* str_props;
// Number of device string properties
uint32_t str_prop_count;
// Metadata to pass to new device
const device_metadata_t* metadata_list;
// Number of metadata blobs in the list
size_t metadata_count;
// List of power_states that the device supports.
// List cannot be more than MAX_DEVICE_POWER_STATES size.
const device_power_state_info_t* power_states;
// Number of power states in the list
uint8_t power_state_count;
// Optional custom protocol for this device
uint32_t proto_id;
// Optional custom protocol operations for this device
const void* proto_ops;
// Optional list of fidl services to offer to child driver.
// These service will automatically be added as bind properties which may be used in
// bind rules.
// Only the service instance named "default" will be used.
// If provided, the `DEVICE_ADD_MUST_ISOLATE` flag must also be specified, and a proxy will not be
// spawned.
const char** fidl_service_offers;
// The number of elements in the above list.
size_t fidl_service_offer_count;
// Optional list of runtime services to offer to child driver.
// Only the service instance named "default" will be used.
// If provided, the `DEVICE_ADD_MUST_ISOLATE` flag must not be specified.
const char** runtime_service_offers;
// The number of elements in the above list.
size_t runtime_service_offer_count;
// Zero or more of DEVICE_ADD_*
uint32_t flags;
// Optional VMO representing that will get used in devfs inspect tree.
zx_handle_t inspect_vmo;
// Optional client channel end for a fuchsia.io.Directory hosting fidl services specified in
// either |fidl_service_offers| or |runtime_service_offers|.
zx_handle_t outgoing_dir_channel;
} device_add_args_t;
typedef struct device_init_reply_args {
// List of power_states that the device supports.
// List cannot be more than MAX_DEVICE_POWER_STATES size.
const device_power_state_info_t* power_states;
// Number of power states in the list
uint8_t power_state_count;
} device_init_reply_args_t;
typedef struct {
// Optional list of device properties.
// Memory owned by the caller.
zx_device_prop_t* props;
// Size of the device properties array.
// To be filled by the caller.
const uint32_t prop_count;
// Count of properties filled with valid data.
// Will be filled by the callee.
uint32_t actual_prop_count;
// Optional list of device string properties.
// Memory owned by the caller.
zx_device_str_prop_t* str_props;
// Size of the device string properties array.
// To be filled by the caller.
const uint32_t str_prop_count;
// Count of string properties filled with valid data.
// Will be filled by the callee.
uint32_t actual_str_prop_count;
} device_props_args_t;
struct zx_driver_rec {
const zx_driver_ops_t* ops;
zx_driver_t* driver;
};
// This global symbol is initialized by the driver loader in devhost
extern zx_driver_rec_t __zircon_driver_rec__;
zx_status_t device_get_properties(zx_device_t* parent, device_props_args_t* out_args);
zx_status_t device_add_from_driver(zx_driver_t* drv, zx_device_t* parent, device_add_args_t* args,
zx_device_t** out);
// Creates a device and adds it to the devmgr.
// device_add_args_t contains all "in" arguments.
// All device_add_args_t values are copied, so device_add_args_t can be stack allocated.
// The device_add_args_t.name string value is copied.
// All other pointer fields are copied as pointers.
// The newly added device will be active before this call returns, so be sure to have
// the "out" pointer point to your device-local structure so callbacks can access
// it immediately.
//
// If this call is successful, but the device needs to be torn down, device_async_remove() should
// be called. If |args->ctx| is backed by memory, it is the programmer's responsibility to not
// free that memory until the device's |release| hook is called.
static inline zx_status_t device_add(zx_device_t* parent, device_add_args_t* args,
zx_device_t** out) {
return device_add_from_driver(__zircon_driver_rec__.driver, parent, args, out);
}
// This is used to signal completion of the device's |init| hook.
// This will make the device visible and able to be unbound.
// This can be called from any thread - it does not necessarily need to be called before
// the |init| hook returns.
// If |status| is ZX_OK, the driver may provide optional power state information via |args|.
// If |status| is not ZX_OK, the device will be scheduled to be removed.
void device_init_reply(zx_device_t* device, zx_status_t status,
const device_init_reply_args_t* args);
// Schedules the removal of the given device and all its descendents. When a device is
// being removed, its |unbind| hook will be invoked.
// It is safe to call this as long as the device has not completed its |release| hook.
// Multiple requests to remove the same device will have no effect.
void device_async_remove(zx_device_t* device);
// This is used to signal completion of the device's |unbind| hook.
// This does not necessarily need to be called from within the |unbind| hook.
void device_unbind_reply(zx_device_t* device);
// This is used to signal completion of the device's |suspend| hook.
// Need not necessarily need to be called from within the |suspend| hook.
// |status| is the status of the suspend.
// If |status| is success, the |out_state| is same as the requested_state that is
// sent to the suspend hook. If |status| is failure, the |out_state| is the
// state that the device can go into.
void device_suspend_reply(zx_device_t* device, zx_status_t status, uint8_t out_state);
// This is used to signal completion of the device's |resume| hook.
// Need not necessarily need to be called from within the |resume| hook.
// |status| is the status of the resume operation.
// If |status| is success, the |out_perf_state| has the working performance state
// that the device is in currently.
// If |status| is failure, the |out_power_state| has the power state
// the device is in currently.
void device_resume_reply(zx_device_t* device, zx_status_t status, uint8_t out_power_state,
uint32_t out_perf_state);
// Requests that the given thread be assigned a profile with parameters appropriate for the given
// role. The available roles and the specific parameters assigned are device-dependent and may also
// vary across builds. Requests are not guaranteed to be honored for all roles and requestors, and
// may either be rejected or silently ignored, based on system-specific criteria.
//
// |role| is name of the requested role. This must not be nullptr, even if |role_size| is zero.
//
// |thread| is a borrowed handle that must have DUPLICATE, TRANSFER, and MANAGE_THREAD rights. It is
// duplicated internally before transfer to the ProfileProvider service to complete the profile
// assignment.
//
// This API should be used in preference to hard coding parameters in drivers.
zx_status_t device_set_profile_by_role(zx_device_t* device, zx_handle_t thread, const char* role,
size_t role_size);
// A description of a part of a device fragment. It provides a bind program
// that will match a device on the path from the root of the device tree to the
// target device.
typedef struct device_fragment_part {
uint32_t instruction_count;
const zx_bind_inst_t* match_program;
} device_fragment_part_t;
// A description of a device that makes up part of a composite device. The
// particular device is identified by a sequence of part descriptions. Each
// part description must match either the target device or one of its ancestors.
// The first element in |parts| must describe the root of the device tree. The
// last element in |parts| must describe the target device itself. The
// remaining elements of |parts| must match devices on the path from the root to
// the target device, in order. Some of those devices may be skipped, but every
// element of |parts| must have a match. This sequence of matches between
// |parts| and devices must be unique.
typedef struct device_fragment {
const char* name;
uint32_t parts_count;
const device_fragment_part_t* parts;
} device_fragment_t;
typedef enum {
ZX_DEVICE_PROPERTY_VALUE_UNDEFINED = 0,
ZX_DEVICE_PROPERTY_VALUE_INT = 1,
ZX_DEVICE_PROPERTY_VALUE_STRING = 2,
ZX_DEVICE_PROPERTY_VALUE_BOOL = 3,
ZX_DEVICE_PROPERTY_VALUE_ENUM = 4
} device_bind_prop_value_type;
// The value type in zx_device_str_prop_val must match what's
// in the union. To ensure that it is set properly, the struct
// should only be constructed with the supporting macros.
typedef struct zx_device_str_prop_val {
uint8_t data_type;
union {
uint32_t int_val;
const char* str_val;
bool bool_val;
const char* enum_val;
} data;
} zx_device_str_prop_val_t;
// Supporting macros to construct zx_device_str_prop_val_t.
#define str_prop_int_val(val) \
zx_device_str_prop_val { \
.data_type = ZX_DEVICE_PROPERTY_VALUE_INT, .data = {.int_val = (val) } \
}
#define str_prop_str_val(val) \
zx_device_str_prop_val { \
.data_type = ZX_DEVICE_PROPERTY_VALUE_STRING, .data = {.str_val = (val) } \
}
#define str_prop_bool_val(val) \
zx_device_str_prop_val { \
.data_type = ZX_DEVICE_PROPERTY_VALUE_BOOL, .data = {.bool_val = (val) } \
}
#define str_prop_enum_val(val) \
zx_device_str_prop_val { \
.data_type = ZX_DEVICE_PROPERTY_VALUE_ENUM, .data = {.enum_val = (val) } \
}
typedef struct zx_device_str_prop {
const char* key;
zx_device_str_prop_val_t property_value;
} zx_device_str_prop_t;
zx_handle_t get_mmio_resource(zx_device_t* dev);
zx_handle_t get_msi_resource(zx_device_t* dev);
zx_handle_t get_power_resource(zx_device_t* dev);
zx_handle_t get_ioport_resource(zx_device_t* dev);
zx_handle_t get_iommu_resource(zx_device_t* dev);
zx_handle_t get_framebuffer_resource(zx_device_t* dev);
zx_handle_t get_irq_resource(zx_device_t* dev);
zx_handle_t get_smc_resource(zx_device_t* dev);
zx_handle_t get_info_resource(zx_device_t* dev);
zx_status_t load_firmware_from_driver(zx_driver_t* drv, zx_device_t* device, const char* path,
zx_handle_t* fw, size_t* size);
// Drivers may need to load firmware for a device, typically during the call to
// bind the device. The devmgr will look for the firmware at the given path
// relative to system-defined locations for device firmware. The load will be done synchronously,
// blocking the current thread until the firmware is loaded. Care should be taken when using this
// call, as it may cause deadlocks if storage is backed by a driver in the same driver host.
static inline zx_status_t load_firmware(zx_device_t* device, const char* path, zx_handle_t* fw,
size_t* size) {
return load_firmware_from_driver(__zircon_driver_rec__.driver, device, path, fw, size);
}
// Opens a connection to the specified FIDL protocol offered by |device|.
//
// |device| is typically the parent of the device invoking this function.
// |protocol_name| can be constructed with
// fidl::DiscoverableProtocolName<my_protocol_name>.
// |request| must be the server end of a zircon channel.
//
// If you are inside a C++ device class, it may be more convenient to use the
// DdkConnectFidlProtocol wrapper method from ddktl, which supplies |device| and
// |protocol_name| automatically.
zx_status_t device_connect_fidl_protocol(zx_device_t* device, const char* protocol_name,
zx_handle_t request);
// Opens a connection to the specified FIDL service offered by |device|.
//
// |device| is typically the parent of the device invoking this function.
// |service_name| can be constructed with `my_service_name::Name`.
// |request| must be the server end of a zircon channel.
//
// If you are inside a C++ device class, it may be more convenient to use the
// DdkConnectFidlProtocol wrapper method from ddktl, which supplies |device| and
// |service_name| automatically.
zx_status_t device_connect_fidl_protocol2(zx_device_t* device, const char* service_name,
const char* protocol_name, zx_handle_t request);
// Opens a connection to the specified FIDL protocol offered by |device|.
//
// |device| should be a composite device. |fragment_name| picks out the specific
// fragment device to use; it must match the fragment name declared in the
// composite device's bind file.
//
// Arguments are otherwise the same as for device_open_fidl_service. The
// ddktl equivalent is DdkConnectFidlProtocol.
zx_status_t device_connect_fragment_fidl_protocol(zx_device_t* device, const char* fragment_name,
const char* service_name,
const char* protocol_name, zx_handle_t request);
// Returns a string containing the variable for the given |name|. If |out| is not large enough,
// |size_actual| will contain the size of the required buffer. |out| is guaranateed to be null
// terminated. |device| should not be null.
zx_status_t device_get_variable(zx_device_t* device, const char* name, char* out, size_t out_size,
size_t* size_actual);
typedef enum {
DEVICE_BIND_PROPERTY_KEY_UNDEFINED = 0,
DEVICE_BIND_PROPERTY_KEY_INT = 1,
DEVICE_BIND_PROPERTY_KEY_STRING = 2,
} device_bind_prop_key_type;
// The value type in device_bind_prop_key_t must match what's in the union. To ensure that it is
// set properly, the struct should only be constructed with the supporting macros.
typedef struct device_bind_prop_key {
uint8_t key_type;
union {
uint32_t int_key;
const char* str_key;
} data;
} device_bind_prop_key_t;
// Supporting macros to construct device_bind_prop_key_t.
#define device_bind_prop_int_key(val) \
device_bind_prop_key { \
.key_type = DEVICE_BIND_PROPERTY_KEY_INT, .data = {.int_key = (val) } \
}
#define device_bind_prop_str_key(val) \
device_bind_prop_key { \
.key_type = DEVICE_BIND_PROPERTY_KEY_STRING, .data = {.str_key = (val) } \
}
typedef struct device_bind_prop_value {
uint8_t data_type;
union {
uint32_t int_value;
const char* str_value;
bool bool_value;
const char* enum_value;
} data;
} device_bind_prop_value_t;
// Supporting macros to construct device_bind_prop_value_t.
#define device_bind_prop_int_val(val) \
device_bind_prop_value { \
.data_type = ZX_DEVICE_PROPERTY_VALUE_INT, .data = {.int_value = (val) } \
}
#define device_bind_prop_str_val(val) \
device_bind_prop_value { \
.data_type = ZX_DEVICE_PROPERTY_VALUE_STRING, .data = {.str_value = (val) } \
}
#define device_bind_prop_bool_val(val) \
device_bind_prop_value { \
.data_type = ZX_DEVICE_PROPERTY_VALUE_BOOL, .data = {.bool_value = (val) } \
}
#define device_bind_prop_enum_val(val) \
device_bind_prop_value { \
.data_type = ZX_DEVICE_PROPERTY_VALUE_ENUM, .data = {.enum_value = (val) } \
}
typedef struct device_bind_prop {
device_bind_prop_key_t key;
device_bind_prop_value_t value;
} device_bind_prop_t;
// Represents the condition for evaluating the property values in a node group.
// The values are accepted or rejected based on the condition.
typedef enum {
DEVICE_BIND_RULE_CONDITION_UNDEFINED = 0,
DEVICE_BIND_RULE_CONDITION_ACCEPT = 1,
DEVICE_BIND_RULE_CONDITION_REJECT = 2,
} device_bind_rule_condition;
// Represents the a property in a node group node.
typedef struct bind_rule {
device_bind_prop_key_t key;
device_bind_rule_condition condition;
const device_bind_prop_value_t* values;
size_t values_count;
} bind_rule_t;
typedef struct parent_spec {
const bind_rule_t* bind_rules;
size_t bind_rule_count;
const device_bind_prop_t* properties;
size_t property_count;
} parent_spec_t;
typedef struct composite_node_spec {
const parent_spec_t* parents;
size_t parent_count;
} composite_node_spec_t;
zx_status_t device_add_composite_spec(zx_device_t* dev, const char* name,
const composite_node_spec_t* spec);
// Protocol Identifiers
#define DDK_PROTOCOL_DEF(tag, val, name, flags) ZX_PROTOCOL_##tag = val,
enum {
#include <lib/ddk/protodefs.h>
};
__END_CDECLS
#endif // SRC_LIB_DDK_INCLUDE_LIB_DDK_DRIVER_H_