| // 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. |
| |
| #include "device_ctx.h" |
| |
| #include "amlogic-video.h" |
| #include "macros.h" |
| |
| #include <zircon/device/media-codec.h> |
| |
| namespace { |
| |
| // TODO(dustingreen): Flip the direction to accept a server channel and serve |
| // that channel, instead of creating a client endpoint and returning the client |
| // end. Also switch to DDK FIDL with one "Connect" message. |
| static zx_status_t amlogic_video_ioctl(void* ctx, uint32_t op, |
| const void* in_buf, size_t in_len, |
| void* out_buf, size_t out_len, |
| size_t* out_actual) { |
| // The only IOCTL we support is get channel. |
| if (op != MEDIA_CODEC_IOCTL_GET_CODEC_FACTORY_CHANNEL) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| if ((out_buf == nullptr) || (out_actual == nullptr) || |
| (out_len != sizeof(zx_handle_t))) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| DeviceCtx* device = reinterpret_cast<DeviceCtx*>(ctx); |
| |
| zx::channel codec_factory_client_endpoint; |
| device->device_fidl()->CreateChannelBoundCodecFactory( |
| &codec_factory_client_endpoint); |
| |
| *(reinterpret_cast<zx_handle_t*>(out_buf)) = |
| codec_factory_client_endpoint.release(); |
| *out_actual = sizeof(zx_handle_t); |
| |
| return ZX_OK; |
| } |
| |
| static zx_protocol_device_t amlogic_video_device_ops = { |
| DEVICE_OPS_VERSION, .ioctl = amlogic_video_ioctl, |
| // TODO(jbauman) or TODO(dustingreen): .suspend .resume, maybe .release if |
| // it would ever be run. Currently ~AmlogicVideo code sets lower power, but |
| // ~AmlogicVideo doesn't run yet. |
| }; |
| |
| } // namespace |
| |
| DeviceCtx::DeviceCtx(DriverCtx* driver) |
| : driver_(driver), |
| codec_admission_control_(driver->shared_fidl_loop()->dispatcher()) { |
| video_ = std::make_unique<AmlogicVideo>(); |
| device_fidl_ = std::make_unique<DeviceFidl>(this); |
| } |
| |
| DeviceCtx::~DeviceCtx() { |
| // TODO(dustingreen): Depending on whether device .release() can get called on |
| // this deivce, we'll likely need to sequence the shutdown more explicitly. |
| // This destruction order seems like a reasonable starting point, but is not |
| // tested: |
| // |
| // ~device_fidl_ |
| // ~video_ |
| // |
| // There are two ways to destroy a fidl::Binding safely: |
| // * Switch to FIDL thread before Unbind() or ~Binding. |
| // * async::Loop Quit() + JoinThreads() before Unbind() or ~Binding |
| // |
| // For now this code (if implementation needed) will choose the first option |
| // by destructing DeviceFidl on the FIDL thread. The current way forces this |
| // thread to wait in this destructor until the shared_fidl_thread() is done |
| // processing ~DeviceFidl, which means we require that ~DeviceCtx is not |
| // itself running on the shared_fidl_thread() (or we could check which thread |
| // here, if we really need to). |
| // |
| // For now, it's not clear that we actually need to implement this destructor |
| // however, since this device is very likely to have a dedicated devhost and |
| // may not .release() the device - and even if it does .release() the device |
| // there is no clear need for the cleanup described above to actually be |
| // implemented. |
| |
| // TODO(dustingreen): Implement this destructor iff it's actually used/called. |
| ZX_PANIC("not implemented"); |
| } |
| |
| zx_status_t DeviceCtx::Bind(zx_device_t* parent) { |
| device_add_args_t vc_video_args = {}; |
| vc_video_args.version = DEVICE_ADD_ARGS_VERSION; |
| vc_video_args.name = "amlogic_video"; |
| vc_video_args.ctx = this; |
| vc_video_args.ops = &amlogic_video_device_ops; |
| |
| // ZX_PROTOCOL_MEDIA_CODEC causes /dev/class/media-codec to get created, and |
| // flags support for MEDIA_CODEC_IOCTL_GET_CODEC_FACTORY_CHANNEL. The |
| // proto_ops_ is empty but has a non-null address, so we don't break the |
| // invariant that devices with a protocol have non-null proto_ops. |
| vc_video_args.proto_id = ZX_PROTOCOL_MEDIA_CODEC; |
| vc_video_args.proto_ops = &proto_ops_; |
| |
| zx_status_t status = device_add(parent, &vc_video_args, &device_); |
| if (status != ZX_OK) { |
| DECODE_ERROR("Failed to bind device"); |
| return ZX_ERR_NO_MEMORY; |
| } |
| return ZX_OK; |
| } |