File: PsychHIDGenericUSBLibSupport.c

package info (click to toggle)
psychtoolbox-3 3.0.14.20170103%2Bgit6-g605ff5c.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 103,044 kB
  • ctags: 69,483
  • sloc: ansic: 167,371; cpp: 11,232; objc: 4,708; sh: 1,875; python: 383; php: 344; makefile: 207; java: 113
file content (209 lines) | stat: -rwxr-xr-x 6,779 bytes parent folder | download | duplicates (6)
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 *  PsychSourceGL/Source/Common/PsychHID/PsychHIDGenericUSBLibSupport.c
 *
 *  PROJECTS: PsychHID
 *
 *  Platform: Linux, Windows
 *
 *  Authors:
 *
 *  mario.kleiner@tuebingen.mpg.de    mk
 *
 *  HISTORY:
 *
 *	22.7.2011	Created.
 *
 *  DESCRIPTION:
 *
 *  Support routines that implement generic USB device handling via libusb-1.0.
 *  Currently used to implement PsychHID support on Linux and MS-Windows.
 *
 */

#include "PsychHID.h"

// Non OS/X only, for now:
#if PSYCH_SYSTEM != PSYCH_OSX

#if PSYCH_SYSTEM == PSYCH_WINDOWS
#pragma warning( disable : 4200 )
#endif

// Master include file for libusb, as used on Linux and Windows:
// CAUTION: This #undef'ines the "interface" keyword on MS-Windows, which
// will cause breakage with DirectX! Therefore this *must* be defined
// *after* include's for dinput.h !!
#include <libusb.h>

static int ctx_refcount = 0;
static libusb_context *ctx = NULL;

// Function Declarations
int ConfigureDevice(libusb_device_handle* dev, int configIdx);

// Perform device control transfer on USB device:
int PsychHIDOSControlTransfer(PsychUSBDeviceRecord* devRecord, psych_uint8 bmRequestType, psych_uint8 bRequest, psych_uint16 wValue, psych_uint16 wIndex, psych_uint16 wLength, void *pData)
{
    int rc;
	libusb_device_handle* dev = (libusb_device_handle*) devRecord->device;
    
	if (dev == NULL) {
		PsychErrorExitMsg(PsychError_internal, "libusb_device_handle* device points to NULL device!");
	}

	// Send the data across the USB bus by executing the device control request. Return status code.
    // We use a timeout value of 10000 msecs, aka 10 seconds for request to complete:
    rc = libusb_control_transfer(dev, bmRequestType, bRequest, wValue, wIndex, (unsigned char*) pData, wLength, 10000);

    // Return value is either number of transmitted bytes, or a negative error code.
    // We map any non-error return to zero, as on OS/X:
    if (rc >= 0) return(0);
    
    // Failure! Map to verbose message:
    // TODO...
    
    return (rc);
}

// Close USB device, mark device record as "free/invalid":
void PsychHIDOSCloseUSBDevice(PsychUSBDeviceRecord* devRecord)
{	
    libusb_close((libusb_device_handle*) devRecord->device);
	devRecord->device = NULL;
	devRecord->valid = 0;

    ctx_refcount--;
    
    if (ctx_refcount == 0) {
        libusb_exit(ctx);
        ctx = NULL;
    }    
}

// Open first USB device that satisfies given matching critera, mark device record as "active/valid":
// errorcode would contain a diagnostic error code on failure, but is not yet used.
// spec contains the specification of the device to open and how to configure it at open time.
// Returns true on success, false on error or if no matching device could be found.
psych_bool PsychHIDOSOpenUSBDevice(PsychUSBDeviceRecord* devRecord, int* errorcode, PsychUSBSetupSpec* spec)
{
    int                     rc;
	psych_uint16            usbVendor  = (psych_uint16) spec->vendorID;
	psych_uint16            usbProduct = (psych_uint16) spec->deviceID;
	libusb_device_handle*   dev = NULL;
    psych_bool              deviceFound = FALSE;
    
    if (NULL == ctx) {
		libusb_init(&ctx);
        libusb_set_debug(ctx, 3);
    }
    
    dev = libusb_open_device_with_vid_pid(ctx, usbVendor, usbProduct);
    if (dev) {	
        // Got it!
        deviceFound = TRUE;

        // Increment refcount of libusb users:
        ctx_refcount++;
        
		// Success! Assign device interface and mark device record as active/open/valid:
		devRecord->device = (void*) dev;
		devRecord->valid = 1;

		// Configure device
		rc = ConfigureDevice(dev, spec->configurationID);
		if (rc != 0) {
            // Failed! Close device again and zero-out data structures:
            PsychHIDOSCloseUSBDevice(devRecord);
            
			*errorcode = rc;
			printf("PsychHID-ERROR: Unable to configure USB device during Open for configuration id %i.\n", spec->configurationID);
            return(FALSE);
		}

		// Set errorcode to success:
		*errorcode = 0;
	}
	else {
		// No matching device found. NULL-out the record, we're done.
		// This is not strictly needed, as this NULL state is the initial
		// state of the record upon entering this function.
		devRecord->device = NULL;
		devRecord->valid = 0;
		*errorcode = -1;
	}

    if (ctx_refcount == 0) {
        libusb_exit(ctx);
        ctx = NULL;
    }    

	// Return the success status.
	return(deviceFound);
}

int ConfigureDevice(libusb_device_handle* dev, int configIdx)
{
	psych_uint8     numConfig;
    libusb_device*  usbdev;
    struct libusb_device_descriptor deviceDesc;
	struct libusb_config_descriptor* configDesc;
    int bConfigurationValue, current_bConfigurationValue;
	int rc;

	// A configIdx == -1 means: Skip configuration.
	if (configIdx == -1) return(0);

    // Get device pointer for handle:
    usbdev = libusb_get_device(dev);

	// Get the number of configurations. The sample code always chooses
	// the first configuration (at index 0) but your code may need a
	// different one
    rc = libusb_get_device_descriptor(usbdev, &deviceDesc);
    numConfig = deviceDesc.bNumConfigurations;

	if (rc || (numConfig == 0)) {
		printf("PsychHID: USB ConfigureDevice: ERROR! Error getting number of configurations or no configurations available at all (err = %d)\n", rc);
		return(rc);
	}

	if (configIdx < 0 || configIdx >= (int) numConfig) {
		printf("PsychHID: USB ConfigureDevice: ERROR! Provided configuration index %i outside support range 0 - %i for this device!\n", configIdx, (int) numConfig);
		return(-1);
	}
	
	// Get the configuration descriptor for index 'configIdx':
    rc = libusb_get_config_descriptor(usbdev, (psych_uint8) configIdx, &configDesc);    	
	if (rc) {
		printf("PsychHID: USB ConfigureDevice: ERROR! Couldn't get configuration descriptor for index %d (err = %d)\n", configIdx, rc);
		return(rc);
	}
	
    // Extract bConfigurationValue:
    bConfigurationValue = (int) configDesc->bConfigurationValue;
    
    // Release descriptor:
    libusb_free_config_descriptor(configDesc);

	rc = libusb_get_configuration(dev, &current_bConfigurationValue);
	if (rc) {
		printf("PsychHID: USB ConfigureDevice: ERROR! Couldn't get current configuration of device (err = %d)\n", rc);
		return(rc);
	}
	
	// If current value is already identical to requested value, we're done:
	if (current_bConfigurationValue == bConfigurationValue) return(0);
	
	// Set the device's configuration. The configuration value is found in
	// the bConfigurationValue field of the configuration descriptor
    rc = libusb_set_configuration(dev, bConfigurationValue);
	if (rc) {
		printf("PsychHID: USB ConfigureDevice: ERROR! Couldn't set configuration to value %d (err = %d)\n", bConfigurationValue, rc);
		return(rc);
	}
	
    // Return success:
	return(0);
}

#endif