1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "platform-proxy-device.h"
6
7#include <stdint.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <threads.h>
12
13#include <ddk/binding.h>
14#include <ddk/debug.h>
15#include <ddk/device.h>
16#include <ddk/driver.h>
17#include <ddk/protocol/platform-bus.h>
18#include <ddk/protocol/platform-device.h>
19#include <fbl/auto_call.h>
20#include <fbl/unique_ptr.h>
21#include <lib/zx/vmar.h>
22#include <lib/zx/vmo.h>
23
24#include "platform-proxy.h"
25#include "proxy-protocol.h"
26
27// The implementation of the platform bus protocol in this file is for use by
28// drivers that exist in a proxy devhost and communicate with the platform bus
29// over an RPC channel.
30//
31// More information can be found at the top of platform-device.cpp.
32
33namespace platform_bus {
34
35zx_status_t ProxyDevice::GpioConfigIn(void* ctx, uint32_t flags) {
36    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
37    auto thiz = gpio_ctx->thiz;
38    rpc_gpio_req_t req = {};
39    rpc_gpio_rsp_t resp = {};
40    req.header.proto_id = ZX_PROTOCOL_GPIO;
41    req.header.op = GPIO_CONFIG_IN;
42    req.index = gpio_ctx->index;
43    req.flags = flags;
44
45    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
46                             sizeof(resp));
47}
48
49zx_status_t ProxyDevice::GpioConfigOut(void* ctx, uint8_t initial_value) {
50    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
51    auto thiz = gpio_ctx->thiz;
52    rpc_gpio_req_t req = {};
53    rpc_gpio_rsp_t resp = {};
54    req.header.proto_id = ZX_PROTOCOL_GPIO;
55    req.header.op = GPIO_CONFIG_OUT;
56    req.index = gpio_ctx->index;
57    req.value = initial_value;
58
59    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
60                             sizeof(resp));
61}
62
63zx_status_t ProxyDevice::GpioSetAltFunction(void* ctx, uint64_t function) {
64    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
65    auto thiz = gpio_ctx->thiz;
66    rpc_gpio_req_t req = {};
67    rpc_gpio_rsp_t resp = {};
68    req.header.proto_id = ZX_PROTOCOL_GPIO;
69    req.header.op = GPIO_SET_ALT_FUNCTION;
70    req.index = gpio_ctx->index;
71    req.alt_function = function;
72
73    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
74                             sizeof(resp));
75}
76
77zx_status_t ProxyDevice::GpioGetInterrupt(void* ctx, uint32_t flags, zx_handle_t* out_handle) {
78    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
79    auto thiz = gpio_ctx->thiz;
80    rpc_gpio_req_t req = {};
81    rpc_gpio_rsp_t resp = {};
82    req.header.proto_id = ZX_PROTOCOL_GPIO;
83    req.header.op = GPIO_GET_INTERRUPT;
84    req.index = gpio_ctx->index;
85    req.flags = flags;
86
87    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp),
88                              nullptr, 0, out_handle, 1, nullptr);
89}
90
91zx_status_t ProxyDevice::GpioSetPolarity(void* ctx, uint32_t polarity) {
92    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
93    auto thiz = gpio_ctx->thiz;
94    rpc_gpio_req_t req = {};
95    rpc_gpio_rsp_t resp = {};
96    req.header.proto_id = ZX_PROTOCOL_GPIO;
97    req.header.op = GPIO_SET_POLARITY;
98    req.index = gpio_ctx->index;
99    req.polarity = polarity;
100
101    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
102                             sizeof(resp));
103}
104
105zx_status_t ProxyDevice::GpioReleaseInterrupt(void* ctx) {
106    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
107    auto thiz = gpio_ctx->thiz;
108    rpc_gpio_req_t req = {};
109    rpc_gpio_rsp_t resp = {};
110    req.header.proto_id = ZX_PROTOCOL_GPIO;
111    req.header.op = GPIO_RELEASE_INTERRUPT;
112    req.index = gpio_ctx->index;
113
114    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
115                             sizeof(resp));
116}
117
118zx_status_t ProxyDevice::GpioRead(void* ctx, uint8_t* out_value) {
119    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
120    auto thiz = gpio_ctx->thiz;
121    rpc_gpio_req_t req = {};
122    rpc_gpio_rsp_t resp = {};
123    req.header.proto_id = ZX_PROTOCOL_GPIO;
124    req.header.op = GPIO_READ;
125    req.index = gpio_ctx->index;
126
127    auto status = thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
128                                    sizeof(resp));
129
130    if (status != ZX_OK) {
131        return status;
132    }
133    *out_value = resp.value;
134    return ZX_OK;
135}
136
137zx_status_t ProxyDevice::GpioWrite(void* ctx, uint8_t value) {
138    auto gpio_ctx = static_cast<GpioCtx*>(ctx);
139    auto thiz = gpio_ctx->thiz;
140    rpc_gpio_req_t req = {};
141    rpc_gpio_rsp_t resp = {};
142    req.header.proto_id = ZX_PROTOCOL_GPIO;
143    req.header.op = GPIO_WRITE;
144    req.index = gpio_ctx->index;
145    req.value = value;
146
147    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
148                             sizeof(resp));
149}
150
151zx_status_t ProxyDevice::I2cGetMaxTransferSize(void* ctx, size_t* out_size) {
152    auto i2c_ctx = static_cast<I2cCtx*>(ctx);
153    auto thiz = i2c_ctx->thiz;
154    rpc_i2c_req_t req = {};
155    rpc_i2c_rsp_t resp = {};
156    req.header.proto_id = ZX_PROTOCOL_I2C;
157    req.header.op = I2C_GET_MAX_TRANSFER;
158    req.index = i2c_ctx->index;
159
160    auto status = thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp.header,
161                                    sizeof(resp));
162    if (status == ZX_OK) {
163        *out_size = resp.max_transfer;
164    }
165    return status;
166}
167
168zx_status_t ProxyDevice::I2cGetInterrupt(void* ctx, uint32_t flags, zx_handle_t* out_handle) {
169    return ZX_ERR_NOT_SUPPORTED;
170}
171
172zx_status_t ProxyDevice::I2cTransact(void* ctx, i2c_op_t* ops, size_t cnt,
173                                     i2c_transact_cb transact_cb, void* cookie) {
174    auto i2c_ctx = static_cast<I2cCtx*>(ctx);
175    auto thiz = i2c_ctx->thiz;
176    size_t writes_length = 0;
177    size_t reads_length = 0;
178    for (size_t i = 0; i < cnt; ++i) {
179        if (ops[i].is_read) {
180            reads_length += ops[i].length;
181        } else {
182            writes_length += ops[i].length;
183        }
184    }
185    if (!writes_length && !reads_length) {
186        return ZX_ERR_INVALID_ARGS;
187    }
188
189    size_t req_length = sizeof(rpc_i2c_req_t) + cnt * sizeof(i2c_rpc_op_t) + writes_length;
190    if (req_length >= PROXY_MAX_TRANSFER_SIZE) {
191        return ZX_ERR_INVALID_ARGS;
192    }
193    uint8_t req_buffer[PROXY_MAX_TRANSFER_SIZE];
194    auto req = reinterpret_cast<rpc_i2c_req_t*>(req_buffer);
195    req->header.proto_id = ZX_PROTOCOL_I2C;
196    req->header.op = I2C_TRANSACT;
197    req->index = i2c_ctx->index;
198    req->cnt = cnt;
199    req->transact_cb = transact_cb;
200    req->cookie = cookie;
201
202    auto rpc_ops = reinterpret_cast<i2c_rpc_op_t*>(req + 1);
203    ZX_ASSERT(cnt < I2C_MAX_RW_OPS);
204    for (size_t i = 0; i < cnt; ++i) {
205        rpc_ops[i].length = ops[i].length;
206        rpc_ops[i].is_read = ops[i].is_read;
207        rpc_ops[i].stop = ops[i].stop;
208    }
209    uint8_t* p_writes = reinterpret_cast<uint8_t*>(rpc_ops) + cnt * sizeof(i2c_rpc_op_t);
210    for (size_t i = 0; i < cnt; ++i) {
211        if (!ops[i].is_read) {
212            memcpy(p_writes, ops[i].buf, ops[i].length);
213            p_writes += ops[i].length;
214        }
215    }
216
217    const size_t resp_length = sizeof(rpc_i2c_rsp_t) + reads_length;
218    if (resp_length >= PROXY_MAX_TRANSFER_SIZE) {
219        return ZX_ERR_INVALID_ARGS;
220    }
221    uint8_t resp_buffer[PROXY_MAX_TRANSFER_SIZE];
222    rpc_i2c_rsp_t* rsp = reinterpret_cast<rpc_i2c_rsp_t*>(resp_buffer);
223    uint32_t actual;
224    auto status = thiz->proxy_->Rpc(thiz->device_id_, &req->header,
225                                    static_cast<uint32_t>(req_length),
226                                    &rsp->header, static_cast<uint32_t>(resp_length),
227                                    nullptr, 0, nullptr, 0, &actual);
228    if (status != ZX_OK) {
229        return status;
230    }
231
232    // TODO(voydanoff) This proxying code actually implements i2c_transact synchronously
233    // due to the fact that it is unsafe to respond asynchronously on the devmgr rxrpc channel.
234    // In the future we may want to redo the plumbing to allow this to be truly asynchronous.
235
236    if (actual != resp_length) {
237        status = ZX_ERR_INTERNAL;
238    } else {
239        status = rsp->header.status;
240    }
241    if (transact_cb) {
242        i2c_op_t read_ops[I2C_MAX_RW_OPS];
243        size_t read_ops_cnt = 0;
244        uint8_t* p_reads = reinterpret_cast<uint8_t*>(rsp + 1);
245        for (size_t i = 0; i < cnt; ++i) {
246            if (ops[i].is_read) {
247                read_ops[read_ops_cnt] = ops[i];
248                read_ops[read_ops_cnt].buf = p_reads;
249                read_ops_cnt++;
250                p_reads += ops[i].length;
251            }
252        }
253        transact_cb(status, read_ops, read_ops_cnt, rsp->cookie);
254    }
255
256    return ZX_OK;
257}
258
259zx_status_t ProxyDevice::ClkEnable(void* ctx, uint32_t index) {
260    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
261    rpc_clk_req_t req = {};
262    platform_proxy_rsp_t resp = {};
263    req.header.proto_id = ZX_PROTOCOL_CLK;
264    req.header.op = CLK_ENABLE;
265    req.index = index;
266
267    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp, sizeof(resp));
268}
269
270zx_status_t ProxyDevice::ClkDisable(void* ctx, uint32_t index) {
271    ProxyDevice* thiz = static_cast<ProxyDevice*>(ctx);
272    rpc_clk_req_t req = {};
273    platform_proxy_rsp_t resp = {};
274    req.header.proto_id = ZX_PROTOCOL_CLK;
275    req.header.op = CLK_DISABLE;
276    req.index = index;
277
278    return thiz->proxy_->Rpc(thiz->device_id_, &req.header, sizeof(req), &resp, sizeof(resp));
279}
280
281zx_status_t ProxyDevice::GetMmio(uint32_t index, pdev_mmio_t* out_mmio) {
282    if (index >= mmios_.size()) {
283        return ZX_ERR_OUT_OF_RANGE;
284    }
285
286    const Mmio& mmio = mmios_[index];
287    const zx_paddr_t vmo_base = ROUNDDOWN(mmio.base, PAGE_SIZE);
288    const size_t vmo_size = ROUNDUP(mmio.base + mmio.length - vmo_base, PAGE_SIZE);
289    zx::vmo vmo;
290
291    zx_status_t status = zx_vmo_create_physical(mmio.resource.get(), vmo_base, vmo_size,
292                                                vmo.reset_and_get_address());
293    if (status != ZX_OK) {
294        zxlogf(ERROR, "%s %s: creating vmo failed %d\n", name_, __FUNCTION__, status);
295        return status;
296    }
297
298    char name[32];
299    snprintf(name, sizeof(name), "%s mmio %u", name_, index);
300    status = vmo.set_property(ZX_PROP_NAME, name, sizeof(name));
301    if (status != ZX_OK) {
302        zxlogf(ERROR, "%s %s: setting vmo name failed %d\n", name_, __FUNCTION__, status);
303        return status;
304    }
305
306    out_mmio->offset = mmio.base - vmo_base;
307    out_mmio->vmo = vmo.release();
308    out_mmio->size = mmio.length;
309    return ZX_OK;
310}
311
312// TODO(surajmalhotra): Remove after migrating all clients off.
313zx_status_t ProxyDevice::MapMmio(uint32_t index, uint32_t cache_policy, void** out_vaddr,
314                                 size_t* out_size, zx_paddr_t* out_paddr,
315                                 zx_handle_t* out_handle) {
316    if (index >= mmios_.size()) {
317        return ZX_ERR_OUT_OF_RANGE;
318    }
319
320    const Mmio& mmio = mmios_[index];
321    const zx_paddr_t vmo_base = ROUNDDOWN(mmio.base, PAGE_SIZE);
322    const size_t vmo_size = ROUNDUP(mmio.base + mmio.length - vmo_base, PAGE_SIZE);
323    zx::vmo vmo;
324
325    zx_status_t status = zx_vmo_create_physical(mmio.resource.get(), vmo_base, vmo_size,
326                                                vmo.reset_and_get_address());
327    if (status != ZX_OK) {
328        zxlogf(ERROR, "%s %s: creating vmo failed %d\n", name_, __FUNCTION__, status);
329        return status;
330    }
331
332    char name[32];
333    snprintf(name, sizeof(name), "%s mmio %u", name_, index);
334    status = vmo.set_property(ZX_PROP_NAME, name, sizeof(name));
335    if (status != ZX_OK) {
336        zxlogf(ERROR, "%s %s: setting vmo name failed %d\n", name_, __FUNCTION__, status);
337        return status;
338    }
339
340    status = vmo.set_cache_policy(cache_policy);
341    if (status != ZX_OK) {
342        zxlogf(ERROR, "%s %s: setting cache policy failed %d\n", name_, __FUNCTION__, status);
343        return status;
344    }
345
346    uintptr_t virt;
347    status = zx::vmar::root_self()->map(0, vmo, 0, vmo_size, ZX_VM_PERM_READ |
348                                        ZX_VM_PERM_WRITE | ZX_VM_MAP_RANGE, &virt);
349    if (status != ZX_OK) {
350        zxlogf(ERROR, "%s %s: mapping vmar failed %d\n", name_, __FUNCTION__, status);
351        return status;
352    }
353
354    *out_size = mmio.length;
355    if (out_paddr) {
356        *out_paddr = mmio.base;
357    }
358    *out_vaddr = reinterpret_cast<void*>(virt + (mmio.base - vmo_base));
359    *out_handle = vmo.release();
360    return ZX_OK;
361
362}
363
364zx_status_t ProxyDevice::MapInterrupt(uint32_t index, uint32_t flags, zx_handle_t* out_handle) {
365    if (index >= irqs_.size()) {
366        return ZX_ERR_OUT_OF_RANGE;
367    }
368
369    Irq* irq = &irqs_[index];
370    if (flags == 0) {
371        flags = irq->mode;
372    }
373    zx_handle_t handle;
374    zx_status_t status = zx_interrupt_create(irq->resource.get(), irq->irq, flags, &handle);
375    if (status != ZX_OK) {
376        zxlogf(ERROR, "%s %s: creating interrupt failed: %d\n", name_, __FUNCTION__, status);
377        return status;
378    }
379
380    *out_handle = handle;
381    return ZX_OK;
382}
383
384zx_status_t ProxyDevice::GetBti(uint32_t index, zx_handle_t* out_handle) {
385    rpc_pdev_req_t req = {};
386    rpc_pdev_rsp_t resp = {};
387    req.header.proto_id = ZX_PROTOCOL_PLATFORM_DEV;
388    req.header.op = PDEV_GET_BTI;
389    req.index = index;
390
391    return proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp), nullptr, 0,
392                       out_handle, 1, nullptr);
393}
394
395zx_status_t ProxyDevice::GetDeviceInfo(pdev_device_info_t* out_info) {
396    rpc_pdev_req_t req = {};
397    rpc_pdev_rsp_t resp = {};
398    req.header.proto_id = ZX_PROTOCOL_PLATFORM_DEV;
399    req.header.op = PDEV_GET_DEVICE_INFO;
400
401    auto status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp));
402    if (status != ZX_OK) {
403        return status;
404    }
405    memcpy(out_info, &resp.device_info, sizeof(*out_info));
406    return ZX_OK;
407}
408
409zx_status_t ProxyDevice::GetBoardInfo(pdev_board_info_t* out_info) {
410    rpc_pdev_req_t req = {};
411    rpc_pdev_rsp_t resp = {};
412    req.header.proto_id = ZX_PROTOCOL_PLATFORM_DEV;
413    req.header.op = PDEV_GET_BOARD_INFO;
414
415    auto status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp));
416    if (status != ZX_OK) {
417        return status;
418    }
419    memcpy(out_info, &resp.board_info, sizeof(*out_info));
420    return ZX_OK;
421}
422
423zx_status_t ProxyDevice::DeviceAdd(uint32_t index, device_add_args_t* args, zx_device_t** out) {
424    rpc_pdev_req_t req = {};
425    rpc_pdev_rsp_t resp = {};
426    req.header.proto_id = ZX_PROTOCOL_PLATFORM_DEV;
427    req.header.op = PDEV_DEVICE_ADD;
428    req.index = index;
429
430    auto status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp));
431    if (status != ZX_OK) {
432        return status;
433    }
434
435    return CreateChild(zxdev(), resp.device_id, proxy_, args);
436}
437
438zx_status_t ProxyDevice::GetProtocol(uint32_t proto_id, uint32_t index, void* out_protocol) {
439    // Return the GPIO protocol for the given index.
440    if (proto_id == ZX_PROTOCOL_GPIO) {
441        if (index >= gpio_ctxs_.size()) {
442            return ZX_ERR_OUT_OF_RANGE;
443        }
444        auto proto = static_cast<gpio_protocol_t*>(out_protocol);
445        proto->ops = &gpio_proto_ops_;
446        proto->ctx = &gpio_ctxs_[index];
447        return ZX_OK;
448    }
449
450    if (proto_id == ZX_PROTOCOL_I2C) {
451        if (index >= i2c_ctxs_.size()) {
452            return ZX_ERR_OUT_OF_RANGE;
453        }
454        auto proto = static_cast<i2c_protocol_t*>(out_protocol);
455        proto->ops = &i2c_proto_ops_;
456        proto->ctx = &i2c_ctxs_[index];
457        return ZX_OK;
458    }
459
460    // For other protocols, fall through to DdkGetProtocol if index is zero
461    if (index != 0) {
462        return ZX_ERR_OUT_OF_RANGE;
463    }
464    return DdkGetProtocol(proto_id, out_protocol);
465}
466
467zx_status_t ProxyDevice::CreateRoot(zx_device_t* parent, fbl::RefPtr<PlatformProxy> proxy) {
468    fbl::AllocChecker ac;
469    auto dev = fbl::make_unique_checked<ProxyDevice>(&ac,parent, ROOT_DEVICE_ID, proxy);
470    if (!ac.check()) {
471        return ZX_ERR_NO_MEMORY;
472    }
473    auto status = dev->InitRoot();
474    if (status != ZX_OK) {
475        return status;
476    }
477
478    // devmgr is now in charge of the device.
479    __UNUSED auto* dummy = dev.release();
480    return ZX_OK;
481}
482
483zx_status_t ProxyDevice::CreateChild(zx_device_t* parent, uint32_t device_id,
484                                     fbl::RefPtr<PlatformProxy> proxy, device_add_args_t* args) {
485    fbl::AllocChecker ac;
486    fbl::unique_ptr<ProxyDevice> dev(new (&ac) platform_bus::ProxyDevice(parent, device_id, proxy));
487    if (!ac.check()) {
488        return ZX_ERR_NO_MEMORY;
489    }
490    auto status = dev->InitChild(args);
491    if (status != ZX_OK) {
492        return status;
493    }
494
495    // devmgr is now in charge of the device.
496    __UNUSED auto* dummy = dev.release();
497    return ZX_OK;
498}
499
500ProxyDevice::ProxyDevice(zx_device_t* parent, uint32_t device_id,
501                         fbl::RefPtr<PlatformProxy> proxy)
502    : ProxyDeviceType(parent), device_id_(device_id), proxy_(proxy) {
503    // Initialize protocol ops
504    clk_proto_ops_.enable = ClkEnable;
505    clk_proto_ops_.disable = ClkDisable;
506    gpio_proto_ops_.config_in = GpioConfigIn;
507    gpio_proto_ops_.config_out = GpioConfigOut;
508    gpio_proto_ops_.set_alt_function = GpioSetAltFunction;
509    gpio_proto_ops_.read = GpioRead;
510    gpio_proto_ops_.write = GpioWrite;
511    gpio_proto_ops_.get_interrupt = GpioGetInterrupt;
512    gpio_proto_ops_.release_interrupt = GpioReleaseInterrupt;
513    gpio_proto_ops_.set_polarity = GpioSetPolarity;
514    i2c_proto_ops_.transact = I2cTransact;
515    i2c_proto_ops_.get_max_transfer_size = I2cGetMaxTransferSize;
516    i2c_proto_ops_.get_interrupt = I2cGetInterrupt;
517}
518
519zx_status_t ProxyDevice::InitCommon() {
520    pdev_device_info_t info;
521    auto status = GetDeviceInfo(&info);
522    if (status != ZX_OK) {
523        return status;
524    }
525    memcpy(name_, info.name, sizeof(name_));
526    metadata_count_ = info.metadata_count;
527
528    fbl::AllocChecker ac;
529
530    for (uint32_t i = 0; i < info.mmio_count; i++) {
531        rpc_pdev_req_t req = {};
532        rpc_pdev_rsp_t resp = {};
533        zx_handle_t rsrc_handle;
534
535        req.header.proto_id = ZX_PROTOCOL_PLATFORM_DEV;
536        req.header.op = PDEV_GET_MMIO;
537        req.index = i;
538        status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp),
539                             NULL, 0, &rsrc_handle, 1, NULL);
540        if (status != ZX_OK) {
541            return status;
542        }
543
544        Mmio mmio;
545        mmio.base = resp.paddr;
546        mmio.length = resp.length;
547        mmio.resource.reset(rsrc_handle);
548        mmios_.push_back(fbl::move(mmio), &ac);
549        if (!ac.check()) {
550            return ZX_ERR_NO_MEMORY;
551        }
552
553        zxlogf(SPEW, "%s: received MMIO %u (base %#lx length %#lx handle %#x)\n", name_, i,
554               mmio.base, mmio.length, mmio.resource.get());
555    }
556
557    for (uint32_t i = 0; i < info.irq_count; i++) {
558        rpc_pdev_req_t req = {};
559        rpc_pdev_rsp_t resp = {};
560        zx_handle_t rsrc_handle;
561
562        req.header.proto_id = ZX_PROTOCOL_PLATFORM_DEV;
563        req.header.op = PDEV_GET_INTERRUPT;
564        req.index = i;
565        status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.header, sizeof(resp),
566                             NULL, 0, &rsrc_handle, 1, NULL);
567        if (status != ZX_OK) {
568            return status;
569        }
570
571        Irq irq;
572        irq.irq = resp.irq;
573        irq.mode = resp.mode;
574        irq.resource.reset(rsrc_handle);
575        irqs_.push_back(fbl::move(irq), &ac);
576        if (!ac.check()) {
577            return ZX_ERR_NO_MEMORY;
578        }
579
580        zxlogf(SPEW, "%s: received IRQ %u (irq %#x handle %#x)\n", name_, i, irq.irq,
581               irq.resource.get());
582    }
583
584    uint32_t gpio_count = info.gpio_count;
585    if (gpio_count > 0) {
586        gpio_ctxs_.reset(new (&ac) GpioCtx[gpio_count], gpio_count);
587        if (!ac.check()) {
588            return ZX_ERR_NO_MEMORY;
589        }
590
591        for (uint32_t i = 0; i < info.gpio_count; i++) {
592            gpio_ctxs_[i].thiz = this;
593            gpio_ctxs_[i].index = i;
594        }
595    }
596
597    uint32_t i2c_count = info.i2c_channel_count;
598    if (i2c_count > 0) {
599        i2c_ctxs_.reset(new (&ac) I2cCtx[i2c_count], i2c_count);
600        if (!ac.check()) {
601            return ZX_ERR_NO_MEMORY;
602        }
603
604        for (uint32_t i = 0; i < i2c_count; i++) {
605            i2c_ctxs_[i].thiz = this;
606            i2c_ctxs_[i].index = i;
607        }
608    }
609
610    return ZX_OK;
611}
612
613zx_status_t ProxyDevice::InitRoot() {
614    auto status = InitCommon();
615    if (status != ZX_OK) {
616        return status;
617    }
618    return DdkAdd(name_);
619}
620
621zx_status_t ProxyDevice::InitChild(device_add_args_t* args) {
622    auto status = InitCommon();
623    if (status != ZX_OK) {
624        return status;
625    }
626
627    ctx_ = args->ctx;
628    device_ops_ = args->ops;
629    proto_id_ = args->proto_id;
630    proto_ops_ = args->proto_ops;
631
632    device_add_args_t new_args = *args;
633    // Replace ctx and device protocol ops with ours so we can intercept device_get_protocol().
634    new_args.ctx = this;
635    new_args.ops = &ddk_device_proto_;
636
637    if (metadata_count_ == 0) {
638        return device_add(parent(), &new_args, &zxdev_);
639    }
640
641    new_args.flags |= DEVICE_ADD_INVISIBLE;
642    status = device_add(parent(), &new_args, &zxdev_);
643    if (status != ZX_OK) {
644        return status;
645    }
646    // Remove ourselves from the devmgr if something goes wrong.
647    auto cleanup = fbl::MakeAutoCall([this]() { DdkRemove(); });
648
649    for (uint32_t i = 0; i < metadata_count_; i++) {
650        rpc_pdev_req_t req = {};
651        rpc_pdev_metadata_rsp_t resp = {};
652        req.header.proto_id = ZX_PROTOCOL_PLATFORM_DEV;
653        req.header.op = PDEV_GET_METADATA;
654        req.index = i;
655
656        status = proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp.pdev.header,
657                             sizeof(resp));
658        if (status != ZX_OK) {
659            return status;
660        }
661        status = DdkAddMetadata(resp.pdev.metadata_type, resp.metadata,
662                                resp.pdev.metadata_length);
663        if (status != ZX_OK) {
664            return status;
665        }
666    }
667
668    cleanup.cancel();
669    // Make ourselves visible after all metadata has been added successfully.
670    DdkMakeVisible();
671    return ZX_OK;
672}
673
674zx_status_t ProxyDevice::DdkGetProtocol(uint32_t proto_id, void* out) {
675    auto* proto = static_cast<ddk::AnyProtocol*>(out);
676
677    // Try driver's get_protocol() first, if it is implemented.
678    if (device_ops_ && device_ops_->get_protocol) {
679        if (device_ops_->get_protocol(ctx_, proto_id, out) == ZX_OK) {
680            return ZX_OK;
681        }
682    }
683
684    // Next try driver's primary protocol.
685    if (proto_ops_ && proto_id_ == proto_id) {
686        proto->ops = proto_ops_;
687        proto->ctx = ctx_;
688        return ZX_OK;
689    }
690
691    // Finally, protocols provided by platform bus.
692    proto->ctx = this;
693    switch (proto_id) {
694    case ZX_PROTOCOL_PLATFORM_DEV: {
695        proto->ops = &pdev_proto_ops_;
696        break;
697    }
698    case ZX_PROTOCOL_GPIO: {
699        auto count = gpio_ctxs_.size();
700        if (count == 0) {
701            return ZX_ERR_NOT_SUPPORTED;
702        } else if (count > 1) {
703            zxlogf(ERROR, "%s: device has more than one GPIO\n", __func__);
704            return ZX_ERR_BAD_STATE;
705        }
706        // Return zeroth GPIO resource.
707        proto->ops = &gpio_proto_ops_;
708        proto->ctx = &gpio_ctxs_[0];
709        return ZX_OK;
710    }
711    case ZX_PROTOCOL_I2C: {
712        auto count = i2c_ctxs_.size();
713        if (count == 0) {
714            return ZX_ERR_NOT_SUPPORTED;
715        } else if (count > 1) {
716            zxlogf(ERROR, "%s: device has more than one I2C channel\n", __func__);
717            return ZX_ERR_BAD_STATE;
718        }
719        // Return zeroth I2C resource.
720        proto->ops = &i2c_proto_ops_;
721        proto->ctx = &i2c_ctxs_[0];
722        return ZX_OK;
723    }
724    case ZX_PROTOCOL_CLK: {
725        proto->ops = &clk_proto_ops_;
726        break;
727    }
728    default:
729        return proxy_->GetProtocol(proto_id, out);;
730    }
731    return ZX_OK;
732}
733
734zx_status_t ProxyDevice::DdkOpen(zx_device_t** dev_out, uint32_t flags) {
735    if (device_ops_ && device_ops_->open) {
736        return device_ops_->open(ctx_, dev_out, flags);
737    }
738    return ZX_ERR_NOT_SUPPORTED;
739}
740
741zx_status_t ProxyDevice::DdkOpenAt(zx_device_t** dev_out, const char* path, uint32_t flags) {
742    if (device_ops_ && device_ops_->open_at) {
743        return device_ops_->open_at(ctx_, dev_out, path, flags);
744    }
745    return ZX_ERR_NOT_SUPPORTED;
746}
747
748zx_status_t ProxyDevice::DdkClose(uint32_t flags) {
749    if (device_ops_ && device_ops_->close) {
750        return device_ops_->close(ctx_, flags);
751    }
752    return ZX_ERR_NOT_SUPPORTED;
753}
754
755void ProxyDevice::DdkUnbind() {
756    if (device_ops_ && device_ops_->unbind) {
757        device_ops_->unbind(ctx_);
758    }
759}
760
761void ProxyDevice::DdkRelease() {
762    if (device_ops_ && device_ops_->release) {
763        device_ops_->release(ctx_);
764    }
765    delete this;
766}
767
768zx_status_t ProxyDevice::DdkRead(void* buf, size_t count, zx_off_t off, size_t* actual) {
769    if (device_ops_ && device_ops_->read) {
770        return device_ops_->read(ctx_, buf, count, off, actual);
771    }
772    return ZX_ERR_NOT_SUPPORTED;
773}
774
775zx_status_t ProxyDevice::DdkWrite(const void* buf, size_t count, zx_off_t off, size_t* actual) {
776    if (device_ops_ && device_ops_->write) {
777        return device_ops_->write(ctx_, buf, count, off, actual);
778    }
779    return ZX_ERR_NOT_SUPPORTED;
780}
781
782zx_off_t ProxyDevice::DdkGetSize() {
783    if (device_ops_ && device_ops_->get_size) {
784        return device_ops_->get_size(ctx_);
785    }
786    return ZX_ERR_NOT_SUPPORTED;
787}
788
789zx_status_t ProxyDevice::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf,
790                                  size_t out_len, size_t* actual) {
791    if (device_ops_ && device_ops_->ioctl) {
792        return device_ops_->ioctl(ctx_, op, in_buf, in_len, out_buf, out_len, actual);
793    }
794    return ZX_ERR_NOT_SUPPORTED;
795}
796
797zx_status_t ProxyDevice::DdkSuspend(uint32_t flags) {
798    if (device_ops_ && device_ops_->suspend) {
799        return device_ops_->suspend(ctx_, flags);
800    }
801    return ZX_ERR_NOT_SUPPORTED;
802}
803
804zx_status_t ProxyDevice::DdkResume(uint32_t flags) {
805    if (device_ops_ && device_ops_->resume) {
806        return device_ops_->resume(ctx_, flags);
807    }
808    return ZX_ERR_NOT_SUPPORTED;
809}
810
811zx_status_t ProxyDevice::DdkRxrpc(zx_handle_t channel) {
812    if (device_ops_ && device_ops_->rxrpc) {
813        return device_ops_->rxrpc(ctx_, channel);
814    }
815    return ZX_ERR_NOT_SUPPORTED;
816}
817
818} // namespace platform_bus
819