blob: 75877ad45e838ea33fc9d3e76dc3bc0ed8398694 [file] [log] [blame]
// 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.
// See the README.md in this directory for documentation.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <ddk/binding.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/platform-defs.h>
#include <ddk/protocol/platform-device.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/resource.h>
#include <zircon/types.h>
#include "cpu-trace-private.h"
static zx_status_t cpu_trace_open(void* ctx, zx_device_t** dev_out, uint32_t flags) {
cpu_trace_device_t* dev = ctx;
if (dev->opened)
return ZX_ERR_ALREADY_BOUND;
dev->opened = true;
return ZX_OK;
}
static zx_status_t cpu_trace_close(void* ctx, uint32_t flags) {
cpu_trace_device_t* dev = ctx;
dev->opened = false;
return ZX_OK;
}
static zx_status_t cpu_trace_ioctl(void* ctx, uint32_t op,
const void* cmd, size_t cmdlen,
void* reply, size_t replymax,
size_t* out_actual) {
cpu_trace_device_t* dev = ctx;
mtx_lock(&dev->lock);
ssize_t result;
switch (IOCTL_FAMILY(op)) {
case IOCTL_FAMILY_CPUPERF:
result = ipm_ioctl(dev, op, cmd, cmdlen,
reply, replymax, out_actual);
break;
case IOCTL_FAMILY_IPT:
result = ipt_ioctl(dev, op, cmd, cmdlen,
reply, replymax, out_actual);
break;
default:
result = ZX_ERR_INVALID_ARGS;
break;
}
mtx_unlock(&dev->lock);
return result;
}
static void cpu_trace_release(void* ctx) {
cpu_trace_device_t* dev = ctx;
ipt_release(dev);
ipm_release(dev);
zx_handle_close(dev->bti);
free(dev);
}
static zx_protocol_device_t cpu_trace_device_proto = {
.version = DEVICE_OPS_VERSION,
.open = cpu_trace_open,
.close = cpu_trace_close,
.ioctl = cpu_trace_ioctl,
.release = cpu_trace_release,
};
static zx_status_t cpu_trace_bind(void* ctx, zx_device_t* parent) {
ipt_init_once();
ipm_init_once();
platform_device_protocol_t pdev;
zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &pdev);
if (status != ZX_OK) {
return status;
}
cpu_trace_device_t* dev = calloc(1, sizeof(*dev));
if (!dev) {
return ZX_ERR_NO_MEMORY;
}
status = pdev_get_bti(&pdev, 0, &dev->bti);
if (status != ZX_OK) {
goto fail;
}
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "cpu-trace",
.ctx = dev,
.ops = &cpu_trace_device_proto,
};
if ((status = device_add(parent, &args, NULL)) < 0) {
goto fail;
}
return ZX_OK;
fail:
zx_handle_close(dev->bti);
free(dev);
return status;
}
static zx_driver_ops_t cpu_trace_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = cpu_trace_bind,
};
ZIRCON_DRIVER_BEGIN(cpu_trace, cpu_trace_driver_ops, "zircon", "0.1", 4)
BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_INTEL),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_INTEL_CPU_TRACE),
ZIRCON_DRIVER_END(cpu_trace)