/***************************************************************************** * this function handles the direct memory access to the buffer * and copies objects to and from memory *****************************************************************************/ int dmarequest(const message_t *request, message_t **response_ptr) { unsigned int offset; /* int blockrequest = 0; */ int verbose = 0; /* these are used for blocking the read requests */ struct timeval tp; struct timespec ts; /* use a local variable for datasel (in GET_DAT) */ datasel_t datasel; /* these are for typecasting */ headerdef_t *headerdef; datadef_t *datadef; eventdef_t *eventdef; eventsel_t *eventsel; /* this will hold the response */ message_t *response; response = (message_t*)malloc(sizeof(message_t)); /* check for "out of memory" problems */ if (response == NULL) { *response_ptr = NULL; return -1; } response->def = (messagedef_t*)malloc(sizeof(messagedef_t)); /* check for "out of memory" problems */ if (response->def == NULL) { *response_ptr = NULL; free(response); return -1; } response->buf = NULL; /* the response should be passed to the calling function, where it should be freed */ *response_ptr = response; if (verbose>1) print_request(request->def); switch (request->def->command) { case PUT_HDR: if (verbose>1) fprintf(stderr, "dmarequest: PUT_HDR\n"); pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexdata); pthread_mutex_lock(&mutexevent); headerdef = (headerdef_t*)request->buf; if (verbose>1) print_headerdef(headerdef); /* delete the old header, data and events */ free_header(); free_data(); free_event(); /* store the header and re-initialize */ header = (header_t*)malloc(sizeof(header_t)); DIE_BAD_MALLOC(header); header->def = (headerdef_t*)malloc(sizeof(headerdef_t)); DIE_BAD_MALLOC(header->def); header->buf = malloc(headerdef->bufsize); DIE_BAD_MALLOC(header->buf); memcpy(header->def, request->buf, sizeof(headerdef_t)); memcpy(header->buf, (char*)request->buf+sizeof(headerdef_t), headerdef->bufsize); header->def->nsamples = 0; header->def->nevents = 0; init_data(); init_event(); response->def->version = VERSION; response->def->bufsize = 0; /* check whether memory could indeed be allocated */ if (data!= NULL && data->buf != NULL && data->def != NULL) { response->def->command = PUT_OK; } else { /* let's at least tell the client that something's wrong */ response->def->command = PUT_ERR; } pthread_mutex_unlock(&mutexevent); pthread_mutex_unlock(&mutexdata); pthread_mutex_unlock(&mutexheader); break; case PUT_DAT: if (verbose>1) fprintf(stderr, "dmarequest: PUT_DAT\n"); pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexdata); datadef = (datadef_t*)request->buf; if (verbose>1) print_datadef(datadef); if (verbose>2) print_buf(request->buf, request->def->bufsize); response->def->version = VERSION; response->def->bufsize = 0; if (request->def->bufsize < sizeof(datadef_t)) response->def->command = PUT_ERR; else if (header==NULL || data==NULL) response->def->command = PUT_ERR; else if (header->def->nchans != datadef->nchans) response->def->command = PUT_ERR; else if (header->def->data_type != datadef->data_type) response->def->command = PUT_ERR; else if (datadef->nsamples > current_max_num_sample) response->def->command = PUT_ERR; else { unsigned int i; unsigned int wordsize = wordsize_from_type(header->def->data_type); unsigned int datasize = wordsize * datadef->nsamples * datadef->nchans; response->def->command = PUT_OK; if (wordsize == 0) { fprintf(stderr, "dmarequest: unsupported data type (%d)\n", datadef->data_type); response->def->command = PUT_ERR; } else if (datasize > datadef->bufsize || (datadef->bufsize + sizeof(datadef_t)) > request->def->bufsize) { fprintf(stderr, "dmarequest: invalid size definitions in PUT_DAT request\n"); response->def->command = PUT_ERR; } else { /* record the time at which the data was received */ if (clock_gettime(CLOCK_REALTIME, &putdat_clock) != 0) { perror("clock_gettime"); return -1; } /* number of bytes per sample (all channels) is given by wordsize x number of channels */ unsigned int chansize = wordsize * data->def->nchans; /* request_data points to actual data samples within the request, use char* for convenience */ const char *request_data = (const char *) request->buf + sizeof(datadef_t); char *buffer_data = (char *)data->buf; for (i=0; i<datadef->nsamples; i++) { memcpy(buffer_data+(thissample*chansize), request_data+(i*chansize), chansize); header->def->nsamples++; thissample++; thissample = WRAP(thissample, current_max_num_sample); } /* Signal possibly waiting threads that we have received data */ pthread_cond_broadcast(&getData_cond); } } pthread_mutex_unlock(&mutexdata); pthread_mutex_unlock(&mutexheader); break; case PUT_EVT: if (verbose>1) fprintf(stderr, "dmarequest: PUT_EVT\n"); pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexevent); /* record the time at which the event was received */ if (clock_gettime(CLOCK_REALTIME, &putevt_clock) != 0) { perror("clock_gettime"); return -1; } /* Give an error message if there is no header, or if the given event array is defined badly */ if (header==NULL || event==NULL || check_event_array(request->def->bufsize, request->buf) < 0) { response->def->version = VERSION; response->def->command = PUT_ERR; response->def->bufsize = 0; } else { /* go over all events and store them one by one */ response->def->version = VERSION; response->def->command = PUT_OK; response->def->bufsize = 0; offset = 0; /* this represents the offset of the event in the buffer */ while (offset<request->def->bufsize) { FREE(event[thisevent].def); FREE(event[thisevent].buf); eventdef = (eventdef_t*)((char*)request->buf+offset); if (verbose>1) print_eventdef(eventdef); event[thisevent].def = (eventdef_t*)malloc(sizeof(eventdef_t)); DIE_BAD_MALLOC(event[thisevent].def); memcpy(event[thisevent].def, (char*)request->buf+offset, sizeof(eventdef_t)); if (event[thisevent].def->sample == EVENT_AUTO_SAMPLE) { /* automatically convert event->def->sample to current sample number */ /* make some fine adjustment of the assigned sample number */ double adjust = (putevt_clock.tv_sec - putdat_clock.tv_sec) + (double)(putevt_clock.tv_nsec - putdat_clock.tv_nsec) / 1000000000L; event[thisevent].def->sample = header->def->nsamples + (int)(header->def->fsample*adjust); } offset += sizeof(eventdef_t); event[thisevent].buf = malloc(eventdef->bufsize); DIE_BAD_MALLOC(event[thisevent].buf); memcpy(event[thisevent].buf, (char*)request->buf+offset, eventdef->bufsize); offset += eventdef->bufsize; if (verbose>1) print_eventdef(event[thisevent].def); thisevent++; thisevent = WRAP(thisevent, MAXNUMEVENT); header->def->nevents++; } } pthread_mutex_unlock(&mutexevent); pthread_mutex_unlock(&mutexheader); break; case GET_HDR: if (verbose>1) fprintf(stderr, "dmarequest: GET_HDR\n"); if (header==NULL) { response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; break; } pthread_mutex_lock(&mutexheader); response->def->version = VERSION; response->def->command = GET_OK; response->def->bufsize = 0; response->def->bufsize = append(&response->buf, response->def->bufsize, header->def, sizeof(headerdef_t)); response->def->bufsize = append(&response->buf, response->def->bufsize, header->buf, header->def->bufsize); pthread_mutex_unlock(&mutexheader); break; case GET_DAT: if (verbose>1) fprintf(stderr, "dmarequest: GET_DAT\n"); if (header==NULL || data==NULL) { response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; break; } pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexdata); if (request->def->bufsize) { /* the selection has been specified */ memcpy(&datasel, request->buf, sizeof(datasel_t)); /* If endsample is -1 read the buffer to the end */ if(datasel.endsample == -1) { datasel.endsample = header->def->nsamples - 1; } } else { /* determine a valid selection */ if (header->def->nsamples>current_max_num_sample) { /* the ringbuffer is completely full */ datasel.begsample = header->def->nsamples - current_max_num_sample; datasel.endsample = header->def->nsamples - 1; } else { /* the ringbuffer is not yet completely full */ datasel.begsample = 0; datasel.endsample = header->def->nsamples - 1; } } /* // if the read should block... if(blockrequest == 1) { // check whether data is available while((datasel.begsample >= (datasel.endsample+1)) || (datasel.endsample > header->def->nsamples - 1)) { // if not unlock all mutexes pthread_mutex_unlock(&mutexdata); pthread_mutex_unlock(&mutexheader); // wait for the condition to be signaled pthread_mutex_lock(&getData_mutex); gettimeofday(&tp, NULL); ts.tv_sec = tp.tv_sec; ts.tv_nsec = tp.tv_usec * 1000; ts.tv_sec += 1; pthread_cond_timedwait(&getData_cond, &getData_mutex, &ts); pthread_mutex_unlock(&getData_mutex); // Lock the mutexes again pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexdata); if(datasel.begsample == (datasel.endsample+1)) datasel.endsample = header->def->nsamples - 1; } } */ if (verbose>1) print_headerdef(header->def); if (verbose>1) print_datasel(&datasel); if (datasel.begsample < 0 || datasel.endsample < 0) { fprintf(stderr, "dmarequest: err1\n"); response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; } else if (datasel.begsample >= header->def->nsamples || datasel.endsample >= header->def->nsamples) { fprintf(stderr, "dmarequest: err2\n"); response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; } else if ((header->def->nsamples - datasel.begsample) > current_max_num_sample) { fprintf(stderr, "dmarequest: err3\n"); response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; } else { unsigned int wordsize = wordsize_from_type(data->def->data_type); if (wordsize==0) { fprintf(stderr, "dmarequest: unsupported data type (%d)\n", data->def->data_type); response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; } else { unsigned int n; response->def->version = VERSION; response->def->command = GET_OK; response->def->bufsize = 0; /* determine the number of samples to return */ n = datasel.endsample - datasel.begsample + 1; response->buf = malloc(sizeof(datadef_t) + n*data->def->nchans*wordsize); if (response->buf == NULL) { /* not enough space for copying data into response */ fprintf(stderr, "dmarequest: out of memory\n"); response->def->command = GET_ERR; } else { /* number of bytes per sample (all channels) */ unsigned int chansize = data->def->nchans * wordsize; /* convenience pointer to start of actual data in response */ char *resp_data = ((char *) response->buf) + sizeof(datadef_t); /* this is the location of begsample within the ringbuffer */ unsigned int start_index = WRAP(datasel.begsample, current_max_num_sample); /* have datadef point into the freshly allocated response buffer and directly fill in the information */ datadef = (datadef_t *) response->buf; datadef->nchans = data->def->nchans; datadef->data_type = data->def->data_type; datadef->nsamples = n; datadef->bufsize = n*chansize; response->def->bufsize = sizeof(datadef_t) + datadef->bufsize; if (start_index + n <= current_max_num_sample) { /* we can copy everything in one go */ memcpy(resp_data, (char*)(data->buf) + start_index*chansize, n*chansize); } else { /* need to wrap around at current_max_num_sample */ unsigned int na = current_max_num_sample - start_index; unsigned int nb = n - na; memcpy(resp_data, (char*)(data->buf) + start_index*chansize, na*chansize); memcpy(resp_data + na*chansize, (char*)(data->buf), nb*chansize); /* printf("Wrapped around!\n"); */ } } } } pthread_mutex_unlock(&mutexdata); pthread_mutex_unlock(&mutexheader); break; case GET_EVT: if (verbose>1) fprintf(stderr, "dmarequest: GET_EVT\n"); if (header==NULL || event==NULL || header->def->nevents==0) { response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; break; } pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexevent); eventsel = (eventsel_t*)malloc(sizeof(eventsel_t)); DIE_BAD_MALLOC(eventsel); /* determine the selection */ if (request->def->bufsize) { /* the selection has been specified */ memcpy(eventsel, request->buf, sizeof(eventsel_t)); } else { /* determine a valid selection */ if (header->def->nevents>MAXNUMEVENT) { /* the ringbuffer is completely full */ eventsel->begevent = header->def->nevents - MAXNUMEVENT; eventsel->endevent = header->def->nevents - 1; } else { /* the ringbuffer is not yet completely full */ eventsel->begevent = 0; eventsel->endevent = header->def->nevents - 1; } } if (verbose>1) print_headerdef(header->def); if (verbose>1) print_eventsel(eventsel); if (eventsel==NULL) { response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; } else if (eventsel->begevent < 0 || eventsel->endevent < 0) { fprintf(stderr, "dmarequest: err1\n"); response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; } else if (eventsel->begevent >= header->def->nevents || eventsel->endevent >= header->def->nevents) { fprintf(stderr, "dmarequest: err2\n"); response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; } else if ((header->def->nevents-eventsel->begevent) > MAXNUMEVENT) { fprintf(stderr, "dmarequest: err3\n"); response->def->version = VERSION; response->def->command = GET_ERR; response->def->bufsize = 0; } else { unsigned int j,n; response->def->version = VERSION; response->def->command = GET_OK; response->def->bufsize = 0; /* determine the number of events to return */ n = eventsel->endevent - eventsel->begevent + 1; for (j=0; j<n; j++) { if (verbose>1) print_eventdef(event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def); response->def->bufsize = append(&response->buf, response->def->bufsize, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def, sizeof(eventdef_t)); response->def->bufsize = append(&response->buf, response->def->bufsize, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].buf, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def->bufsize); } } FREE(eventsel); pthread_mutex_unlock(&mutexevent); pthread_mutex_unlock(&mutexheader); break; case FLUSH_HDR: pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexdata); pthread_mutex_lock(&mutexevent); if (header) { free_header(); free_data(); free_event(); response->def->version = VERSION; response->def->command = FLUSH_OK; response->def->bufsize = 0; } else { response->def->version = VERSION; response->def->command = FLUSH_ERR; response->def->bufsize = 0; } pthread_mutex_unlock(&mutexevent); pthread_mutex_unlock(&mutexdata); pthread_mutex_unlock(&mutexheader); break; case FLUSH_DAT: pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexdata); if (header && data) { header->def->nsamples = thissample = 0; response->def->version = VERSION; response->def->command = FLUSH_OK; response->def->bufsize = 0; } else { response->def->version = VERSION; response->def->command = FLUSH_ERR; response->def->bufsize = 0; } pthread_mutex_unlock(&mutexdata); pthread_mutex_unlock(&mutexheader); break; case FLUSH_EVT: pthread_mutex_lock(&mutexheader); pthread_mutex_lock(&mutexevent); if (header && event) { unsigned int i; header->def->nevents = thisevent = 0; for (i=0; i<MAXNUMEVENT; i++) { FREE(event[i].def); FREE(event[i].buf); } response->def->version = VERSION; response->def->command = FLUSH_OK; response->def->bufsize = 0; } else { response->def->version = VERSION; response->def->command = FLUSH_ERR; response->def->bufsize = 0; } pthread_mutex_unlock(&mutexevent); pthread_mutex_unlock(&mutexheader); break; case WAIT_DAT: /* SK: This request means that the client wants to wait until MORE than waitdef_t.threshold.nsamples samples OR MORE THAN waitdef_t.threshold.nevents events are in the buffer, BUT only for the time given in waitdef_t.milliseconds. The response is just the number of samples and events in the buffer as described by samples_events_t. */ response->def->version = VERSION; if (header==NULL || request->def->bufsize!=sizeof(waitdef_t)) { response->def->command = WAIT_ERR; response->def->bufsize = 0; } else { int waiterr; waitdef_t *wd = (waitdef_t *) request->buf; samples_events_t *nret = malloc(sizeof(samples_events_t)); UINT32_T nsmp, nevt; if (nret == NULL) { /* highly unlikely, but we cannot allocate a sample_event_t - return an error */ response->def->command = WAIT_ERR; response->def->bufsize = 0; break; } /* Let response->buf point to the new sample_event_t structure */ response->def->command = WAIT_OK; response->def->bufsize = sizeof(samples_events_t); response->buf = nret; /* get current number of samples */ pthread_mutex_lock(&mutexheader); nsmp = header->def->nsamples; nevt = header->def->nevents; pthread_mutex_unlock(&mutexheader); if (wd->milliseconds == 0 || nsmp > wd->threshold.nsamples || nevt > wd->threshold.nevents) { /* the client doesn't want to wait, or we're already above the threshold: return immediately */ nret->nsamples = nsmp; nret->nevents = nevt; break; } gettimeofday(&tp, NULL); ts.tv_sec = tp.tv_sec + (wd->milliseconds/1000); ts.tv_nsec = 1000 * (tp.tv_usec + (wd->milliseconds % 1000)*1000); while (ts.tv_nsec >= 1000000000) { ts.tv_sec++; ts.tv_nsec-=1000000000; } /* FIXME: The getData condition variable is only triggered by incoming data, not events */ do { pthread_mutex_lock(&getData_mutex); waiterr = pthread_cond_timedwait(&getData_cond, &getData_mutex, &ts); pthread_mutex_unlock(&getData_mutex); /* get current number of samples */ pthread_mutex_lock(&mutexheader); nsmp = header->def->nsamples; nevt = header->def->nevents; pthread_mutex_unlock(&mutexheader); } while (nsmp <= wd->threshold.nsamples && nevt <= wd->threshold.nevents && waiterr==0); nret->nsamples = nsmp; nret->nevents = nevt; } break; default: fprintf(stderr, "dmarequest: unknown command\n"); } if (verbose>0) fprintf(stderr, "dmarequest: thissample = %u, thisevent = %u\n", thissample, thisevent); /* everything went fine */ return 0; }
int buffer_getevt(int server, mxArray *plhs[], const mxArray *prhs[]) { int verbose = 0; int i, nevents; int offset; double *val; int result; mxArray *bufptr; message_t *request = NULL; message_t *response = NULL; eventsel_t eventsel; /* this is for the Matlab specific output */ const char *field_names[] = { "type", "value", "sample", "offset", "duration" }; /* allocate the elements that will be used in the communication */ request = malloc(sizeof(message_t)); request->def = malloc(sizeof(messagedef_t)); request->buf = NULL; request->def->version = VERSION; request->def->command = GET_EVT; request->def->bufsize = 0; if ((prhs[0]!=NULL) && (mxGetNumberOfElements(prhs[0])==2) && (mxIsDouble(prhs[0])) && (!mxIsComplex(prhs[0]))) { /* fprintf(stderr, "args OK\n"); */ val = (double *)mxGetData(prhs[0]); eventsel.begevent = (UINT32_T)(val[0]); eventsel.endevent = (UINT32_T)(val[1]); if (verbose) print_eventsel(&eventsel); request->def->bufsize = append(&request->buf, request->def->bufsize, &eventsel, sizeof(eventsel_t)); } if (verbose) print_request(request->def); result = clientrequest(server, request, &response); if (verbose) print_response(response->def); if (result == 0) { if (response->def->command==GET_OK) { eventdef_t *event_def; /* first count the number of events */ nevents = 0; offset = 0; while (offset<response->def->bufsize) { event_def = (eventdef_t *)((char *)response->buf + offset); /* event_buf = (char *)response->buf + offset + sizeof(eventdef_t); */ if (verbose) print_eventdef(event_def); offset += sizeof(eventdef_t) + event_def->bufsize; nevents++; } /* create a structure array that can hold all events */ plhs[0] = mxCreateStructMatrix(1, nevents, NUMBER_OF_FIELDS, field_names); offset = 0; for (i=0; i<nevents; i++) { char *buf_type,*buf_value; event_def = (eventdef_t *) ((char *)response->buf + offset); buf_type = (char *) response->buf + offset + sizeof(eventdef_t); buf_value = buf_type + event_def->type_numel * wordsize_from_type(event_def->type_type); mxSetFieldByNumber(plhs[0], i, 0, matrix_from_ft_type_data(event_def->type_type, 1, event_def->type_numel, buf_type)); mxSetFieldByNumber(plhs[0], i, 1, matrix_from_ft_type_data(event_def->value_type, 1, event_def->value_numel, buf_value)); mxSetFieldByNumber(plhs[0], i, 2, mxCreateDoubleScalar((double)event_def->sample+1)); /* 1-based in Matlab, 0-based in protocol */ mxSetFieldByNumber(plhs[0], i, 3, mxCreateDoubleScalar((double)event_def->offset)); mxSetFieldByNumber(plhs[0], i, 4, mxCreateDoubleScalar((double)event_def->duration)); offset += sizeof(eventdef_t) + event_def->bufsize; } } else { result = response->def->command; } } if (request) { FREE(request->def); FREE(request->buf); FREE(request); } if (response) { FREE(response->def); FREE(response->buf); FREE(response); } return result; }