| // 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_DEVICE_H_ |
| #define SRC_LIB_DDK_INCLUDE_LIB_DDK_DEVICE_H_ |
| |
| #include <lib/fdf/types.h> |
| #include <zircon/compiler.h> |
| #include <zircon/fidl.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/types.h> |
| |
| __BEGIN_CDECLS |
| |
| typedef struct zx_device zx_device_t; |
| typedef struct zx_driver zx_driver_t; |
| typedef struct zx_device_prop zx_device_prop_t; |
| |
| typedef struct zx_protocol_device zx_protocol_device_t; |
| |
| // Max device name length, not including a null-terminator |
| #define ZX_DEVICE_NAME_MAX 31 |
| |
| // echo -n "zx_device_ops_v0.52" | sha256sum | cut -c1-16 |
| #define DEVICE_OPS_VERSION_0_52 0xb834fdab33623bb4 |
| |
| // Current Version |
| #define DEVICE_OPS_VERSION DEVICE_OPS_VERSION_0_52 |
| |
| // TODO: temporary flags used by devcoord to communicate |
| // with the system bus device. |
| #define DEVICE_SUSPEND_FLAG_REBOOT 0xdcdc0100 |
| #define DEVICE_SUSPEND_FLAG_POWEROFF 0xdcdc0200 |
| #define DEVICE_SUSPEND_FLAG_MEXEC 0xdcdc0300 |
| #define DEVICE_SUSPEND_FLAG_SUSPEND_RAM 0xdcdc0400 |
| #define DEVICE_SUSPEND_REASON_MASK 0xffffff00 |
| |
| // These values should be same as the enum fuchsia.device.DevicePowerState |
| // generated from FIDL. The system wide power manager will be using the |
| // power states from FIDL generated file. |
| #define DEV_POWER_STATE_D0 UINT8_C(0) |
| #define DEV_POWER_STATE_D1 UINT8_C(1) |
| #define DEV_POWER_STATE_D2 UINT8_C(2) |
| #define DEV_POWER_STATE_D3HOT UINT8_C(3) |
| #define DEV_POWER_STATE_D3COLD UINT8_C(4) |
| |
| // Performance state |
| #define DEV_PERFORMANCE_STATE_P0 UINT32_C(0) |
| |
| // reboot modifiers |
| #define DEVICE_SUSPEND_FLAG_REBOOT_BOOTLOADER (DEVICE_SUSPEND_FLAG_REBOOT | 0x01) |
| #define DEVICE_SUSPEND_FLAG_REBOOT_RECOVERY (DEVICE_SUSPEND_FLAG_REBOOT | 0x02) |
| #define DEVICE_SUSPEND_FLAG_REBOOT_KERNEL_INITIATED (DEVICE_SUSPEND_FLAG_REBOOT | 0x03) |
| |
| #define DEVICE_SUSPEND_REASON_POWEROFF UINT8_C(0x10) |
| #define DEVICE_SUSPEND_REASON_SUSPEND_RAM UINT8_C(0x20) |
| #define DEVICE_SUSPEND_REASON_MEXEC UINT8_C(0x30) |
| #define DEVICE_SUSPEND_REASON_REBOOT UINT8_C(0x40) |
| #define DEVICE_SUSPEND_REASON_REBOOT_RECOVERY (UINT8_C(DEVICE_SUSPEND_REASON_REBOOT | 0x01)) |
| #define DEVICE_SUSPEND_REASON_REBOOT_BOOTLOADER (UINT8_C(DEVICE_SUSPEND_REASON_REBOOT | 0x02)) |
| #define DEVICE_SUSPEND_REASON_REBOOT_KERNEL_INITIATED (UINT8_C(DEVICE_SUSPEND_REASON_REBOOT | 0x03)) |
| #define DEVICE_SUSPEND_REASON_SELECTIVE_SUSPEND UINT8_C(0x50) |
| #define DEVICE_MASK_SUSPEND_REASON UINT8_C(0xf0) |
| |
| //@doc(docs/ddk/device-ops.md) |
| |
| typedef struct device_fidl_txn device_fidl_txn_t; |
| // An outstanding FIDL transaction used when the driver host is managing |
| // a FIDL channel. |
| struct device_fidl_txn { |
| // Internal value used for driver host bookkeeping. Must not be mutated. |
| uintptr_t driver_host_context; |
| }; |
| |
| //@ # The Device Protocol |
| // |
| // Device drivers implement a set of hooks (methods) to support the |
| // operations that may be done on the devices that they publish. |
| // |
| // These are described below, including the action that is taken |
| // by the default implementation that is used for each hook if the |
| // driver does not provide its own implementation. |
| |
| typedef struct zx_protocol_device { |
| //@ ## version |
| // This field must be set to `DEVICE_OPS_VERSION` |
| uint64_t version; |
| |
| //@ ## get_protocol |
| // The get_protocol hook is called when a driver invokes |
| // **device_get_protocol()** on a device object. The implementation must |
| // populate *protocol* with a protocol structure determined by *proto_id*. |
| // If the requested *proto_id* is not supported, the implementation must |
| // return ZX_ERR_NOT_SUPPORTED. |
| // |
| // The default get_protocol hook returns with *protocol*=*proto_ops* if *proto_id* |
| // matches the one given when **device_add()** created the device, and returns |
| // ZX_ERR_NOT_SUPPORTED otherwise. |
| // |
| // See the **device_get_protocol()** docs for a description of the layout of |
| // *protocol*. |
| // |
| // This hook is never called by the devhost runtime other than when |
| // **device_get_protocol()** is invoked by some driver. It is executed |
| // synchronously in the same thread as the caller. |
| zx_status_t (*get_protocol)(void* ctx, uint32_t proto_id, void* protocol); |
| |
| //@ ## init |
| // The init hook is called when a device is initially added. |
| // |
| // If implemented, the device is guaranteed to be invisible and not able to be unbound until the |
| // driver calls **device_init_reply()** on itself. **device_init_reply()** can be called from |
| // any thread - it does not necessarily need to be called before the |init| hook returns. |
| // |
| // This allows drivers to safely complete initialization without explicit synchronization with |
| // the unbind hook, such as adding device metadata or completing blocking operations in a |
| // worker thread. Once the initialization is completed, **device_init_reply()** should be |
| // called to make the device visible and able to be unbound. |
| // |
| // The hook is always called from the devhost's main thread. |
| void (*init)(void* ctx); |
| |
| //@ ## unbind |
| // The unbind hook is called to begin removal of a device (due to hot unplug, fatal error, etc). |
| // |
| // The driver should avoid further method calls to its parent device or any |
| // protocols obtained from that device, and expect that any further such calls |
| // will return with an error. |
| // |
| // The driver should adjust its state to encourage its client connections to close |
| // (cause IO to error out, etc), and call **device_unbind_reply()** on itself when ready. |
| // See the docs for **device_unbind_reply()** for important semantics. |
| // |
| // The driver must continue to handle all device hooks except for message, open, read, and write |
| // until the **release** hook is invoked. |
| // |
| // Prior to unbind being called, the DDK will suspend processing of all FIDL messages and new |
| // connections will be disallowed at this point. A device driver is responsible for ensuring that |
| // any pending FIDL transactions are replied to or closed prior to replying to unbind. A device |
| // which handles asynchronous FIDL messages *must* implement this hook. |
| // |
| // **Note:** This hook will not be called for a **device instance**. |
| // |
| // This is an optional hook (except for drivers that implement message). The default |
| // implementation will be a hook that replies immediately with **device_unbind_reply()**. |
| // |
| // This hook will be called from the devhost's main thread. It will be executed sometime |
| // after any of the following events occuring: **device_async_remove()** is invoked on the |
| // device, the device's parent has completed its unbind hook via **device_unbind_reply**, |
| // or a fuchsia.device.Controller/ScheduleUnbind request is received. |
| void (*unbind)(void* ctx); |
| |
| //@ ## release |
| // The release hook is called after this device has finished unbinding, all open client |
| // connections of the device have been closed, and all child devices have been unbound and |
| // released. |
| // |
| // At the point release is invoked, the driver will not receive any further calls |
| // and absolutely must not use the underlying **zx_device_t** or any protocols obtained |
| // from that device once this method returns. |
| // |
| // The driver must free all memory and release all resources related to this device |
| // before returning. |
| // |
| // This hook may be called from any thread including the devhost's main |
| // thread. |
| void (*release)(void* ctx); |
| |
| //@ ## suspend |
| // The suspend hook is used for suspending a device from a working to |
| // non-working low power state(sleep state), or from a non-working sleep state |
| // to a deeper sleep state. |
| // |
| // requested_state is always a non-working sleep state. |
| // enable_wake is whether to configure the device for wakeup from the requested non |
| // working sleep state. If enable_wake is true and the device does not support |
| // wake up, the hook fails without suspending the device. |
| // suspend_reason provides information for the driver why the suspend hook is called. |
| // Bus drivers and platform drivers like acpi will find this information useful to |
| // issue any system calls or save the reboot reason. |
| // |
| // The driver should put the device into the requested_state and call **device_suspend_reply()** |
| // on itself. device_suspend_reply() will take in two parameters: status of the suspend operation |
| // and an out_state. If status is success, the out_state is same as requested_state. |
| // If status is failure, out_state is the low power state the device is currently in. |
| // |
| // This hook assumes that the drivers are aware of their current state. This hook will only |
| // be executed on the devhost's main thread. |
| void (*suspend)(void* ctx, uint8_t requested_state, bool enable_wake, uint8_t suspend_reason); |
| |
| //@ ## resume |
| // The resume hook is used for resuming a device from a non-working sleep |
| // state to a working state. It requires reinitializing the device completely |
| // or partially depending on the sleep state that device was in, when the |
| // resume call was made. |
| // |
| // requested_state is the performance state that the device has to be in. |
| // |
| // The driver should put the device into the requested_state and call **device_resume_reply()** |
| // on itself. device_resume_reply() will take in the following parameters: |
| // (1)Status of the resume operation (2)out_power_state (3) out_perf_state |
| // On success, the device has been resumed successfully to a working state, |
| // out_perf_state is same as requested state. |
| // If the device is not able to resume to a working state, the hook returns a |
| // failure. out_power_state has the non working state the device is in. |
| // if out_power_state is a working state, out_perf_state has the performance |
| // state the device is in. |
| // This hook assumes that the drivers are aware of their current state. |
| // |
| // This hook will only be executed on the devhost's main thread. |
| void (*resume)(void* ctx, uint32_t requested_state); |
| |
| //@ ## configure_autosuspend |
| // The configure_autosuspend hook is used for configuring whether a driver can |
| // auto suspend the device depending on the activity and idleness of the device. |
| // |
| // If "enable" is true, auto suspend is configured. deepest_sleep_state is the deepest |
| // sleep state the device is expected to go into when the device is suspended. |
| // |
| // On success, the device is configured to be autosuspended |
| // On failure, the device not configured to be autosuspended. If the device does |
| // not implement the autosuspend hook, it means the device does not support autosuspend. |
| // |
| // This hook will only be executed on the devhost's main thread. |
| // |
| |
| zx_status_t (*configure_auto_suspend)(void* ctx, bool enable, uint8_t deepest_sleep_state); |
| |
| //@ ## rxrpc |
| // Only called for bus devices. |
| // When the "shadow" of a busdev sends an rpc message, the |
| // device that is shadowing is notified by the rxrpc op and |
| // should attempt to read and respond to a single message on |
| // the provided channel. |
| // |
| // Any error return from this method will result in the channel |
| // being closed and the remote "shadow" losing its connection. |
| // |
| // This method is called with ZX_HANDLE_INVALID for the channel |
| // when a new client connects -- at which point any state from |
| // the previous client should be torn down. |
| // |
| // This hook will only be executed on the devhost's main thread. |
| zx_status_t (*rxrpc)(void* ctx, zx_handle_t channel); |
| |
| //@ ## message |
| // Process a FIDL rpc message. This is used to handle class or |
| // device specific messaging. |
| // |
| // The entire message becomes the responsibility of the driver, |
| // including the handles. |
| // |
| // The txn provided to respond to the message is only valid for |
| // the duration of the message() call. It must not be retained |
| // and used later. |
| // |
| // If this method wishes to respond asynchronously, the txn |
| // should be copied. |
| // |
| // This hook will only be executed on the devhost's main thread. |
| void (*message)(void* ctx, fidl_incoming_msg_t msg, device_fidl_txn_t txn); |
| |
| //@ ## child_pre_release |
| // The child_pre_release hook is used to signal that a child device |
| // will soon be released. This is after the child and all its descendents |
| // have been unbound and removed from the device filesystem, and all client |
| // connections to the child have been closed. |
| // |
| // The device may want to drop any references to the child context or child |
| // **zx_device_t**. |
| // |
| // This hook may be called from any thread including the devhost's main |
| // thread. |
| void (*child_pre_release)(void* ctx, void* child_ctx); |
| |
| //@ ## made_visible |
| // The made_visible hook is used to signal that the device has been made |
| // visible in devfs. It can be used as a synchornization point to inform |
| // clients that they may try and open the device. |
| // |
| // This hook will only be executed on the devhost's main thread. |
| void (*made_visible)(void* ctx); |
| |
| } zx_protocol_device_t; |
| |
| // protocols look like: |
| // typedef struct { |
| // protocol_xyz_ops_t* ops; |
| // void* ctx; |
| // } protocol_xyz_t; |
| zx_status_t device_get_protocol(const zx_device_t* dev, uint32_t proto_id, void* protocol); |
| |
| // Structured configuration VMO |
| // Returns the configuration VMO. This call can only be made once per device. |
| zx_status_t device_get_config_vmo(zx_device_t* device, zx_handle_t* config_vmo); |
| |
| // Direct Device Ops Functions |
| |
| // Opens a connection to the specified runtime 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 |
| // DdkConnectRuntimeProtocol wrapper method from ddktl, which supplies |device| and |
| // |service_name| automatically. |
| zx_status_t device_connect_runtime_protocol(zx_device_t* device, const char* service_name, |
| const char* protocol_name, fdf_handle_t request); |
| |
| // Opens a connection to the specified runtime service 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_connect_runtime_protocol. |
| // |
| // The ddktl equivalent is DdkConnectFragmentRuntimeProtocol. |
| // |
| // Returns |ZX_ERR_UNAVAILABLE| if the parent (or fragment) does not have an outgoing directory. |
| // Returns |ZX_ERR_NOT_FOUND| if |fragment_name| is not the name of a parent. |
| // Returns |ZX_ERR_NOT_SUPPORTED| if |fragment_name| specified by the device is not a |
| // composite node. |
| // Returns |ZX_ERR_BAD_HANDLE| if |request| is not a valid runtime handle. |
| zx_status_t device_connect_fragment_runtime_protocol(zx_device_t* device, const char* fragment_name, |
| const char* service_name, |
| const char* protocol_name, |
| fdf_handle_t request); |
| |
| // Opens a connection to the specified protocol in driver's incoming namespace. |
| // See |fdio_service_connect_at| for more information. |
| zx_status_t device_connect_ns_protocol(zx_device_t* device, const char* protocol_name, |
| zx_handle_t request); |
| // Device Metadata Support |
| |
| // retrieves metadata for a specific device |
| // searches parent devices to find a match |
| zx_status_t device_get_metadata(zx_device_t* dev, uint32_t type, void* buf, size_t buflen, |
| size_t* actual); |
| |
| // retrieves metadata size for a specific device |
| // searches parent devices to find a match |
| zx_status_t device_get_metadata_size(zx_device_t* dev, uint32_t type, size_t* out_size); |
| |
| // Adds metadata to a specific device. |
| zx_status_t device_add_metadata(zx_device_t* dev, uint32_t type, const void* data, size_t length); |
| |
| // Returns the specific protocol from the named fragment, identified by the name |
| // provided when it was created via`device_add_composite`. Returns ZX_ERR_NOT_FOUND if |
| // no fragment exists. |
| zx_status_t device_get_fragment_protocol(zx_device_t* dev, const char* name, uint32_t proto_id, |
| void* protocol); |
| |
| // retrieves metadata for a specific device |
| // searches parent devices to find a match |
| zx_status_t device_get_fragment_metadata(zx_device_t* dev, const char* name, uint32_t type, |
| void* buf, size_t buflen, size_t* actual); |
| |
| // Device State Change Functions. These match up with the signals defined in |
| // the fuchsia.device.Controller interface. |
| // |
| //@ #### Device State Bits |
| //{ |
| #define DEV_STATE_READABLE ZX_USER_SIGNAL_0 |
| #define DEV_STATE_WRITABLE ZX_USER_SIGNAL_2 |
| #define DEV_STATE_ERROR ZX_USER_SIGNAL_3 |
| #define DEV_STATE_HANGUP ZX_USER_SIGNAL_4 |
| #define DEV_STATE_OOB ZX_USER_SIGNAL_1 |
| //} |
| |
| __END_CDECLS |
| |
| #endif // SRC_LIB_DDK_INCLUDE_LIB_DDK_DEVICE_H_ |