Bemanitools v5.26 release
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
libs += aciodrv
|
||||
|
||||
libs_aciodrv := \
|
||||
|
||||
src_aciodrv := \
|
||||
device.c \
|
||||
icca.c \
|
||||
port.c \
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
#define LOG_MODULE "aciodrv-device"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
#include "aciodrv/port.h"
|
||||
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/* Enable to dump all data to the logger */
|
||||
//#define AC_IO_MSG_LOG
|
||||
|
||||
static uint8_t aciodrv_device_msg_counter = 1;
|
||||
static uint8_t aciodrv_device_node_count;
|
||||
static char aviodrv_device_node_products[16][4];
|
||||
|
||||
static bool aciodrv_device_init(void)
|
||||
{
|
||||
uint8_t init_seq[1] = {AC_IO_SOF};
|
||||
|
||||
/* init/reset the device by sending 0xAA until 0xAA is returned */
|
||||
int read = 0;
|
||||
do {
|
||||
if (aciodrv_port_write(init_seq, sizeof(init_seq)) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
read = aciodrv_port_read(init_seq, sizeof(init_seq));
|
||||
} while (read == 0);
|
||||
|
||||
if (read > 0) {
|
||||
/* empty buffer by reading all data */
|
||||
while (read > 0) {
|
||||
read = aciodrv_port_read(init_seq, sizeof(init_seq));
|
||||
}
|
||||
|
||||
return read == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
static void aciodrv_device_log_buffer(const char* msg, const uint8_t* buffer,
|
||||
int length)
|
||||
{
|
||||
char str[4096];
|
||||
|
||||
hex_encode_uc((const void*) buffer, length, str, sizeof(str));
|
||||
log_misc("%s, length %d: %s", msg, length, str);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool aciodrv_device_send(const uint8_t* buffer, int length)
|
||||
{
|
||||
uint8_t send_buf[512];
|
||||
int send_buf_pos = 0;
|
||||
uint8_t checksum = 0;
|
||||
|
||||
if (length > sizeof(send_buf)) {
|
||||
log_warning("Send buffer overflow");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Send (1)", buffer, length);
|
||||
#endif
|
||||
|
||||
send_buf[send_buf_pos++] = AC_IO_SOF;
|
||||
|
||||
/* TODO overrun checks */
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (buffer[i] == AC_IO_SOF || buffer[i] == AC_IO_ESCAPE) {
|
||||
send_buf[send_buf_pos++] = AC_IO_ESCAPE;
|
||||
send_buf[send_buf_pos++] = ~buffer[i];
|
||||
} else {
|
||||
send_buf[send_buf_pos++] = buffer[i];
|
||||
}
|
||||
|
||||
checksum += buffer[i];
|
||||
}
|
||||
|
||||
/* we have to escape the checksum as well! */
|
||||
if (checksum == AC_IO_SOF || checksum == AC_IO_ESCAPE) {
|
||||
send_buf[send_buf_pos++] = AC_IO_ESCAPE;
|
||||
send_buf[send_buf_pos++] = ~checksum;
|
||||
} else {
|
||||
send_buf[send_buf_pos++] = checksum;
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Send (2)", send_buf, send_buf_pos);
|
||||
#endif
|
||||
|
||||
if (aciodrv_port_write(send_buf, send_buf_pos) != send_buf_pos) {
|
||||
log_warning("Sending data with length %d failed", send_buf_pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int aciodrv_device_receive(uint8_t* buffer, int size)
|
||||
{
|
||||
uint8_t recv_buf[512];
|
||||
int recv_size = 0;
|
||||
int read = 0;
|
||||
uint8_t checksum = 0;
|
||||
int result_size = 0;
|
||||
|
||||
/* reading a byte stream, we are getting a varying amount
|
||||
of 0xAAs before we get a valid message. */
|
||||
recv_buf[0] = AC_IO_SOF;
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf, 1);
|
||||
} while (recv_buf[0] == AC_IO_SOF);
|
||||
|
||||
if (read > 0) {
|
||||
size += 1;
|
||||
|
||||
/* recv_buf[0] is already the first byte of the message.
|
||||
now read until nothing's left */
|
||||
recv_size++;
|
||||
size--;
|
||||
|
||||
/* important: we have to know how much data we expect
|
||||
and have to read until we reach the requested amount.
|
||||
Because this can be interrupted by 0 reads and we
|
||||
need to handle escaping (which relies on an up to
|
||||
date recv_buf[recv_size]) we loop until we get a
|
||||
non-zero read. */
|
||||
while (size > 0) {
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf + recv_size, 1);
|
||||
} while (read == 0);
|
||||
|
||||
if (read < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* check for escape byte. these don't count towards the
|
||||
size we expect! */
|
||||
if (recv_buf[recv_size] == AC_IO_ESCAPE)
|
||||
{
|
||||
/* next byte is our real data
|
||||
overwrite escape byte */
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf + recv_size, 1);
|
||||
} while (read == 0);
|
||||
|
||||
if (read < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
recv_buf[recv_size] = ~recv_buf[recv_size];
|
||||
}
|
||||
|
||||
recv_size += read;
|
||||
size -= read;
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Recv (1)", recv_buf, recv_size);
|
||||
#endif
|
||||
|
||||
/* recv_size - 1: omit checksum for checksum calc */
|
||||
for (int i = 0; i < recv_size - 1; i++) {
|
||||
checksum += recv_buf[i];
|
||||
buffer[i] = recv_buf[i];
|
||||
}
|
||||
|
||||
result_size = recv_size - 1;
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Recv (2)", buffer, result_size);
|
||||
#endif
|
||||
|
||||
if (checksum != recv_buf[recv_size - 1]) {
|
||||
log_warning("Invalid message checksum: %02X != %02X",
|
||||
checksum, recv_buf[recv_size - 1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result_size;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint8_t aciodrv_device_enum_nodes(void)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = 0x00;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_ASSIGN_ADDRS);
|
||||
msg.cmd.nbytes = 1;
|
||||
msg.cmd.count = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Enumerating nodes failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_info("Enumerated %d nodes", msg.cmd.count);
|
||||
|
||||
return msg.cmd.count;
|
||||
}
|
||||
|
||||
static bool aciodrv_device_get_version(uint8_t node_id, char product[4])
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_GET_VERSION);
|
||||
msg.cmd.nbytes = 0;
|
||||
|
||||
if ( !aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
sizeof(struct ac_io_version))) {
|
||||
log_warning("Get version of node %d failed", node_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Node %d: type %d, flag %d, version %d.%d.%d, product %c%c%c%c, "
|
||||
"build date: %s %s",
|
||||
node_id,
|
||||
msg.cmd.version.type,
|
||||
msg.cmd.version.flag,
|
||||
msg.cmd.version.major,
|
||||
msg.cmd.version.minor,
|
||||
msg.cmd.version.revision,
|
||||
msg.cmd.version.product_code[0],
|
||||
msg.cmd.version.product_code[1],
|
||||
msg.cmd.version.product_code[2],
|
||||
msg.cmd.version.product_code[3],
|
||||
msg.cmd.version.date,
|
||||
msg.cmd.version.time);
|
||||
|
||||
memcpy(product, msg.cmd.version.product_code, 4);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool aciodrv_device_start_node(uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_START_UP);
|
||||
msg.cmd.nbytes = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Starting node %d failed", node_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Started node %d, status: %d", node_id, msg.cmd.status);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_device_open(const char* port, int baud)
|
||||
{
|
||||
if (!aciodrv_port_open(port, baud)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aciodrv_device_init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aciodrv_device_node_count = aciodrv_device_enum_nodes();
|
||||
if (aciodrv_device_node_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aciodrv_device_node_count; i++) {
|
||||
if (!aciodrv_device_get_version(i + 1, aviodrv_device_node_products[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aciodrv_device_node_count; i++) {
|
||||
if (!aciodrv_device_start_node(i + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t aciodrv_device_get_node_count(void)
|
||||
{
|
||||
return aciodrv_device_node_count;
|
||||
}
|
||||
|
||||
bool aciodrv_device_get_node_product_ident(uint8_t node_id, char product[4])
|
||||
{
|
||||
if (aciodrv_device_node_count == 0 || node_id > aciodrv_device_node_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(product, aviodrv_device_node_products[node_id], 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_send_and_recv(struct ac_io_message* msg, int resp_size)
|
||||
{
|
||||
msg->cmd.seq_no = aciodrv_device_msg_counter++;
|
||||
|
||||
if (aciodrv_device_send((uint8_t*) msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + msg->cmd.nbytes) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t req_code = msg->cmd.code;
|
||||
|
||||
if (aciodrv_device_receive((uint8_t*) msg,
|
||||
resp_size) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req_code != msg->cmd.code) {
|
||||
log_warning("Received invalid response %04X for request %04X",
|
||||
msg->cmd.code, req_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void aciodrv_device_close(void)
|
||||
{
|
||||
aciodrv_port_close();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef ACIODRV_DEVICE_H
|
||||
#define ACIODRV_DEVICE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
/**
|
||||
* Open an ACIO device connected to a serial port.
|
||||
*
|
||||
* @param port Port the device is connected to (e.g. "COM1")
|
||||
* @param baud Baud rate for communication (e.g. 57600 for ICCA)
|
||||
* @return True if opening the port and resetting the device was successful,
|
||||
* false on error.
|
||||
*/
|
||||
bool aciodrv_device_open(const char* port, int baud);
|
||||
|
||||
/**
|
||||
* Get the node count on the opened device.
|
||||
*
|
||||
* @return Total num of nodes enumerated on the ACIO device.
|
||||
*/
|
||||
uint8_t aciodrv_device_get_node_count(void);
|
||||
|
||||
/**
|
||||
* Get the product identifier of an enumerated node.
|
||||
*
|
||||
* @param node_id Id of the node. Needs to be in range of the total node count.
|
||||
* @param product Buffer to return the product id to.
|
||||
* @return True on success, false on error. If True the variable product contains
|
||||
* the identifier of the queried node.
|
||||
*/
|
||||
bool aciodrv_device_get_node_product_ident(uint8_t node_id, char product[4]);
|
||||
|
||||
/**
|
||||
* Send a message to the ACIO bus and receive an answer.
|
||||
* Use this to implement the protocol for each type of device that can be
|
||||
* part of the bus.
|
||||
*
|
||||
* @param msg Msg to send to the bus. Make sure that the buffer backing
|
||||
* this message is big enough to receive the response as well.
|
||||
* @param resp_size Size of the expecting response.
|
||||
* @return True on success, false on error.
|
||||
*/
|
||||
bool aciodrv_send_and_recv(struct ac_io_message* msg, int resp_size);
|
||||
|
||||
/**
|
||||
* Close the previously opened ACIO device.
|
||||
*/
|
||||
void aciodrv_device_close(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
#define LOG_MODULE "aciodrv-icca"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static bool aciodrv_icca_queue_loop_start(uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_QUEUE_LOOP_START);
|
||||
msg.cmd.nbytes = 1;
|
||||
msg.cmd.status = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Starting queue loop failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Started queue loop of node %d, status: %d",
|
||||
node_id, msg.cmd.status);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_init(uint8_t node_id)
|
||||
{
|
||||
if (!aciodrv_icca_queue_loop_start(node_id + 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_set_state(uint8_t node_id, int slot_state,
|
||||
struct ac_io_icca_state* state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_SET_SLOT_STATE);
|
||||
msg.cmd.nbytes = 2;
|
||||
/* buffer size of data we expect */
|
||||
msg.cmd.raw[0] = sizeof(struct ac_io_icca_state);
|
||||
msg.cmd.raw[1] = slot_state;
|
||||
|
||||
if ( !aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
msg.cmd.raw[0])) {
|
||||
log_warning("Setting state of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state != NULL) {
|
||||
memcpy(state, msg.cmd.raw, sizeof(struct ac_io_icca_state));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state* state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_POLL);
|
||||
msg.cmd.nbytes = 1;
|
||||
/* buffer size of data we expect */
|
||||
msg.cmd.count = sizeof(struct ac_io_icca_state);
|
||||
|
||||
if ( !aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
msg.cmd.count)) {
|
||||
log_warning("Getting state of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state != NULL) {
|
||||
memcpy(state, msg.cmd.raw, sizeof(struct ac_io_icca_state));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_read_card(uint8_t node_id, struct ac_io_icca_state* state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_ENGAGE);
|
||||
msg.cmd.nbytes = 1;
|
||||
/* buffer size of data we expect */
|
||||
msg.cmd.count = sizeof(struct ac_io_icca_state);
|
||||
|
||||
if ( !aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
msg.cmd.count)) {
|
||||
log_warning("Reading card of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state != NULL) {
|
||||
memcpy(state, msg.cmd.raw, sizeof(struct ac_io_icca_state));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef ACIODRV_ICCA_H
|
||||
#define ACIODRV_ICCA_H
|
||||
|
||||
#include "acio/icca.h"
|
||||
|
||||
/**
|
||||
* Initialize an ICCA node.
|
||||
*
|
||||
* @param node_id Id of the node to initialize (0 based).
|
||||
* @return True if successful, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_init(uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Set the state of on ICCA node.
|
||||
*
|
||||
* @param node_id Id of the node to set the state for (0 based).
|
||||
* @param slot_state State of the slot (refer to corresponding enum).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
* @return True on success, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_set_state(uint8_t node_id, int slot_state, struct ac_io_icca_state* state);
|
||||
|
||||
/**
|
||||
* Get the current state of an ICCA node.
|
||||
*
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
* @return True on success, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state* state);
|
||||
|
||||
/**
|
||||
* Trigger a card read action on the ICCA reader. Make sure to call this
|
||||
* when you want to read the card. Just polling the state is not sufficient
|
||||
* to get the most recent card data. Make sure to re-get the state after
|
||||
* a read call. The state returned here might not be up to date for some reason.
|
||||
*
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
* @return True on success, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_read_card(uint8_t node_id, struct ac_io_icca_state* state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
#define LOG_MODULE "aciodrv-port"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static HANDLE aciodrv_port_fd;
|
||||
|
||||
bool aciodrv_port_open(const char* port, int baud)
|
||||
{
|
||||
COMMTIMEOUTS ct;
|
||||
DCB dcb;
|
||||
|
||||
log_info("Opening ACIO on %s at %d baud", port, baud);
|
||||
|
||||
aciodrv_port_fd = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (aciodrv_port_fd == INVALID_HANDLE_VALUE) {
|
||||
log_warning("Failed to open %s", port);
|
||||
|
||||
goto early_fail;
|
||||
}
|
||||
|
||||
if (!SetCommMask(aciodrv_port_fd, EV_RXCHAR)) {
|
||||
log_warning("SetCommMask failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetupComm(aciodrv_port_fd, 0x1000, 0x1000)) {
|
||||
log_warning("SetupComm failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!PurgeComm(aciodrv_port_fd,
|
||||
PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) {
|
||||
|
||||
log_warning("PurgeComm failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ct.ReadTotalTimeoutConstant = 0;
|
||||
ct.WriteTotalTimeoutConstant = 0;
|
||||
ct.ReadIntervalTimeout = -1;
|
||||
ct.ReadTotalTimeoutMultiplier = 0;
|
||||
ct.WriteTotalTimeoutMultiplier = 0;
|
||||
|
||||
if (!SetCommTimeouts(aciodrv_port_fd, &ct)) {
|
||||
log_warning("SetCommTimeouts failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcb.DCBlength = sizeof(dcb);
|
||||
|
||||
if (!GetCommState(aciodrv_port_fd, &dcb)) {
|
||||
log_warning("GetCommState failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcb.BaudRate = baud;
|
||||
dcb.fBinary = TRUE;
|
||||
dcb.fParity = FALSE;
|
||||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
dcb.fDsrSensitivity = FALSE;
|
||||
dcb.fOutX = FALSE;
|
||||
dcb.fInX = FALSE;
|
||||
dcb.fErrorChar = FALSE;
|
||||
dcb.fNull = FALSE;
|
||||
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
dcb.fAbortOnError = FALSE;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
dcb.XonChar = 17;
|
||||
dcb.XoffChar = 19;
|
||||
dcb.XonLim = 100;
|
||||
dcb.XoffLim = 100;
|
||||
|
||||
if (!SetCommState(aciodrv_port_fd, &dcb)) {
|
||||
log_warning("SetCommState failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!EscapeCommFunction(aciodrv_port_fd, SETDTR)) {
|
||||
log_warning("SETDTR failed: err = %lu", GetLastError());
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_info("Opened ACIO device on %s", port);
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
CloseHandle(aciodrv_port_fd);
|
||||
|
||||
early_fail:
|
||||
aciodrv_port_fd = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
int aciodrv_port_read(void *bytes, int nbytes)
|
||||
{
|
||||
DWORD nread;
|
||||
|
||||
if (aciodrv_port_fd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ClearCommError(aciodrv_port_fd, NULL, NULL)) {
|
||||
log_warning("ClearCommError failed");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ReadFile(aciodrv_port_fd, bytes, nbytes, &nread, NULL)) {
|
||||
log_warning("ReadFile failed: err = %lu", GetLastError());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
int aciodrv_port_write(const void *bytes, int nbytes)
|
||||
{
|
||||
DWORD nwrit;
|
||||
|
||||
if (aciodrv_port_fd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!WriteFile(aciodrv_port_fd, bytes, nbytes, &nwrit, NULL)) {
|
||||
log_warning("WriteFile failed: err = %lu", GetLastError());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nwrit;
|
||||
}
|
||||
|
||||
void aciodrv_port_close(void)
|
||||
{
|
||||
if (aciodrv_port_fd != NULL) {
|
||||
CloseHandle(aciodrv_port_fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef ACIODRV_PORT_H
|
||||
#define ACIODRV_PORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Open a serial port for communication with a ACIO device.
|
||||
*
|
||||
* @param port Port the device is connected to (e.g. "COM1")
|
||||
* @param baud Baud rate for communication (e.g. 57600 for ICCA)
|
||||
* @return True if opening the com port was successful, false on error.
|
||||
* @note This will open and setup the com port, only.
|
||||
*/
|
||||
bool aciodrv_port_open(const char* port, int baud);
|
||||
|
||||
/**
|
||||
* Read data from the opened com port.
|
||||
*
|
||||
* @param bytes Pointer to an allocated buffer to read the data into.
|
||||
* @param nbytes Number of bytes to read. Has to be less or equal the allocated
|
||||
* buffer size.
|
||||
* @return Number of bytes read on success or -1 on error.
|
||||
*/
|
||||
int aciodrv_port_read(void *bytes, int nbytes);
|
||||
|
||||
/**
|
||||
* Write data to the opened com port.
|
||||
*
|
||||
* @param bytes Pointer to an allocated buffer with data to write.
|
||||
* @param nbytes Number of bytes to write. Has to be equal or less the size
|
||||
* of the allocated buffer.
|
||||
* @return Number of bytes written on success or -1 on error.
|
||||
*/
|
||||
int aciodrv_port_write(const void *bytes, int nbytes);
|
||||
|
||||
/**
|
||||
* Close the previously opened com port.
|
||||
*/
|
||||
void aciodrv_port_close(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user