void ReportCallback(void *target,IOReturn result,void *refcon,void *sender,psych_uint32 bufferSize)
{
	int deviceIndex,i,n,m;
	unsigned char *ptr;
	ReportStruct *r;
	
	CountReports("ReportCallback beginning.");
	
	deviceIndex=(int)refcon;
	if(deviceIndex<0 | deviceIndex>MAXDEVICEINDEXS-1){
		printf("ReportCallback received out-of-range deviceIndex %d. Substituting zero.\n",deviceIndex);
		deviceIndex=0;
	}
	
	// take report from free list.
	if(freeReportsPtr==NULL){
		// Darn. We're full. It might be elegant to discard oldest report, but for now, we'll just ignore the new one.
		printf("ReportCallback warning. No more free reports. Discarding new report.\n");
		return;
	}
	r=freeReportsPtr;
	freeReportsPtr=r->next;
	r->next=NULL;
	
	// install report into the device's list.
	r->next=deviceReportsPtr[deviceIndex];
	deviceReportsPtr[deviceIndex]=r;
	
	// fill in the rest of the report struct
	r->error=result;
	r->bytes=bufferSize;
	r->deviceIndex=deviceIndex;
	ptr=target;
	for(i=0;i<bufferSize && i<MAXREPORTSIZE;i++)r->report[i]=*(ptr+i);
	PsychGetPrecisionTimerSeconds(&r->time);
	if(optionsPrintReportSummary){
		// print diagnostic summary of the report
		int serial;
		
		serial=r->report[62]+256*r->report[63]; // 32-bit serial number at end of AInScan report from PMD-1208FS
		printf("Got input report %4d: %2ld bytes, dev. %d, %4.0f ms. ",serial,(long)r->bytes,deviceIndex,1000*(r->time-AInScanStart));
		if(r->bytes>0){
			printf(" report ");
			n=r->bytes;
			if(n>6)n=6;
			for(i=0;i<n;i++)printf("%3d ",(int)r->report[i]);
			m=r->bytes-2;
			if(m>i){
				printf("... ");
				i=m;
			}
			for(;i<r->bytes;i++)printf("%3d ",(int)r->report[i]);
		}
		printf("\n");
	}
	CountReports("ReportCallback end.");
	return;
}
// GiveMeReports is called solely by PsychHIDGiveMeReports, but the code resides here
// in PsychHIDReceiveReports because it uses the typedefs and static variables that
// are defined solely in this file. The linked lists of reports are unknown outside of this file.
PsychError GiveMeReports(int deviceIndex,int reportBytes)
{
	mwSize dims[]={1,1};
	mxArray **outReports;
	ReportStruct *r,*rTail;
	const char *fieldNames[]={"report", "device", "time"};
	mxArray *fieldValue;
	unsigned char *reportBuffer;
	int i,n;
    unsigned int j;
	long error=0;
	double now;
	
	CountReports("GiveMeReports beginning.");

	outReports=PsychGetOutArgMxPtr(1); 
	r=deviceReportsPtr[deviceIndex];
	n=0;
	while(r!=NULL){
		n++;
		rTail=r;
		r=r->next;
	}
	*outReports=mxCreateStructMatrix(1,n,3,fieldNames);
	r=deviceReportsPtr[deviceIndex];
	PsychGetPrecisionTimerSeconds(&now);
	for(i=n-1;i>=0;i--){
		// assert(r!=NULL);
		if(r->error)error=r->error;
		dims[0]=1;
		//printf("%2d: r->bytes %2d, reportBytes %4d, -%4.1f s\n",i,(int)r->bytes,(int)reportBytes, now-r->time);
		if(r->bytes> (unsigned int) reportBytes)r->bytes=reportBytes;
		dims[1]=r->bytes;
		fieldValue=mxCreateNumericArray(2,(void *)dims,mxUINT8_CLASS,mxREAL);
		reportBuffer=(void *)mxGetData(fieldValue);
		for(j=0;j<r->bytes;j++)reportBuffer[j]=r->report[j];
		if(fieldValue==NULL)PrintfExit("Couldn't allocate report array.");
		mxSetField(*outReports,i,"report",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		*mxGetPr(fieldValue)=(double)r->deviceIndex;
		mxSetField(*outReports,i,"device",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		*mxGetPr(fieldValue)=r->time;
		mxSetField(*outReports,i,"time",fieldValue);
		r=r->next;
	}
	if(deviceReportsPtr[deviceIndex]!=NULL){
		// transfer all these now-obsolete reports to the free list
		rTail->next=freeReportsPtr;
		freeReportsPtr=deviceReportsPtr[deviceIndex];
		deviceReportsPtr[deviceIndex]=NULL;
	}
	CountReports("GiveMeReports end.");
	return error;
}
// Called solely by PsychHIDGetReport, but resides here in order to access the linked list of reports.
PsychError GiveMeReport(int deviceIndex,psych_bool *reportAvailablePtr,unsigned char *reportBuffer,psych_uint32 *reportBytesPtr,double *reportTimePtr)
{
	ReportStruct *r,*rOld;
	long error;
	unsigned int i;
	
	CountReports("GiveMeReport beginning.");

	r=deviceReportsPtr[deviceIndex];
	if(r!=NULL){ // report available?
				 // grab the oldest report for this device
		*reportAvailablePtr=1;
		if(r->next==NULL){
			deviceReportsPtr[deviceIndex]=NULL;
		}else{
			while(r->next->next!=NULL)r=r->next;
			rOld=r;
			r=r->next;
			rOld->next=NULL;
		}
		if(*reportBytesPtr > r->bytes)*reportBytesPtr=r->bytes;
		for(i=0;i<*reportBytesPtr;i++)reportBuffer[i]=r->report[i];
		*reportTimePtr=r->time;
		error=r->error;
		
		// add it to the free list
		r->next=freeReportsPtr;
		freeReportsPtr=r;
	}else{
		*reportAvailablePtr=0;
		*reportBytesPtr=0;
		*reportTimePtr=0.0;
		error=0;
	}
	CountReports("GiveMeReport end.");
	return error;
}				  
/* Do all the report processing: Iterates in a fetch loop
 * until either no more reports pending for processing, error condition,
 * or a maximum allowable processing time of optionSecs seconds has been
 * exceeded.
 *
 * Calls hidlib function hid_read() to get reports, one at a time, enqueues
 * it in our own reports lists for later retrieval by 'GiveMeReports' or
 * 'GiveMeReport'.
 *
 */
PsychError ReceiveReports(int deviceIndex)
{
    double deadline, now;

    pRecDevice device;
    int n, m;
    unsigned int i;
    ReportStruct *r;
    long error = 0;

    CountReports("ReceiveReports beginning.");
    if(freeReportsPtr==NULL) PrintfExit("No free reports.");

    if(deviceIndex < 0 || deviceIndex > MAXDEVICEINDEXS-1) PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex, (int) MAXDEVICEINDEXS-1);

    PsychHIDVerifyInit();
    device = PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
    last_hid_device = (hid_device*) device->interface;
    
    PsychGetAdjustedPrecisionTimerSeconds(&deadline);
    deadline += optionsSecs;
    
    // Iterate until deadline reached or no more pending reports to process:
    while (TRUE) {
        // Test for timeout:
        PsychGetAdjustedPrecisionTimerSeconds(&now);
        if (now >= deadline) break;
                
        CountReports("ReportCallback beginning.");
        
        // take report from free list.
        if(freeReportsPtr == NULL){
            // Darn. We're full. It might be elegant to discard oldest report, but for now, we'll just ignore the new one.
            printf("PsychHID: WARNING! ReportCallback warning. No more free reports. Discarding new report.\n");
            break;
        }
        
        r = freeReportsPtr;
        freeReportsPtr = r->next;
        r->next=NULL;
        
        // install report into the device's list.
        r->next = deviceReportsPtr[deviceIndex];
        deviceReportsPtr[deviceIndex] = r;
        
        // fill in the rest of the report struct
        r->deviceIndex = deviceIndex;

        // Fetch the actual data: Bytes fetched, or zero for no reports available, or
        // -1 for error condition.
        r->error = hid_read((hid_device*) device->interface, &(r->report[0]), MAXREPORTSIZE);
        if (r->error >= 0) {
            // Success: Reset error, assign size of retrieved report:
            r->bytes = r->error;
            r->error = 0;
        }
        else {
            // Error: No data assigned.
            r->bytes = 0;
            
            // Signal error return code -1:
            error = -1;
            
            // Abort fetch loop:
            break;
        }
        
        // Timestamp processing:
        PsychGetPrecisionTimerSeconds(&r->time);

        if (optionsPrintReportSummary) {
            // print diagnostic summary of the report
            int serial;
            
            serial = r->report[62] + 256 * r->report[63]; // 32-bit serial number at end of AInScan report from PMD-1208FS
            printf("Got input report %4d: %2ld bytes, dev. %d, %4.0f ms. ", serial, (long) r->bytes, deviceIndex, 1000 * (r->time - AInScanStart));
            if(r->bytes>0) {
                printf(" report ");
                n = r->bytes;
                if (n > 6) n=6;
                for(i=0; i < (unsigned int) n; i++) printf("%3d ", (int) r->report[i]);
                m = r->bytes - 2;
                if (m > (int) i) {
                    printf("... ");
                    i = m;
                }
                for(; i < r->bytes; i++) printf("%3d ", (int) r->report[i]);
            }
            printf("\n");
        }
        CountReports("ReportCallback end.");
        
        // If no data has been returned then we're done for now:
        if (r->bytes == 0) break;
    }
    
	CountReports("ReceiveReports end.");
	return error;
}
PsychError ReceiveReports(int deviceIndex)
{
	long error=0;
	pRecDevice device;
	IOHIDDeviceInterface122** interface=NULL;
	int reason; // kCFRunLoopRunFinished, kCFRunLoopRunStopped, kCFRunLoopRunTimedOut, kCFRunLoopRunHandledSource

	CountReports("ReceiveReports beginning.");
	if(freeReportsPtr==NULL)PrintfExit("No free reports.");

	PsychHIDVerifyInit();
	device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(!HIDIsValidDevice(device))PrintfExit("PsychHID: Invalid device.\n");
	interface=device->interface;
	if(interface==NULL)PrintfExit("PsychHID: No interface for device.\n");
	if(deviceIndex<0 || deviceIndex>MAXDEVICEINDEXS-1)PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex,(int)MAXDEVICEINDEXS-1);
	CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__);
	if(!ready[deviceIndex]){
		// setInterruptReportHandlerCallback
		static unsigned char buffer[MAXREPORTSIZE];
		psych_uint32 bufferSize=MAXREPORTSIZE;
		psych_bool createSource;

		createSource=(source[deviceIndex]==NULL);
		if(createSource){
			if(optionsPrintCrashers && createSource)printf("%d: createAsyncEventSource\n",deviceIndex);
			error=(*interface)->createAsyncEventSource(interface,&(source[deviceIndex]));
			if(error)PrintfExit("ReceiveReports - createAsyncEventSource error 0x%lx.",error);
			if(0 && optionsPrintCrashers && createSource)
				printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex]
					   ,CFRunLoopSourceIsValid(source[deviceIndex])
					   ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode));
		}
		if(optionsPrintCrashers && createSource)printf("%d: getAsyncEventSource\n",deviceIndex);
		CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__);
		if(optionsPrintCrashers && createSource)printf("%d: CFRunLoopAddSource\n",deviceIndex);
		CFRunLoopAddSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode);
		if(0 && optionsPrintCrashers && createSource)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex]
			   ,CFRunLoopSourceIsValid(source[deviceIndex])
			   ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode));
		ready[deviceIndex]=1;
		CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__);
		if(optionsPrintCrashers && createSource)printf("%d: setInterruptReportHandlerCallback\n",deviceIndex);
		error=(*interface)->setInterruptReportHandlerCallback(interface,buffer,bufferSize,ReportCallback,buffer,(void *)deviceIndex);
		if(error)PrintfExit("ReceiveReports - setInterruptReportHandlerCallback error 0x%lx.",error);
		if(optionsPrintCrashers && createSource)printf("%d: CFRunLoopRunInMode.\n",deviceIndex);
	}
	//printf("%d: CFRunLoopRunInMode\n",deviceIndex);
	reason=CFRunLoopRunInMode(myRunLoopMode,optionsSecs,false);
	if(reason!=kCFRunLoopRunTimedOut && reason!=kCFRunLoopRunHandledSource){
		char *s;
		switch(reason){
			case kCFRunLoopRunFinished: s="kCFRunLoopRunFinished"; break;
			case kCFRunLoopRunStopped: s="kCFRunLoopRunStopped"; break;
			case kCFRunLoopRunTimedOut: s="kCFRunLoopRunTimedOut"; break;
			case kCFRunLoopRunHandledSource: s="kCFRunLoopRunHandledSource"; break;
			default: s="of unknown reason.";
		}
		printf("RunLoop ended at %.3f s because %s.\n",CFAbsoluteTimeGetCurrent()-AInScanStart,s);			
	}
	CountReports("ReceiveReports end.");
	return error;
}
/* Do all the report processing for all devices: Iterates in a fetch loop
 * until error condition, or a maximum allowable processing time of
 * optionSecs seconds has been exceeded.
 *
 * Calls hidlib function hid_read() to get reports, one at a time, enqueues
 * it in our own reports lists for later retrieval by 'GiveMeReports' or
 * 'GiveMeReport'.
 *
 */
PsychError ReceiveReports(int deviceIndex)
{
    int rateLimit[MAXDEVICEINDEXS] = { 0 };
    double deadline, now;
    pRecDevice device;
    int n, m;
    unsigned int i;
    ReportStruct *r;
    long error = 0;

    PsychHIDVerifyInit();

    if(deviceIndex < 0 || deviceIndex >= MAXDEVICEINDEXS) PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex, (int) MAXDEVICEINDEXS-1);

    // Allocate report buffers if needed:
    PsychHIDAllocateReports(deviceIndex);

    CountReports("ReceiveReports beginning.");
    if (freeReportsPtr[deviceIndex] == NULL) PrintfExit("No free reports.");

    // Enable this device for hid report reception:
    ready[deviceIndex] = TRUE;

    PsychGetAdjustedPrecisionTimerSeconds(&now);
    deadline = now + optionsSecs;
    
    // Iterate until deadline reached or no more pending reports to process:
    while ((error == 0) && (now <= deadline)) {
        // Iterate over all active devices:
        for (deviceIndex = 0; deviceIndex < MAXDEVICEINDEXS; deviceIndex++) {            
            // Test for timeout:
            PsychGetAdjustedPrecisionTimerSeconds(&now);
            if (now > deadline) break;

            // Skip this device if it isn't enabled to receive hid reports:
            if (!ready[deviceIndex]) continue;
            
            // Free target report buffers?
            if(freeReportsPtr[deviceIndex] == NULL) {
                // Darn. We're full. It might be elegant to discard oldest report, but for now, we'll just ignore the new one.
                if (!rateLimit[deviceIndex]) printf("PsychHID: WARNING! ReportCallback warning. No more free reports for deviceIndex %i. Discarding new report.\n", deviceIndex);
                rateLimit[deviceIndex] = 1;
                continue;
            }
            
            // Handle one report for this device:
            CountReports("ReportCallback beginning.");

            device = PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
            last_hid_device = (hid_device*) device->interface;

            // Get a report struct to fill in:
            r = freeReportsPtr[deviceIndex];

            // Fetch the actual data: Bytes fetched, or zero for no reports available, or
            // -1 for error condition.
            r->error = hid_read((hid_device*) device->interface, &(r->report[0]), MaxDeviceReportSize[deviceIndex]);

            // Skip remainder if no data received:
            if (r->error == 0) continue;

            // Ok, we got something, even if it is only an error code. Need
            // to move the (r)eport from the free list to the received list:
            freeReportsPtr[deviceIndex] = r->next;
            r->next=NULL;
            
            // install report into the device's list.
            r->next = deviceReportsPtr[deviceIndex];
            deviceReportsPtr[deviceIndex] = r;
            
            // fill in the rest of the report struct
            r->deviceIndex = deviceIndex;
            
            // Timestamp processing:
            PsychGetPrecisionTimerSeconds(&r->time);

            // Success or error?
            if (r->error > 0) {
                // Success: Reset error, assign size of retrieved report:
                r->bytes = r->error;
                r->error = 0;
            }
            else {
                // Error: No data assigned.
                r->bytes = 0;
                
                // Signal error return code -1:
                error = -1;
                
                // Abort fetch loop:
                break;
            }

            if (optionsPrintReportSummary) {
                // print diagnostic summary of the report
                int serial;
                
                serial = r->report[62] + 256 * r->report[63]; // 32-bit serial number at end of AInScan report from PMD-1208FS
                printf("Got input report %4d: %2ld bytes, dev. %d, %4.0f ms. ", serial, (long) r->bytes, deviceIndex, 1000 * (r->time - AInScanStart));
                if(r->bytes>0) {
                    printf(" report ");
                    n = r->bytes;
                    if (n > 6) n=6;
                    for(i=0; i < (unsigned int) n; i++) printf("%3d ", (int) r->report[i]);
                    m = r->bytes - 2;
                    if (m > (int) i) {
                        printf("... ");
                        i = m;
                    }
                    for(; i < r->bytes; i++) printf("%3d ", (int) r->report[i]);
                }
                printf("\n");
            }
            CountReports("ReportCallback end.");
        }
    }
    
    CountReports("ReceiveReports end.");
    return error;
}