#include #include #include #include #include #include #include #include "libvxi11client.h" #include "vxi11.h" // uncomment for debugging output: #define DEBUG #ifdef DEBUG #include #include #endif /** * This is a thin wrapper around the rpcgen generated code to give it a simpler interface. */ #define FLAG_TERMCHRSET (1 << 7) #define FLAG_END (1 << 3) #define FLAG_WAITLOCK 1 // in milliseconds #define LOCK_TIMEOUT 1000 #define IO_TIMEOUT 5000 #define POLL_TIMEOUT 1000 static GThread* interruptthread; static void (*interruptcallback)(char*) = NULL; static bool interruptserverstarted = false; static unsigned int interruptserverport = -1; static GMutex* mutex; static GCond* cond; void * device_intr_srq_1_svc(Device_SrqParms *argp, struct svc_req *rqstp) { #ifdef DEBUG printf("device_intr_srq_1_svc()\n"); #endif if (interruptcallback != NULL ) { interruptcallback(g_strndup(argp->handle.handle_val, argp->handle.handle_len)); } 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(VXI11Context* context, char* address, char* device) { context->clnt = clnt_create(address, DEVICE_CORE, DEVICE_CORE_VERSION, "tcp"); if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } else { Create_LinkParms link_parms; link_parms.clientId = (long) context->clnt; link_parms.lockDevice = 0; link_parms.lock_timeout = LOCK_TIMEOUT; link_parms.device = device != NULL ? device : "device0"; Create_LinkResp* linkresp = create_link_1(&link_parms, context->clnt); if (linkresp != NULL && linkresp->error == 0) { context->devicelink = linkresp->lid; #ifdef DEBUG printf("Link created, lid is %d, abort channel port %d\n", (int) linkresp->lid, linkresp->abortPort); #endif struct sockaddr_in serveraddr; if (clnt_control(context->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; context->abortclnt = clnttcp_create(&serveraddr, DEVICE_ASYNC, DEVICE_ASYNC_VERSION, &sock, 0, 0); if (context->abortclnt == NULL ) return 0; } else // failed! return 0; context->interruptchannelopen = false; context->interruptsenabled = false; return 1; } else if (linkresp == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(linkresp->error); } } /** * read the status byte of the connected server * returns the status byte or'ed with 0x100 on success * so that you can tell a zero status byte from an error */ int vxi11_readstatusbyte(VXI11Context* context, bool waitforlock) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_GenericParms params = { .lid = context->devicelink, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout = LOCK_TIMEOUT, .io_timeout = IO_TIMEOUT }; Device_ReadStbResp* resp = device_readstb_1(¶ms, context->clnt); if (resp != NULL && resp->error == 0) return resp->stb | (1 << 8); // this sets a bit above the byte so that we can tell whether there was a state issue // or if the instrument returned 0 else if (resp == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(resp->error); } /** * write to the connected device. If len is less than 0 the length will be calculated with strlen * **only safe for standard terminated strings** */ int vxi11_write(VXI11Context* context, char* data, int len, bool waitlock, bool end) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_WriteParms params = { .lid = context->devicelink, .io_timeout = IO_TIMEOUT, .lock_timeout = LOCK_TIMEOUT, .flags = vxi11_generateflags(waitlock, end, false) }; params.data.data_len = len < 0 ? strlen(data) : len; params.data.data_val = data; Device_WriteResp* resp = device_write_1(¶ms, context->clnt); if (resp != NULL && resp->error == 0) { errno = 0; return resp->size; } else if (resp == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(resp->error); } /** * read from the connected device */ int vxi11_read(VXI11Context* context, char* buffer, unsigned int bufferlen, bool waitlock, bool termchrset, char termchr, unsigned int* reason) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_ReadParms params = { .lid = context->devicelink, .requestSize = 256, .io_timeout = IO_TIMEOUT, .lock_timeout = LOCK_TIMEOUT, .flags = vxi11_generateflags(waitlock, false, termchrset), .termChar = termchrset ? termchr : 0 }; Device_ReadResp* resp = device_read_1(¶ms, context->clnt); if (resp != NULL && resp->error == 0) { #ifdef DEBUG printf("Got \"%s\" from server\n", resp->data.data_val); #endif errno = 0; if (buffer != NULL && resp->data.data_val != NULL ) { int lengthtocopy = ((bufferlen - 1) < resp->data.data_len ? (bufferlen - 1) : resp->data.data_len); strncpy(buffer, resp->data.data_val, lengthtocopy); } #ifdef DEBUG else printf("Supplied buffer is null!\n"); #endif if (reason != NULL ) *reason = resp->reason; return resp->data.data_len; } else if (resp == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(resp->error); } /** * call docmd with the specified command * datainlen will be calculated with strlen if less than 0 */ int vxi11_docmd(VXI11Context* context, char* datain, int datainlen, char* dataout, int outbufferlen, int* dataoutlen, unsigned long cmd, bool waitforlock) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_DocmdParms params = { .lid = context->devicelink, .flags = vxi11_generateflags(waitforlock, false, false), .io_timeout = IO_TIMEOUT, .lock_timeout = LOCK_TIMEOUT, .cmd = cmd, .network_order = 0, .datasize = 0 }; if (datain == NULL ) datainlen = 0; else if (datainlen < 0) datainlen = strlen(datain) + 1; params.data_in.data_in_len = datainlen; params.data_in.data_in_val = datain; Device_DocmdResp* resp = device_docmd_1(¶ms, context->clnt); if (resp != NULL && resp->error == 0) { if (dataout != NULL ) strncpy(dataout, resp->data_out.data_out_val, (resp->data_out.data_out_len > outbufferlen ? outbufferlen : resp->data_out.data_out_len)); *dataoutlen = resp->data_out.data_out_len; return 1; } else if (resp == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(resp->error); } /** * trigger the connected device */ int vxi11_trigger(VXI11Context* context, bool waitforlock) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_GenericParms params = { .lid = context->devicelink, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout = LOCK_TIMEOUT, .io_timeout = IO_TIMEOUT }; Device_Error* error = device_trigger_1(¶ms, context->clnt); if (error->error == 0) return 1; else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * clear the connected device */ int vxi11_clear(VXI11Context* context, bool waitforlock) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_GenericParms params = { .lid = context->devicelink, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout = LOCK_TIMEOUT, .io_timeout = IO_TIMEOUT }; Device_Error* error = device_clear_1(¶ms, context->clnt); if (error != NULL && error->error == 0) return 1; else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * remote the connected device */ int vxi11_remote(VXI11Context* context, bool waitforlock) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_GenericParms params = { .lid = context->devicelink, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout = LOCK_TIMEOUT, .io_timeout = IO_TIMEOUT }; Device_Error* error = device_remote_1(¶ms, context->clnt); if (error != NULL && error->error == 0) return 1; else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * local the connected device */ int vxi11_local(VXI11Context* context, bool waitforlock) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_GenericParms params = { .lid = context->devicelink, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout = LOCK_TIMEOUT, .io_timeout = IO_TIMEOUT }; Device_Error* error = device_local_1(¶ms, context->clnt); if (error != NULL && error->error == 0) return 1; else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * lock the connected device */ int vxi11_lock(VXI11Context* context, bool waitforlock) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_LockParms params = { .lid = context->devicelink, .flags = vxi11_generateflags(waitforlock, false, false), .lock_timeout = LOCK_TIMEOUT }; Device_Error* error = device_lock_1(¶ms, context->clnt); if (error != NULL && error->error == 0) return 1; else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * unlock the connected device */ int vxi11_unlock(VXI11Context* context) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_Error* error = device_unlock_1(&(context->devicelink), context->clnt); if (error != NULL && error->error == 0) return 1; else if (error == NULL ) { errno = ERRNO_NULLRESULT; 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; } interruptserverport = transp->xp_port; #ifdef DEBUG printf("Interrupt channel on port %d tcp\n", interruptserverport); #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; } g_mutex_lock(mutex); g_cond_signal(cond); g_mutex_unlock(mutex); int i; struct pollfd *my_pollfd = NULL; int last_max_pollfd = 0; while (interruptserverstarted) { int max_pollfd = svc_max_pollfd; if (max_pollfd == 0 && svc_pollfd == NULL ) break; if (last_max_pollfd != max_pollfd) { struct pollfd *new_pollfd = realloc(my_pollfd, sizeof(struct pollfd) * max_pollfd); if (new_pollfd == NULL ) { break; } my_pollfd = new_pollfd; last_max_pollfd = max_pollfd; } for (i = 0; i < max_pollfd; ++i) { my_pollfd[i].fd = svc_pollfd[i].fd; my_pollfd[i].events = svc_pollfd[i].events; my_pollfd[i].revents = 0; } switch (i = poll(my_pollfd, max_pollfd, POLL_TIMEOUT)) { case -1: break; case 0: continue; default: svc_getreq_poll(my_pollfd, i); continue; } break; } free(my_pollfd); #ifdef DEBUG printf("Interrupt channel thread ended\n"); #endif return NULL ; } int vxi11_start_interrupt_server(void (*callback)(gchar* handle)) { interruptcallback = callback; interruptserverstarted = true; g_thread_init(NULL ); mutex = g_mutex_new(); cond = g_cond_new(); g_mutex_lock(mutex); interruptthread = g_thread_create(interruptthreadfunc, NULL, true, NULL ); if (interruptthread == NULL ) return 0; #ifdef DEBUG printf("Waiting for interrupt thread to start\n"); #endif g_cond_wait(cond, mutex); g_mutex_unlock(mutex); g_cond_free(cond); g_mutex_free(mutex); cond = NULL; mutex = NULL; #ifdef DEBUG printf("Interrupt thread started, port is %d\n", interruptserverport); #endif if (interruptserverport == -1) return 0; return 1; } int vxi11_stop_interrupt_server() { #ifdef DEBUG printf("Waiting for interrupt thread to die\n"); #endif interruptserverstarted = false; g_thread_join(interruptthread); interruptthread = NULL; return 1; } /** * create an interrupt channel from the connected device */ int vxi11_create_intr_chan(VXI11Context* context) { if (context->clnt == NULL || context->interruptchannelopen) { errno = ERRNO_BADSTATE; return 0; } else if (interruptthread == NULL ) { #ifdef DEBUG printf("interrupt thread isn't running\n"); #endif errno = ERRNO_INTERRUPTHANDLERTHREADISDEAD; return 0; } struct sockaddr_in myaddress; get_myaddress(&myaddress); Device_RemoteFunc remotefunc = { .hostAddr = ntohl(myaddress.sin_addr.s_addr), .hostPort = interruptserverport, .progNum = DEVICE_INTR, .progVers = DEVICE_INTR_VERSION, .progFamily = DEVICE_TCP }; Device_Error* error = create_intr_chan_1(&remotefunc, context->clnt); if (error != NULL && error->error == 0) { context->interruptchannelopen = true; return 1; } else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * destroy an interrupt channel from the connected device */ int vxi11_destroy_intr_chan(VXI11Context* context) { if (context->clnt == NULL || !(context->interruptchannelopen)) { errno = ERRNO_BADSTATE; return 0; } else if (interruptthread == NULL ) { errno = ERRNO_INTERRUPTHANDLERTHREADISDEAD; return 0; } Device_Error* error = destroy_intr_chan_1(NULL, context->clnt); if (error != NULL && error->error == 0) { context->interruptchannelopen = false; return 1; } else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * enable interrupts */ int vxi11_enable_srq(VXI11Context* context, bool enable, char* handle) { if (context->clnt == NULL || !(context->interruptchannelopen)) { errno = ERRNO_BADSTATE; return 0; } else if (interruptthread == NULL ) { errno = ERRNO_INTERRUPTHANDLERTHREADISDEAD; return 0; } Device_EnableSrqParms params = { .lid = context->devicelink, .enable = enable }; if (enable) { if (handle != NULL ) { params.handle.handle_val = handle; params.handle.handle_len = strlen(handle); } } else { params.handle.handle_val = NULL; params.handle.handle_len = 0; } Device_Error* error = device_enable_srq_1(¶ms, context->clnt); if (error != NULL && error->error == 0) { return 1; } else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * send an abort to the connected device */ int vxi11_abort(VXI11Context* context) { if (context->abortclnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_Error* error = device_abort_1(&(context->devicelink), context->abortclnt); if (error != NULL && error->error == 0) return 1; else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); } /** * close the current link and free the RPC client */ int vxi11_close(VXI11Context* context) { if (context->clnt == NULL ) { errno = ERRNO_BADSTATE; return 0; } Device_Error* error = destroy_link_1(&(context->devicelink), context->clnt); clnt_destroy(context->clnt); context->clnt = NULL; clnt_destroy(context->abortclnt); context->abortclnt = NULL; if (error != NULL && error->error == 0) return 1; else if (error == NULL ) { errno = ERRNO_NULLRESULT; return 0; } else return -(error->error); }