File: PsychHIDSetReport.c

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (192 lines) | stat: -rw-r--r-- 7,580 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
 * Source/Common/PsychHID/PsychHIDSetReport.c
 *
 * PROJECTS: PsychHID
 *
 * PLATFORMS:  All.
 *
 * AUTHORS:
 *  denis.pelli@nyu.edu             dgp
 *  mario.kleiner.de@gmail.com      mk
 *
 * HISTORY:
 *  3/15/05  dgp        Wrote it.
 *  4/23/05  dgp        Now use low-level setReport instead of high-level HIDSetReport so that we can specify a time out (50 ms) instead of hanging forever when there is trouble.
 *
 */

#include "PsychHID.h"

static char useString[] = "err = PsychHID('SetReport', deviceNumber, reportType, reportID, report)";
//                         1                           1             2           3         4
static char synopsisString[] =
    "Send a report to the connected USB HID device.\n"
    "\"deviceNumber\" specifies which device.\n"
    "\"reportType\" is 2=output, 3=feature (0 to just echo arguments).\n"
    "\"reportID\" is either zero or an integer (1 to 255) specifying the topic, e.g. read analog, read digital, write analog, etc. "
    "If you provide a non-zero reportID, the first byte of your \"report\" will be overwritten with this reportID. You have to "
    "take this into account, ie., leave a leading byte of space for the reportID to avoid corrupting your actual report data. "
    "If reportID is zero, then your \"report\" will be sent as-is, without any special treatment of the first byte.\n"
    #if PSYCH_LANGUAGE == PSYCH_MATLAB
    "\"report\" must be an array of char or integer (8-, 16-, 32-, or 64-bit, signed or unsigned) holding "
    "the correct total number of bytes.\n"
    #else
    "\"report\" must be an array of bytes (= unsigned 8 bit integers) holding the correct total number of bytes.\n"
    #endif
    "The returned value \"err.n\" is zero upon success and a nonzero error code upon failure, "
    "as spelled out by \"err.name\" and \"err.description\".\n";

static char seeAlsoString[] = "GetReport";

/*
 * The HID firmware notes I have to program the Measurement Computing PMD1208FS indicate that the reportID is one byte. It specifies
 * what the report is for (0x03 read digital, 0x04 write digital, 0x10 read analog, 0x14 write analog, etc.).
 */

extern double AInScanStart;

PsychError PSYCHHIDSetReport(void)
{
    static unsigned char scratchBuffer[MAXREPORTSIZE+1];

    PsychGenericScriptType *outErr;
    const char *fieldNames[] = {"n", "name", "description"};
    char *name = "", *description = "";
    long error;
    pRecDevice device;
    int deviceIndex;
    int reportType; // kIOHIDReportTypeInput=0, kIOHIDReportTypeOutput=1, or kIOHIDReportTypeFeature=2
    int reportID;
    unsigned char *reportBuffer;
    int reportSize;
    int i;

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

    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(4));
    PsychErrorExit(PsychRequireNumInputArgs(4));

    PsychCopyInIntegerArg(1, TRUE, &deviceIndex);
    PsychCopyInIntegerArg(2, TRUE, &reportType);
    PsychCopyInIntegerArg(3, TRUE, &reportID);

#if PSYCH_LANGUAGE == PSYCH_MATLAB
    // For sake of backwards compatibility, keep the needlessly large scope of acceptable input data types:
    switch (PsychGetArgType(4)) {
        case PsychArgType_char:
        case PsychArgType_uint8:
        case PsychArgType_uint16:
        case PsychArgType_uint32:
        case PsychArgType_uint64:
        case PsychArgType_int8:
        case PsychArgType_int16:
        case PsychArgType_int32:
        case PsychArgType_int64:
            break;

        default:
            PrintfExit("\"report\" array must be char or (unsigned)integer (8-, 16-, 32-, or 64-bit).");
            break;
    }

    {
        const mxArray *report = PsychGetInArgMxPtr(4);
        reportBuffer = mxGetData(report);
        reportSize = mxGetElementSize(report) * mxGetNumberOfElements(report);
    }
#else
    {
        // Use less insane approach for non-Octave/Matlab: Just a byte array, the only meaningful
        // thing to send in HID reports anyway:
        int m, n, p;
        PsychAllocInUnsignedByteMatArg(4, kPsychArgRequired, &m, &n, &p, &reportBuffer);
        reportSize = m * n * p;
    }
#endif

    if (reportSize > MAXREPORTSIZE)
        PsychErrorExitMsg(PsychError_user, "Tried to send a HID report which exceeds the maximum allowable size! Aborted.");

    if (reportSize < 1)
        PsychErrorExitMsg(PsychError_user, "Tried to send an empty HID report! Aborted.");

    // No invalid numbers, no input reports for 'SetReport':
    if (reportType < 0 || reportType > 3 || reportType == 1)
        PsychErrorExitMsg(PsychError_user, "Invalid 'reportType' for this function provided!");

    PsychHIDVerifyInit();

    device = PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);

    // For non-zero reportID, the first byte of buffer is always overwritten with reportID on all os platforms:
    if (reportID != 0) *reportBuffer = 0xff & reportID; // copy low byte of reportID to first byte of report.

    if (reportType == 0) {
        // Echo report:
        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 PSYCH_SYSTEM == PSYCH_OSX
        IOHIDDeviceInterface122 **interface = NULL;
        interface = PsychHIDGetDeviceInterfacePtrFromIndex(deviceIndex);
        if (interface)
            error = (*interface)->setReport(interface, reportType - 1, reportID, reportBuffer, (psych_uint32)reportSize, 50, NULL, NULL, NULL);
        else
            PrintfExit("PsychHID SetReport: Bad interface.\n");

        // Timestamp:
        if (reportID == 0x11)
            PsychGetPrecisionTimerSeconds(&AInScanStart);

        // Error condition?
        PsychHIDErrors(NULL, error, &name, &description);
    #else
        // reportID zero is a special case. We must not touch/overwrite the 1st byte
        // of the usercode provided buffer, but still prefix the buffer passed to HIDLIB
        // with a zero byte:
        if (reportID == 0) {
            memcpy(&scratchBuffer[1], reportBuffer, reportSize);
            scratchBuffer[0] = (unsigned char)0;
            reportBuffer = &scratchBuffer[0];
            reportSize++;
        }

        // Which report type?
        if (reportType == 2) {
            // Output report:

            // Write it: error == -1 would mean error, otherwise it is number of bytes written.
            error = hid_write((hid_device*)device->interface, reportBuffer, (size_t)reportSize);
        }
        else {
            // reportType == 3, aka Feature report:

            // Send it: error == -1 would mean error, otherwise it is number of bytes written.
            error = hid_send_feature_report((hid_device*)device->interface, reportBuffer, (size_t)reportSize);
        }

        // Timestamp:
        if (reportID == 0x11)
            PsychGetPrecisionTimerSeconds(&AInScanStart);

        // Positive value means no error:
        if (error >= 0)
            error = 0;

        // Error condition?
        PsychHIDErrors((hid_device*)device->interface, error, &name, &description);
    #endif
    }

    PsychAllocOutStructArray(1, kPsychArgOptional, -1, 3, fieldNames, &outErr);
    PsychSetStructArrayStringElement("name", 0, name, outErr);
    PsychSetStructArrayStringElement("description", 0, description, outErr);
    PsychSetStructArrayDoubleElement("n", 0, (double) error, outErr);

    return(PsychError_none);
}