1// Copyright 2017 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-device.h"
6
7#include <assert.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include <ddk/binding.h>
13#include <ddk/debug.h>
14#include <ddk/device.h>
15#include <ddk/driver.h>
16#include <ddk/metadata.h>
17#include <ddk/protocol/platform-defs.h>
18#include <fbl/function.h>
19#include <zircon/syscalls/resource.h>
20
21#include "platform-bus.h"
22
23namespace platform_bus {
24
25zx_status_t PlatformDevice::Create(const pbus_dev_t* pdev, zx_device_t* parent, PlatformBus* bus,
26                                   fbl::unique_ptr<platform_bus::PlatformDevice>* out) {
27    fbl::AllocChecker ac;
28    fbl::unique_ptr<platform_bus::PlatformDevice> dev(new (&ac)
29                                          platform_bus::PlatformDevice(parent, bus, pdev));
30    if (!ac.check()) {
31        return ZX_ERR_NO_MEMORY;
32    }
33    auto status = dev->Init(pdev);
34    if (status != ZX_OK) {
35        return status;
36    }
37    out->swap(dev);
38    return ZX_OK;
39}
40
41PlatformDevice::PlatformDevice(zx_device_t* parent, PlatformBus* bus, const pbus_dev_t* pdev)
42    : PlatformDeviceType(parent), bus_(bus), vid_(pdev->vid), pid_(pdev->pid),
43      did_(pdev->did), resource_tree_(ROOT_DEVICE_ID) {
44    strlcpy(name_, pdev->name, sizeof(name_));
45}
46
47zx_status_t PlatformDevice::Init(const pbus_dev_t* pdev) {
48    uint32_t next_device_id = ROOT_DEVICE_ID + 1;
49    auto status = resource_tree_.Init(pdev, &next_device_id);
50    if (status != ZX_OK) {
51        return status;
52    }
53
54    fbl::AllocChecker ac;
55    device_index_.reserve(resource_tree_.DeviceCount(), &ac);
56    if (!ac.check()) {
57        return ZX_ERR_NO_MEMORY;
58    }
59    resource_tree_.BuildDeviceIndex(&device_index_);
60
61    return ZX_OK;
62}
63
64// Create a resource and pass it back to the proxy along with necessary metadata
65// to create/map the VMO in the driver process.
66zx_status_t PlatformDevice::RpcGetMmio(const DeviceResources* dr, uint32_t index, zx_paddr_t* out_paddr,
67                                       size_t* out_length, zx_handle_t* out_handle,
68                                       uint32_t* out_handle_count) {
69    if (index >= dr->mmio_count()) {
70        return ZX_ERR_OUT_OF_RANGE;
71    }
72
73    const pbus_mmio_t& mmio = dr->mmio(index);
74    zx_handle_t handle;
75    char rsrc_name[ZX_MAX_NAME_LEN];
76    snprintf(rsrc_name, ZX_MAX_NAME_LEN - 1, "%s.pbus[%u]", name_, index);
77    zx_status_t status = zx_resource_create(bus_->GetResource(), ZX_RSRC_KIND_MMIO, mmio.base,
78                                            mmio.length, rsrc_name, sizeof(rsrc_name), &handle);
79    if (status != ZX_OK) {
80        zxlogf(ERROR, "%s: pdev_rpc_get_mmio: zx_resource_create failed: %d\n", name_, status);
81        return status;
82    }
83
84    *out_paddr = mmio.base;
85    *out_length = mmio.length;
86    *out_handle_count = 1;
87    *out_handle = handle;
88    return ZX_OK;
89}
90
91// Create a resource and pass it back to the proxy along with necessary metadata
92// to create the IRQ in the driver process.
93zx_status_t PlatformDevice::RpcGetInterrupt(const DeviceResources* dr, uint32_t index,
94                                            uint32_t* out_irq, uint32_t* out_mode,
95                                            zx_handle_t* out_handle, uint32_t* out_handle_count) {
96    if (index >= dr->irq_count()) {
97        return ZX_ERR_OUT_OF_RANGE;
98    }
99
100    zx_handle_t handle;
101    const pbus_irq_t& irq = dr->irq(index);
102    uint32_t options = ZX_RSRC_KIND_IRQ | ZX_RSRC_FLAG_EXCLUSIVE;
103    char rsrc_name[ZX_MAX_NAME_LEN];
104    snprintf(rsrc_name, ZX_MAX_NAME_LEN - 1, "%s.pbus[%u]", name_, index);
105    zx_status_t status = zx_resource_create(bus_->GetResource(), options, irq.irq, 1, rsrc_name,
106                                            sizeof(rsrc_name), &handle);
107    if (status != ZX_OK) {
108        return status;
109    }
110
111    *out_irq = irq.irq;
112    *out_mode = irq.mode;
113    *out_handle_count = 1;
114    *out_handle = handle;
115    return status;
116}
117
118zx_status_t PlatformDevice::RpcGetBti(const DeviceResources* dr, uint32_t index,
119                                      zx_handle_t* out_handle, uint32_t* out_handle_count) {
120    if (index >= dr->bti_count()) {
121        return ZX_ERR_OUT_OF_RANGE;
122    }
123
124    const pbus_bti_t& bti = dr->bti(index);
125
126    zx_status_t status = bus_->GetBti(bti.iommu_index, bti.bti_id, out_handle);
127
128    if (status == ZX_OK) {
129        *out_handle_count = 1;
130    }
131
132    return status;
133}
134
135zx_status_t PlatformDevice::RpcGetDeviceInfo(const DeviceResources* dr, pdev_device_info_t* out_info) {
136    pdev_device_info_t info = {
137        .vid = vid_,
138        .pid = pid_,
139        .did = did_,
140        .mmio_count = static_cast<uint32_t>(dr->mmio_count()),
141        .irq_count = static_cast<uint32_t>(dr->irq_count()),
142        .gpio_count = static_cast<uint32_t>(dr->gpio_count()),
143        .i2c_channel_count = static_cast<uint32_t>(dr->i2c_channel_count()),
144        .clk_count = static_cast<uint32_t>(dr->clk_count()),
145        .bti_count = static_cast<uint32_t>(dr->bti_count()),
146        .metadata_count = static_cast<uint32_t>(dr->metadata_count() + dr->boot_metadata_count()),
147        .reserved = {},
148        .name = {},
149    };
150    static_assert(sizeof(info.name) == sizeof(name_), "");
151    memcpy(info.name, name_, sizeof(out_info->name));
152    memcpy(out_info, &info, sizeof(info));
153
154    return ZX_OK;
155}
156
157zx_status_t PlatformDevice::RpcDeviceAdd(const DeviceResources* dr, uint32_t index,
158                                         uint32_t* out_device_id) {
159    if (index >= dr->child_count()) {
160        return ZX_ERR_OUT_OF_RANGE;
161    }
162    // TODO(voydanoff) verify that this device has not already been added?
163    *out_device_id = dr->child_index(index);
164    return ZX_OK;
165}
166
167zx_status_t PlatformDevice::RpcGetMetadata(const DeviceResources* dr, uint32_t index,
168                                           uint32_t* out_type, uint8_t* buf, uint32_t buf_size,
169                                           uint32_t* actual) {
170    if (index >= dr->metadata_count() + dr->boot_metadata_count()) {
171        return ZX_ERR_OUT_OF_RANGE;
172    }
173
174    if (index < dr->metadata_count()) {
175        auto& metadata = dr->metadata(index);
176        if (metadata.len > buf_size) {
177            return ZX_ERR_BUFFER_TOO_SMALL;
178        }
179        memcpy(buf, metadata.data, metadata.len);
180        *out_type = metadata.type;
181        *actual = metadata.len;
182        return ZX_OK;
183    } else {
184        // boot_metadata indices follow metadata indices.
185        index -= static_cast<uint32_t>(dr->metadata_count());
186
187        auto& metadata = dr->boot_metadata(index);
188        const void* data;
189        uint32_t length;
190        auto status = bus_->GetZbiMetadata(metadata.zbi_type, metadata.zbi_extra, &data, &length);
191        if (status == ZX_OK) {
192            if (length > buf_size) {
193                return ZX_ERR_BUFFER_TOO_SMALL;
194            }
195            memcpy(buf, data, length);
196            *out_type = metadata.zbi_type;
197            *actual = length;
198        }
199        return status;
200    }
201}
202
203zx_status_t PlatformDevice::RpcGetProtocols(const DeviceResources* dr, uint32_t* out_protocols,
204                                            uint32_t* out_protocol_count) {
205    auto count = dr->protocol_count();
206    memcpy(out_protocols, dr->protocols(), count * sizeof(*out_protocols));
207    *out_protocol_count = static_cast<uint32_t>(count);
208    return ZX_OK;
209}
210
211zx_status_t PlatformDevice::RpcGpioConfigIn(const DeviceResources* dr, uint32_t index, uint32_t flags) {
212    if (bus_->gpio() == nullptr) {
213        return ZX_ERR_NOT_SUPPORTED;
214    }
215    if (index >= dr->gpio_count()) {
216        return ZX_ERR_OUT_OF_RANGE;
217    }
218
219    return bus_->gpio()->ConfigIn(dr->gpio(index).gpio, flags);
220}
221
222zx_status_t PlatformDevice::RpcGpioConfigOut(const DeviceResources* dr, uint32_t index,
223                                             uint8_t initial_value) {
224    if (bus_->gpio() == nullptr) {
225        return ZX_ERR_NOT_SUPPORTED;
226    }
227    if (index >= dr->gpio_count()) {
228        return ZX_ERR_OUT_OF_RANGE;
229    }
230
231    return bus_->gpio()->ConfigOut(dr->gpio(index).gpio, initial_value);
232}
233
234zx_status_t PlatformDevice::RpcGpioSetAltFunction(const DeviceResources* dr, uint32_t index,
235                                                  uint64_t function) {
236    if (bus_->gpio() == nullptr) {
237        return ZX_ERR_NOT_SUPPORTED;
238    }
239    if (index >= dr->gpio_count()) {
240        return ZX_ERR_OUT_OF_RANGE;
241    }
242
243    return bus_->gpio()->SetAltFunction(dr->gpio(index).gpio, function);
244}
245
246zx_status_t PlatformDevice::RpcGpioRead(const DeviceResources* dr, uint32_t index,
247                                        uint8_t* out_value) {
248    if (bus_->gpio() == nullptr) {
249        return ZX_ERR_NOT_SUPPORTED;
250    }
251    if (index >= dr->gpio_count()) {
252        return ZX_ERR_OUT_OF_RANGE;
253    }
254
255    return bus_->gpio()->Read(dr->gpio(index).gpio, out_value);
256}
257
258zx_status_t PlatformDevice::RpcGpioWrite(const DeviceResources* dr, uint32_t index, uint8_t value) {
259    if (bus_->gpio() == nullptr) {
260        return ZX_ERR_NOT_SUPPORTED;
261    }
262    if (index >= dr->gpio_count()) {
263        return ZX_ERR_OUT_OF_RANGE;
264    }
265
266    return bus_->gpio()->Write(dr->gpio(index).gpio, value);
267}
268
269zx_status_t PlatformDevice::RpcGpioGetInterrupt(const DeviceResources* dr, uint32_t index,
270                                                uint32_t flags, zx_handle_t* out_handle,
271                                                uint32_t* out_handle_count) {
272    if (bus_->gpio() == nullptr) {
273        return ZX_ERR_NOT_SUPPORTED;
274    }
275    if (index >= dr->gpio_count()) {
276        return ZX_ERR_OUT_OF_RANGE;
277    }
278
279    zx_status_t status = bus_->gpio()->GetInterrupt(dr->gpio(index).gpio, flags, out_handle);
280    if (status == ZX_OK) {
281        *out_handle_count = 1;
282    }
283    return status;
284}
285
286zx_status_t PlatformDevice::RpcGpioReleaseInterrupt(const DeviceResources* dr, uint32_t index) {
287    if (bus_->gpio() == nullptr) {
288        return ZX_ERR_NOT_SUPPORTED;
289    }
290    if (index >= dr->gpio_count()) {
291        return ZX_ERR_OUT_OF_RANGE;
292    }
293    return bus_->gpio()->ReleaseInterrupt(dr->gpio(index).gpio);
294}
295
296zx_status_t PlatformDevice::RpcGpioSetPolarity(const DeviceResources* dr, uint32_t index,
297                                               uint32_t flags) {
298    if (bus_->gpio() == nullptr) {
299        return ZX_ERR_NOT_SUPPORTED;
300    }
301    if (index >= dr->gpio_count()) {
302        return ZX_ERR_OUT_OF_RANGE;
303    }
304    return bus_->gpio()->SetPolarity(dr->gpio(index).gpio, flags);
305}
306
307zx_status_t PlatformDevice::RpcI2cTransact(const DeviceResources* dr, uint32_t txid,
308                                           rpc_i2c_req_t* req, zx_handle_t channel) {
309    if (bus_->i2c() == nullptr) {
310        return ZX_ERR_NOT_SUPPORTED;
311    }
312    uint32_t index = req->index;
313    if (index >= dr->i2c_channel_count()) {
314        return ZX_ERR_OUT_OF_RANGE;
315    }
316    const pbus_i2c_channel_t& pdev_channel = dr->i2c_channel(index);
317
318    return bus_->I2cTransact(txid, req, &pdev_channel, channel);
319}
320
321zx_status_t PlatformDevice::RpcI2cGetMaxTransferSize(const DeviceResources* dr, uint32_t index,
322                                                     size_t* out_size) {
323    if (bus_->i2c() == nullptr) {
324        return ZX_ERR_NOT_SUPPORTED;
325    }
326    if (index >= dr->i2c_channel_count()) {
327        return ZX_ERR_OUT_OF_RANGE;
328    }
329    const pbus_i2c_channel_t& pdev_channel = dr->i2c_channel(index);
330
331    return bus_->i2c()->GetMaxTransferSize(pdev_channel.bus_id, out_size);
332}
333
334zx_status_t PlatformDevice::RpcClkEnable(const DeviceResources* dr, uint32_t index) {
335    if (bus_->clk() == nullptr) {
336        return ZX_ERR_NOT_SUPPORTED;
337    }
338    if (index >= dr->clk_count()) {
339        return ZX_ERR_OUT_OF_RANGE;
340    }
341
342    return bus_->clk()->Enable(dr->clk(index).clk);
343}
344
345zx_status_t PlatformDevice::RpcClkDisable(const DeviceResources* dr, uint32_t index) {
346    if (bus_->clk() == nullptr) {
347        return ZX_ERR_NOT_SUPPORTED;
348    }
349    if (index >= dr->clk_count()) {
350        return ZX_ERR_OUT_OF_RANGE;
351    }
352
353    return bus_->clk()->Disable(dr->clk(index).clk);
354}
355
356zx_status_t PlatformDevice::DdkRxrpc(zx_handle_t channel) {
357    if (channel == ZX_HANDLE_INVALID) {
358        // proxy device has connected
359        return ZX_OK;
360    }
361
362    uint8_t req_buf[PROXY_MAX_TRANSFER_SIZE];
363    uint8_t resp_buf[PROXY_MAX_TRANSFER_SIZE];
364    auto* req_header = reinterpret_cast<platform_proxy_req_t*>(&req_buf);
365    auto* resp_header = reinterpret_cast<platform_proxy_rsp_t*>(&resp_buf);
366    uint32_t actual;
367    zx_handle_t req_handles[ZX_CHANNEL_MAX_MSG_HANDLES];
368    zx_handle_t resp_handles[ZX_CHANNEL_MAX_MSG_HANDLES];
369    uint32_t req_handle_count;
370    uint32_t resp_handle_count = 0;
371
372    auto status = zx_channel_read(channel, 0, &req_buf, req_handles, sizeof(req_buf),
373                                  fbl::count_of(req_handles), &actual, &req_handle_count);
374    if (status != ZX_OK) {
375        zxlogf(ERROR, "platform_dev_rxrpc: zx_channel_read failed %d\n", status);
376        return status;
377    }
378
379    const uint32_t index = req_header->device_id;
380    if (index >= device_index_.size()) {
381        return ZX_ERR_OUT_OF_RANGE;
382    }
383    const DeviceResources* dr = device_index_[index];
384
385    resp_header->txid = req_header->txid;
386    uint32_t resp_len;
387
388    switch (req_header->proto_id) {
389    case ZX_PROTOCOL_PLATFORM_DEV: {
390        auto req = reinterpret_cast<rpc_pdev_req_t*>(&req_buf);
391        if (actual < sizeof(*req)) {
392            zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req));
393            return ZX_ERR_INTERNAL;
394        }
395        auto resp = reinterpret_cast<rpc_pdev_rsp_t*>(&resp_buf);
396        resp_len = sizeof(*resp);
397
398        switch (req_header->op) {
399        case PDEV_GET_MMIO:
400            status = RpcGetMmio(dr, req->index, &resp->paddr, &resp->length, resp_handles,
401                                &resp_handle_count);
402            break;
403        case PDEV_GET_INTERRUPT:
404            status = RpcGetInterrupt(dr, req->index, &resp->irq, &resp->mode, resp_handles,
405                                     &resp_handle_count);
406            break;
407        case PDEV_GET_BTI:
408            status = RpcGetBti(dr, req->index, resp_handles, &resp_handle_count);
409            break;
410        case PDEV_GET_DEVICE_INFO:
411            status = RpcGetDeviceInfo(dr, &resp->device_info);
412            break;
413        case PDEV_GET_BOARD_INFO:
414            status = bus_->GetBoardInfo(&resp->board_info);
415            break;
416        case PDEV_DEVICE_ADD:
417            status = RpcDeviceAdd(dr, req->index, &resp->device_id);
418            break;
419        case PDEV_GET_METADATA: {
420            auto resp = reinterpret_cast<rpc_pdev_metadata_rsp_t*>(resp_buf);
421            static_assert(sizeof(*resp) == sizeof(resp_buf), "");
422            auto buf_size = static_cast<uint32_t>(sizeof(resp_buf) - sizeof(*resp_header));
423            status = RpcGetMetadata(dr, req->index, &resp->pdev.metadata_type, resp->metadata,
424                                    buf_size, &resp->pdev.metadata_length);
425            resp_len += resp->pdev.metadata_length;
426            break;
427        }
428        case PDEV_GET_PROTOCOLS: {
429            auto protos = reinterpret_cast<uint32_t*>(&resp[1]);
430            status = RpcGetProtocols(dr, protos, &resp->protocol_count);
431            resp_len += static_cast<uint32_t>(resp->protocol_count * sizeof(*protos));
432            break;
433        }
434        default:
435            zxlogf(ERROR, "%s: unknown pdev op %u\n", __func__, req_header->op);
436            return ZX_ERR_INTERNAL;
437        }
438        break;
439    }
440    case ZX_PROTOCOL_GPIO: {
441        auto req = reinterpret_cast<rpc_gpio_req_t*>(&req_buf);
442        if (actual < sizeof(*req)) {
443            zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req));
444            return ZX_ERR_INTERNAL;
445        }
446        auto resp = reinterpret_cast<rpc_gpio_rsp_t*>(&resp_buf);
447        resp_len = sizeof(*resp);
448
449        switch (req_header->op) {
450        case GPIO_CONFIG_IN:
451            status = RpcGpioConfigIn(dr, req->index, req->flags);
452            break;
453        case GPIO_CONFIG_OUT:
454            status = RpcGpioConfigOut(dr, req->index, req->value);
455            break;
456        case GPIO_SET_ALT_FUNCTION:
457            status = RpcGpioSetAltFunction(dr, req->index, req->alt_function);
458            break;
459        case GPIO_READ:
460            status = RpcGpioRead(dr, req->index, &resp->value);
461            break;
462        case GPIO_WRITE:
463            status = RpcGpioWrite(dr, req->index, req->value);
464            break;
465        case GPIO_GET_INTERRUPT:
466            status = RpcGpioGetInterrupt(dr, req->index, req->flags, resp_handles,
467                                         &resp_handle_count);
468            break;
469        case GPIO_RELEASE_INTERRUPT:
470            status = RpcGpioReleaseInterrupt(dr, req->index);
471            break;
472        case GPIO_SET_POLARITY:
473            status = RpcGpioSetPolarity(dr, req->index, req->polarity);
474            break;
475        default:
476            zxlogf(ERROR, "%s: unknown GPIO op %u\n", __func__, req_header->op);
477            return ZX_ERR_INTERNAL;
478        }
479        break;
480    }
481    case ZX_PROTOCOL_I2C: {
482        auto req = reinterpret_cast<rpc_i2c_req_t*>(&req_buf);
483        if (actual < sizeof(*req)) {
484            zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req));
485            return ZX_ERR_INTERNAL;
486        }
487        auto resp = reinterpret_cast<rpc_i2c_rsp_t*>(&resp_buf);
488        resp_len = sizeof(*resp);
489
490        switch (req_header->op) {
491        case I2C_GET_MAX_TRANSFER:
492            status = RpcI2cGetMaxTransferSize(dr, req->index, &resp->max_transfer);
493            break;
494        case I2C_TRANSACT: {
495            status = RpcI2cTransact(dr, req_header->txid, req, channel);
496            if (status == ZX_OK) {
497                // If platform_i2c_transact succeeds, we return immmediately instead of calling
498                // zx_channel_write below. Instead we will respond in platform_i2c_complete().
499                return ZX_OK;
500            }
501            break;
502        }
503        default:
504            zxlogf(ERROR, "%s: unknown I2C op %u\n", __func__, req_header->op);
505            return ZX_ERR_INTERNAL;
506        }
507        break;
508    }
509    case ZX_PROTOCOL_CLK: {
510        auto req = reinterpret_cast<rpc_clk_req_t*>(&req_buf);
511        if (actual < sizeof(*req)) {
512            zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req));
513            return ZX_ERR_INTERNAL;
514        }
515        resp_len = sizeof(*resp_header);
516
517        switch (req_header->op) {
518        case CLK_ENABLE:
519            status = RpcClkEnable(dr, req->index);
520            break;
521        case CLK_DISABLE:
522            status = RpcClkDisable(dr, req->index);
523            break;
524        default:
525            zxlogf(ERROR, "%s: unknown clk op %u\n", __func__, req_header->op);
526            return ZX_ERR_INTERNAL;
527        }
528        break;
529    }
530    default: {
531        platform_proxy_args_t args = {
532            .req = req_header,
533            .req_size = actual,
534            .resp = resp_header,
535            .resp_size = sizeof(resp_buf),
536            .req_handles = req_handles,
537            .req_handle_count = req_handle_count,
538            .resp_handles = resp_handles,
539            .resp_handle_count = fbl::count_of(resp_handles),
540            .resp_actual_size = 0,
541            .resp_actual_handles = 0,
542        };
543        status = bus_->Proxy(&args);
544        if (status == ZX_OK) {
545            status = args.resp->status;
546        }
547        resp_len = args.resp_actual_size;
548        resp_handle_count = args.resp_actual_handles;
549        break;
550    }
551    }
552
553    // set op to match request so zx_channel_write will return our response
554    resp_header->status = status;
555    status = zx_channel_write(channel, 0, resp_header, resp_len,
556                              (resp_handle_count ? resp_handles : nullptr), resp_handle_count);
557    if (status != ZX_OK) {
558        zxlogf(ERROR, "platform_dev_rxrpc: zx_channel_write failed %d\n", status);
559    }
560    return status;
561}
562
563void PlatformDevice::DdkRelease() {
564    delete this;
565}
566
567zx_status_t PlatformDevice::Start() {
568    char name[ZX_DEVICE_NAME_MAX];
569    if (vid_ == PDEV_VID_GENERIC && pid_ == PDEV_PID_GENERIC && did_ == PDEV_DID_KPCI) {
570        strlcpy(name, "pci", sizeof(name));
571    } else {
572        snprintf(name, sizeof(name), "%02x:%02x:%01x", vid_, pid_, did_);
573    }
574    char argstr[64];
575    snprintf(argstr, sizeof(argstr), "pdev:%s,", name);
576
577    // Platform devices run in their own devhosts.
578    uint32_t device_add_flags = DEVICE_ADD_MUST_ISOLATE;
579
580    const DeviceResources* dr = device_index_[ROOT_DEVICE_ID];
581    const size_t metadata_count = dr->metadata_count();
582    const size_t boot_metadata_count = dr->boot_metadata_count();
583    if (metadata_count > 0 || boot_metadata_count > 0) {
584        // Keep device invisible until after we add its metadata.
585        device_add_flags |= DEVICE_ADD_INVISIBLE;
586    }
587
588    zx_status_t status;
589    if (dr->protocol_count() > 0) {
590        // PlatformDevice::Start with protocols
591        status = DdkAdd(name, device_add_flags, nullptr, 0, ZX_PROTOCOL_PLATFORM_PROXY, argstr);
592    } else {
593    zx_device_prop_t props[] = {
594            {BIND_PLATFORM_DEV_VID, 0, vid_},
595            {BIND_PLATFORM_DEV_PID, 0, pid_},
596            {BIND_PLATFORM_DEV_DID, 0, did_},
597        };
598
599        status = DdkAdd(name, device_add_flags, props, fbl::count_of(props), ZX_PROTOCOL_PLATFORM_DEV,
600                        argstr);
601    }
602
603    if (status != ZX_OK) {
604        return status;
605    }
606
607    if (metadata_count > 0 || boot_metadata_count > 0) {
608        for (size_t i = 0; i < metadata_count; i++) {
609            const auto& metadata = dr->metadata(i);
610            status = DdkAddMetadata(metadata.type, metadata.data, metadata.len);
611            if (status != ZX_OK) {
612                DdkRemove();
613                return status;
614            }
615        }
616
617        for (size_t i = 0; i < boot_metadata_count; i++) {
618            const auto& metadata = dr->boot_metadata(i);
619            const void* data;
620            uint32_t length;
621            status = bus_->GetZbiMetadata(metadata.zbi_type, metadata.zbi_extra, &data, &length);
622            if (status == ZX_OK) {
623                status = DdkAddMetadata(metadata.zbi_type, data, length);
624            }
625            if (status != ZX_OK) {
626                DdkRemove();
627                return status;
628            }
629        }
630
631        DdkMakeVisible();
632    }
633
634    return ZX_OK;
635}
636
637} // namespace platform_bus
638