vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_Retrolink.C
Go to the documentation of this file.
1// vrpn_Retrolink.C: VRPN driver for Retrolink Classic Controller devices
2
3#include <stdio.h> // for fprintf, stderr, NULL
4#include <string.h> // for memset
5#include <math.h> // for fabs
6
7#include "vrpn_Retrolink.h"
9
10#if defined(VRPN_USE_HID)
11
12static const double POLL_INTERVAL = 1e+6 / 30.0; // If we have not heard, ask.
13
14// USB vendor and product IDs for the models we support
15static const vrpn_uint16 RETROLINK_VENDOR = 0x0079;
16static const vrpn_uint16 RETROLINK_GAMECUBE = 0x0006;
17static const vrpn_uint16 RETROLINK_GENESIS = 0x0011;
18
19// A channel goes from -1 (value 0) to 1 (value 255), with
20// an initial value between 0x80 and 0x83 for the one I developed
21// with.
22// XXX Consider adding a dead zone and putting it in the config file
23static vrpn_float64 normalize_axis(const vrpn_uint8 value)
24{
25 vrpn_float64 offset = static_cast<vrpn_float64>(value) - 128;
26 vrpn_float64 scaled = offset / 127;
27 if (scaled > 1) { scaled = 1; }
28 if (scaled < -1) { scaled = -1; }
29 return scaled;
30}
31
32// Convert the 0-f nybble for the rocker switch into its angle
33static void angle_and_buttons_from_rocker_byte(const vrpn_uint8 value,
34 vrpn_float64 *angle,
35 bool *up, bool *right, bool *down, bool *left)
36{
37 switch (value) {
38 case 0: *angle = 0; *up = true; *right = false; *down = false; *left = false; break;
39 case 1: *angle = 45; *up = true; *right = true; *down = false; *left = false; break;
40 case 2: *angle = 90; *up = false; *right = true; *down = false; *left = false; break;
41 case 3: *angle = 135; *up = false; *right = true; *down = true; *left = false; break;
42 case 4: *angle = 180; *up = false; *right = false; *down = true; *left = false; break;
43 case 5: *angle = 225; *up = false; *right = false; *down = true; *left = true; break;
44 case 6: *angle = 270; *up = false; *right = false; *down = false; *left = true; break;
45 case 7: *angle = 315; *up = true; *right = false; *down = false; *left = true; break;
46 default: *angle = -1; *up = false; *right = false; *down = false; *left = false; break;
47 }
48}
49
50// Convert the two bytes for the rocker switch into its angle
51static void angle_and_buttons_from_rocker_doublet(
52 const vrpn_uint8 value0, const vrpn_uint8 value1,
53 vrpn_float64 *angle,
54 bool *up, bool *right, bool *down, bool *left)
55{
56 vrpn_uint8 value = 8; // Default is nothing set in case we don't recognize it.
57 switch (value0) {
58 case 0x00:
59 switch (value1) {
60 case 0x00: value = 7; break;
61 case 0x7F: value = 6; break;
62 case 0xFF: value = 5; break;
63 }
64 break;
65
66 case 0x7F:
67 switch (value1) {
68 case 0x00: value = 0; break;
69 case 0x7F: value = 8; break;
70 case 0xFF: value = 4; break;
71 }
72 break;
73
74 case 0xFF:
75 switch (value1) {
76 case 0x00: value = 1; break;
77 case 0x7F: value = 2; break;
78 case 0xFF: value = 3; break;
79 }
80 break;
81 }
82 angle_and_buttons_from_rocker_byte(value, angle, up, right, down, left);
83}
84
86 vrpn_uint16 vendor, vrpn_uint16 product)
87 : vrpn_BaseClass(name, c)
88 , vrpn_HidInterface(filter, vendor, product)
89 , _filter(filter)
90{
91 init_hid();
92}
93
95{
96 try {
97 delete _filter;
98 } catch (...) {
99 fprintf(stderr, "vrpn_Retrolink::~vrpn_Retrolink(): delete failed\n");
100 return;
101 }
102}
103
109
110void vrpn_Retrolink::on_data_received(size_t bytes, vrpn_uint8 *buffer)
111{
112 decodePacket(bytes, buffer);
113}
114
116{
117 return 0;
118}
119
121{
122 return 0;
123}
124
126 : vrpn_Retrolink(new vrpn_HidProductAcceptor(RETROLINK_VENDOR, RETROLINK_GAMECUBE), name, c, RETROLINK_VENDOR, RETROLINK_GAMECUBE)
127 , vrpn_Analog(name, c)
128 , vrpn_Button_Filter(name, c)
129{
132
133 // Initialize the state of all the analogs, buttons
134 memset(buttons, 0, sizeof(buttons));
135 memset(lastbuttons, 0, sizeof(lastbuttons));
136 memset(channel, 0, sizeof(channel));
137}
138
140{
141 update();
143 struct timeval current_time;
144 vrpn_gettimeofday(&current_time, NULL);
145 if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL ) {
146 _timestamp = current_time;
148
149 // Call the server_mainloop on our unique base class.
151 }
152}
153
154void vrpn_Retrolink_GameCube::report(vrpn_uint32 class_of_service) {
156 {
158 }
160 {
162 }
163
165 {
166 vrpn_Analog::report(class_of_service);
167 }
169 {
171 }
172}
173
174void vrpn_Retrolink_GameCube::report_changes(vrpn_uint32 class_of_service) {
176 {
178 }
180 {
182 }
183
185 {
186 vrpn_Analog::report(class_of_service);
187 }
189 {
191 }
192}
193
194void vrpn_Retrolink_GameCube::decodePacket(size_t bytes, vrpn_uint8 *buffer)
195{
196 /*
197 // Print the report so we can figure out what is going on.
198 for (size_t i = 0; i < bytes; i++) {
199 printf("%02x ", buffer[i]);
200 }
201 printf("\n");
202 return;
203 */
204
205 // Because there is only one type of report, the initial "0" report-type
206 // byte is removed by the HIDAPI driver.
207 // Reports should be 8 bytes. They are encoded as follows:
208 // 0 = Left joystick X axis, goes from 0 (left) to 255 (right)
209 // 1 = Left joystick Y axis, goes from 0 (up) to 255 (down)
210 // 2 = Uncontrolled jibberish, must be from an unconnected channel
211 // 3 = Left joystick X axis, goes from 0 (left) to 255 (right)
212 // 4 = Left joystick Y axis, goes from 0 (left) to 255 (right)
213 // 5 upper nybble = bit 0 (Y), 1 (X), 2 (A), 3 (B)
214 // 5 lower nybble = rocker: 255 when nothing; 0 (up), 1 (UR), 2 (right), .. 7 (UL)
215 // 6 = bit # 0 (left trigger), 1 (right trigger), 2 (Z), 5 (Start/pause)
216 // 7 = always 0x20 (maybe unconnected buttons; probably safest to ignore)
217
218 // Controls for the Nintendo 64 classic
219 // 0 = Left joystick X axis, goes from 0 (left) to 255 (right)
220 // 1 = Left joystick Y axis, goes from 0 (up) to 255 (down)
221 // 2 = Uncontrolled jibberish, must be from an unconnected channel
222 // 5 upper nybble = bit 0 (C up), 1 (C right), 2 (C down), 3 (C Left)
223 // 5 lower nybble = rocker: 255 when nothing; 0 (up), 1 (UR), 2 (right), .. 7 (UL)
224 // 6 = bit # 0 (left trigger), 1 (right trigger), 2 (A), 3 (Z), 4 (B), 5 (Start)
225 // 7 = always 0x20 (maybe unconnected buttons; probably safest to ignore)
226
227 if (bytes == 8) {
228 // Figure out the joystick axes.
229 channel[0] = normalize_axis(buffer[0]);
230 channel[1] = normalize_axis(buffer[1]);
231 channel[2] = normalize_axis(buffer[3]);
232 channel[3] = normalize_axis(buffer[4]);
233
234 // Figure out the buttons.
235 buttons[0] = (buffer[5] & (1 << 4)) != 0;
236 buttons[1] = (buffer[5] & (1 << 5)) != 0;
237 buttons[2] = (buffer[5] & (1 << 6)) != 0;
238 buttons[3] = (buffer[5] & (1 << 7)) != 0;
239 buttons[4] = (buffer[6] & (1 << 0)) != 0;
240 buttons[5] = (buffer[6] & (1 << 1)) != 0;
241 buttons[6] = (buffer[6] & (1 << 2)) != 0;
242 buttons[7] = (buffer[6] & (1 << 5)) != 0;
243
244 // Figure out the rocker.
245 vrpn_uint8 rocker = buffer[5] & 0x0f;
246 vrpn_float64 angle;
247 bool up, right, down, left;
248 angle_and_buttons_from_rocker_byte(rocker, &angle, &up, &right, &down, &left);
249 channel[4] = angle;
250 buttons[8] = up; buttons[9] = right;
251 buttons[10] = down;
252 buttons[11] = left;
253 } else {
254 fprintf(stderr, "vrpn_Retrolink_GameCube: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
255 }
256}
257
259 : vrpn_Retrolink(new vrpn_HidProductAcceptor(RETROLINK_VENDOR, RETROLINK_GENESIS), name, c, RETROLINK_VENDOR, RETROLINK_GENESIS)
260 , vrpn_Analog(name, c)
261 , vrpn_Button_Filter(name, c)
262{
265
266 // Initialize the state of all the analogs, buttons
267 memset(buttons, 0, sizeof(buttons));
268 memset(lastbuttons, 0, sizeof(lastbuttons));
269 memset(channel, 0, sizeof(channel));
270}
271
273{
274 update();
276 struct timeval current_time;
277 vrpn_gettimeofday(&current_time, NULL);
278 if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL ) {
279 _timestamp = current_time;
281
282 // Call the server_mainloop on our unique base class.
284 }
285}
286
287void vrpn_Retrolink_Genesis::report(vrpn_uint32 class_of_service) {
289 {
291 }
293 {
295 }
296
298 {
299 vrpn_Analog::report(class_of_service);
300 }
302 {
304 }
305}
306
307void vrpn_Retrolink_Genesis::report_changes(vrpn_uint32 class_of_service) {
309 {
311 }
313 {
315 }
316
318 {
319 vrpn_Analog::report(class_of_service);
320 }
322 {
324 }
325}
326
327void vrpn_Retrolink_Genesis::decodePacket(size_t bytes, vrpn_uint8 *buffer)
328{
329 /*
330 // Print the report so we can figure out what is going on.
331 for (size_t i = 0; i < bytes; i++) {
332 printf("%02x ", buffer[i]);
333 }
334 printf("\n");
335 return;
336 */
337
338 // Because there is only one type of report, the initial "0" report-type
339 // byte is removed by the HIDAPI driver.
340 // Reports should be 8 bytes. They are encoded as follows:
341 // 0 = No input (has the value 01)
342 // 1 = No input (has the value 7F)
343 // 2 = No input (has the value 7F)
344 // 3 = Rocker X axis, goes from 00 (left) to 7F (center) to FF (right)
345 // 4 = Rocker Y axis, goes from 00 (top) to 7F (center) to FF (bottom)
346 // 5 = bit # 4 (Y), 5 (B), 6 (A), 7 (X); lower nybble is F
347 // 6 = bit # 0 (Z), 1 (C), 4 (Mode), 5 (Start)
348 // 7 = always 0x00 (maybe unconnected buttons; probably safest to ignore)
349
350 if (bytes == 8) {
351 // Figure out the buttons.
352 buttons[0] = (buffer[5] & (1 << 6)) != 0;
353 buttons[1] = (buffer[5] & (1 << 5)) != 0;
354 buttons[2] = (buffer[6] & (1 << 1)) != 0;
355 buttons[3] = (buffer[5] & (1 << 7)) != 0;
356 buttons[4] = (buffer[5] & (1 << 4)) != 0;
357 buttons[5] = (buffer[6] & (1 << 0)) != 0;
358 buttons[6] = (buffer[6] & (1 << 4)) != 0;
359 buttons[7] = (buffer[6] & (1 << 5)) != 0;
360
361 // Figure out the rocker.
362 vrpn_uint8 rocker0 = buffer[3];
363 vrpn_uint8 rocker1 = buffer[4];
364 vrpn_float64 angle;
365 bool up, right, down, left;
366 angle_and_buttons_from_rocker_doublet(rocker0, rocker1,
367 &angle, &up, &right, &down, &left);
368 channel[0] = angle;
369 buttons[8] = up;
370 buttons[9] = right;
371 buttons[10] = down;
372 buttons[11] = left;
373 } else {
374 fprintf(stderr, "vrpn_Retrolink_Genesis: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
375 }
376}
377
378// End of VRPN_USE_HID
379#endif
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition vrpn_Analog.h:38
struct timeval timestamp
Definition vrpn_Analog.h:41
vrpn_int32 num_channel
Definition vrpn_Analog.h:40
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report whether something has changed or not (for servers) Optionally, tell what time to stamp ...
Definition vrpn_Analog.C:94
int register_autodeleted_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
Registers a handler with the connection, and remembers to delete at destruction.
vrpn_Connection * d_connection
Connection that this object talks to.
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
Class from which all user-level (and other) classes that communicate with vrpn_Connections should der...
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition vrpn_Button.h:66
virtual void report_changes(void)
vrpn_int32 num_buttons
Definition vrpn_Button.h:48
struct timeval timestamp
Definition vrpn_Button.h:49
virtual void report_changes(void)
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition vrpn_Button.h:46
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition vrpn_Button.h:45
Generic connection class not specific to the transport mechanism.
virtual vrpn_int32 register_message_type(const char *name)
virtual void update()
Polls the device buffers and causes on_data_received callbacks if appropriate You NEED to call this f...
Accepts any device with the given vendor and product IDs.
This structure is what is passed to a vrpn_Connection message callback.
#define VRPN_SUPPRESS_EMPTY_OBJECT_WARNING()
const char * vrpn_dropped_last_connection
const char * vrpn_got_connection
#define POLL_INTERVAL
Definition vrpn_IDEA.C:26
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
#define vrpn_gettimeofday
Definition vrpn_Shared.h:99