summaryrefslogtreecommitdiff
path: root/libvxi11client/libvxi11client.c
diff options
context:
space:
mode:
Diffstat (limited to 'libvxi11client/libvxi11client.c')
-rw-r--r--libvxi11client/libvxi11client.c487
1 files changed, 487 insertions, 0 deletions
diff --git a/libvxi11client/libvxi11client.c b/libvxi11client/libvxi11client.c
new file mode 100644
index 0000000..2f36147
--- /dev/null
+++ b/libvxi11client/libvxi11client.c
@@ -0,0 +1,487 @@
+#include <stdbool.h>
+#include <rpc/rpc.h>
+#include <netinet/in.h>
+#include <glib.h>
+#include <poll.h>
+#include "vxi11.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+#include <stdio.h>
+#include <arpa/inet.h>
+#endif
+
+/**
+ * This is a thin wrapper around the rpcgen generated code to give it a simpler interface.
+ * Only one server with a single link is supported.
+ */
+
+#define FLAG_TERMCHRSET (1 << 7)
+#define FLAG_END (1 << 3)
+#define FLAG_WAITLOCK 1
+
+#define VXI11_DEFAULT_TIMEOUT 1000
+
+static CLIENT* clnt = NULL;
+static CLIENT* abortclnt = NULL;
+static Device_Link link;
+static GThread* interruptthread;
+static bool interruptchannelopen = false;
+
+static void killinterruptthreadandwait() {
+ interruptchannelopen = false;
+ g_thread_join(interruptthread);
+ interruptthread = NULL;
+}
+
+void *
+device_intr_srq_1_svc(Device_SrqParms *argp, struct svc_req *rqstp) {
+#ifdef DEBUG
+ printf("device_intr_srq_1_svc()\n");
+#endif
+ static char * result;
+ return (void *) &result;
+}
+
+static void device_intr_1(struct svc_req *rqstp, register SVCXPRT *transp) {
+ union {
+ Device_SrqParms device_intr_srq_1_arg;
+ } argument;
+ char *result;
+ xdrproc_t _xdr_argument, _xdr_result;
+ char *(*local)(char *, struct svc_req *);
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void) svc_sendreply(transp, (xdrproc_t) xdr_void, (char *) NULL);
+ return;
+
+ case device_intr_srq:
+ _xdr_argument = (xdrproc_t) xdr_Device_SrqParms;
+ _xdr_result = (xdrproc_t) xdr_void;
+ local = (char *(*)(char *, struct svc_req *)) device_intr_srq_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ memset((char *) &argument, 0, sizeof(argument));
+ if (!svc_getargs(transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)((char *) &argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
+ fprintf(stderr, "%s", "unable to free arguments");
+ exit(1);
+ }
+ return;
+}
+
+static Device_Flags vxi11_generateflags(bool waitlock, bool end, bool termchrset) {
+ Device_Flags flags = 0;
+ if (waitlock)
+ flags |= FLAG_WAITLOCK;
+ if (end)
+ flags |= FLAG_END;
+ if (termchrset)
+ flags |= FLAG_TERMCHRSET;
+ return flags;
+}
+
+/**
+ * create an RPC client and open a link to the server at $address.
+ * $device is apparently used for VXI-11 -> GPIB gateways.. this is untested.
+ */
+
+int vxi11_open(char* address, char* device) {
+ clnt = clnt_create(address, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp");
+
+ if (clnt == NULL)
+ return 0;
+
+ else {
+ Create_LinkParms link_parms;
+ link_parms.clientId = (long) clnt;
+ link_parms.lockDevice = 0;
+ link_parms.lock_timeout = VXI11_DEFAULT_TIMEOUT;
+ link_parms.device = device != NULL ? device : "device0";
+
+ Create_LinkResp* linkresp = create_link_1(&link_parms, clnt);
+ if (linkresp != NULL && linkresp->error == 0) {
+ link = linkresp->lid;
+
+#ifdef DEBUG
+ printf("Link created, lid is %d, abort channel port %d\n", linkresp->lid, linkresp->abortPort);
+#endif
+
+ struct sockaddr_in serveraddr;
+ if (clnt_control(clnt, CLGET_SERVER_ADDR, (char*) &serveraddr)) {
+#ifdef DEBUG
+ char addressstring[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &serveraddr.sin_addr, addressstring, sizeof(addressstring));
+ printf("Remote is %s\n", addressstring);
+#endif
+ serveraddr.sin_port = htons(linkresp->abortPort);
+ int sock = RPC_ANYSOCK;
+ abortclnt = clnttcp_create(&serveraddr, DEVICE_ASYNC, DEVICE_ASYNC_VERSION, &sock, 0, 0);
+ if (abortclnt == NULL)
+ return 0;
+
+ }
+ else
+ // failed!
+ return 0;
+
+ return 1;
+ }
+ else if (linkresp == NULL)
+ return 0;
+ else
+ return -(linkresp->error);
+ }
+}
+
+/**
+ * read the status byte of the connected server
+ */
+
+int vxi11_readstatusbyte(bool waitforlock) {
+ if (clnt == NULL)
+ return 0;
+
+ Device_GenericParms params = { .lid = link, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout =
+ VXI11_DEFAULT_TIMEOUT, .io_timeout = VXI11_DEFAULT_TIMEOUT };
+ Device_ReadStbResp* resp = device_readstb_1(&params, clnt);
+
+ if (resp != NULL && resp->error == 0)
+ return resp->stb;
+ else if (resp == NULL)
+ return 0;
+ else
+ return -1;
+}
+
+/**
+ * write to the connected device
+ */
+
+int vxi11_write(char* data, unsigned int len, bool waitlock, bool end) {
+ if (clnt == NULL)
+ return 0;
+
+ Device_WriteParms params = { .lid = link, .io_timeout = VXI11_DEFAULT_TIMEOUT,
+ .lock_timeout = VXI11_DEFAULT_TIMEOUT, .flags = vxi11_generateflags(waitlock, end, false) };
+ params.data.data_len = len;
+ params.data.data_val = data;
+
+ Device_WriteResp* resp = device_write_1(&params, clnt);
+ if (resp != NULL && resp->error == 0)
+ return resp->size;
+ else if (resp == NULL)
+ return 0;
+ else
+ return -1;
+}
+
+/**
+ * read from the connected device
+ */
+
+int vxi11_read(char* buffer, unsigned int bufferlen, bool waitlock, bool termchrset, char termchr) {
+ if (clnt == NULL)
+ return 0;
+
+ Device_ReadParms params = { .lid = link, .requestSize = 256, .io_timeout = VXI11_DEFAULT_TIMEOUT, .lock_timeout =
+ VXI11_DEFAULT_TIMEOUT, .flags = vxi11_generateflags(waitlock, false, termchrset), .termChar =
+ termchrset ? termchr : 0 };
+
+ Device_ReadResp* resp = device_read_1(&params, clnt);
+ if (resp != NULL && resp->error == 0)
+ return resp->data.data_len;
+ else if (resp == NULL)
+ return 0;
+ else
+ return -1;
+}
+
+/**
+ *
+ */
+
+int vxi11_docmd(unsigned long cmd, bool waitforlock) {
+ if (clnt == NULL)
+ return 0;
+
+ Device_DocmdParms params =
+ { .lid = link, .flags = vxi11_generateflags(waitforlock, false, false), .io_timeout = VXI11_DEFAULT_TIMEOUT,
+ .lock_timeout = VXI11_DEFAULT_TIMEOUT, .cmd = cmd, .network_order = 0, .datasize = 0 };
+
+ params.data_in.data_in_len = 0;
+ params.data_in.data_in_val = NULL;
+
+ Device_DocmdResp* resp = device_docmd_1(&params, clnt);
+ if (resp != NULL && resp->error == 0)
+ return 1;
+ else if (resp == NULL)
+ return 0;
+ else
+ return -(resp->error);
+}
+
+/**
+ * trigger the connected device
+ */
+
+int vxi11_trigger(bool waitforlock) {
+ if (clnt == NULL)
+ return 0;
+
+ Device_GenericParms params = { .lid = link, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout =
+ VXI11_DEFAULT_TIMEOUT, .io_timeout = VXI11_DEFAULT_TIMEOUT };
+ Device_Error* error = device_trigger_1(&params, clnt);
+
+ if (error->error == 0)
+ return 1;
+ else
+ return -(error->error);
+}
+
+/**
+ * clear the connected device
+ */
+
+int vxi11_clear(bool waitforlock) {
+ if (clnt == NULL)
+ return 0;
+ Device_GenericParms params = { .lid = link, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout =
+ VXI11_DEFAULT_TIMEOUT, .io_timeout = VXI11_DEFAULT_TIMEOUT };
+ Device_Error* error = device_clear_1(&params, clnt);
+ if (error != NULL && error->error == 0)
+ return 1;
+ else if (error == NULL)
+ return 0;
+ else
+ return -(error->error);
+}
+
+/**
+ * remote the connected device
+ */
+
+int vxi11_remote(bool waitforlock) {
+ if (clnt == NULL)
+ return 0;
+ Device_GenericParms params = { .lid = link, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout =
+ VXI11_DEFAULT_TIMEOUT, .io_timeout = VXI11_DEFAULT_TIMEOUT };
+ Device_Error* error = device_remote_1(&params, clnt);
+ if (error != NULL && error->error == 0)
+ return 1;
+ else if (error == NULL)
+ return 0;
+ else
+ return -(error->error);
+}
+
+/**
+ * local the connected device
+ */
+
+int vxi11_local(bool waitforlock) {
+ if (clnt == NULL)
+ return 0;
+ Device_GenericParms params = { .lid = link, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout =
+ VXI11_DEFAULT_TIMEOUT, .io_timeout = VXI11_DEFAULT_TIMEOUT };
+ Device_Error* error = device_local_1(&params, clnt);
+ if (error != NULL && error->error == 0)
+ return 1;
+ else if (error == NULL)
+ return 0;
+ else
+ return -(error->error);
+}
+
+/**
+ * lock the connected device
+ */
+
+int vxi11_lock(bool waitforlock) {
+ if (clnt == NULL)
+ return 0;
+ Device_LockParms params = { .lid = link, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout =
+ VXI11_DEFAULT_TIMEOUT };
+ Device_Error* error = device_lock_1(&params, clnt);
+ if (error != NULL && error->error == 0)
+ return 1;
+ else if (error == NULL)
+ return 0;
+ else
+ return -(error->error);
+}
+
+/**
+ * unlock the connected device
+ */
+
+int vxi11_unlock() {
+ if (clnt == NULL)
+ return 0;
+ Device_Error* error = device_unlock_1(&link, clnt);
+ if (error != NULL && error->error == 0)
+ return 1;
+ else if (error == NULL)
+ return 0;
+ else
+ return -(error->error);
+}
+
+static gpointer interruptthreadfunc(gpointer data) {
+#ifdef DEBUG
+ printf("Interrupt channel thread started\n");
+#endif
+ SVCXPRT *transp;
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL) {
+ fprintf(stderr, "%s", "cannot create tcp service.");
+ return 0;
+ }
+
+#ifdef DEBUG
+ printf("Interrupt channel on port %d tcp\n", transp->xp_port);
+#endif
+
+ if (!svc_register(transp, DEVICE_INTR, DEVICE_INTR_VERSION, device_intr_1, 0)) {
+ fprintf(stderr, "%s", "unable to register (DEVICE_INTR, DEVICE_INTR_VERSION, tcp).\n");
+ return 0;
+ }
+ *((unsigned int*) data) = transp->xp_port;
+
+ int no_of_fds;
+ int i;
+ struct pollfd pollfd_set[1024];
+
+ //svc_run();
+ // stolen from: http://pic.dhe.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.commtechref%2Fdoc%2Fcommtrf1%2Fsvc_getreq_poll.htm
+ while (interruptchannelopen) {
+ /* initialize the pollfd_set array and
+ get no of file descriptors in "no_of_fds"*/
+
+ /* Keep polling on file descriptors */
+ switch (i = poll(pollfd_set, no_of_fds, -1)) {
+ case -1:
+ case 0:
+ continue;
+ default:
+ /* Handle RPC request on each file descriptor */
+ svc_getreq_poll(pollfd_set, i);
+ }
+ }
+
+#ifdef DEBUG
+ printf("Interrupt channel thread ended\n");
+#endif
+ return NULL;
+}
+
+/**
+ * create an interrupt channel from the connected device
+ */
+int vxi11_create_intr_chan() {
+ if (interruptchannelopen)
+ return 0;
+ else if (clnt == NULL)
+ return 0;
+ else if (interruptthread != NULL)
+ return 0;
+
+ interruptchannelopen = true;
+ unsigned int port = -1;
+ g_thread_init(NULL);
+ interruptthread = g_thread_create(interruptthreadfunc, &port, true, NULL);
+
+ while (port == -1) { // spin
+ };
+
+ struct sockaddr_in myaddress;
+ get_myaddress(&myaddress);
+
+ Device_RemoteFunc remotefunc = { .hostAddr = myaddress.sin_addr.s_addr, .hostPort = port, .progNum = DEVICE_INTR,
+ .progVers = DEVICE_INTR_VERSION, .progFamily = DEVICE_TCP };
+ Device_Error* error = create_intr_chan_1(&remotefunc, clnt);
+ if (error != NULL && error->error == 0)
+ return 1;
+ else if (error == NULL) {
+ killinterruptthreadandwait();
+ return 0;
+ }
+ else {
+ killinterruptthreadandwait();
+ return -(error->error);
+ }
+}
+
+/**
+ * destroy an interrupt channel from the connected device
+ */
+
+int vxi11_destroy_intr_chan() {
+ if (!interruptchannelopen)
+ return 0;
+ else if (clnt == NULL)
+ return 0;
+ else if (interruptthread == NULL)
+ return 0;
+
+ Device_Error* error = destroy_intr_chan_1(NULL, clnt);
+ if (error != NULL && error->error == 0) {
+ killinterruptthreadandwait();
+ return 1;
+ }
+ else if (error == NULL)
+ return 0;
+ else
+ return -(error->error);
+}
+
+/**
+ * send an abort to the connected device
+ */
+
+int vxi11_abort() {
+ if (abortclnt == NULL)
+ return 0;
+ Device_Error* error = device_abort_1(&link, abortclnt);
+ if (error != NULL && error->error == 0)
+ return 1;
+ else if (error == NULL)
+ return 0;
+ else
+ return -(error->error);
+}
+
+/**
+ * close the current link and free the RPC client
+ */
+
+int vxi11_close() {
+ if (clnt == NULL)
+ return 0;
+
+ Device_Error* error = destroy_link_1(&link, clnt);
+ clnt_destroy(clnt);
+ clnt = NULL;
+ clnt_destroy(abortclnt);
+ abortclnt = NULL;
+
+ if (error != NULL && error->error == 0)
+ return 1;
+ else if (error == NULL)
+ return 0;
+ else
+ return -(error->error);
+}