PsychError PSYCHHIDReceiveReportsStop(void) 
{
	long error=0;
	int deviceIndex;
	mxArray **outErr;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(1));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	
	error=ReceiveReportsStop(deviceIndex);

	outErr=PsychGetOutArgMxPtr(1);
	if(outErr!=NULL){
		char *name="",*description="";
		const char *fieldNames[]={"n", "name", "description"};
		mxArray *fieldValue;

		PsychHIDErrors(NULL, error,&name,&description); // Get error name and description, if available.
		*outErr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
	}
    return(PsychError_none);	
}
PsychError PSYCHHIDGiveMeReports(void)
{
    PsychGenericScriptType *outErr;
    const char *fieldNames[] = {"n", "name", "description"};

    char *name = "", *description = "";
    long error = 0;
    int deviceIndex;
    int reportBytes = 1024;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if (PsychIsGiveHelp()) { PsychGiveHelp(); return(PsychError_none); };

    PsychErrorExit(PsychCapNumOutputArgs(2));
    PsychErrorExit(PsychCapNumInputArgs(2));

    PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
    PsychCopyInIntegerArg(2,false,&reportBytes);

    PsychHIDVerifyInit();

    // Returns 1st return argument 'reports':
    error = GiveMeReports(deviceIndex,reportBytes); // PsychHIDReceiveReports.c

    // Return 2nd return argument 'err' struct:
    PsychHIDErrors(NULL, error, &name, &description);
    PsychAllocOutStructArray(2, kPsychArgOptional, -1, 3, fieldNames, &outErr);
    PsychSetStructArrayStringElement("name", 0, name, outErr);
    PsychSetStructArrayStringElement("description", 0, description, outErr);
    PsychSetStructArrayDoubleElement("n", 0, (double) error, outErr);

    return(PsychError_none);
}
PsychError PSYCHHIDGiveMeReports(void) 
{
	long error=0;
	int deviceIndex;
	int reportBytes=1024;
	mxArray **outErr;
	pRecDevice device;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(2));
    PsychErrorExit(PsychCapNumInputArgs(2));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	PsychCopyInIntegerArg(2,false,&reportBytes);

	PsychHIDVerifyInit();
    device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(!HIDIsValidDevice(device))PrintfExit("PsychHID:GiveMeReports: Invalid device.\n");

	// reports
	error=GiveMeReports(deviceIndex,reportBytes); // PsychHIDReceiveReports.c
	
	// err
	outErr=PsychGetOutArgMxPtr(2); // outErr==NULL if optional argument is absent.
	if(outErr!=NULL){
		const char *fieldNames[]={"n", "name", "description"};
		mxArray *fieldValue;
		char *name="",*description="";

		PsychHIDErrors(error,&name,&description); // Get error name and description, if available.
		*outErr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
	}
    return(PsychError_none);	
}
PsychError PSYCHHIDReceiveReports(void)
{
	long error=0;
	int deviceIndex;
	mxArray **mxErrPtr;
	const mxArray *mxOptions,*mx;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(2));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	/*
	 "\"options.print\" =1 (default 0) enables diagnostic printing of a summary of each report when our callback routine receives it. "	
	 "\"options.printCrashers\" =1 (default 0) enables diagnostic printing of the creation of the callback source and its addition to the CFRunLoop. "
	 "\"options.maxReports\" (default 10000) allocate space for at least this many reports, shared among all devices. "
	 "\"options.maxReportSize\" (default 64) allocate this many bytes per report. "
	 */
	//optionsPrintReportSummary=0;	// options.print
	//optionsPrintCrashers=0;		// options.printCrashers
	//optionsMaxReports=10000;		// options.maxReports
	//optionsMaxReportSize=64;		// options.maxReportSize
	//optionsSecs=0.010;			// options.secs
	mxOptions=PsychGetInArgMxPtr(2);
	if(mxOptions!=NULL){
		mx=mxGetField(mxOptions,0,"print");
		if(mx!=NULL)optionsPrintReportSummary=(psych_bool)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"printCrashers");
		if(mx!=NULL)optionsPrintCrashers=(psych_bool)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"maxReports");
		if(mx!=NULL)optionsMaxReports=(int)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"maxReportSize");
		if(mx!=NULL)optionsMaxReportSize=(int)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"secs");
		if(mx!=NULL)optionsSecs=mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"consistencyChecks");
		if(mx!=NULL)optionsConsistencyChecks=(psych_bool)mxGetScalar(mx);
	}
	if(optionsMaxReports>MAXREPORTS)printf("PsychHID ReceiveReports: Sorry, maxReports is fixed at %d.\n",(int)MAXREPORTS);
	if(optionsMaxReportSize>MAXREPORTSIZE)printf("PsychHID ReceiveReports: Sorry, maxReportSize is fixed at %d.\n",(int)MAXREPORTSIZE);

	error=ReceiveReports(deviceIndex);

	mxErrPtr=PsychGetOutArgMxPtr(1);
	if(mxErrPtr!=NULL){
		const char *fieldNames[]={"n", "name", "description"};
		char *name="",*description="";
		mxArray *fieldValue;

		PsychHIDErrors(NULL, error,&name,&description); // Get error name and description, if available.
		*mxErrPtr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		mxSetField(*mxErrPtr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		mxSetField(*mxErrPtr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*mxErrPtr,0,"n",fieldValue);
	}
    return(PsychError_none);	
}
PsychError PSYCHHIDReceiveReports(void)
{
	long error=0;
	int deviceIndex;
	mxArray **mxErrPtr;
	const mxArray *mxOptions,*mx;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(2));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);

    if(deviceIndex < 0 || deviceIndex >= MAXDEVICEINDEXS) PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex, (int) MAXDEVICEINDEXS-1);
    
	/*
	 "\"options.print\" =1 (default 0) enables diagnostic printing of a summary of each report when our callback routine receives it. "	
	 "\"options.printCrashers\" =1 (default 0) enables diagnostic printing of the creation of the callback source and its addition to the CFRunLoop. "
	 "\"options.maxReports\" (default 10000) allocate space for at least this many reports, shared among all devices. "
	 "\"options.maxReportSize\" (default 65) allocate this many bytes per report. "
	 */
	//optionsPrintReportSummary=0;	// options.print
	//optionsPrintCrashers=0;		// options.printCrashers
	//optionsMaxReports=10000;		// options.maxReports
	//optionsMaxReportSize=65;		// options.maxReportSize
	//optionsSecs=0.010;			// options.secs
    
	mxOptions=PsychGetInArgMxPtr(2);
	if(mxOptions!=NULL){
		mx=mxGetField(mxOptions,0,"print");
		if(mx!=NULL)optionsPrintReportSummary=(psych_bool)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"printCrashers");
		if(mx!=NULL)optionsPrintCrashers=(psych_bool)mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"secs");
		if(mx!=NULL)optionsSecs=mxGetScalar(mx);
		mx=mxGetField(mxOptions,0,"consistencyChecks");
		if(mx!=NULL)optionsConsistencyChecks=(psych_bool)mxGetScalar(mx);

        // Changing maxReports or maxReportSize triggers a reallocation of
        // buffer memory:
		mx=mxGetField(mxOptions,0,"maxReports");
		if(mx!=NULL) {
            oneShotRealloc = TRUE;
            optionsMaxReports = (int) mxGetScalar(mx);
        }

		mx=mxGetField(mxOptions,0,"maxReportSize");
		if(mx!=NULL) {
            oneShotRealloc = TRUE;
            optionsMaxReportSize = (int) mxGetScalar(mx);
        }
	}

    // Sanity check:
	if(optionsMaxReports < 1) PsychErrorExitMsg(PsychError_user, "PsychHID ReceiveReports: Sorry, requested maxReports count must be at least 1!");
	if(optionsMaxReportSize < 1) PsychErrorExitMsg(PsychError_user, "PsychHID ReceiveReports: Sorry, requested maxReportSize must be at least 1 byte!");
	if(optionsMaxReportSize > MAXREPORTSIZE) {
        printf("PsychHID ReceiveReports: Sorry, requested maximum report size %d bytes exceeds built-in maximum of %d bytes.\n", optionsMaxReportSize, (int) MAXREPORTSIZE);
        PsychErrorExitMsg(PsychError_user, "Invalid option.maxReportSize provided!");
    }
    
    // Start reception of reports: This will also allocate memory for the reports
    // on first invocation for this deviceIndex:
	error = ReceiveReports(deviceIndex);

	mxErrPtr=PsychGetOutArgMxPtr(1);
	if(mxErrPtr!=NULL){
		const char *fieldNames[]={"n", "name", "description"};
		char *name="",*description="";
		mxArray *fieldValue;

		PsychHIDErrors(NULL, error,&name,&description); // Get error name and description, if available.
		*mxErrPtr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		mxSetField(*mxErrPtr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		mxSetField(*mxErrPtr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("PSYCHHIDReceiveReports: Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*mxErrPtr,0,"n",fieldValue);
	}
    return(PsychError_none);	
}
PsychError PSYCHHIDSetReport(void) 
{
	long error;
	pRecDevice device;
	int deviceIndex;
	int reportType; // kIOHIDReportTypeInput=0, kIOHIDReportTypeOutput=1, or kIOHIDReportTypeFeature=2
	int reportID;
	unsigned char *reportBuffer;
	int reportSize;
	const mxArray *report;
	mxArray **outErr;
	int i;
	
    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(4));
	outErr=PsychGetOutArgMxPtr(1);
	assert(outErr!=NULL);
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	PsychCopyInIntegerArg(2,TRUE,&reportType);
	PsychCopyInIntegerArg(3,TRUE,&reportID);
	report=PsychGetInArgMxPtr(4); 
	reportBuffer=(void *)mxGetPr(report);
	assert(reportBuffer!=NULL);
	switch(mxGetClassID(report)){
		case mxCHAR_CLASS:    
		case mxINT8_CLASS: 
		case mxUINT8_CLASS:   
		case mxINT16_CLASS:  
		case mxUINT16_CLASS:  
		case mxINT32_CLASS:  
		case mxUINT32_CLASS: 
			break;
		default:
			PrintfExit("\"report\" array must be char or integer (8-, 16-, or 32-bit).");
			break;
	}
	reportSize=mxGetElementSize(report)*mxGetNumberOfElements(report);
	PsychHIDVerifyInit();
    device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(reportSize>0 && reportID!=0) *reportBuffer=0xff&reportID; // copy low byte of reportID to first byte of report.
																 // Apple defines constants for the reportType with values (0,1,2) that are one less that those specified by USB (1,2,3).
	assert(kIOHIDReportTypeInput==0 && kIOHIDReportTypeOutput==1 && kIOHIDReportTypeFeature==2);
	if(reportType==0){
		printf("SetReport(reportType %d, reportID %d, report ",reportType,reportID);
		for(i=0;i<reportSize;i++)printf("%d ",(int)reportBuffer[i]);
		printf(")\n");
		error=0;
	}else{
		if(1){
			IOHIDDeviceInterface122 **interface;
			interface=(IOHIDDeviceInterface122 **)(device->interface);
			if(interface)error=(*interface)->setReport(interface,reportType-1,reportID,reportBuffer,(UInt32)reportSize,50,NULL,NULL,NULL);
			else PrintfExit("PsychHID SetReport: Bad interface.\n");
		}else error=HIDSetReport(device,reportType-1,reportID,reportBuffer,(UInt32)reportSize);
	}
	if(reportID==0x11){
		PsychGetPrecisionTimerSeconds(&AInScanStart);
	}
	//if(error)printf("Warning: PsychHID: HIDSetReport error %ld (0x%lx).",error,error);
	if(0){
		*outErr=mxCreateDoubleMatrix(1,1,mxREAL);
		if(*outErr==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(*outErr)=(double)error;
	}else{
		const char *fieldNames[]={"n", "name", "description"};
		char *name="",*description="";
		mxArray *fieldValue;
		
		PsychHIDErrors(error,&name,&description);
		*outErr=mxCreateStructMatrix(1,1,3,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
	}		
    return(PsychError_none);	
}
PsychError PSYCHHIDGetReport(void) 
{
	long error=0;
	pRecDevice device;
	int deviceIndex;
	int reportType; // 1=input, 2=output, 3=feature
	unsigned char *reportBuffer;
	UInt32 reportBytes=0;
	int reportBufferSize=0;
	int reportID=0;
	long dims[]={1,1};
	mxArray **outReport,**outErr;
	char *name="",*description="",string[256];
	IOHIDDeviceInterface122** interface=NULL;
	boolean reportAvailable;
	double reportTime;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(2));
    PsychErrorExit(PsychCapNumInputArgs(4));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	PsychCopyInIntegerArg(2,TRUE,&reportType);
	PsychCopyInIntegerArg(3,TRUE,&reportID);
	PsychCopyInIntegerArg(4,TRUE,&reportBufferSize);
	outReport=PsychGetOutArgMxPtr(1); 
	outErr=PsychGetOutArgMxPtr(2); // outErr==NULL if optional argument is absent.
	dims[0]=1;
	dims[1]=reportBufferSize;
	*outReport=mxCreateNumericArray(2,(void *)dims,mxUINT8_CLASS,mxREAL);
	if(*outReport==NULL)PrintfExit("Couldn't allocate report array.");
	reportBuffer=(void *)mxGetData(*outReport);
	if(reportBuffer==NULL)PrintfExit("Couldn't allocate report buffer.");
	reportBytes=reportBufferSize;
	PsychHIDVerifyInit();
    device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(!HIDIsValidDevice(device))PrintfExit("PsychHID:GetReport: Invalid device.\n");
	interface=device->interface;
	if(interface==NULL)PrintfExit("PsychHID:GetReport: No interface for device.\n");
	if(reportType==0){
		printf("GetReport(reportType %d, reportID %d, reportBytes %d)\n",reportType,reportID,(int)reportBytes);
	}else{
		// Apple defines constants for the reportType with values (0,1,2) that are one less that those specified by USB (1,2,3).
		if(0){
			// HIDGetReport
			error=HIDGetReport(device,reportType-1,reportID,reportBuffer,&reportBytes);
		}
		if(0){
			// getReport
			error=(*interface)->getReport(interface,reportType-1,reportID,reportBuffer,&reportBytes,-1,nil,nil,nil);
		}
		if(0){
			// handleReport
		}
		if(1){
			// using setInterruptReportHandlerCallback and CFRunLoopRunInMode
			error=ReceiveReports(deviceIndex);
			error=GiveMeReport(deviceIndex,&reportAvailable,reportBuffer,&reportBytes,&reportTime);
		}else{
			// using getReport
			if(error==0)reportAvailable=1;
			PsychGetPrecisionTimerSeconds(&reportTime);
		}
	}
	if(outReport==NULL)PrintfExit("Output argument is required."); // I think MATLAB always provides this.
	if(error==0 && reportBytes>reportBufferSize){
		error=2;
		name="Warning";
		description=string;
		sprintf(description,"GetReport overflow. Expected %ld but received %ld bytes.",(long)reportBufferSize,(long)reportBytes); 
	}
	if(error==0 && reportBytes<reportBufferSize){
		// Reduce the declared size of the array to that of the received report.
		if(reportBytes>0)error=3;
		name="Warning";
		description=string;
		sprintf(description,"GetReport expected %ld but received %ld bytes.",(long)reportBufferSize,(long)reportBytes);
		mxSetN(*outReport,reportBytes);
	}
	if(outErr!=NULL){
		const char *fieldNames[]={"n", "name", "description", "reportLength", "reportTime"};
		mxArray *fieldValue;

		PsychHIDErrors(error,&name,&description); // Get error name and description, if available.
		*outErr=mxCreateStructMatrix(1,1,5,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		if(reportAvailable)*mxGetPr(fieldValue)=(double)reportBytes;
		else *mxGetPr(fieldValue)=-1.0;
		mxSetField(*outErr,0,"reportLength",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=reportTime;
		mxSetField(*outErr,0,"reportTime",fieldValue);
	}
    return(PsychError_none);	
}