diff options
Diffstat (limited to 'libvxi11client/libvxi11client.c')
-rw-r--r-- | libvxi11client/libvxi11client.c | 487 |
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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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); +} |