blob: 01edc6e638978d60ff14c7c1abe2f084fdceba62 [file] [log] [blame]
// 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 <zircon/compiler.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include "devhost.h"
#include <stdarg.h>
#include <stdio.h>
// These are the API entry-points from drivers
// They must take the devhost_api_lock before calling devhost_* internals
//
// Driver code MUST NOT directly call devhost_* APIs
// LibDriver Device Interface
__EXPORT zx_status_t device_add_from_driver(zx_driver_t* drv, zx_device_t* parent,
device_add_args_t* args, zx_device_t** out) {
zx_status_t r;
zx_device_t* dev = NULL;
if (!parent) {
return ZX_ERR_INVALID_ARGS;
}
if (!args || args->version != DEVICE_ADD_ARGS_VERSION) {
return ZX_ERR_INVALID_ARGS;
}
if (!args->ops || args->ops->version != DEVICE_OPS_VERSION) {
return ZX_ERR_INVALID_ARGS;
}
if (args->flags & ~(DEVICE_ADD_NON_BINDABLE | DEVICE_ADD_INSTANCE | DEVICE_ADD_BUSDEV)) {
return ZX_ERR_INVALID_ARGS;
}
if ((args->flags & DEVICE_ADD_INSTANCE) && (args->flags & DEVICE_ADD_BUSDEV)) {
return ZX_ERR_INVALID_ARGS;
}
DM_LOCK();
r = devhost_device_create(drv, parent, args->name, args->ctx, args->ops, &dev);
if (r != ZX_OK) {
DM_UNLOCK();
return r;
}
if (args->proto_id) {
dev->protocol_id = args->proto_id;
dev->protocol_ops = args->proto_ops;
}
if (args->flags & DEVICE_ADD_NON_BINDABLE) {
dev->flags |= DEV_FLAG_UNBINDABLE;
}
// out must be set before calling devhost_device_add().
// devhost_device_add() may result in child devices being created before it returns,
// and those children may call ops on the device before device_add() returns.
if (out) {
*out = dev;
}
if (args->flags & DEVICE_ADD_BUSDEV) {
r = devhost_device_add(dev, parent, args->props, args->prop_count, args->busdev_args,
args->rsrc);
} else if (args->flags & DEVICE_ADD_INSTANCE) {
dev->flags |= DEV_FLAG_INSTANCE | DEV_FLAG_UNBINDABLE;
r = devhost_device_add(dev, parent, NULL, 0, NULL, ZX_HANDLE_INVALID);
} else {
r = devhost_device_add(dev, parent, args->props, args->prop_count, NULL, ZX_HANDLE_INVALID);
}
if (r != ZX_OK) {
if (out) {
*out = NULL;
}
devhost_device_destroy(dev);
}
DM_UNLOCK();
return r;
}
__EXPORT zx_status_t device_remove(zx_device_t* dev) {
zx_status_t r;
DM_LOCK();
r = devhost_device_remove(dev);
DM_UNLOCK();
return r;
}
__EXPORT void device_unbind(zx_device_t* dev) {
DM_LOCK();
devhost_device_unbind(dev);
DM_UNLOCK();
}
__EXPORT zx_status_t device_rebind(zx_device_t* dev) {
zx_status_t r;
DM_LOCK();
r = devhost_device_rebind(dev);
DM_UNLOCK();
return r;
}
__EXPORT const char* device_get_name(zx_device_t* dev) {
return dev->name;
}
__EXPORT zx_device_t* device_get_parent(zx_device_t* dev) {
return dev->parent;
}
typedef struct {
void* ops;
void* ctx;
} generic_protocol_t;
__EXPORT zx_status_t device_get_protocol(zx_device_t* dev, uint32_t proto_id, void* out) {
generic_protocol_t* proto = out;
if (dev->ops->get_protocol) {
return dev->ops->get_protocol(dev->ctx, proto_id, out);
}
if ((proto_id == dev->protocol_id) && (dev->protocol_ops != NULL)) {
proto->ops = dev->protocol_ops;
proto->ctx = dev->ctx;
return ZX_OK;
}
return ZX_ERR_NOT_SUPPORTED;
}
__EXPORT zx_handle_t device_get_resource(zx_device_t* dev) {
zx_handle_t h;
if (zx_handle_duplicate(dev->resource, ZX_RIGHT_SAME_RIGHTS, &h) < 0) {
return ZX_HANDLE_INVALID;
} else {
return h;
}
}
__EXPORT void device_state_clr_set(zx_device_t* dev, zx_signals_t clearflag, zx_signals_t setflag) {
zx_object_signal(dev->event, clearflag, setflag);
}
__EXPORT zx_off_t device_get_size(zx_device_t* dev) {
return dev->ops->get_size(dev->ctx);
}
__EXPORT zx_status_t device_read(zx_device_t* dev, void* buf, size_t count,
zx_off_t off, size_t* actual) {
return dev->ops->read(dev->ctx, buf, count, off, actual);
}
__EXPORT zx_status_t device_write(zx_device_t* dev, const void* buf, size_t count,
zx_off_t off, size_t* actual) {
return dev->ops->write(dev->ctx, buf, count, off, actual);
}
__EXPORT zx_status_t device_ioctl(zx_device_t* dev, uint32_t op,
const void* in_buf, size_t in_len,
void* out_buf, size_t out_len,
size_t* out_actual) {
return dev->ops->ioctl(dev->ctx, op, in_buf, in_len, out_buf, out_len, out_actual);
}
__EXPORT zx_status_t device_iotxn_queue(zx_device_t* dev, iotxn_t* txn) {
if (dev->ops->iotxn_queue != NULL) {
dev->ops->iotxn_queue(dev->ctx, txn);
return ZX_OK;
} else {
return ZX_ERR_NOT_SUPPORTED;
}
}
// LibDriver Misc Interfaces
extern zx_handle_t root_resource_handle;
__EXPORT zx_handle_t get_root_resource(void) {
return root_resource_handle;
}
__EXPORT zx_status_t load_firmware(zx_device_t* dev, const char* path,
zx_handle_t* fw, size_t* size) {
zx_status_t r;
DM_LOCK();
r = devhost_load_firmware(dev, path, fw, size);
DM_UNLOCK();
return r;
}
// Interface Used by DevHost RPC Layer
zx_status_t device_bind(zx_device_t* dev, const char* drv_libname) {
zx_status_t r;
DM_LOCK();
r = devhost_device_bind(dev, drv_libname);
DM_UNLOCK();
return r;
}
zx_status_t device_open_at(zx_device_t* dev, zx_device_t** out, const char* path, uint32_t flags) {
zx_status_t r;
DM_LOCK();
r = devhost_device_open_at(dev, out, path, flags);
DM_UNLOCK();
return r;
}
zx_status_t device_close(zx_device_t* dev, uint32_t flags) {
zx_status_t r;
DM_LOCK();
r = devhost_device_close(dev, flags);
DM_UNLOCK();
return r;
}