mercury-api-0.1.0.2: cbits/glue/glue.c
#define _GNU_SOURCE
#include <locale.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(__APPLE__)
#include <xlocale.h>
#elif !defined(_WIN32)
#include <locale.h>
#endif
#include <HsFFI.h>
#include "glue.h"
#include "serial_reader_imp.h"
static TMR_Status free_transport_listener (TMR_Reader *reader,
TransportListenerEtc *listener,
TransportListenerEtc **prev)
{
TMR_Status stat;
stat = TMR_removeTransportListener (reader, &listener->block);
free (listener->key);
hs_free_fun_ptr (listener->funPtr);
*prev = listener->next;
free (listener);
return stat;
}
static void free_all_transport_listeners (ReaderEtc *reader)
{
while (reader->transportListeners) {
free_transport_listener (&reader->reader,
reader->transportListeners,
&reader->transportListeners);
}
}
/* These wrapper functions have at least two purposes:
* 1) Check whether the reader has been destroyed
* 2) Many of the reader methods are actually macros, so wrapping
* them in a real function makes it possible to call them
* from Haskell.
*
* Some of the wrapper functions also perform additional actions to
* make calling from Haskell easier.
*/
TMR_Status c_TMR_create (ReaderEtc *reader, const char *deviceUri)
{
reader->transportListeners = NULL;
reader->readPlan = NULL;
reader->destroyed = false;
strcpy (reader->uri, deviceUri);
return TMR_create (&reader->reader, deviceUri);
}
TMR_Status c_TMR_connect (ReaderEtc *reader)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_connect (&reader->reader);
}
TMR_Status c_TMR_destroy (ReaderEtc *reader)
{
if (reader->destroyed) {
return ERROR_ALREADY_DESTROYED;
} else {
TMR_Status status;
free_all_transport_listeners (reader);
status = TMR_destroy (&reader->reader);
free (reader->readPlan);
reader->destroyed = true;
return status;
}
}
TMR_Status c_TMR_read (ReaderEtc *reader, uint32_t timeoutMs, int32_t *tagCount)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_read (&reader->reader, timeoutMs, tagCount);
}
TMR_Status c_TMR_hasMoreTags (ReaderEtc *reader)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_hasMoreTags (&reader->reader);
}
TMR_Status c_TMR_getNextTag (ReaderEtc *reader, TMR_TagReadData *tagData)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_getNextTag (&reader->reader, tagData);
}
TMR_Status c_TMR_executeTagOp (ReaderEtc *reader,
TMR_TagOp *tagop,
TMR_TagFilter *filter,
TMR_uint8List *data)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_executeTagOp (&reader->reader, tagop, filter, data);
}
TMR_Status c_TMR_gpoSet (ReaderEtc *reader,
uint8_t count,
const TMR_GpioPin state[])
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_gpoSet (&reader->reader, count, state);
}
TMR_Status c_TMR_gpiGet (ReaderEtc *reader,
uint8_t *count,
TMR_GpioPin state[])
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_gpiGet (&reader->reader, count, state);
}
TMR_Status c_TMR_firmwareLoad (ReaderEtc *reader,
uint8_t *firmwareStart,
uint32_t firmwareSize)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else {
TMR_memoryCookie cookie;
cookie.firmwareStart = firmwareStart;
cookie.firmwareSize = firmwareSize;
return TMR_firmwareLoad (&reader->reader, &cookie, TMR_memoryProvider);
}
}
TMR_Status c_TMR_paramSet(ReaderEtc *reader,
TMR_Param key,
const void *value)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else {
TMR_Status status;
status = TMR_paramSet (&reader->reader, key, value);
if (key == TMR_PARAM_READ_PLAN) {
if (status == TMR_SUCCESS) {
/* TMR_paramSet appears to only do a shallow copy of the
* read plan, meaning that the internal pointers need to
* stay pointing at valid memory. We handle this by
* keeping ownership of the ReadPlanEtc, which includes
* space for the lists to point at.
*
* When the read plan is set, we need to free the old
* ReadPlanEtc, and keep a pointer to the new one.
*/
free (reader->readPlan);
reader->readPlan = (ReadPlanEtc *) value;
}
}
return status;
}
}
TMR_Status c_TMR_paramGet(ReaderEtc *reader, TMR_Param key, void *value)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else {
/* TMR_PARAM_READ_PLAN appears to be write-only, so fulfill the
* read from our cached copy. */
if (key == TMR_PARAM_READ_PLAN) {
TMR_ReadPlan *rp;
rp = (TMR_ReadPlan *) value;
if (reader->readPlan) {
/* Shallow copy the ReadPlan, although the pointers will
* still point at parts of the cached ReadPlanEtc. */
*rp = reader->readPlan->plan;
} else {
/* No cached read plan, which means it's initialized to
* the default. */
c_default_read_plan (rp);
}
return TMR_SUCCESS;
} else {
return TMR_paramGet (&reader->reader, key, value);
}
}
}
TMR_Status c_TMR_reboot (ReaderEtc *reader)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_reboot (&reader->reader);
}
void c_default_read_plan (TMR_ReadPlan *rp)
{
/* This should match the call to TMR_RP_init_simple() in
* TMR_reader_init_internal() in tm_reader.c. */
TMR_RP_init_simple (rp, 0, NULL, TMR_TAG_PROTOCOL_GEN2, 1);
}
TMR_Status c_TMR_paramList (ReaderEtc *reader, TMR_Param *keys, uint32_t *len)
{
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
else
return TMR_paramList (&reader->reader, keys, len);
}
/* takes ownership of func and unique */
TMR_Status c_TMR_addTransportListener (ReaderEtc *reader,
TMR_TransportListener func,
char *unique)
{
TransportListenerEtc *listener;
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
listener = (TransportListenerEtc *) malloc (sizeof (TransportListenerEtc));
if (! listener)
return TMR_ERROR_OUT_OF_MEMORY;
listener->block.listener = func;
listener->block.cookie = NULL;
listener->key = unique;
listener->funPtr = (HsFunPtr) func;
listener->next = reader->transportListeners;
reader->transportListeners = listener;
return TMR_addTransportListener (&reader->reader, &listener->block);
}
TMR_Status c_TMR_removeTransportListener (ReaderEtc *reader,
const char *unique)
{
TransportListenerEtc *listener, **prev;
if (reader->destroyed)
return ERROR_ALREADY_DESTROYED;
prev = &reader->transportListeners;
for (listener = reader->transportListeners;
listener != NULL;
prev = &listener->next, listener = listener->next) {
if (0 == strcmp (unique, listener->key))
return free_transport_listener (&reader->reader, listener, prev);
}
return TMR_ERROR_INVALID;
}
const char *c_TMR_strerr (ReaderEtc *reader, TMR_Status status)
{
TMR_Reader *tmr;
if (reader && !reader->destroyed)
tmr = &reader->reader;
else
tmr = NULL;
switch (status) {
case ERROR_ALREADY_DESTROYED:
return "Attempt to use reader after it was destroyed";
default:
return TMR_strerr (tmr, status);
}
}
void *c_new_c_locale (void)
{
/* We need to call tzset once before calling localtime_r, and this seems
* like a good place to do it. */
tzset();
#ifdef _WIN32
/* Unused on Windows, so value doesn't matter, but can't be NULL. */
return (void*) 1;
#else
/* Allocate a new locale. We only do this once, so it's okay
* that it leaks. */
return (void *) newlocale(LC_ALL_MASK, "C", (locale_t) 0);
#endif
}
int c_format_time (char *buf,
size_t len,
const char *fmt,
time_t seconds,
bool local,
void *locale)
{
time_t t;
struct tm stm, *stmp;
t = seconds;
#ifdef _WIN32
if (local)
stmp = localtime (&t);
else
stmp = gmtime (&t);
#else
if (local)
stmp = localtime_r (&t, &stm);
else
stmp = gmtime_r (&t, &stm);
#endif
if (! stmp)
return -1;
#ifdef _WIN32
if (0 == strftime (buf, len, fmt, stmp))
return -1;
#else
if (0 == strftime_l (buf, len, fmt, stmp, (locale_t) locale))
return -1;
#endif
return 0;
}