* Copyright 2002-2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* JΓ©rΓ΄me Duval, korli@berlios.de
* Axel DΓΆrfler, axeld@pinc-software.de
* David Reid
*/
#include <stdio.h>
#include <device_manager.h>
#include <Drivers.h>
#include <generic_syscall.h>
#include <kernel.h>
#include <malloc.h>
#include <random_defs.h>
#include <string.h>
#include <util/AutoLock.h>
#include "yarrow_rng.h"
#ifdef TRACE_DRIVER
# define TRACE(x...) dprintf("random: " x)
#else
# define TRACE(x...) ;
#endif
#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
#define RANDOM_DRIVER_MODULE_NAME "bus_managers/random/driver_v1"
#define RANDOM_DEVICE_MODULE_NAME "bus_managers/random/device_v1"
static mutex sRandomLock;
static void *sRandomCookie;
device_manager_info* gDeviceManager;
typedef struct {
device_node* node;
} random_driver_info;
static status_t
random_queue_randomness(uint64 value)
{
MutexLocker locker(&sRandomLock);
RANDOM_ENQUEUE(value);
return B_OK;
}
static status_t
random_init_device(void* _info, void** _cookie)
{
return B_OK;
}
static void
random_uninit_device(void* _cookie)
{
}
static status_t
random_open(void *deviceCookie, const char *name, int flags, void **cookie)
{
TRACE("open(\"%s\")\n", name);
return B_OK;
}
static status_t
random_read(void *cookie, off_t position, void *_buffer, size_t *_numBytes)
{
TRACE("read(%lld,, %ld)\n", position, *_numBytes);
MutexLocker locker(&sRandomLock);
return RANDOM_READ(sRandomCookie, _buffer, _numBytes);
}
static status_t
random_write(void *cookie, off_t position, const void *buffer, size_t *_numBytes)
{
TRACE("write(%lld,, %ld)\n", position, *_numBytes);
MutexLocker locker(&sRandomLock);
return RANDOM_WRITE(sRandomCookie, buffer, _numBytes);
}
static status_t
random_control(void *cookie, uint32 op, void *arg, size_t length)
{
TRACE("ioctl(%ld)\n", op);
return B_ERROR;
}
static status_t
random_generic_syscall(const char* subsystem, uint32 function, void* buffer,
size_t bufferSize)
{
switch (function) {
case RANDOM_GET_ENTROPY:
{
random_get_entropy_args args;
if (bufferSize != sizeof(args) || !IS_USER_ADDRESS(buffer))
return B_BAD_VALUE;
if (user_memcpy(&args, buffer, sizeof(args)) != B_OK)
return B_BAD_ADDRESS;
if (!IS_USER_ADDRESS(args.buffer))
return B_BAD_ADDRESS;
status_t result = random_read(NULL, 0, args.buffer, &args.length);
if (result < 0)
return result;
return user_memcpy(buffer, &args, sizeof(args));
}
}
return B_BAD_HANDLER;
}
static status_t
random_close(void *cookie)
{
TRACE("close()\n");
return B_OK;
}
static status_t
random_free(void *cookie)
{
TRACE("free()\n");
return B_OK;
}
static status_t
random_select(void *cookie, uint8 event, selectsync *sync)
{
TRACE("select()\n");
if (event == B_SELECT_READ) {
notify_select_event(sync, event);
} else if (event == B_SELECT_WRITE) {
notify_select_event(sync, event);
}
return B_OK;
}
static status_t
random_deselect(void *cookie, uint8 event, selectsync *sync)
{
TRACE("deselect()\n");
return B_OK;
}
static float
random_supports_device(device_node *parent)
{
CALLED();
const char *bus;
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
return -1;
if (strcmp(bus, "root"))
return 0.0;
return 1.0;
}
static status_t
random_register_device(device_node *node)
{
CALLED();
device_attr attrs[] = {
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "Random" }},
{ B_DEVICE_FLAGS, B_UINT32_TYPE, { .ui32 = B_KEEP_DRIVER_LOADED }},
{ NULL }
};
return gDeviceManager->register_node(node, RANDOM_DRIVER_MODULE_NAME,
attrs, NULL, NULL);
}
static status_t
random_init_driver(device_node *node, void **cookie)
{
CALLED();
random_driver_info* info = (random_driver_info*)malloc(
sizeof(random_driver_info));
if (info == NULL)
return B_NO_MEMORY;
mutex_init(&sRandomLock, "/dev/random lock");
RANDOM_INIT();
memset(info, 0, sizeof(*info));
info->node = node;
register_generic_syscall(RANDOM_SYSCALLS, random_generic_syscall, 1, 0);
*cookie = info;
return B_OK;
}
static void
random_uninit_driver(void *_cookie)
{
CALLED();
unregister_generic_syscall(RANDOM_SYSCALLS, 1);
RANDOM_UNINIT();
mutex_destroy(&sRandomLock);
random_driver_info* info = (random_driver_info*)_cookie;
free(info);
}
static status_t
random_register_child_devices(void* _cookie)
{
CALLED();
random_driver_info* info = (random_driver_info*)_cookie;
status_t status = gDeviceManager->publish_device(info->node, "random",
RANDOM_DEVICE_MODULE_NAME);
if (status == B_OK) {
gDeviceManager->publish_device(info->node, "urandom",
RANDOM_DEVICE_MODULE_NAME);
}
return status;
}
module_dependency module_dependencies[] = {
{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager},
{}
};
struct device_module_info sRandomDevice = {
{
RANDOM_DEVICE_MODULE_NAME,
0,
NULL
},
random_init_device,
random_uninit_device,
NULL,
random_open,
random_close,
random_free,
random_read,
random_write,
NULL,
random_control,
random_select,
random_deselect,
};
struct driver_module_info sRandomDriver = {
{
RANDOM_DRIVER_MODULE_NAME,
0,
NULL
},
random_supports_device,
random_register_device,
random_init_driver,
random_uninit_driver,
random_register_child_devices,
NULL,
NULL,
};
random_for_controller_interface sRandomForControllerModule = {
{
{
RANDOM_FOR_CONTROLLER_MODULE_NAME,
0,
NULL
},
NULL,
NULL,
NULL,
NULL,
NULL
},
random_queue_randomness,
};
module_info* modules[] = {
(module_info*)&sRandomDriver,
(module_info*)&sRandomDevice,
(module_info*)&sRandomForControllerModule,
NULL
};