root/src/add-ons/kernel/busses/random/ccp/ccp.cpp
/*
 * Copyright 2022, Jérôme Duval, jerome.duval@gmail.com.
 * Distributed under the terms of the MIT License.
 */


#include <new>
#include <stdio.h>
#include <string.h>

#include <dpc.h>

#include "random.h"

#include "ccp.h"


#define CCP_REG_TRNG    0xc


device_manager_info* gDeviceManager;
random_for_controller_interface *gRandom;
dpc_module_info *gDPC;


static void
handleDPC(void *arg)
{
        CALLED();
        ccp_device_info* bus = reinterpret_cast<ccp_device_info*>(arg);

        uint32 lowValue = read32(bus->registers + CCP_REG_TRNG);
        uint32 highValue = read32(bus->registers + CCP_REG_TRNG);
        if (lowValue == 0 || highValue == 0)
                return;
        gRandom->queue_randomness((uint64)lowValue | ((uint64)highValue << 32));
}


static int32
handleTimerHook(struct timer* timer)
{
        ccp_device_info* bus = reinterpret_cast<ccp_device_info*>(timer->user_data);

        gDPC->queue_dpc(bus->dpcHandle, handleDPC, bus);
        return B_HANDLED_INTERRUPT;
}


//      #pragma mark -


static status_t
register_device(device_node* parent)
{
        device_attr attrs[] = {
                {B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "CCP device"}},
                {B_DEVICE_BUS, B_STRING_TYPE, {.string = "CCP"}},
                {}
        };

        return gDeviceManager->register_node(parent,
                CCP_DEVICE_MODULE_NAME, attrs, NULL, NULL);
}


static status_t
init_bus(device_node* node, void** bus_cookie)
{
        CALLED();

        driver_module_info* driver;
        ccp_device_info* bus;
        device_node* parent = gDeviceManager->get_parent_node(node);
        gDeviceManager->get_driver(parent, &driver, (void**)&bus);
        gDeviceManager->put_node(parent);

        TRACE_ALWAYS("init_bus() addr 0x%" B_PRIxPHYSADDR " size 0x%" B_PRIx64
                " \n", bus->base_addr, bus->map_size);

        bus->registersArea = map_physical_memory("CCP memory mapped registers",
                bus->base_addr, bus->map_size, B_ANY_KERNEL_ADDRESS,
                B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
                (void **)&bus->registers);
        if (bus->registersArea < 0)
                return bus->registersArea;

        status_t status = gDPC->new_dpc_queue(&bus->dpcHandle, "ccp timer",
                B_LOW_PRIORITY);
        if (status != B_OK) {
                ERROR("dpc setup failed (%s)\n", strerror(status));
                return status;
        }

        bus->extractTimer.user_data = bus;
        status = add_timer(&bus->extractTimer, &handleTimerHook, 1 * 1000 * 1000, B_PERIODIC_TIMER);
        if (status != B_OK) {
                ERROR("timer setup failed (%s)\n", strerror(status));
                return status;
        }

        // trigger also now
        gDPC->queue_dpc(bus->dpcHandle, handleDPC, bus);

        *bus_cookie = bus;
        return B_OK;

}


static void
uninit_bus(void* bus_cookie)
{
        ccp_device_info* bus = (ccp_device_info*)bus_cookie;

        cancel_timer(&bus->extractTimer);
        gDPC->delete_dpc_queue(&bus->dpcHandle);

        if (bus->registersArea >= 0)
                delete_area(bus->registersArea);

}


//      #pragma mark -


module_dependency module_dependencies[] = {
        { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
        { RANDOM_FOR_CONTROLLER_MODULE_NAME, (module_info**)&gRandom },
        { B_DPC_MODULE_NAME, (module_info **)&gDPC },
        {}
};


static driver_module_info sCcpDeviceModule = {
        {
                CCP_DEVICE_MODULE_NAME,
                0,
                NULL
        },

        NULL,   // supports device
        register_device,
        init_bus,
        uninit_bus,
        NULL,   // register child devices
        NULL,   // rescan
        NULL,   // device removed
};


module_info* modules[] = {
        (module_info* )&gCcpAcpiDevice,
        (module_info* )&sCcpDeviceModule,
        (module_info* )&gCcpPciDevice,
        NULL
};