| // Copyright 2017 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 "platform-device.h" |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <ddk/debug.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddk/binding.h> |
| #include <ddk/metadata.h> |
| #include <ddk/protocol/platform-defs.h> |
| #include <zircon/syscalls/resource.h> |
| |
| #include "platform-bus.h" |
| |
| namespace platform_bus { |
| |
| zx_status_t PlatformDevice::Create(const pbus_dev_t* pdev, zx_device_t* parent, PlatformBus* bus, |
| uint32_t flags, |
| fbl::unique_ptr<platform_bus::PlatformDevice>* out) { |
| fbl::AllocChecker ac; |
| fbl::unique_ptr<platform_bus::PlatformDevice> dev(new (&ac) |
| platform_bus::PlatformDevice(parent, bus, flags, pdev)); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| auto status = dev->Init(pdev); |
| if (status != ZX_OK) { |
| return status; |
| } |
| out->swap(dev); |
| return ZX_OK; |
| } |
| |
| PlatformDevice::PlatformDevice(zx_device_t* parent, PlatformBus* bus, uint32_t flags, |
| const pbus_dev_t* pdev) |
| : PlatformDeviceType(parent), bus_(bus), flags_(flags), vid_(pdev->vid), pid_(pdev->pid), |
| did_(pdev->did), serial_port_info_(pdev->serial_port_info) { |
| strlcpy(name_, pdev->name, sizeof(name_)); |
| } |
| |
| zx_status_t PlatformDevice::Init(const pbus_dev_t* pdev) { |
| fbl::AllocChecker ac; |
| |
| if (pdev->mmio_count) { |
| mmios_.reserve(pdev->mmio_count, &ac); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| for (uint32_t i = 0; i < pdev->mmio_count; i++) { |
| mmios_.push_back(pdev->mmios[i]); |
| } |
| } |
| if (pdev->irq_count) { |
| irqs_.reserve(pdev->irq_count, &ac); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| for (uint32_t i = 0; i < pdev->irq_count; i++) { |
| irqs_.push_back(pdev->irqs[i]); |
| } |
| } |
| if (pdev->gpio_count) { |
| gpios_.reserve(pdev->gpio_count, &ac); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| for (uint32_t i = 0; i < pdev->gpio_count; i++) { |
| gpios_.push_back(pdev->gpios[i]); |
| } |
| } |
| if (pdev->i2c_channel_count) { |
| i2c_channels_.reserve(pdev->i2c_channel_count, &ac); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| for (uint32_t i = 0; i < pdev->i2c_channel_count; i++) { |
| i2c_channels_.push_back(pdev->i2c_channels[i]); |
| } |
| } |
| if (pdev->clk_count) { |
| clks_.reserve(pdev->clk_count, &ac); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| for (uint32_t i = 0; i < pdev->clk_count; i++) { |
| clks_.push_back(pdev->clks[i]); |
| } |
| } |
| if (pdev->bti_count) { |
| btis_.reserve(pdev->bti_count, &ac); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| for (uint32_t i = 0; i < pdev->bti_count; i++) { |
| btis_.push_back(pdev->btis[i]); |
| } |
| } |
| if (pdev->metadata_count) { |
| metadata_.reserve(pdev->metadata_count, &ac); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| for (uint32_t i = 0; i < pdev->metadata_count; i++) { |
| metadata_.push_back(pdev->metadata[i]); |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| |
| zx_status_t PlatformDevice::MapMmio(uint32_t index, uint32_t cache_policy, void** out_vaddr, |
| size_t* out_size, zx_paddr_t* out_paddr, |
| zx_handle_t* out_handle) { |
| if (index >= mmios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| const pbus_mmio_t& mmio = mmios_[index]; |
| const zx_paddr_t vmo_base = ROUNDDOWN(mmio.base, PAGE_SIZE); |
| const size_t vmo_size = ROUNDUP(mmio.base + mmio.length - vmo_base, PAGE_SIZE); |
| zx_handle_t vmo_handle; |
| zx_status_t status = zx_vmo_create_physical(bus_->GetResource(), vmo_base, vmo_size, |
| &vmo_handle); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "platform_dev_map_mmio: zx_vmo_create_physical failed %d\n", status); |
| return status; |
| } |
| |
| status = zx_vmo_set_cache_policy(vmo_handle, cache_policy); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "platform_dev_map_mmio: zx_vmo_set_cache_policy failed %d\n", status); |
| goto fail; |
| } |
| |
| uintptr_t virt; |
| status = zx_vmar_map(zx_vmar_root_self(), 0, vmo_handle, 0, vmo_size, |
| ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE | ZX_VM_FLAG_MAP_RANGE, |
| &virt); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "platform_dev_map_mmio: zx_vmar_map failed %d\n", status); |
| goto fail; |
| } |
| |
| *out_size = mmio.length; |
| *out_handle = vmo_handle; |
| if (out_paddr) { |
| *out_paddr = vmo_base; |
| } |
| *out_vaddr = reinterpret_cast<void*>(virt + (mmio.base - vmo_base)); |
| return ZX_OK; |
| |
| fail: |
| zx_handle_close(vmo_handle); |
| return status; |
| } |
| |
| zx_status_t PlatformDevice::MapInterrupt(uint32_t index, uint32_t flags, zx_handle_t* out_handle) { |
| if (index >= irqs_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| if (out_handle == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| pbus_irq_t& irq = irqs_[index]; |
| if (flags == 0) { |
| flags = irq.mode; |
| } |
| zx_status_t status = zx_interrupt_create(bus_->GetResource(), irq.irq, flags, out_handle); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "platform_dev_map_interrupt: zx_interrupt_create failed %d\n", status); |
| return status; |
| } |
| return status; |
| } |
| |
| zx_status_t PlatformDevice::GetBti(uint32_t index, zx_handle_t* out_handle) { |
| if (index >= btis_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| if (out_handle == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| pbus_bti_t& bti = btis_[index]; |
| |
| return bus_->GetBti(bti.iommu_index, bti.bti_id, out_handle); |
| } |
| |
| zx_status_t PlatformDevice::GetDeviceInfo(pdev_device_info_t* out_info) { |
| memset(out_info, 0, sizeof(*out_info)); |
| out_info->vid = vid_; |
| out_info->pid = pid_; |
| out_info->did = did_; |
| memcpy(&out_info->serial_port_info, &serial_port_info_, sizeof(out_info->serial_port_info)); |
| out_info->mmio_count = static_cast<uint32_t>(mmios_.size()); |
| out_info->irq_count = static_cast<uint32_t>(irqs_.size()); |
| out_info->gpio_count = static_cast<uint32_t>(gpios_.size()); |
| out_info->i2c_channel_count = static_cast<uint32_t>(i2c_channels_.size()); |
| out_info->clk_count = static_cast<uint32_t>(clks_.size()); |
| out_info->bti_count = static_cast<uint32_t>(btis_.size()); |
| out_info->metadata_count = static_cast<uint32_t>(metadata_.size()); |
| memcpy(out_info->name, name_, sizeof(out_info->name)); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t PlatformDevice::GetBoardInfo(pdev_board_info_t* out_info) { |
| return bus_->GetBoardInfo(out_info); |
| } |
| |
| // Create a resource and pass it back to the proxy along with necessary metadata |
| // to create/map the VMO in the driver process. |
| zx_status_t PlatformDevice::RpcGetMmio(uint32_t index, zx_paddr_t* out_paddr, size_t *out_length, |
| zx_handle_t* out_handle, uint32_t* out_handle_count) { |
| if (index >= mmios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| pbus_mmio_t* mmio = &mmios_[index]; |
| zx_handle_t handle; |
| char rsrc_name[ZX_MAX_NAME_LEN]; |
| snprintf(rsrc_name, ZX_MAX_NAME_LEN-1, "%s.pbus[%u]", name_, index); |
| zx_status_t status = zx_resource_create(bus_->GetResource(), ZX_RSRC_KIND_MMIO, mmio->base, |
| mmio->length, rsrc_name, sizeof(rsrc_name), &handle); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: pdev_rpc_get_mmio: zx_resource_create failed: %d\n", name_, status); |
| return status; |
| } |
| |
| *out_paddr = mmio->base; |
| *out_length = mmio->length; |
| *out_handle_count = 1; |
| *out_handle = handle; |
| return ZX_OK; |
| } |
| |
| // Create a resource and pass it back to the proxy along with necessary metadata |
| // to create the IRQ in the driver process. |
| zx_status_t PlatformDevice::RpcGetInterrupt(uint32_t index, uint32_t* out_irq, uint32_t* out_mode, |
| zx_handle_t* out_handle, uint32_t* out_handle_count) { |
| if (index >= irqs_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| zx_handle_t handle; |
| pbus_irq_t* irq = &irqs_[index]; |
| uint32_t options = ZX_RSRC_KIND_IRQ | ZX_RSRC_FLAG_EXCLUSIVE; |
| char rsrc_name[ZX_MAX_NAME_LEN]; |
| snprintf(rsrc_name, ZX_MAX_NAME_LEN-1, "%s.pbus[%u]", name_, index); |
| zx_status_t status = zx_resource_create(bus_->GetResource(), options, irq->irq, 1, rsrc_name, |
| sizeof(rsrc_name), &handle); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| *out_irq = irq->irq; |
| *out_mode = irq->mode; |
| *out_handle_count = 1; |
| *out_handle = handle; |
| return status; |
| } |
| |
| zx_status_t PlatformDevice::RpcGetBti(uint32_t index, zx_handle_t* out_handle, |
| uint32_t* out_handle_count) { |
| zx_status_t status = GetBti(index, out_handle); |
| if (status == ZX_OK) { |
| *out_handle_count = 1; |
| } |
| return status; |
| } |
| |
| zx_status_t PlatformDevice::RpcUmsSetMode(usb_mode_t mode) { |
| if (bus_->ums() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return bus_->ums()->SetMode(mode); |
| } |
| |
| zx_status_t PlatformDevice::RpcGpioConfig(uint32_t index, uint32_t flags) { |
| if (bus_->gpio() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= gpios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| return bus_->gpio()->Config(gpios_[index].gpio, flags); |
| } |
| |
| zx_status_t PlatformDevice::RpcGpioSetAltFunction(uint32_t index, uint64_t function) { |
| if (bus_->gpio() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= gpios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| return bus_->gpio()->SetAltFunction(gpios_[index].gpio, function); |
| } |
| |
| zx_status_t PlatformDevice::RpcGpioRead(uint32_t index, uint8_t* out_value) { |
| if (bus_->gpio() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= gpios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| return bus_->gpio()->Read(gpios_[index].gpio, out_value); |
| } |
| |
| zx_status_t PlatformDevice::RpcGpioWrite(uint32_t index, uint8_t value) { |
| if (bus_->gpio() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= gpios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| return bus_->gpio()->Write(gpios_[index].gpio, value); |
| } |
| |
| zx_status_t PlatformDevice::RpcGpioGetInterrupt(uint32_t index, uint32_t flags, |
| zx_handle_t* out_handle, |
| uint32_t* out_handle_count) { |
| if (bus_->gpio() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= gpios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| zx_status_t status = bus_->gpio()->GetInterrupt(gpios_[index].gpio, flags, out_handle); |
| if (status == ZX_OK) { |
| *out_handle_count = 1; |
| } |
| return status; |
| } |
| |
| zx_status_t PlatformDevice::RpcGpioReleaseInterrupt(uint32_t index) { |
| if (bus_->gpio() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= gpios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| return bus_->gpio()->ReleaseInterrupt(gpios_[index].gpio); |
| } |
| |
| zx_status_t PlatformDevice::RpcGpioSetPolarity(uint32_t index, uint32_t flags) { |
| if (bus_->gpio() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= gpios_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| return bus_->gpio()->SetPolarity(gpios_[index].gpio, flags); |
| } |
| |
| zx_status_t PlatformDevice::RpcCanvasConfig(zx_handle_t vmo, size_t offset, |
| canvas_info_t* info, uint8_t* canvas_idx) { |
| if (bus_->canvas() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return bus_->canvas()->Config(vmo, offset, info, canvas_idx); |
| } |
| |
| zx_status_t PlatformDevice::RpcCanvasFree(uint8_t canvas_idx) { |
| if (bus_->canvas() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return bus_->canvas()->Free(canvas_idx); |
| } |
| |
| zx_status_t PlatformDevice::RpcScpiGetSensor(char* name, uint32_t *sensor_id) { |
| if (bus_->scpi() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return bus_->scpi()->GetSensor(name, sensor_id); |
| } |
| |
| zx_status_t PlatformDevice::RpcScpiGetSensorValue(uint32_t sensor_id, uint32_t* sensor_value) { |
| if (bus_->scpi() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return bus_->scpi()->GetSensorValue(sensor_id, sensor_value); |
| } |
| |
| zx_status_t PlatformDevice::RpcScpiGetDvfsInfo(uint8_t power_domain, scpi_opp_t* opps) { |
| if (bus_->scpi() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return bus_->scpi()->GetDvfsInfo(power_domain, opps); |
| } |
| |
| zx_status_t PlatformDevice::RpcScpiGetDvfsIdx(uint8_t power_domain, uint16_t* idx) { |
| if (bus_->scpi() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return bus_->scpi()->GetDvfsIdx(power_domain, idx); |
| } |
| |
| zx_status_t PlatformDevice::RpcScpiSetDvfsIdx(uint8_t power_domain, uint16_t idx) { |
| if (bus_->scpi() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return bus_->scpi()->SetDvfsIdx(power_domain, idx); |
| } |
| |
| zx_status_t PlatformDevice::RpcI2cTransact(uint32_t txid, rpc_i2c_req_t* req, uint8_t* data, |
| zx_handle_t channel) { |
| if (bus_->i2c() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| uint32_t index = req->index; |
| if (index >= i2c_channels_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| // pbus_i2c_channel_t* pdev_channel = &i2c_channels_[index]; |
| |
| //FIXME return bus_->i2c()->Transact(pdev_channel->bus_id, out_size); |
| return 0; |
| } |
| |
| zx_status_t PlatformDevice::RpcI2cGetMaxTransferSize(uint32_t index, size_t* out_size) { |
| if (bus_->i2c() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= i2c_channels_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| pbus_i2c_channel_t* pdev_channel = &i2c_channels_[index]; |
| |
| return bus_->i2c()->GetMaxTransferSize(pdev_channel->bus_id, out_size); |
| } |
| |
| zx_status_t PlatformDevice::RpcClkEnable(uint32_t index) { |
| if (bus_->clk() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= clks_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| return bus_->clk()->Enable(clks_[index].clk); |
| } |
| |
| zx_status_t PlatformDevice::RpcDisable(uint32_t index) { |
| if (bus_->clk() == nullptr) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (index >= clks_.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| |
| return bus_->clk()->Disable(clks_[index].clk); |
| } |
| |
| zx_status_t PlatformDevice::DdkRxrpc(zx_handle_t channel) { |
| if (channel == ZX_HANDLE_INVALID) { |
| // proxy device has connected |
| return ZX_OK; |
| } |
| |
| uint8_t req_buf[PROXY_MAX_TRANSFER_SIZE]; |
| uint8_t resp_buf[PROXY_MAX_TRANSFER_SIZE]; |
| auto* req_header = reinterpret_cast<rpc_req_header_t*>(&req_buf); |
| auto* resp_header = reinterpret_cast<rpc_rsp_header_t*>(&resp_buf); |
| uint32_t actual; |
| zx_handle_t in_handle; |
| uint32_t in_handle_count = 1; |
| |
| auto status = zx_channel_read(channel, 0, &req_buf, &in_handle, sizeof(req_buf), |
| in_handle_count, &actual, &in_handle_count); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "platform_dev_rxrpc: zx_channel_read failed %d\n", status); |
| return status; |
| } |
| |
| resp_header->txid = req_header->txid; |
| zx_handle_t handle = ZX_HANDLE_INVALID; |
| uint32_t handle_count = 0; |
| uint32_t resp_len; |
| |
| switch (req_header->protocol) { |
| case ZX_PROTOCOL_PLATFORM_DEV: { |
| auto req = reinterpret_cast<rpc_pdev_req_t*>(&req_buf); |
| if (actual < sizeof(*req)) { |
| zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req)); |
| return ZX_ERR_INTERNAL; |
| } |
| auto resp = reinterpret_cast<rpc_pdev_rsp_t*>(&resp_buf); |
| resp_len = sizeof(*resp); |
| |
| switch (req_header->op) { |
| case PDEV_GET_MMIO: |
| status = RpcGetMmio(req->index, &resp->paddr, &resp->length, &handle, &handle_count); |
| break; |
| case PDEV_GET_INTERRUPT: |
| status = RpcGetInterrupt(req->index, &resp->irq, &resp->mode, &handle, &handle_count); |
| break; |
| case PDEV_GET_BTI: |
| status = RpcGetBti(req->index, &handle, &handle_count); |
| break; |
| case PDEV_GET_DEVICE_INFO: |
| status = GetDeviceInfo(&resp->device_info); |
| break; |
| case PDEV_GET_BOARD_INFO: |
| status = bus_->GetBoardInfo(&resp->board_info); |
| break; |
| default: |
| zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op); |
| return ZX_ERR_INTERNAL; |
| } |
| break; |
| } |
| case ZX_PROTOCOL_USB_MODE_SWITCH: { |
| auto req = reinterpret_cast<rpc_ums_req_t*>(&req_buf); |
| if (actual < sizeof(*req)) { |
| zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req)); |
| return ZX_ERR_INTERNAL; |
| } |
| resp_len = sizeof(*resp_header); |
| |
| switch (req_header->op) { |
| case UMS_SET_MODE: |
| status = RpcUmsSetMode(req->usb_mode); |
| break; |
| default: |
| zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op); |
| return ZX_ERR_INTERNAL; |
| } |
| break; |
| } |
| case ZX_PROTOCOL_GPIO: { |
| auto req = reinterpret_cast<rpc_gpio_req_t*>(&req_buf); |
| if (actual < sizeof(*req)) { |
| zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req)); |
| return ZX_ERR_INTERNAL; |
| } |
| auto resp = reinterpret_cast<rpc_gpio_rsp_t*>(&resp_buf); |
| resp_len = sizeof(*resp); |
| |
| switch (req_header->op) { |
| case GPIO_CONFIG: |
| status = RpcGpioConfig(req->index, req->flags); |
| break; |
| case GPIO_SET_ALT_FUNCTION: |
| status = RpcGpioSetAltFunction(req->index, req->alt_function); |
| break; |
| case GPIO_READ: |
| status = RpcGpioRead(req->index, &resp->value); |
| break; |
| case GPIO_WRITE: |
| status = RpcGpioWrite(req->index, req->value); |
| break; |
| case GPIO_GET_INTERRUPT: |
| status = RpcGpioGetInterrupt(req->index, req->flags, &handle, &handle_count); |
| break; |
| case GPIO_RELEASE_INTERRUPT: |
| status = RpcGpioReleaseInterrupt(req->index); |
| break; |
| case GPIO_SET_POLARITY: |
| status = RpcGpioSetPolarity(req->index, req->polarity); |
| break; |
| default: |
| zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op); |
| return ZX_ERR_INTERNAL; |
| } |
| break; |
| } |
| case ZX_PROTOCOL_SCPI: { |
| auto req = reinterpret_cast<rpc_scpi_req_t*>(&req_buf); |
| if (actual < sizeof(*req)) { |
| zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req)); |
| return ZX_ERR_INTERNAL; |
| } |
| auto resp = reinterpret_cast<rpc_scpi_rsp_t*>(&resp_buf); |
| resp_len = sizeof(*resp); |
| |
| switch (req_header->op) { |
| case SCPI_GET_SENSOR: |
| status = RpcScpiGetSensor(req->name, &resp->sensor_id); |
| break; |
| case SCPI_GET_SENSOR_VALUE: |
| status = RpcScpiGetSensorValue(req->sensor_id, &resp->sensor_value); |
| break; |
| case SCPI_GET_DVFS_INFO: |
| status = RpcScpiGetDvfsInfo(req->power_domain, &resp->opps); |
| break; |
| case SCPI_GET_DVFS_IDX: |
| status = RpcScpiGetDvfsIdx(req->power_domain, &resp->dvfs_idx); |
| break; |
| case SCPI_SET_DVFS_IDX: |
| status = RpcScpiSetDvfsIdx(req->power_domain, req->idx); |
| break; |
| default: |
| zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op); |
| return ZX_ERR_INTERNAL; |
| } |
| break; |
| } |
| case ZX_PROTOCOL_I2C: { |
| auto req = reinterpret_cast<rpc_i2c_req_t*>(&req_buf); |
| if (actual < sizeof(*req)) { |
| zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req)); |
| return ZX_ERR_INTERNAL; |
| } |
| auto resp = reinterpret_cast<rpc_i2c_rsp_t*>(&resp_buf); |
| resp_len = sizeof(*resp); |
| |
| switch (req_header->op) { |
| case I2C_GET_MAX_TRANSFER: |
| status = RpcI2cGetMaxTransferSize(req->index, &resp->max_transfer); |
| break; |
| case I2C_TRANSACT: { |
| auto buf = reinterpret_cast<uint8_t*>(&req[1]); |
| status = RpcI2cTransact(req_header->txid, req, buf, channel); |
| if (status == ZX_OK) { |
| // If platform_i2c_transact succeeds, we return immmediately instead of calling |
| // zx_channel_write below. Instead we will respond in platform_i2c_complete(). |
| return ZX_OK; |
| } |
| break; |
| } |
| default: |
| zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op); |
| return ZX_ERR_INTERNAL; |
| } |
| break; |
| } |
| case ZX_PROTOCOL_CLK: { |
| auto req = reinterpret_cast<rpc_clk_req_t*>(&req_buf); |
| if (actual < sizeof(*req)) { |
| zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req)); |
| return ZX_ERR_INTERNAL; |
| } |
| resp_len = sizeof(*resp_header); |
| |
| switch (req_header->op) { |
| case CLK_ENABLE: |
| status = RpcClkEnable(req->index); |
| break; |
| case CLK_DISABLE: |
| status = RpcDisable(req->index); |
| break; |
| default: |
| zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op); |
| return ZX_ERR_INTERNAL; |
| } |
| break; |
| } |
| case ZX_PROTOCOL_CANVAS: { |
| auto req = reinterpret_cast<rpc_canvas_req_t*>(&req_buf); |
| if (actual < sizeof(*req)) { |
| zxlogf(ERROR, "%s received %u, expecting %zu\n", __FUNCTION__, actual, sizeof(*req)); |
| return ZX_ERR_INTERNAL; |
| } |
| auto resp = reinterpret_cast<rpc_canvas_rsp_t*>(&resp_buf); |
| resp_len = sizeof(*resp); |
| |
| switch (req_header->op) { |
| case CANVAS_CONFIG: |
| status = RpcCanvasConfig(in_handle, req->offset, &req->info, |
| &resp->idx); |
| break; |
| case CANVAS_FREE: |
| status = RpcCanvasFree(req->idx); |
| break; |
| default: |
| zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op); |
| return ZX_ERR_INTERNAL; |
| } |
| break; |
| } |
| default: |
| zxlogf(ERROR, "platform_dev_rxrpc: unknown op %u\n", req_header->op); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| // set op to match request so zx_channel_write will return our response |
| resp_header->status = status; |
| status = zx_channel_write(channel, 0, resp_header, resp_len, |
| (handle_count == 1 ? &handle : nullptr), handle_count); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "platform_dev_rxrpc: zx_channel_write failed %d\n", status); |
| } |
| return status; |
| } |
| |
| zx_status_t PlatformDevice::DdkGetProtocol(uint32_t proto_id, void* out) { |
| if (proto_id == ZX_PROTOCOL_PLATFORM_DEV) { |
| auto proto = static_cast<platform_device_protocol_t*>(out); |
| proto->ops = &pdev_proto_ops_; |
| proto->ctx = this; |
| return ZX_OK; |
| } else { |
| return bus_->DdkGetProtocol(proto_id, out); |
| } |
| } |
| |
| void PlatformDevice::DdkRelease() { |
| delete this; |
| } |
| |
| zx_status_t PlatformDevice::AddMetaData(const pbus_metadata_t& pbm) { |
| const uint32_t type = pbm.type; |
| const uint32_t extra = pbm.extra; |
| const uint8_t* metadata = bus_->metadata(); |
| const size_t metadata_size = bus_->metadata_size(); |
| size_t offset = 0; |
| |
| while (offset < metadata_size) { |
| auto header = reinterpret_cast<const zbi_header_t*>(metadata); |
| size_t length = ZBI_ALIGN(sizeof(zbi_header_t) + header->length); |
| |
| if (header->type == type && header->extra == extra) { |
| return DdkAddMetadata(type, header + 1, length - sizeof(zbi_header_t)); |
| } |
| metadata += length; |
| offset += length; |
| } |
| zxlogf(ERROR, "%s metadata not found for type %08x, extra %u\n", __FUNCTION__, type, extra); |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| zx_status_t PlatformDevice::Enable(bool enable) { |
| zx_status_t status = ZX_OK; |
| |
| if (enable && !enabled_) { |
| zx_device_prop_t props[] = { |
| {BIND_PLATFORM_DEV_VID, 0, vid_}, |
| {BIND_PLATFORM_DEV_PID, 0, pid_}, |
| {BIND_PLATFORM_DEV_DID, 0, did_}, |
| }; |
| |
| char namestr[ZX_DEVICE_NAME_MAX]; |
| if (vid_ == PDEV_VID_GENERIC && pid_ == PDEV_PID_GENERIC && did_ == PDEV_DID_KPCI) { |
| strlcpy(namestr, "pci", sizeof(namestr)); |
| } else { |
| |
| snprintf(namestr, sizeof(namestr), "%02x:%02x:%01x", vid_, pid_, did_); |
| } |
| char argstr[64]; |
| snprintf(argstr, sizeof(argstr), "pdev:%s,", namestr); |
| uint32_t flags = 0; |
| if (!(flags_ & PDEV_ADD_PBUS_DEVHOST)) { |
| flags |= DEVICE_ADD_MUST_ISOLATE; |
| } |
| if (metadata_.size() > 0) { |
| flags |= DEVICE_ADD_INVISIBLE; |
| } |
| |
| status = DdkAdd(namestr, flags, props, countof(props), argstr); |
| |
| if (metadata_.size() > 0) { |
| for (const auto& pbm : metadata_) { |
| if (pbm.data && pbm.len) { |
| DdkAddMetadata(pbm.type, pbm.data, pbm.len); |
| } else { |
| AddMetaData(pbm); |
| } |
| } |
| DdkMakeVisible(); |
| } |
| } else if (!enable && enabled_) { |
| DdkRemove(); |
| } |
| |
| if (status == ZX_OK) { |
| enabled_ = enable; |
| } |
| |
| return status; |
| } |
| |
| } // namespace platform_bus |