bemanitools/src/main/aciomgr/manager.c

211 lines
5.0 KiB
C

#define LOG_MODULE "aciomgr-manager"
#include <windows.h>
#include <stdatomic.h>
#include "aciomgr/manager-init.h"
#include "aciomgr/manager.h"
#include "acio/acio.h"
#include "aciodrv/device.h"
#include "util/array.h"
#include "util/log.h"
#define MAX_PORT_PATH_LENGTH 256
struct aciomgr_port_dispatcher {
CRITICAL_SECTION cs;
atomic_size_t references;
struct aciodrv_device_ctx *device;
char path[MAX_PORT_PATH_LENGTH];
int baud;
bool has_failure;
};
static void _aciomgr_setup_port_dispatcher(
struct aciomgr_port_dispatcher *dispatcher, const char *path, int baud);
static void
_aciomgr_destroy_port_dispatcher(struct aciomgr_port_dispatcher *dispatcher);
// DLL-globals
static atomic_bool running;
static CRITICAL_SECTION mgr_cs;
static struct array active_ports;
static void _aciomgr_setup_port_dispatcher(
struct aciomgr_port_dispatcher *dispatcher, const char *path, int baud)
{
InitializeCriticalSection(&dispatcher->cs);
strcpy(dispatcher->path, path);
dispatcher->baud = baud;
dispatcher->device = aciodrv_device_open_path(path, baud);
dispatcher->has_failure = false;
if (dispatcher->device == NULL) {
log_info("Opening ACIO device on %s failed", path);
dispatcher->has_failure = true;
}
dispatcher->references = 0;
}
static void
_aciomgr_destroy_port_dispatcher(struct aciomgr_port_dispatcher *dispatcher)
{
aciodrv_device_close(dispatcher->device);
DeleteCriticalSection(&dispatcher->cs);
}
void _aciomgr_init()
{
if (running) {
log_warning("_aciomgr_init called when already running");
_aciomgr_fini();
}
InitializeCriticalSection(&mgr_cs);
array_init(&active_ports);
running = true;
}
void _aciomgr_fini()
{
if (!running) {
log_warning("_aciomgr_fini called when not running");
}
running = false;
DeleteCriticalSection(&mgr_cs);
array_fini(&active_ports);
}
void aciomgr_set_loggers(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal)
{
log_to_external(misc, info, warning, fatal);
}
struct aciomgr_port_dispatcher *aciomgr_port_init(const char *path, int baud)
{
if (!running) {
log_warning("aciomgr_port_init: called when not running");
return NULL;
}
if (strlen(path) >= MAX_PORT_PATH_LENGTH) {
log_warning("aciomgr_port_init: path too long");
return NULL;
}
struct aciomgr_port_dispatcher *entry;
EnterCriticalSection(&mgr_cs);
for (size_t i = 0; i < active_ports.nitems; i++) {
entry = array_item(struct aciomgr_port_dispatcher, &active_ports, i);
if (strcmp(entry->path, path) == 0) {
// found
if (entry->baud != baud) {
log_warning(
"Device at %s opened with baud of %d, but requested %d",
path,
entry->baud,
baud);
}
goto done;
}
}
entry = array_append(struct aciomgr_port_dispatcher, &active_ports);
_aciomgr_setup_port_dispatcher(entry, path, baud);
done:
if (!entry->has_failure) {
entry->references++;
}
LeaveCriticalSection(&mgr_cs);
if (entry->has_failure) {
return NULL;
}
return entry;
}
void aciomgr_port_fini(struct aciomgr_port_dispatcher *dispatcher)
{
if (!running) {
log_warning("aciomgr_port_fini: called when not running");
return;
}
EnterCriticalSection(&mgr_cs);
dispatcher->references--;
LeaveCriticalSection(&mgr_cs);
if (dispatcher->references == 0) {
_aciomgr_destroy_port_dispatcher(dispatcher);
}
}
// this function don't require the lock
uint8_t aciomgr_get_node_count(struct aciomgr_port_dispatcher *dispatcher)
{
return aciodrv_device_get_node_count(dispatcher->device);
}
// this function don't require the lock
bool aciomgr_get_node_product_ident(
struct aciomgr_port_dispatcher *dispatcher,
uint8_t node_id,
char product[ACIOMGR_NODE_PRODUCT_CODE_LEN])
{
return aciodrv_device_get_node_product_ident(
dispatcher->device, node_id, product);
}
bool aciomgr_port_submit_packet(
struct aciomgr_port_dispatcher *dispatcher,
struct ac_io_message *msg,
int max_resp_size)
{
// CS's although lightweight, may still be a burden, short circuit
if (dispatcher->references > 1) {
EnterCriticalSection(&dispatcher->cs);
bool response =
aciodrv_send_and_recv(dispatcher->device, msg, max_resp_size);
LeaveCriticalSection(&dispatcher->cs);
return response;
}
return aciodrv_send_and_recv(dispatcher->device, msg, max_resp_size);
}
struct aciodrv_device_ctx *
aciomgr_port_checkout(struct aciomgr_port_dispatcher *dispatcher)
{
if (dispatcher->references > 1) {
EnterCriticalSection(&dispatcher->cs);
}
return dispatcher->device;
}
void aciomgr_port_checkin(struct aciomgr_port_dispatcher *dispatcher)
{
if (dispatcher->references > 1) {
LeaveCriticalSection(&dispatcher->cs);
}
}