| // Copyright 2016 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 <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddk/binding.h> |
| |
| #include <stdio.h> |
| |
| #include "devcoordinator.h" |
| |
| typedef struct { |
| const zx_device_prop_t* props; |
| const zx_device_prop_t* end; |
| uint32_t protocol_id; |
| uint32_t binding_size; |
| const zx_bind_inst_t* binding; |
| const char* name; |
| uint32_t autobind; |
| } bpctx_t; |
| |
| static uint32_t dev_get_prop(bpctx_t* ctx, uint32_t id) { |
| const zx_device_prop_t* props = ctx->props; |
| const zx_device_prop_t* end = ctx->end; |
| |
| while (props < end) { |
| if (props->id == id) { |
| return props->value; |
| } |
| props++; |
| } |
| |
| // fallback for devices without properties |
| switch (id) { |
| case BIND_PROTOCOL: |
| return ctx->protocol_id; |
| case BIND_AUTOBIND: |
| return ctx->autobind; |
| default: |
| // TODO: better process for missing properties |
| return 0; |
| } |
| } |
| |
| static bool is_bindable(bpctx_t* ctx) { |
| const zx_bind_inst_t* ip = ctx->binding; |
| const zx_bind_inst_t* end = ip + (ctx->binding_size / sizeof(zx_bind_inst_t)); |
| uint32_t flags = 0; |
| |
| while (ip < end) { |
| uint32_t inst = ip->op; |
| bool cond; |
| |
| if (BINDINST_CC(inst) != COND_AL) { |
| uint32_t value = ip->arg; |
| uint32_t pid = BINDINST_PB(inst); |
| uint32_t pval; |
| if (pid != BIND_FLAGS) { |
| pval = dev_get_prop(ctx, pid); |
| } else { |
| pval = flags; |
| } |
| |
| // evaluate condition |
| switch (BINDINST_CC(inst)) { |
| case COND_EQ: |
| cond = (pval == value); |
| break; |
| case COND_NE: |
| cond = (pval != value); |
| break; |
| case COND_LT: |
| cond = (pval < value); |
| break; |
| case COND_GT: |
| cond = (pval > value); |
| break; |
| case COND_LE: |
| cond = (pval <= value); |
| break; |
| case COND_GE: |
| cond = (pval >= value); |
| break; |
| case COND_MASK: |
| cond = ((pval & value) != 0); |
| break; |
| case COND_BITS: |
| cond = ((pval & value) == value); |
| break; |
| default: |
| // illegal instruction: abort |
| printf("devmgr: driver '%s' illegal bindinst 0x%08x\n", ctx->name, inst); |
| return false; |
| } |
| } else { |
| cond = true; |
| } |
| |
| if (cond) { |
| switch (BINDINST_OP(inst)) { |
| case OP_ABORT: |
| return false; |
| case OP_MATCH: |
| return true; |
| case OP_GOTO: { |
| uint32_t label = BINDINST_PA(inst); |
| while (++ip < end) { |
| if ((BINDINST_OP(ip->op) == OP_LABEL) && |
| (BINDINST_PA(ip->op) == label)) { |
| goto next_instruction; |
| } |
| } |
| printf("devmgr: driver '%s' illegal GOTO\n", ctx->name); |
| return false; |
| } |
| case OP_SET: |
| flags |= BINDINST_PA(inst); |
| break; |
| case OP_CLEAR: |
| flags &= ~(BINDINST_PA(inst)); |
| break; |
| case OP_LABEL: |
| // no op |
| break; |
| default: |
| // illegal instruction: abort |
| printf("devmgr: driver '%s' illegal bindinst 0x%08x\n", ctx->name, inst); |
| return false; |
| } |
| } |
| |
| next_instruction: |
| ip++; |
| } |
| |
| // default if we leave the program is no-match |
| return false; |
| } |
| |
| bool dc_is_bindable(zx_binding_t* binding, uint32_t protocol_id, |
| zx_device_prop_t* props, size_t prop_count) { |
| if (binding->bindcount == 0) { |
| return false; |
| } |
| bpctx_t ctx; |
| ctx.props = props; |
| ctx.end = props + prop_count; |
| ctx.protocol_id = protocol_id; |
| ctx.binding = binding->bindings; |
| ctx.binding_size = binding->bindcount * sizeof(zx_bind_inst_t); |
| ctx.name = "dependency"; |
| ctx.autobind = 1; |
| return is_bindable(&ctx); |
| } |
| |
| bool dc_drv_is_bindable(driver_t* drv, uint32_t protocol_id, |
| zx_device_prop_t* props, size_t prop_count, |
| bool autobind) { |
| if (drv->binding_size == 0) { |
| return false; |
| } |
| bpctx_t ctx; |
| ctx.props = props; |
| ctx.end = props + prop_count; |
| ctx.protocol_id = protocol_id; |
| ctx.binding = drv->binding; |
| ctx.binding_size = drv->binding_size; |
| ctx.name = drv->name; |
| ctx.autobind = autobind ? 1 : 0; |
| return is_bindable(&ctx); |
| } |