2 Copyright © 2018-2019, The AROS Development Team. All rights reserved.
5 Desc: Gamepad (XInput) USB class driver
10 #include <aros/libcall.h>
11 #include <devices/timer.h>
13 #include <proto/timer.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
);
31 static int libInit(LIBBASETYPEPTR arosxb
)
34 mybug(0, ("libInit arosxb: 0x%08lx SysBase: 0x%08lx\n", arosxb
, SysBase
));
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
61 mybug(-1, ("libInit: MakeLibrary(\"arosx.library\") failed!\n"));
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"));
75 static int libOpen(LIBBASETYPEPTR arosxb
)
77 mybug(0, ("libOpen arosxb: 0x%08lx\n", arosxb
));
81 static int libExpunge(LIBBASETYPEPTR arosxb
)
83 mybug(10, ("libExpunge arosxb: 0x%08lx\n", arosxb
));
84 //CloseLibrary((struct Library *) UtilityBase);
88 ADD2INITLIB(libInit
, 0)
89 ADD2OPENLIB(libOpen
, 0)
90 ADD2EXPUNGELIB(libExpunge
, 0)
94 * ***********************************************************************
95 * * Library functions *
96 * ***********************************************************************
100 /* /// "usbAttemptInterfaceBinding()" */
101 struct AROSXClassController
* usbAttemptInterfaceBinding(struct AROSXClassBase
*arosxb
, struct PsdInterface
*pif
)
105 struct AROSXClassController
*arosxc
;
111 struct PsdConfig
*pc
;
112 struct PsdDevice
*pd
;
114 struct PsdDescriptor
*pdd
;
118 struct Task
*tmptask
;
120 mybug(0, ("nepHidAttemptInterfaceBinding(%08lx)\n", pif
));
122 if((ps
= OpenLibrary("poseidon.library", 4)))
125 psdGetAttrs(PGA_INTERFACE
, pif
,
127 IFA_SubClass
, &subclass
,
128 IFA_Protocol
, &proto
,
132 psdGetAttrs(PGA_CONFIG
, pc
,
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
));
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
);
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
));
174 if((arosxc
= AROSXClass_ConnectController(arosxb
, AROSX_CONTROLLER_TYPE_GAMEPAD
)))
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
);
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
);
201 arosxc
->ReadySigTask
= NULL
;
202 //FreeSignal(arosxc->ReadySignal);
203 AROSXClass_DisconnectController(arosxb
, arosxc
);;
212 /* /// "usbReleaseInterfaceBinding()" */
213 void usbReleaseInterfaceBinding(struct AROSXClassBase
*arosxb
, struct AROSXClassController
*arosxc
)
216 struct PsdConfig
*pc
;
217 struct PsdDevice
*pd
;
220 mybug(0, ("nepHidReleaseInterfaceBinding(%08lx)\n", arosxc
));
222 /* Kill the GUITask */
225 Signal(arosxc
->GUITask
, SIGBREAKF_CTRL_C
);
228 if((ps
= OpenLibrary("poseidon.library", 4)))
231 arosxc
->ReadySignal
= SIGB_SINGLE
;
232 arosxc
->ReadySigTask
= FindTask(NULL
);
235 Signal(arosxc
->Task
, SIGBREAKF_CTRL_C
);
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
);
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
)
267 mybug(0, ("nepHidGetAttrsA(%ld, %08lx, %08lx)\n", type
, usbstruct
, tags
));
271 if((ti
= FindTagItem(UCCA_Priority
, tags
)))
273 *((SIPTR
*) ti
->ti_Data
) = -100;
276 if((ti
= FindTagItem(UCCA_Description
, tags
)))
278 *((STRPTR
*) ti
->ti_Data
) = "Gamepad (XInput)";
281 if((ti
= FindTagItem(UCCA_HasClassCfgGUI
, tags
)))
283 *((IPTR
*) ti
->ti_Data
) = FALSE
;
286 if((ti
= FindTagItem(UCCA_HasBindingCfgGUI
, tags
)))
288 *((IPTR
*) ti
->ti_Data
) = TRUE
;
291 if((ti
= FindTagItem(UCCA_AfterDOSRestart
, tags
)))
293 *((IPTR
*) ti
->ti_Data
) = FALSE
;
298 if((ti
= FindTagItem(UCCA_UsingDefaultCfg
, tags
)))
300 *((IPTR
*) ti
->ti_Data
) = FALSE
;
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
)
323 /* /// "usbDoMethodA()" */
324 AROS_LH2(IPTR
, usbDoMethodA
,
325 AROS_LHA(ULONG
, methodid
, D0
),
326 AROS_LHA(IPTR
*, methoddata
, A1
),
327 LIBBASETYPEPTR
, arosxb
, 7, nep
)
331 mybug(0, ("Do Method %ld\n", 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]);
344 case UCM_OpenBindingCfgWindow
:
345 return(nOpenCfgWindow((struct AROSXClassController
*) methoddata
[0]));
357 struct AROSXClassController
*AROSXClass_CreateController(LIBBASETYPEPTR arosxb
, UBYTE id
) {
359 struct AROSXClassController
*arosxc
;
361 arosxc
= AllocVec(sizeof(struct AROSXClassController
), MEMF_ANY
|MEMF_CLEAR
);
364 mybug(-1, ("[AROSXClass] AROSXClass_CreateController: Failed to create new controller structure for controller %01x\n", 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)) {
387 #define TimerBase arosxc->TimerBase
388 struct timeval current
;
389 GetSysTime(¤t
);
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
);
402 DeleteExtIO((struct IORequest
*)arosxc
->TimerIO
);
404 DeletePort(arosxc
->TimerMP
);
408 AROSXClass_DestroyController(arosxb
, arosxc
);
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
) {
425 ObtainSemaphore(&arosxb
->arosxc_lock
);
427 FreeVec(arosxb
->arosxc_0
);
428 arosxb
->arosxc_0
= NULL
;
430 FreeVec(arosxb
->arosxc_1
);
431 arosxb
->arosxc_1
= NULL
;
433 FreeVec(arosxb
->arosxc_2
);
434 arosxb
->arosxc_2
= NULL
;
436 FreeVec(arosxb
->arosxc_3
);
437 arosxb
->arosxc_3
= NULL
;
439 ReleaseSemaphore(&arosxb
->arosxc_lock
);
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
;
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"));
466 ReleaseSemaphore(&arosxb
->arosxc_lock
);
467 mybug(-1, ("[AROSXClass] AROSXClass_ConnectController: How did you get here? Failing...\n"));
471 ReleaseSemaphore(&arosxb
->arosxc_lock
);
472 mybug(-1, ("[AROSXClass] AROSXClass_ConnectController: Controller count exceeded, failing...\n"));
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"));
491 mybug(-1,("Attach event not sent\n"));
494 ReleaseSemaphore(&arosxb
->arosxc_lock
);
500 void AROSXClass_DisconnectController(LIBBASETYPEPTR arosxb
, struct AROSXClassController
*arosxc
) {
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"));
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
;
531 while((en
= (struct AROSX_EventNote
*) GetMsg(&arosxb
->event_reply_port
))) {
532 mybug(0, (" Free SendEvent (%p)\n", 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
);
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
);
554 eh
= (struct AROSX_EventHook
*) eh
->eh_Node
.ln_Succ
;
556 ReleaseSemaphore(&arosxb
->event_lock
);
559 mybug(0,("Event sent\n"));
561 mybug(0,("Event not sent\n"));
570 /**************************************************************************/
573 #define ps arosxc->Base
575 #define TimerBase arosxc->TimerBase
577 /* /// "nHidTask()" */
578 AROS_UFH0(void, nHidTask
)
582 struct AROSXClassController
*arosxc
;
583 struct AROSXClassBase
*arosxb
;
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
;
615 if(arosxc
->ReadySigTask
)
617 Signal(arosxc
->ReadySigTask
, 1L<<arosxc
->ReadySignal
);
620 sigmask
= (1L<<arosxc
->TaskMsgPort
->mp_SigBit
)|SIGBREAKF_CTRL_C
;
624 psdPipeSetup(arosxc
->EP0Pipe
, URTF_IN
|URTF_VENDOR
|URTF_INTERFACE
, 0x01, 0x0100, 0x00);
626 ioerr
= psdDoPipe(arosxc
->EP0Pipe
, ep0buf
, 20);
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
;
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
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.
678 bufout
= arosxc
->EPOutBuf
;
682 bufout
[2] = arosxc
->id
+ 2;
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
));
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);
721 mybug(1, ("Int Pipe failed %ld\n", ioerr
));
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
729 psdSendPipe(arosxc
->EPInPipe
, epinbuf
, 20);
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
);
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
;
757 /* TODO: Check the input message type... */
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
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
771 Msg: 00 14 00 00 00 00 80 00 80 00 80 00 80 00 9c 00 55 00 00 00
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
;
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
;
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
;
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
;
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
;
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
;
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
;
858 bufout = arosxc->EPOutBuf;
868 psdDoPipe(arosxc->EPOutPipe, bufout, 8);
872 GetSysTime(¤t
);
873 arosx_gamepad
->Timestamp
= (ULONG
)((((current
.tv_secs
-arosxc
->initial_tv_secs
) * 1000000) + (current
.tv_micro
-arosxc
->initial_tv_micro
))/1000);
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)))
896 psdGetAttrs(PGA_INTERFACE
, arosxc
->Interface
,
897 IFA_Config
, &arosxc
->Config
,
898 IFA_InterfaceNum
, &arosxc
->IfNum
,
900 psdGetAttrs(PGA_CONFIG
, arosxc
->Config
,
901 CA_Device
, &arosxc
->Device
,
904 arosxc
->EPIn
= psdFindEndpoint(arosxc
->Interface
, NULL
,
906 EA_TransferType
, USEAF_INTERRUPT
,
909 arosxc
->EPOut
= psdFindEndpoint(arosxc
->Interface
, NULL
,
911 EA_TransferType
, USEAF_INTERRUPT
,
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!");
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
,
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
,
950 if((arosxc
->EPOutBuf
= psdAllocVec(100)))
952 arosxc
->Task
= thistask
;
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
);
974 CloseLibrary(arosxc
->Base
);
977 if(arosxc
->ReadySigTask
)
979 Signal(arosxc
->ReadySigTask
, 1L<<arosxc
->ReadySignal
);
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
);
1003 arosxc
->Task
= NULL
;
1004 if(arosxc
->ReadySigTask
)
1006 Signal(arosxc
->ReadySigTask
, 1L<<arosxc
->ReadySignal
);
1011 /**************************************************************************/
1015 /* /// "nOpenCfgWindow()" */
1016 LONG
nOpenCfgWindow(struct AROSXClassController
*arosxc
)
1019 mybug(10, ("Opening GUI...\n"));
1020 if(!(ps
= OpenLibrary("poseidon.library", 4)))
1025 if(!arosxc
->GUITask
)
1027 if((arosxc
->GUITask
= psdSpawnSubTask(MOD_NAME_STRING
" GUI", nGUITask
, arosxc
)))