revert commit 56204.
[AROS.git] / rom / usb / classes / arosx / arosx.class.c
blob48c62e6adcbf5cb1af45491c8a6f824af6b2b70d
1 /*
2 Copyright © 2018-2019, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Gamepad (XInput) USB class driver
6 Lang: English
7 */
10 #include <aros/libcall.h>
11 #include <devices/timer.h>
13 #include <proto/timer.h>
15 #include "debug.h"
17 #include "arosx.class.h"
20 struct AROSXBase * AROSXInit(void);
22 struct AROSXClassController *AROSXClass_CreateController(LIBBASETYPEPTR arosxb, UBYTE id);
23 struct AROSXClassController *AROSXClass_ConnectController(LIBBASETYPEPTR arosxb, UBYTE type);
24 void AROSXClass_DisconnectController(LIBBASETYPEPTR arosxb, struct AROSXClassController *arosxc);
25 void AROSXClass_DestroyController(LIBBASETYPEPTR arosxb, struct AROSXClassController *arosxc);
26 BOOL AROSXClass_SendEvent(LIBBASETYPEPTR arosxb, ULONG ehmt, APTR param1, APTR param2);
30 /* /// "Lib Stuff" */
31 static int libInit(LIBBASETYPEPTR arosxb)
34 mybug(0, ("libInit arosxb: 0x%08lx SysBase: 0x%08lx\n", arosxb, SysBase));
36 arosxb->tv_secs = 0;
37 arosxb->tv_micro = 0;
39 arosxb->arosxc_count = 0;
41 arosxb->arosxc_0 = AROSXClass_CreateController(arosxb, 0);
42 arosxb->arosxc_1 = AROSXClass_CreateController(arosxb, 1);
43 arosxb->arosxc_2 = AROSXClass_CreateController(arosxb, 2);
44 arosxb->arosxc_3 = AROSXClass_CreateController(arosxb, 3);
46 InitSemaphore(&arosxb->arosxc_lock);
47 InitSemaphore(&arosxb->event_lock);
49 memset(&arosxb->event_reply_port, 0, sizeof(arosxb->event_reply_port));
50 arosxb->event_reply_port.mp_Flags = PA_IGNORE;
51 NewList(&arosxb->event_reply_port.mp_MsgList);
53 NewList(&arosxb->event_port_list);
55 arosxb->AROSXBase = AROSXInit();
57 #define AROSXBase arosxb->AROSXBase
59 if(!AROSXBase)
61 mybug(-1, ("libInit: MakeLibrary(\"arosx.library\") failed!\n"));
62 return(FALSE);
65 AROSXBase->arosxb = arosxb;
67 mybug(-1, ("AROSX: AROSXBase 0x%08lx\n", AROSXBase));
68 //AROS_LC0(ULONG, Dummy1, LIBBASETYPEPTR, AROSXBase, 5, arosx);
70 mybug(0, ("libInit: Ok\n"));
72 return(TRUE);
75 static int libOpen(LIBBASETYPEPTR arosxb)
77 mybug(0, ("libOpen arosxb: 0x%08lx\n", arosxb));
78 return(TRUE);
81 static int libExpunge(LIBBASETYPEPTR arosxb)
83 mybug(10, ("libExpunge arosxb: 0x%08lx\n", arosxb));
84 //CloseLibrary((struct Library *) UtilityBase);
85 return(TRUE);
88 ADD2INITLIB(libInit, 0)
89 ADD2OPENLIB(libOpen, 0)
90 ADD2EXPUNGELIB(libExpunge, 0)
91 /* \\\ */
94 * ***********************************************************************
95 * * Library functions *
96 * ***********************************************************************
99 #define ps ps
100 /* /// "usbAttemptInterfaceBinding()" */
101 struct AROSXClassController * usbAttemptInterfaceBinding(struct AROSXClassBase *arosxb, struct PsdInterface *pif)
103 struct Library *ps;
105 struct AROSXClassController *arosxc;
107 IPTR ifclass;
108 IPTR subclass;
109 IPTR proto;
111 struct PsdConfig *pc;
112 struct PsdDevice *pd;
114 struct PsdDescriptor *pdd;
115 UBYTE *xinput_desc;
117 UBYTE buf[64];
118 struct Task *tmptask;
120 mybug(0, ("nepHidAttemptInterfaceBinding(%08lx)\n", pif));
122 if((ps = OpenLibrary("poseidon.library", 4)))
125 psdGetAttrs(PGA_INTERFACE, pif,
126 IFA_Class, &ifclass,
127 IFA_SubClass, &subclass,
128 IFA_Protocol, &proto,
129 IFA_Config, &pc,
130 TAG_DONE);
132 psdGetAttrs(PGA_CONFIG, pc,
133 CA_Device, &pd,
134 TAG_END);
137 Check to see if it is believed to be an XInput Gamepad interface and if so store the XInput descriptor pdd and the data
138 - We could extend this class to also house code for other XInput devices,
139 but for now it's only XInput Gamepad
141 pdd = psdFindDescriptor(pd, NULL, DDA_DescriptorType, 33, DDA_Interface, pif, TAG_END);
142 if(((ifclass != 255) || (subclass != 93) || (proto != 1) || (pdd == NULL)))
144 mybug(0, ("nepHidAttemptInterfaceBinding(%08lx) %d %d %d Nope!\n", pif, ifclass, subclass, proto));
145 CloseLibrary(ps);
146 return(NULL);
150 Make sure the XInput descriptor takes the form we expect
151 [16] 33 16 1 1 [36] [129] 20 3 0 3 19 2 0 3 0
152 [17] 33 16 1 1 [37] [129] 20 3 3 3 4 19 2 8 3 3
153 XInput descriptor length has to match with the "nibble count"
154 - XInput "USAGE" seems to take the form of nibbles on the bitmask
155 - Nibble byte count seems to relate to the size of the descriptor (adjusted)
156 TODO: Make the class bailout earlier if the interface isn't what we want and clean this mess
159 psdGetAttrs(PGA_DESCRIPTOR, pdd, DDA_DescriptorData, &xinput_desc, TAG_END);
161 UBYTE nibble_check;
162 nibble_check = ( (( (xinput_desc[5]>>1) + (xinput_desc[5] & 1) ) - 2) );
164 mybug(0, ("nepHidAttemptInterfaceBinding(%08lx) Nibble check %d\n", pif, nibble_check));
165 nDebugMem(ps, xinput_desc, xinput_desc[0]);
167 if( (xinput_desc[6] != 129) | (nibble_check != xinput_desc[0]) )
169 mybug(-1, ("nepHidAttemptInterfaceBinding(%08lx) Not a gamepad! (that we know of...)\n", pif));
170 CloseLibrary(ps);
171 return(NULL);
174 if((arosxc = AROSXClass_ConnectController(arosxb, AROSX_CONTROLLER_TYPE_GAMEPAD)))
177 arosxc->Device = pd;
178 arosxc->Interface = pif;
180 psdSafeRawDoFmt(buf, 64, "arosx.class.gamepad.%01x", arosxc->id);
181 arosxc->ReadySignal = SIGB_SINGLE;
182 arosxc->ReadySigTask = FindTask(NULL);
183 SetSignal(0, SIGF_SINGLE);
184 if((tmptask = psdSpawnSubTask(buf, nHidTask, arosxc)))
186 psdBorrowLocksWait(tmptask, 1UL<<arosxc->ReadySignal);
187 if(arosxc->Task)
189 arosxc->ReadySigTask = NULL;
190 //FreeSignal(arosxc->ReadySignal);
191 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &arosxc->devname, TAG_END);
193 psdSafeRawDoFmt(arosxc->name, 64, "%s (%01x)", arosxc->devname, arosxc->id);
195 psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "Play it again, '%s'!", arosxc->name);
197 CloseLibrary(ps);
198 return(arosxc);
201 arosxc->ReadySigTask = NULL;
202 //FreeSignal(arosxc->ReadySignal);
203 AROSXClass_DisconnectController(arosxb, arosxc);;
205 CloseLibrary(ps);
208 return(NULL);
210 /* \\\ */
212 /* /// "usbReleaseInterfaceBinding()" */
213 void usbReleaseInterfaceBinding(struct AROSXClassBase *arosxb, struct AROSXClassController *arosxc)
215 struct Library *ps;
216 struct PsdConfig *pc;
217 struct PsdDevice *pd;
218 STRPTR devname;
220 mybug(0, ("nepHidReleaseInterfaceBinding(%08lx)\n", arosxc));
222 /* Kill the GUITask */
223 if(arosxc->GUITask)
225 Signal(arosxc->GUITask, SIGBREAKF_CTRL_C);
228 if((ps = OpenLibrary("poseidon.library", 4)))
230 Forbid();
231 arosxc->ReadySignal = SIGB_SINGLE;
232 arosxc->ReadySigTask = FindTask(NULL);
233 if(arosxc->Task)
235 Signal(arosxc->Task, SIGBREAKF_CTRL_C);
237 Permit();
238 while(arosxc->Task)
240 Wait(1L<<arosxc->ReadySignal);
242 //FreeSignal(arosxc->ReadySignal);
243 psdGetAttrs(PGA_INTERFACE, arosxc->Interface, IFA_Config, &pc, TAG_END);
244 psdGetAttrs(PGA_CONFIG, pc, CA_Device, &pd, TAG_END);
245 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
246 psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "'%s' fell silent!", devname);
248 AROSXClass_DisconnectController(arosxb, arosxc);
250 CloseLibrary(ps);
253 /* \\\ */
255 /* /// "usbGetAttrsA()" */
256 AROS_LH3(LONG, usbGetAttrsA,
257 AROS_LHA(ULONG, type, D0),
258 AROS_LHA(APTR, usbstruct, A0),
259 AROS_LHA(struct TagItem *, tags, A1),
260 LIBBASETYPEPTR, arosxb, 5, nep)
262 AROS_LIBFUNC_INIT
264 struct TagItem *ti;
265 LONG count = 0;
267 mybug(0, ("nepHidGetAttrsA(%ld, %08lx, %08lx)\n", type, usbstruct, tags));
268 switch(type)
270 case UGA_CLASS:
271 if((ti = FindTagItem(UCCA_Priority, tags)))
273 *((SIPTR *) ti->ti_Data) = -100;
274 count++;
276 if((ti = FindTagItem(UCCA_Description, tags)))
278 *((STRPTR *) ti->ti_Data) = "Gamepad (XInput)";
279 count++;
281 if((ti = FindTagItem(UCCA_HasClassCfgGUI, tags)))
283 *((IPTR *) ti->ti_Data) = FALSE;
284 count++;
286 if((ti = FindTagItem(UCCA_HasBindingCfgGUI, tags)))
288 *((IPTR *) ti->ti_Data) = TRUE;
289 count++;
291 if((ti = FindTagItem(UCCA_AfterDOSRestart, tags)))
293 *((IPTR *) ti->ti_Data) = FALSE;
294 count++;
296 break;
297 case UGA_BINDING:
298 if((ti = FindTagItem(UCCA_UsingDefaultCfg, tags)))
300 *((IPTR *) ti->ti_Data) = FALSE;
301 count++;
303 break;
305 return(count);
306 AROS_LIBFUNC_EXIT
308 /* \\\ */
310 /* /// "usbSetAttrsA()" */
311 AROS_LH3(LONG, usbSetAttrsA,
312 AROS_LHA(ULONG, type, D0),
313 AROS_LHA(APTR, usbstruct, A0),
314 AROS_LHA(struct TagItem *, tags, A1),
315 LIBBASETYPEPTR, arosxb, 6, nep)
317 AROS_LIBFUNC_INIT
318 return(0);
319 AROS_LIBFUNC_EXIT
321 /* \\\ */
323 /* /// "usbDoMethodA()" */
324 AROS_LH2(IPTR, usbDoMethodA,
325 AROS_LHA(ULONG, methodid, D0),
326 AROS_LHA(IPTR *, methoddata, A1),
327 LIBBASETYPEPTR, arosxb, 7, nep)
329 AROS_LIBFUNC_INIT
331 mybug(0, ("Do Method %ld\n", methodid));
332 switch(methodid)
334 case UCM_AttemptInterfaceBinding:
335 return((IPTR) usbAttemptInterfaceBinding(arosxb, (struct PsdInterface *) methoddata[0]));
337 case UCM_ForceInterfaceBinding:
338 return((IPTR) usbAttemptInterfaceBinding(arosxb, (struct PsdInterface *) methoddata[0]));
340 case UCM_ReleaseInterfaceBinding:
341 usbReleaseInterfaceBinding(arosxb, (struct AROSXClassController *) methoddata[0]);
342 return(TRUE);
344 case UCM_OpenBindingCfgWindow:
345 return(nOpenCfgWindow((struct AROSXClassController *) methoddata[0]));
347 default:
348 break;
350 return(0);
351 AROS_LIBFUNC_EXIT
353 /* \\\ */
357 struct AROSXClassController *AROSXClass_CreateController(LIBBASETYPEPTR arosxb, UBYTE id) {
359 struct AROSXClassController *arosxc;
361 arosxc = AllocVec(sizeof(struct AROSXClassController), MEMF_ANY|MEMF_CLEAR);
363 if(arosxc == NULL) {
364 mybug(-1, ("[AROSXClass] AROSXClass_CreateController: Failed to create new controller structure for controller %01x\n", id));
365 return NULL;
366 } else {
367 arosxc->id = id;
369 arosxc->status.connected = FALSE;
370 arosxc->status.wireless = FALSE;
371 arosxc->status.signallost = FALSE;
373 arosxc->arosxb = arosxb;
375 mybug(-1, ("[AROSXClass] AROSXClass_CreateController: Created new controller structure %04lx for controller %01x\n", arosxc, arosxc->id));
377 if (arosxc->TimerMP = CreatePort(NULL, 0)) {
378 if (arosxc->TimerIO = (struct timerequest *)CreateExtIO(arosxc->TimerMP, sizeof(struct timerequest))) {
379 if (!(OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)arosxc->TimerIO, 0))) {
380 arosxc->TimerBase = arosxc->TimerIO->tr_node.io_Device;
383 Timestamp starts from zero once the first controller structure gets build
385 if((arosxb->tv_secs == 0) && (arosxb->tv_micro == 0)) {
386 #undef TimerBase
387 #define TimerBase arosxc->TimerBase
388 struct timeval current;
389 GetSysTime(&current);
390 arosxb->tv_secs = current.tv_secs;
391 arosxb->tv_micro = current.tv_micro;
393 mybug(-1,("Initial timestamp %u %u\n", arosxb->tv_secs, arosxb->tv_micro));
396 arosxc->initial_tv_secs = arosxb->tv_secs;
397 arosxc->initial_tv_micro = arosxb->tv_micro;
399 FreeSignal(arosxc->TimerMP->mp_SigBit);
400 return arosxc;
402 DeleteExtIO((struct IORequest *)arosxc->TimerIO);
404 DeletePort(arosxc->TimerMP);
408 AROSXClass_DestroyController(arosxb, arosxc);
410 return NULL;
415 Just does a FreeVec, no checks to see if someone is using it...
416 - Implemented some sanity
418 void AROSXClass_DestroyController(LIBBASETYPEPTR arosxb, struct AROSXClassController *arosxc) {
420 UBYTE id;
422 if(arosxc != NULL) {
423 id = arosxc->id;
425 ObtainSemaphore(&arosxb->arosxc_lock);
426 if(id == 0) {
427 FreeVec(arosxb->arosxc_0);
428 arosxb->arosxc_0 = NULL;
429 }else if(id == 1) {
430 FreeVec(arosxb->arosxc_1);
431 arosxb->arosxc_1 = NULL;
432 }else if(id == 2) {
433 FreeVec(arosxb->arosxc_2);
434 arosxb->arosxc_2 = NULL;
435 }else if(id == 3) {
436 FreeVec(arosxb->arosxc_3);
437 arosxb->arosxc_3 = NULL;
439 ReleaseSemaphore(&arosxb->arosxc_lock);
440 }else{
441 mybug(-1, ("[AROSXClass] AROSXClass_DestroyController: Called on non existing controller...\n"));
445 struct AROSXClassController *AROSXClass_ConnectController(LIBBASETYPEPTR arosxb, UBYTE type) {
447 struct AROSXClassController *arosxc;
448 arosxc = NULL;
450 ObtainSemaphore(&arosxb->arosxc_lock);
452 if(arosxb->arosxc_count != 4) {
453 if(arosxb->arosxc_0->status.connected == FALSE) {
454 arosxc = arosxb->arosxc_0;
455 mybug(-1, ("[AROSXClass] AROSXClass_ConnectController: Assigned to controller number 0\n"));
456 }else if(arosxb->arosxc_1->status.connected == FALSE) {
457 arosxc = arosxb->arosxc_1;
458 mybug(-1, ("[AROSXClass] AROSXClass_ConnectController: Assigned to controller number 1\n"));
459 }else if(arosxb->arosxc_2->status.connected == FALSE) {
460 arosxc = arosxb->arosxc_2;
461 mybug(-1, ("[AROSXClass] AROSXClass_ConnectController: Assigned to controller number 2\n"));
462 }else if(arosxb->arosxc_3->status.connected == FALSE) {
463 arosxc = arosxb->arosxc_3;
464 mybug(-1, ("[AROSXClass] AROSXClass_ConnectController: Assigned to controller number 3\n"));
465 }else {
466 ReleaseSemaphore(&arosxb->arosxc_lock);
467 mybug(-1, ("[AROSXClass] AROSXClass_ConnectController: How did you get here? Failing...\n"));
468 return NULL;
470 }else {
471 ReleaseSemaphore(&arosxb->arosxc_lock);
472 mybug(-1, ("[AROSXClass] AROSXClass_ConnectController: Controller count exceeded, failing...\n"));
473 return NULL;
476 arosxb->arosxc_count++;
478 arosxc->controller_type = type;
480 arosxc->status.connected = TRUE;
483 Send connect event from this controller.
484 - If no event handler has been installed then the msg goes to nowhere and is lost
485 - New connect event is sent when event handler is created including this controller
488 if(AROSXClass_SendEvent(arosxb, ((((1L<<arosxc->id))<<28) | ((arosxc->controller_type)<<20) | AROSX_EHMF_CONNECT), (APTR)1, (APTR)2)) {
489 mybug(-1,("Attach event sent\n"));
490 } else {
491 mybug(-1,("Attach event not sent\n"));
494 ReleaseSemaphore(&arosxb->arosxc_lock);
496 return arosxc;
500 void AROSXClass_DisconnectController(LIBBASETYPEPTR arosxb, struct AROSXClassController *arosxc) {
502 if(arosxc != NULL) {
503 ObtainSemaphore(&arosxb->arosxc_lock);
505 arosxb->arosxc_count--;
507 arosxc->status.connected = FALSE;
509 if(AROSXClass_SendEvent(arosxb, ((((1L<<arosxc->id))<<28) | AROSX_EHMF_DISCONNECT), (APTR)1, (APTR)2)) {
510 mybug(-1,("Detach event sent\n"));
511 } else {
512 mybug(-1,("Detach event not sent\n"));
515 arosxc->controller_type = AROSX_CONTROLLER_TYPE_UNKNOWN;
516 ReleaseSemaphore(&arosxb->arosxc_lock);
518 mybug(-1, ("[AROSXClass] AROSXClass_DisconnectController: Disconnected controller number %01x\n", arosxc->id));
523 BOOL AROSXClass_SendEvent(LIBBASETYPEPTR arosxb, ULONG ehmt, APTR param1, APTR param2) {
525 struct AROSX_EventNote *en;
526 struct AROSX_EventHook *eh;
527 ULONG msgmask = ehmt;
529 BOOL ret = FALSE;
531 while((en = (struct AROSX_EventNote *) GetMsg(&arosxb->event_reply_port))) {
532 mybug(0, (" Free SendEvent (%p)\n", en));
533 FreeVec(en);
536 ObtainSemaphore(&arosxb->event_lock);
537 eh = (struct AROSX_EventHook *) arosxb->event_port_list.lh_Head;
538 while(eh->eh_Node.ln_Succ) {
540 TODO: Make message event mask differentiate controller type also
542 if((eh->eh_MsgMask>>28) & (msgmask)>>28) {
543 if((en = AllocVec(sizeof(struct AROSX_EventNote), MEMF_CLEAR|MEMF_ANY))) {
544 en->en_Msg.mn_ReplyPort = &arosxb->event_reply_port;
545 en->en_Msg.mn_Length = sizeof(struct AROSX_EventNote);
546 en->en_Event = ehmt;
547 en->en_Param1 = param1;
548 en->en_Param2 = param2;
549 mybug(0, ("[AROSXClass] SendEvent(%p, %p, %p)\n", ehmt, param1, param2));
550 PutMsg(eh->eh_MsgPort, &en->en_Msg);
551 ret = TRUE;
554 eh = (struct AROSX_EventHook *) eh->eh_Node.ln_Succ;
556 ReleaseSemaphore(&arosxb->event_lock);
558 if(ret) {
559 mybug(0,("Event sent\n"));
560 } else {
561 mybug(0,("Event not sent\n"));
564 return ret;
570 /**************************************************************************/
572 #undef ps
573 #define ps arosxc->Base
574 #undef TimerBase
575 #define TimerBase arosxc->TimerBase
577 /* /// "nHidTask()" */
578 AROS_UFH0(void, nHidTask)
580 AROS_USERFUNC_INIT
582 struct AROSXClassController *arosxc;
583 struct AROSXClassBase *arosxb;
585 struct PsdPipe *pp;
587 ULONG sigmask;
588 ULONG sigs;
590 UBYTE *epinbuf;
591 UBYTE *ep0buf;
593 LONG ioerr;
594 ULONG len;
597 This does not allocate arosxc, it is already present. It only fetches it from the task tc_UserData.
598 - Currently code assumes controller to be a gamepad
600 if((arosxc = nAllocHid()))
603 arosxb = arosxc->arosxb;
605 arosxc->TimerMP->mp_SigBit = AllocSignal(-1);
606 arosxc->TimerIO->tr_node.io_Message.mn_ReplyPort->mp_SigBit = arosxc->TimerMP->mp_SigBit;
607 arosxc->TimerIO->tr_node.io_Message.mn_ReplyPort->mp_SigTask = FindTask(NULL);
609 epinbuf = arosxc->EPInBuf;
610 ep0buf = arosxc->EP0Buf;
612 arosxc->status.signallost = TRUE;
614 Forbid();
615 if(arosxc->ReadySigTask)
617 Signal(arosxc->ReadySigTask, 1L<<arosxc->ReadySignal);
619 Permit();
620 sigmask = (1L<<arosxc->TaskMsgPort->mp_SigBit)|SIGBREAKF_CTRL_C;
622 psdDelayMS(2000);
624 psdPipeSetup(arosxc->EP0Pipe, URTF_IN|URTF_VENDOR|URTF_INTERFACE, 0x01, 0x0100, 0x00);
625 do {
626 ioerr = psdDoPipe(arosxc->EP0Pipe, ep0buf, 20);
627 } while(ioerr);
629 mybug(0, ("EP0: %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx\n",
630 ep0buf[0], ep0buf[1], ep0buf[2], ep0buf[3], ep0buf[4], ep0buf[5], ep0buf[6], ep0buf[7], ep0buf[8], ep0buf[9], ep0buf[10],
631 ep0buf[11], ep0buf[12], ep0buf[13], ep0buf[14], ep0buf[15], ep0buf[16], ep0buf[17], ep0buf[18], ep0buf[19]));
633 arosxc->status.wireless = (ep0buf[18]&(1<<0))? TRUE:FALSE;
636 :) First
638 Wireless Logitech F710
639 EP0: 00 14 ff f7 ff ff c0 ff c0 ff c0 ff c0 ff 00 00 00 00 01 00
640 - What we have here is a bitmask for all(?) the inputs
641 - We're the first, I think...
642 - If this holds true then the analog thumb stick values aren't exactly 16-bit wide :)
643 EPIn: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 b4 00 55 00 00 00
645 Wired Logitech F310
646 EP0: 00 14 ff f7 ff ff c0 ff c0 ff c0 ff c0 ff 00 00 00 00 00 00
648 XInput descriptors for various gamepads, we have one for this interface in *xinput_desc as a UBYTE array
649 - Check if it's an index to the bitmask
651 [Gamepad F710 descriptors, wireless]
652 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
653 16 33 16 1 1 36 129 20 3 0 3 19 2 0 3 0
655 [Gamepad F510 descriptors]
656 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
657 16 33 16 1 1 36 129 20 3 0 3 19 2 0 3 0
659 [Gamepad F310 descriptors]
660 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
661 16 33 16 1 1 36 129 20 3 0 3 19 2 0 3 0
663 [Xbox360 Controller, other]
664 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
665 16 33 16 1 1 36 129 20 3 0 3 19 2 0 3 0
667 [Xbox360 Controller, v1.60]
668 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
669 17 33 16 1 1 37 129 20 3 3 3 4 19 2 8 3 3
674 Set led ring to gamepad number. Should flash for a while and then lid on constantly.
677 UBYTE *bufout;
678 bufout = arosxc->EPOutBuf;
680 bufout[0] = 0x01;
681 bufout[1] = 0x03;
682 bufout[2] = arosxc->id + 2;
683 bufout[3] = 0x00;
684 bufout[4] = 0x00;
685 bufout[5] = 0x00;
686 bufout[6] = 0x00;
687 bufout[7] = 0x00;
688 bufout[8] = 0x00;
689 bufout[9] = 0x00;
690 bufout[10] = 0x00;
691 bufout[11] = 0x00;
693 psdDoPipe(arosxc->EPOutPipe, bufout, 12);
695 psdSendPipe(arosxc->EPInPipe, epinbuf, 20);
698 sigs = Wait(sigmask);
699 while((pp = (struct PsdPipe *) GetMsg(arosxc->TaskMsgPort)))
701 if(pp == arosxc->EPInPipe)
703 if(!(ioerr = psdGetPipeError(pp)))
705 len = psdGetPipeActual(pp);
707 if(Gamepad_ParseMsg(arosxc, epinbuf, len)) {
708 AROSXClass_SendEvent(arosxb, (((1L<<(arosxc->id)))<<28), (APTR)1, (APTR)2);
709 mybug(0,("Timestamp %u #%x\n", arosxc->arosx_gamepad.Timestamp, arosxc->id));
712 /* Wait */
714 arosxc->TimerIO->tr_node.io_Command = TR_ADDREQUEST;
715 arosxc->TimerIO->tr_time.tv_secs = 0;
716 arosxc->TimerIO->tr_time.tv_micro = 1000;
717 DoIO((struct IORequest *)arosxc->TimerIO);
720 } else {
721 mybug(1, ("Int Pipe failed %ld\n", ioerr));
722 psdDelayMS(200);
725 TODO: One Chinese gamepad doesn't wait for new input but sends data back at once (8mS apart...)
726 - Check if response is the same and not much time has elapsed between and set some babble flag and force wait between calls
728 //psdDelayMS(1);
729 psdSendPipe(arosxc->EPInPipe, epinbuf, 20);
730 break;
733 } while(!(sigs & SIGBREAKF_CTRL_C));
735 mybug(-1, ("(%d) Going down the river!\n", arosxc->id));
736 psdAbortPipe(arosxc->EPInPipe);
737 psdWaitPipe(arosxc->EPInPipe);
738 nFreeHid(arosxc);
741 AROS_USERFUNC_EXIT
743 /* \\\ */
745 /* /// "Gamepad_ParseMsg()" */
746 BOOL Gamepad_ParseMsg(struct AROSXClassController *arosxc, UBYTE *buf, ULONG len) {
748 struct AROSX_GAMEPAD arosx_gamepad_new;
750 struct AROSX_GAMEPAD *arosx_gamepad;
751 arosx_gamepad = &arosxc->arosx_gamepad;
753 struct timeval current;
755 BOOL ret = FALSE;
757 /* TODO: Check the input message type... */
760 Ta-daa!!
761 When Logitech Wireless Gamepad F710 goes to sleep we get this in our endpoint
762 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 84 00 00 00 0 0 00
763 bit 4 on byte 14
765 And this is the first message after it wakes on "A" button press
766 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 94 00 55 00 00 00
767 followed by this, the "A" button msg
768 Msg: 00 14 00 10 00 00 80 00 80 00 80 00 80 00 b4 00 55 00 00 00
770 Mode LED on
771 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 9c 00 55 00 00 00
773 Mode LED off
774 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 94 00 55 00 00 00
776 Long vibration (Enables rumble effect on controller)
777 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 b4 00 55 00 00 00
779 Short vibration (Disables rumble effect on controller)
780 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 94 00 55 00 00 00
782 Taking the controller out of range and we get this (same as removing the battery)
783 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 a4 00 00 00 00 00
785 Taking the battery out and the dongle soon sends this
786 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 a4 00 00 00 00 00
788 Re-inserting the battery and we get this
789 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 b4 00 55 00 00 00
791 Pressing "A" on Logitech (wired) Gamepad F310
792 Msg: 00 14 00 10 00 00 80 00 80 00 80 00 80 00 00 00 00 00 00 00
794 Toggling mode button on Logitech (wired) Gamepad F310 has no effect on the msg but we get one
795 some bit from byte 14 and on could tell if it's a wireless or wired (bit 7 of byte 14)
796 Byte 16 might be the battery level on Wireless F710?
797 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 00 00 00 00 00 00
798 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 00 00 00 00 00 00
800 mybug(0, ("EPIn: %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx\n",
801 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10],
802 buf[11], buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]));
806 Works at least with Logitech F710
808 arosxc->status.signallost = (buf[14]&(1<<4))? FALSE:TRUE;
811 This will map everything according to Microsoft game controller API
812 Check if our gamepad needs a timestamp (change on inputs)
814 arosx_gamepad_new.Buttons = (UWORD)(buf[2]<<0) | (buf[3]<<8);
815 if(arosx_gamepad_new.Buttons != arosx_gamepad->Buttons) {
816 arosx_gamepad->Buttons = arosx_gamepad_new.Buttons;
817 ret = TRUE;
820 arosx_gamepad_new.LeftTrigger = (UBYTE)(buf[4]);
821 if(arosx_gamepad_new.LeftTrigger != arosx_gamepad->LeftTrigger) {
822 arosx_gamepad->LeftTrigger = arosx_gamepad_new.LeftTrigger;
823 ret = TRUE;
826 arosx_gamepad_new.RightTrigger = (UBYTE)(buf[5]);
827 if(arosx_gamepad_new.RightTrigger != arosx_gamepad->RightTrigger) {
828 arosx_gamepad->RightTrigger = arosx_gamepad_new.RightTrigger;
829 ret = TRUE;
832 arosx_gamepad_new.ThumbLX = (WORD)((buf[6]) | (buf[7]<<8));
833 if(arosx_gamepad_new.ThumbLX != arosx_gamepad->ThumbLX) {
834 arosx_gamepad->ThumbLX = arosx_gamepad_new.ThumbLX;
835 ret = TRUE;
838 arosx_gamepad_new.ThumbLY = (WORD)((buf[8]) | (buf[9]<<8));
839 if(arosx_gamepad_new.ThumbLY != arosx_gamepad->ThumbLY) {
840 arosx_gamepad->ThumbLY = arosx_gamepad_new.ThumbLY;
841 ret = TRUE;
844 arosx_gamepad_new.ThumbRX = (WORD)((buf[10]) | (buf[11]<<8));
845 if(arosx_gamepad_new.ThumbRX != arosx_gamepad->ThumbRX) {
846 arosx_gamepad->ThumbRX = arosx_gamepad_new.ThumbRX;
847 ret = TRUE;
850 arosx_gamepad_new.ThumbRY = (WORD)((buf[12]) | (buf[13]<<8));
851 if(arosx_gamepad_new.ThumbRY != arosx_gamepad->ThumbRY) {
852 arosx_gamepad->ThumbRY = arosx_gamepad_new.ThumbRY;
853 ret = TRUE;
856 /* Rumble effect
857 UBYTE *bufout;
858 bufout = arosxc->EPOutBuf;
860 bufout[0] = 0x00;
861 bufout[1] = 0x08;
862 bufout[2] = 0x00;
863 bufout[3] = buf[6];
864 bufout[4] = buf[7];
865 bufout[5] = 0x00;
866 bufout[6] = 0x00;
867 bufout[7] = 0x00;
868 psdDoPipe(arosxc->EPOutPipe, bufout, 8);
871 if(ret) {
872 GetSysTime(&current);
873 arosx_gamepad->Timestamp = (ULONG)((((current.tv_secs-arosxc->initial_tv_secs) * 1000000) + (current.tv_micro-arosxc->initial_tv_micro))/1000);
876 return ret;
879 /* \\\ */
881 /* /// "nAllocHid()" */
882 struct AROSXClassController * nAllocHid(void)
884 struct Task *thistask;
885 struct AROSXClassController *arosxc;
887 thistask = FindTask(NULL);
888 arosxc = thistask->tc_UserData;
891 if(!(arosxc->Base = OpenLibrary("poseidon.library", 4)))
893 Alert(AG_OpenLib);
894 break;
896 psdGetAttrs(PGA_INTERFACE, arosxc->Interface,
897 IFA_Config, &arosxc->Config,
898 IFA_InterfaceNum, &arosxc->IfNum,
899 TAG_END);
900 psdGetAttrs(PGA_CONFIG, arosxc->Config,
901 CA_Device, &arosxc->Device,
902 TAG_END);
904 arosxc->EPIn = psdFindEndpoint(arosxc->Interface, NULL,
905 EA_IsIn, TRUE,
906 EA_TransferType, USEAF_INTERRUPT,
907 TAG_END);
909 arosxc->EPOut = psdFindEndpoint(arosxc->Interface, NULL,
910 EA_IsIn, FALSE,
911 EA_TransferType, USEAF_INTERRUPT,
912 TAG_END);
914 if((!arosxc->EPIn)|(!arosxc->EPOut))
916 mybug(1, ("Ooops!?! No Endpoints defined?\n"));
917 psdAddErrorMsg(RETURN_FAIL, (STRPTR) libname,
918 "Failed to get endpoints!");
919 break;
921 if((arosxc->InpMsgPort = CreateMsgPort()))
923 if((arosxc->InpIOReq = (struct IOStdReq *) CreateIORequest(arosxc->InpMsgPort, sizeof(struct IOStdReq))))
925 if(!OpenDevice("input.device", 0, (struct IORequest *) arosxc->InpIOReq, 0))
927 arosxc->InputBase = (struct Library *) arosxc->InpIOReq->io_Device;
928 if((arosxc->TaskMsgPort = CreateMsgPort()))
930 if((arosxc->EP0Pipe = psdAllocPipe(arosxc->Device, arosxc->TaskMsgPort, NULL)))
932 if((arosxc->EPInPipe = psdAllocPipe(arosxc->Device, arosxc->TaskMsgPort, arosxc->EPIn)))
934 psdSetAttrs(PGA_PIPE, arosxc->EPInPipe,
935 PPA_NakTimeout, FALSE,
936 PPA_AllowRuntPackets, TRUE,
937 TAG_END);
939 if((arosxc->EP0Buf = psdAllocVec(100)))
941 if((arosxc->EPInBuf = psdAllocVec(100)))
943 if((arosxc->EPOutPipe = psdAllocPipe(arosxc->Device, arosxc->TaskMsgPort, arosxc->EPOut)))
945 psdSetAttrs(PGA_PIPE, arosxc->EPOutPipe,
946 PPA_NakTimeout, FALSE,
947 PPA_AllowRuntPackets, TRUE,
948 TAG_END);
950 if((arosxc->EPOutBuf = psdAllocVec(100)))
952 arosxc->Task = thistask;
953 return(arosxc);
955 psdFreePipe(arosxc->EPOutPipe);
957 psdFreeVec(arosxc->EPInBuf);
959 psdFreeVec(arosxc->EP0Buf);
961 psdFreePipe(arosxc->EPInPipe);
963 psdFreePipe(arosxc->EP0Pipe);
965 DeleteMsgPort(arosxc->TaskMsgPort);
967 CloseDevice((struct IORequest *) arosxc->InpIOReq);
969 DeleteIORequest((struct IORequest *) arosxc->InpIOReq);
971 DeleteMsgPort(arosxc->InpMsgPort);
973 } while(FALSE);
974 CloseLibrary(arosxc->Base);
975 Forbid();
976 arosxc->Task = NULL;
977 if(arosxc->ReadySigTask)
979 Signal(arosxc->ReadySigTask, 1L<<arosxc->ReadySignal);
981 return(NULL);
983 /* \\\ */
985 /* /// "nFreeHid()" */
986 void nFreeHid(struct AROSXClassController *arosxc)
988 psdFreeVec(arosxc->EPOutBuf);
989 psdFreeVec(arosxc->EPInBuf);
991 psdFreePipe(arosxc->EPOutPipe);
992 psdFreePipe(arosxc->EPInPipe);
993 psdFreePipe(arosxc->EP0Pipe);
995 DeleteMsgPort(arosxc->TaskMsgPort);
996 CloseDevice((struct IORequest *) arosxc->InpIOReq);
997 DeleteIORequest((struct IORequest *) arosxc->InpIOReq);
998 DeleteMsgPort(arosxc->InpMsgPort);
1000 CloseLibrary(arosxc->Base);
1002 Forbid();
1003 arosxc->Task = NULL;
1004 if(arosxc->ReadySigTask)
1006 Signal(arosxc->ReadySigTask, 1L<<arosxc->ReadySignal);
1009 /* \\\ */
1011 /**************************************************************************/
1013 #undef ps
1015 /* /// "nOpenCfgWindow()" */
1016 LONG nOpenCfgWindow(struct AROSXClassController *arosxc)
1018 struct Library *ps;
1019 mybug(10, ("Opening GUI...\n"));
1020 if(!(ps = OpenLibrary("poseidon.library", 4)))
1022 return(FALSE);
1024 Forbid();
1025 if(!arosxc->GUITask)
1027 if((arosxc->GUITask = psdSpawnSubTask(MOD_NAME_STRING " GUI", nGUITask, arosxc)))
1029 Permit();
1030 CloseLibrary(ps);
1031 return(TRUE);
1034 Permit();
1035 CloseLibrary(ps);
1036 return(FALSE);
1038 /* \\\ */