|  | // Copyright 2018 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. | 
|  |  | 
|  | library fuchsia.device.manager; | 
|  |  | 
|  | // TODO(teisenbe): Move these interfaces to be internal to the devmgr codebase | 
|  |  | 
|  | using zx; | 
|  |  | 
|  | /// This has the same structure as zx_device_prop_t. | 
|  | struct DeviceProperty { | 
|  | uint16 id; | 
|  | uint16 reserved; | 
|  | uint32 value; | 
|  | }; | 
|  |  | 
|  | // TODO(bwb): Make this descriptive of binding, not zx_bind_inst_t | 
|  | // currently a magic bitfield defined in binding.h | 
|  | struct BindInstruction { | 
|  | /// bitfield that encodes the operation and execution conditions | 
|  | uint32 op; | 
|  | /// bitfield that encodes the arguments | 
|  | uint32 arg; | 
|  | /// bitfield that encodes debugging information | 
|  | uint32 debug; | 
|  | }; | 
|  |  | 
|  | // Identifier used to let the devcoordinator describe specific devices during | 
|  | // composite construction | 
|  | alias LocalDeviceId = uint64; | 
|  |  | 
|  | /// This definition must match `ZX_DEVICE_NAME_MAX` and is checked by a static assert. | 
|  | const uint32 DEVICE_NAME_MAX = 31; | 
|  |  | 
|  | /// Maximum number of bytes in a path | 
|  | // The simple name PATH_MAX collides with a musl #define on c++ bindings. | 
|  | const uint32 DEVICE_PATH_MAX = 1024; | 
|  |  | 
|  | /// Maximum number of bytes in a device arguments string. | 
|  | const uint32 DEVICE_ARGS_MAX = 1024; | 
|  |  | 
|  | /// Maximum number of bytes in a metadata payload | 
|  | const uint32 METADATA_BYTES_MAX = 8192; | 
|  |  | 
|  | /// Maximum number of metadata that can be added to a device | 
|  | const uint32 METADATA_MAX = 32; | 
|  |  | 
|  | /// Maximum number of properties that can be attached to a device | 
|  | const uint32 PROPERTIES_MAX = 256; | 
|  |  | 
|  | /// Maximum number of fragments that a composite device can have | 
|  | const uint32 FRAGMENTS_MAX = 16; | 
|  |  | 
|  | /// Maximum number of parts that a composite device fragment can have | 
|  | const uint32 DEVICE_FRAGMENT_PARTS_MAX = 16; | 
|  |  | 
|  | /// Maximum number of instructions in the match program of a device fragment part | 
|  | const uint32 DEVICE_FRAGMENT_PART_INSTRUCTIONS_MAX = 32; | 
|  |  | 
|  | // Maximum number of instructions in a driver bind program | 
|  | const uint32 BIND_PROGRAM_INSTRUCTIONS_MAX = 256; | 
|  |  | 
|  | /// Bit flags for device add configuration | 
|  | bits AddDeviceConfig : uint32 { | 
|  | /// Device can be a fragment in multiple composite devices | 
|  | ALLOW_MULTI_COMPOSITE = 0x00000001; | 
|  | /// Device should be marked invisible initially (used for deprecated DEVICE_ADD_INVISIBLE) | 
|  | INVISIBLE = 0x00000002; | 
|  | /// Device should not trigger the auto-bind mechanism | 
|  | SKIP_AUTOBIND = 0x00000004; | 
|  | }; | 
|  |  | 
|  | /// A part of a description of a DeviceFragment | 
|  | struct DeviceFragmentPart { | 
|  | // This is an awful hack around the LLCPP bindings not being ready yet. | 
|  | // Since we're using the C ones for now, we can only embed these structures as | 
|  | // arrays instead of vectors. | 
|  | uint32 match_program_count; | 
|  | array<BindInstruction>:DEVICE_FRAGMENT_PART_INSTRUCTIONS_MAX match_program; | 
|  | }; | 
|  |  | 
|  | /// A piece of a composite device | 
|  | struct DeviceFragment { | 
|  | // This is an awful hack around the LLCPP bindings not being ready yet. | 
|  | // Since we're using the C ones for now, we can only embed these structures as | 
|  | // arrays instead of vectors. | 
|  | string:32 name; | 
|  | uint32 parts_count; | 
|  | array<DeviceFragmentPart>:DEVICE_FRAGMENT_PARTS_MAX parts; | 
|  | }; | 
|  |  | 
|  | /// Metadata that can be added to a device | 
|  | struct DeviceMetadata { | 
|  | uint32 key; | 
|  | vector<uint8>:METADATA_BYTES_MAX data; | 
|  | }; | 
|  |  | 
|  | /// Composite device parts and properties | 
|  | struct CompositeDeviceDescriptor { | 
|  | vector<DeviceProperty>:PROPERTIES_MAX props; | 
|  | vector<DeviceFragment>:FRAGMENTS_MAX fragments; | 
|  | uint32 coresident_device_index; | 
|  | vector<DeviceMetadata>:METADATA_MAX? metadata; | 
|  | }; | 
|  |  | 
|  | /// A enum of CompatibilityTestStatus | 
|  | enum CompatibilityTestStatus : uint32 { | 
|  | OK = 1; | 
|  | ERR_BIND_NO_DDKADD = 2; | 
|  | ERR_BIND_TIMEOUT = 3; | 
|  | ERR_UNBIND_NO_DDKREMOVE = 4; | 
|  | ERR_UNBIND_TIMEOUT = 5; | 
|  | ERR_SUSPEND_DDKREMOVE = 6; | 
|  | ERR_INTERNAL = 7; | 
|  | }; | 
|  |  | 
|  | /// Protocol for controlling devices in a devhost process from the devcoordinator | 
|  | protocol DeviceController { | 
|  | /// Bind the requested driver to this device.  `driver_path` is informational, | 
|  | /// but all calls to BindDriver/CreateDevice should use the same `driver_path` | 
|  | /// each time they use a `driver` VMO with the same contents. Returns a `status` | 
|  | /// and optionally a channel to the driver's test output. `test_output` will be | 
|  | /// not present unless the driver is configured to run its run_unit_tests hook, in | 
|  | /// which case the other end of the channel will have been passed to the driver. | 
|  | BindDriver(string:DEVICE_PATH_MAX driver_path, zx.handle:VMO driver) | 
|  | -> (zx.status status, zx.handle:CHANNEL? test_output); | 
|  |  | 
|  | /// Give this device a channel to its shadow in another process. | 
|  | ConnectProxy(zx.handle:CHANNEL shadow); | 
|  |  | 
|  | /// Ask devhost to call the device init hook. | 
|  | Init() -> (zx.status status); | 
|  |  | 
|  | /// Ask devhost to unbind this device. On success, the remote end of this | 
|  | /// interface channel will close instead of returning a result. | 
|  | Unbind() -> () error zx.status; | 
|  |  | 
|  | /// Ask the devhost to complete the removal of this device, which previously had | 
|  | /// invoked `ScheduleRemove`. This is a special case that can be removed | 
|  | /// once `device_remove` invokes `unbind`. | 
|  | CompleteRemoval() -> () error zx.status; | 
|  |  | 
|  | /// Ask devhost to suspend this device, using the target state indicated by `flags`. | 
|  | Suspend(uint32 flags) -> (zx.status status); | 
|  |  | 
|  | /// Ask devhost to resume this device, using the target system state indicated by | 
|  | /// 'target_system_state'. | 
|  | Resume(uint32 target_system_state) -> (zx.status status); | 
|  |  | 
|  | /// Inform devhost about the compatibility test status when compatibility tests | 
|  | /// fail or complete successfully. | 
|  | // TODO(ravoorir) : This should be an asynchronous call from devhost to | 
|  | // devcoordinator as a reply to RunCompatibilityTests, when llcpp can support | 
|  | // making an asynchronous fidl call. | 
|  | CompleteCompatibilityTests(CompatibilityTestStatus status); | 
|  | }; | 
|  |  | 
|  | const uint32 FRAGMENT_NAME_MAX = 32; | 
|  |  | 
|  | [ForDeprecatedCBindings] | 
|  | struct Fragment { | 
|  | string:FRAGMENT_NAME_MAX name; | 
|  | LocalDeviceId id; | 
|  | }; | 
|  |  | 
|  | /// Protocol for controlling a devhost process from the devcoordinator | 
|  | protocol DevhostController { | 
|  | /// Create a device in the devhost that only implements the device protocol | 
|  | /// and claims to support the given `protocol_id`.  This device will communicate | 
|  | /// with the devcoordinator via `coordinator`. Implements DeviceController on device_controller_rpc | 
|  | CreateDeviceStub(Coordinator coordinator_rpc, request<DeviceController> device_controller_rpc, uint32 protocol_id, LocalDeviceId local_device_id); | 
|  |  | 
|  | /// Create a device in the devhost representing the shadowed half of device | 
|  | /// in another devhost.  This new device will communicate with the devcoordinator | 
|  | /// via `rpc`, and with its other half via `parent_proxy`. | 
|  | /// | 
|  | /// The new device will have the given driver responsible for running its half | 
|  | /// of the driver's cross-process protocol.  It's create() method will be invoked, | 
|  | /// giving it access to `parent_proxy` and `proxy_args`. | 
|  | /// | 
|  | /// parent_proxy, if present, will usually be a channel to the upper half of | 
|  | /// a shadowed device.  The one exception is when this method is used | 
|  | /// to create the Platform Bus, in which case it will be a channel to a | 
|  | /// fuchsia.boot.Items protocol. | 
|  | /// | 
|  | /// `local_device_id` will be a unique value within the device's devhost | 
|  | CreateDevice(Coordinator coordinator_rpc, request<DeviceController> device_controller_rpc, string:DEVICE_PATH_MAX driver_path, | 
|  | zx.handle:VMO driver, handle? parent_proxy, | 
|  | string:DEVICE_ARGS_MAX? proxy_args, LocalDeviceId local_device_id); | 
|  |  | 
|  | /// Introduce a composite device that has the given name and properties. | 
|  | /// `fragments` will be a list of all of the composite's fragments, | 
|  | /// described using devhost local device ids.  The order of the fragments | 
|  | /// will match the original composite creation request.  The new device will | 
|  | /// communicate with devcoordinator via `rpc`. | 
|  | /// | 
|  | /// `local_device_id` will be a unique value within the device's devhost, identifying | 
|  | /// the resulting composite device. | 
|  | CreateCompositeDevice(Coordinator coordinator_rpc, request<DeviceController> device_controller_rpc, | 
|  | vector<Fragment>:FRAGMENTS_MAX fragments, | 
|  | string:DEVICE_NAME_MAX name, LocalDeviceId local_device_id) | 
|  | -> (zx.status status); | 
|  | }; | 
|  |  | 
|  | /// Interface for the devices in devhosts to coordinate with the devcoordinator. | 
|  | protocol Coordinator { | 
|  | /// Record the addition of a new device that can be communicated with via `rpc`. | 
|  | /// For binding purposes, it is has properties `props`. `name` and `driver_path` | 
|  | /// are informational and used for debugging.  The device will have `protocol_id` | 
|  | /// as its primary protocol id.  `args` should only be used for shadowed devices, | 
|  | /// and will be forwarded to the shadow device. `client_remote`, if present, | 
|  | /// will be passed to the device as an open connection for the client. | 
|  | /// On success, the returned `local_device_id` is the identifier assigned by devmgr. | 
|  | AddDevice(request<Coordinator> coordinator, DeviceController device_controller, | 
|  | vector<DeviceProperty>:PROPERTIES_MAX props, | 
|  | string:DEVICE_NAME_MAX name, uint32 protocol_id, | 
|  | string:DEVICE_PATH_MAX? driver_path, string:DEVICE_ARGS_MAX? args, | 
|  | AddDeviceConfig device_add_config, bool has_init, zx.handle:VMO? inspect, | 
|  | zx.handle:CHANNEL? client_remote) | 
|  | -> (LocalDeviceId local_device_id) error zx.status; | 
|  |  | 
|  | /// Requests the devcoordinator schedule the removal of this device, | 
|  | /// and the unbinding of its children. | 
|  | /// If `unbind_self` is true, the unbind hook for this device will also be called. | 
|  | ScheduleRemove(bool unbind_self); | 
|  |  | 
|  | /// Requests the devcoordinator schedule the unbinding of this device's children. | 
|  | ScheduleUnbindChildren(); | 
|  |  | 
|  | /// Mark this device as visible. | 
|  | MakeVisible() -> () error zx.status; | 
|  |  | 
|  | /// Attempt to bind a driver against this device.  If `driver_path` is null, | 
|  | /// this will initiate the driver matching algorithm. | 
|  | // TODO(teisenbe): Specify the behavior of invoking this multiple times.  I believe | 
|  | // the current behavior is a bug. | 
|  | BindDevice(string:DEVICE_PATH_MAX? driver_path) -> () error zx.status; | 
|  |  | 
|  | /// Returns the topological path of this device. | 
|  | GetTopologicalPath() -> (string:DEVICE_PATH_MAX path) error zx.status; | 
|  |  | 
|  | /// Requests that the firmware at the given path be loaded and returned. | 
|  | LoadFirmware(string:DEVICE_PATH_MAX fw_path) | 
|  | -> (zx.handle:VMO vmo, uint64 size) error zx.status; | 
|  |  | 
|  | /// Retrieve the metadata blob associated with this device and the given key. | 
|  | GetMetadata(uint32 key) | 
|  | -> (vector<uint8>:METADATA_BYTES_MAX data) error zx.status; | 
|  |  | 
|  | /// Retrieve the metadata size associated with this device and the given key. | 
|  | GetMetadataSize(uint32 key) | 
|  | -> (uint64 size) error zx.status; | 
|  |  | 
|  | /// Add metadata blob associated with this device and the given key. | 
|  | // TODO(teisenbe): Document the behavior of calling this twice with the same | 
|  | // key.  I believe the current behavior results in inaccessible data that is | 
|  | // kept around for the lifetime of the device. | 
|  | AddMetadata(uint32 key, vector<uint8>:METADATA_BYTES_MAX? data) | 
|  | -> () error zx.status; | 
|  |  | 
|  | /// Behaves like AddMetadata, but instead of associating it with the | 
|  | /// requesting device, associates it with the device at `device_path`.  If | 
|  | /// the device at `device_path` is not a child of the requesting device AND | 
|  | /// the requesting device is not running in the sys devhost, then this will | 
|  | /// fail. | 
|  | PublishMetadata(string:DEVICE_PATH_MAX device_path, uint32 key, | 
|  | vector<uint8>:METADATA_BYTES_MAX? data) -> () error zx.status; | 
|  |  | 
|  | /// Adds the given composite device.  This causes the devcoordinator to try to match the | 
|  | /// fragments against the existing device tree, and to monitor all new device additions | 
|  | /// in order to find the fragments as they are created. | 
|  | AddCompositeDevice(string:DEVICE_NAME_MAX name, CompositeDeviceDescriptor comp_desc) | 
|  | -> () error zx.status; | 
|  |  | 
|  | /// Watches a directory, receiving events of added messages on the | 
|  | /// watcher request channel. | 
|  | /// See fuchsia.io.Directory for more information. | 
|  | DirectoryWatch(uint32 mask, uint32 options, zx.handle:CHANNEL watcher) | 
|  | -> () error zx.status; | 
|  |  | 
|  | /// Run Compatibility tests for the driver that binds to this device. | 
|  | /// The hook_wait_time is the time that the driver expects to take for | 
|  | /// each device hook in nanoseconds. | 
|  | /// Returns whether the compatibility tests started, and does not convey | 
|  | /// anything about the status of the test. | 
|  | RunCompatibilityTests(int64 hook_wait_time) -> () error zx.status; | 
|  | }; | 
|  |  | 
|  | /// Protocol for getting the information needed to debug bind programs from the devcoordinator. | 
|  | [Discoverable] | 
|  | protocol BindDebugger { | 
|  | /// Returns the bind program of the given driver. | 
|  | /// ZX_ERR_NOT_FOUND indicates that there is no driver matching the given path. | 
|  | /// ZX_ERR_BUFFER_TOO_SMALL indicates that the driver's bind program is longer than the | 
|  | /// maximum number of instructions (BIND_PROGRAM_INSTRUCTIONS_MAX). | 
|  | GetBindProgram(string:DEVICE_PATH_MAX driver_path) | 
|  | -> (vector<BindInstruction>:BIND_PROGRAM_INSTRUCTIONS_MAX instructions) | 
|  | error zx.status; | 
|  |  | 
|  | /// Returns the list of properties of the given device. | 
|  | /// ZX_ERR_NOT_FOUND indicates that there is no device matching the given path. | 
|  | /// ZX_ERR_BAD_PATH indicates that the given path is not valid. | 
|  | /// ZX_ERR_BUFFER_TOO_SMALL indicates either that the given path is too long, | 
|  | /// or that the device has more than the maximum number of properties (PROPERTIES_MAX). | 
|  | GetDeviceProperties(string:DEVICE_PATH_MAX device_path) | 
|  | -> (vector<DeviceProperty>:PROPERTIES_MAX props) error zx.status; | 
|  | }; |