2 Copyright © 1995-2006, The AROS Development Team. All rights reserved.
9 /* NOTE: Currently, only mice are supported */
11 /****************************************************************************************/
13 #include <exec/interrupts.h>
14 #include <exec/initializers.h>
15 #include <hardware/intbits.h>
16 #include <devices/inputevent.h>
17 #include <devices/gameport.h>
18 #include <devices/newstyle.h>
19 #include <devices/rawkeycodes.h>
20 #include <proto/exec.h>
21 #include <proto/dos.h>
22 #include <proto/oop.h>
23 #include <exec/memory.h>
24 #include <exec/errors.h>
25 #include <exec/lists.h>
27 #include <utility/utility.h>
28 #include <hidd/mouse.h>
29 #include <aros/libcall.h>
30 #include <aros/asmcall.h>
31 #include <aros/symbolsets.h>
32 #include "gameport_intern.h"
33 #include "devs_private.h"
36 #include "gameport_gcc.h"
40 #include <aros/debug.h>
42 #include LC_LIBDEFS_FILE
44 /****************************************************************************************/
46 #define NEWSTYLE_DEVICE 1
47 #define ALIGN_IS_EVIL 1
49 #define ioStd(x) ((struct IOStdReq *)x)
50 #define gpUn ((struct GPUnit *)(ioreq->io_Unit))
52 #define min(a,b) ((a) < (b)) ? (a) : (b)
53 #define ABS(a) ((a) >= 0) ? (a) : (-(a))
54 #define ALIGN(x) ((((x) + (__AROS_STRUCTURE_ALIGNMENT - 1)) / \
55 __AROS_STRUCTURE_ALIGNMENT) * __AROS_STRUCTURE_ALIGNMENT)
59 #define NUM_INPUTEVENTS(bytesize) ((bytesize) / sizeof(struct InputEvent))
60 #define NEXT_INPUTEVENT(event) (((struct InputEvent *)(event)) + 1)
64 /* Number of InputEvents we can store in io_Data */
65 /* be careful, the io_Length might be the size of the InputEvent structure,
66 but it can be that the ALIGN() returns a larger size and then nEvents would
70 #define NUM_INPUTEVENTS(bytesize) (((bytesize) == sizeof(struct InputEvent)) ? \
71 1 : (bytesize) / ALIGN(sizeof(struct InputEvent)))
72 #define NEXT_INPUTEVENT(event) ((struct InputEvent *)((UBYTE*)(event) + \
73 ALIGN(sizeof(struct InputEvent))))
75 #endif /* ALIGN_IS_EVIL */
77 #define IECODE_DUMMY_WHEEL 0xFE
79 /****************************************************************************************/
83 static const UWORD SupportedCommands
[] =
98 /****************************************************************************************/
100 static BOOL
fillrequest(struct IORequest
*ioreq
, BOOL
*trigged
, struct GameportBase
*GPBase
);
101 static VOID
mouseCallback(struct GameportBase
*GPBase
,
102 struct pHidd_Mouse_Event
*ev
);
103 AROS_UFP3S(VOID
, gpSendQueuedEvents
,
104 AROS_UFPA(struct GameportBase
*, GPBase
, A1
),
105 AROS_UFPA(APTR
, thisfunc
, A5
),
106 AROS_UFPA(struct ExecBase
* , SysBase
, A6
));
109 /****************************************************************************************/
111 /* 'data' is a pointer to GPBase->gp_nTicks. */
113 AROS_UFH4(ULONG
, gpVBlank
,
114 AROS_UFHA(ULONG
, dummy
, A0
),
115 AROS_UFHA(void *, data
, A1
),
116 AROS_UFHA(ULONG
, dummy2
, A5
),
117 AROS_UFHA(struct ExecBase
*, mySysBase
, A6
))
121 if ((*(ULONG
*)data
) < ~0)
131 static int GM_UNIQUENAME(init
)(LIBBASETYPEPTR GPBase
)
135 /* reset static data */
138 for(i
= 0; i
< GP_NUNITS
; i
++)
140 GPBase
->gp_cTypes
[i
] = GPCT_NOCONTROLLER
;
143 InitSemaphore(&GPBase
->gp_QueueLock
);
144 InitSemaphore(&GPBase
->gp_Lock
);
145 NEWLIST(&GPBase
->gp_PendingQueue
);
147 GPBase
->gp_Interrupt
.is_Node
.ln_Type
= NT_INTERRUPT
;
148 GPBase
->gp_Interrupt
.is_Node
.ln_Pri
= 0;
149 GPBase
->gp_Interrupt
.is_Data
= (APTR
)GPBase
;
150 GPBase
->gp_Interrupt
.is_Code
= gpSendQueuedEvents
;
152 GPBase
->gp_VBlank
.is_Code
= (APTR
)&gpVBlank
;
153 GPBase
->gp_VBlank
.is_Data
= (APTR
)&GPBase
->gp_nTicks
;
154 GPBase
->gp_VBlank
.is_Node
.ln_Name
= "Gameport VBlank server";
155 GPBase
->gp_VBlank
.is_Node
.ln_Pri
= 0;
156 GPBase
->gp_VBlank
.is_Node
.ln_Type
= NT_INTERRUPT
;
158 /* Add a VBLANK server to take care of event timing. */
159 AddIntServer(INTB_VERTB
, &GPBase
->gp_VBlank
);
164 /****************************************************************************************/
166 static int GM_UNIQUENAME(open
)
168 LIBBASETYPEPTR GPBase
,
169 struct IORequest
*ioreq
,
174 /* Erroneous unit? */
175 if (unitnum
> GP_MAXUNIT
)
177 ioreq
->io_Error
= IOERR_OPENFAIL
;
182 if (ioreq
->io_Message
.mn_Length
< sizeof(struct IOStdReq
))
184 D(bug("gameport.device/open: IORequest structure passed to OpenDevice "
186 ioreq
->io_Error
= IOERR_OPENFAIL
;
191 if (GPBase
->gp_eventBuffer
== NULL
)
193 GPBase
->gp_eventBuffer
= AllocMem(sizeof(UWORD
) * GP_BUFFERSIZE
,
197 /* No memory for key buffer? */
198 if (GPBase
->gp_eventBuffer
== NULL
)
200 ioreq
->io_Error
= IOERR_OPENFAIL
;
205 if ((ioreq
->io_Unit
= AllocMem(sizeof(GPUnit
), MEMF_CLEAR
)) == NULL
)
207 ioreq
->io_Error
= IOERR_OPENFAIL
;
212 gpUn
->gpu_unitNum
= unitnum
;
216 HiddMouseAB
= OOP_ObtainAttrBase(IID_Hidd_Mouse
);
220 ioreq
->io_Error
= IOERR_OPENFAIL
;
221 D(bug("gameport.device: Could not get attrbase\n"));
227 D(bug("gameport.device: Attrbase: %x\n", HiddMouseAB
));
229 /******* nlorentz: End of stuff added by me ********/
234 /****************************************************************************************/
236 static int GM_UNIQUENAME(close
)
238 LIBBASETYPEPTR GPBase
,
239 struct IORequest
*ioreq
242 FreeMem(ioreq
->io_Unit
, sizeof(GPUnit
));
247 /****************************************************************************************/
249 ADD2INITLIB(GM_UNIQUENAME(init
),0)
250 ADD2OPENDEV(GM_UNIQUENAME(open
),0)
251 ADD2CLOSEDEV(GM_UNIQUENAME(close
),0)
253 /****************************************************************************************/
255 AROS_LH1(void, beginio
,
256 AROS_LHA(struct IORequest
*, ioreq
, A1
),
257 struct GameportBase
*, GPBase
, 5, Gameport
)
261 BOOL request_queued
= FALSE
;
263 D(bug("gpd: beginio(ioreq=%p, cmd=%d)\n", ioreq
, ioreq
->io_Command
));
265 /* WaitIO will look into this */
266 ioreq
->io_Message
.mn_Node
.ln_Type
= NT_MESSAGE
;
269 switch (ioreq
->io_Command
)
272 case NSCMD_DEVICEQUERY
:
273 if(ioStd(ioreq
)->io_Length
< ((LONG
)OFFSET(NSDeviceQueryResult
, SupportedCommands
)) + sizeof(UWORD
*))
275 ioreq
->io_Error
= IOERR_BADLENGTH
;
279 struct NSDeviceQueryResult
*d
;
281 d
= (struct NSDeviceQueryResult
*)ioStd(ioreq
)->io_Data
;
283 d
->DevQueryFormat
= 0;
284 d
->SizeAvailable
= sizeof(struct NSDeviceQueryResult
);
285 d
->DeviceType
= NSDEVTYPE_GAMEPORT
;
286 d
->DeviceSubType
= 0;
287 d
->SupportedCommands
= (UWORD
*)SupportedCommands
;
289 ioStd(ioreq
)->io_Actual
= sizeof(struct NSDeviceQueryResult
);
295 gpUn
->gpu_readPos
= GPBase
->gp_writePos
;
299 if (ioStd(ioreq
)->io_Length
< sizeof(UBYTE
))
301 ioreq
->io_Error
= IOERR_BADLENGTH
;
305 ObtainSemaphoreShared(&GPBase
->gp_Lock
);
306 *((UBYTE
*)(ioStd(ioreq
)->io_Data
)) = (GPBase
->gp_cTypes
)[gpUn
->gpu_unitNum
];
307 ReleaseSemaphore(&GPBase
->gp_Lock
);
311 if (ioStd(ioreq
)->io_Length
!= sizeof(UBYTE
))
313 ioreq
->io_Error
= IOERR_BADLENGTH
;
317 ObtainSemaphore(&GPBase
->gp_Lock
);
318 (GPBase
->gp_cTypes
)[gpUn
->gpu_unitNum
] = *((UBYTE
*)(ioStd(ioreq
)->io_Data
));
319 ReleaseSemaphore(&GPBase
->gp_Lock
);
323 if (ioStd(ioreq
)->io_Length
!= sizeof(struct GamePortTrigger
))
325 ioreq
->io_Error
= IOERR_BADLENGTH
;
329 *((struct GamePortTrigger
*)(ioStd(ioreq
)->io_Data
)) = gpUn
->gpu_trigger
;
333 if (ioStd(ioreq
)->io_Length
!= sizeof(struct GamePortTrigger
))
335 ioreq
->io_Error
= IOERR_BADLENGTH
;
339 gpUn
->gpu_trigger
= *((struct GamePortTrigger
*)(ioStd(ioreq
)->io_Data
));
344 if(((IPTR
)(&(ioStd(ioreq
)->io_Data
)) & (__AROS_STRUCTURE_ALIGNMENT
- 1)) != 0)
346 D(bug("gpd: Bad address\n"));
347 ioreq
->io_Error
= IOERR_BADADDRESS
;
352 D(bug("gpd: Readpos: %d, Writepos: %d\n", gpUn
->gpu_readPos
,
353 GPBase
->gp_writePos
));
355 /* We queue the request if there are no events in the queue or if
356 the unit didn't trig on the events thate were in the queue. */
360 if (gpUn
->gpu_readPos
== GPBase
->gp_writePos
)
362 request_queued
= TRUE
;
368 fillrequest(ioreq
, &trigged
, GPBase
);
372 request_queued
= TRUE
;
378 ioreq
->io_Flags
&= ~IOF_QUICK
;
380 D(bug("gpd: No mouse events, putting request in queue\n"));
382 gpUn
->gpu_flags
|= GBUF_PENDING
;
383 AddTail((struct List
*)&GPBase
->gp_PendingQueue
,
384 (struct Node
*)ioreq
);
392 /* nlorentz: This command lets the gameport.device initialize
393 the HIDD to use. It must be done this way, because
394 HIDDs might be loaded from disk, and gameport.device is
395 inited before DOS is up and running.
396 The name of the HIDD class is in
397 ioStd(rew)->io_Data. Note that maybe we should
398 receive a pointer to an allreay created HIDD object instead.
399 Also note that the below is just a temporary hack, should
400 probably use IRQ HIDD instead to set the IRQ handler.
405 struct TagItem tags
[] =
407 { aHidd_Mouse_IrqHandler
, (IPTR
)mouseCallback
},
408 { aHidd_Mouse_IrqHandlerData
, (IPTR
)GPBase
},
411 IPTR relativecoords
= FALSE
;
413 D(bug("gameport.device: Received CMD_HIDDINIT, hiddname=\"%s\"\n",
414 (STRPTR
)ioStd(ioreq
)->io_Data
));
416 if (GPBase
->gp_Hidd
!= NULL
)
417 OOP_DisposeObject(GPBase
->gp_Hidd
);
419 GPBase
->gp_Hidd
= OOP_NewObject(NULL
, (STRPTR
)ioStd(ioreq
)->io_Data
, tags
);
420 if (!GPBase
->gp_Hidd
)
422 D(bug("gameport.device: Failed to open hidd\n"));
423 ioreq
->io_Error
= IOERR_OPENFAIL
;
426 OOP_GetAttr(GPBase
->gp_Hidd
, aHidd_Mouse_RelativeCoords
, &relativecoords
);
430 GPBase
->gp_RelativeMouse
= TRUE
;
436 ioreq
->io_Error
= IOERR_NOCMD
;
439 } /* switch (ioreq->io_Command) */
441 /* If the quick bit is not set, send the message to the port */
442 if (!(ioreq
->io_Flags
& IOF_QUICK
) && !request_queued
)
444 ReplyMsg(&ioreq
->io_Message
);
450 /******************************************************************************/
453 AROS_LH1(LONG
, abortio
,
454 AROS_LHA(struct IORequest
*, ioreq
, A1
),
455 struct GameportBase
*, GPBase
, 6, Gameport
)
463 if (gpUn
->gpu_flags
& GBUF_PENDING
)
465 if (ioreq
->io_Message
.mn_Node
.ln_Type
== NT_MESSAGE
)
467 Remove((struct Node
*)ioreq
);
468 ReplyMsg(&ioreq
->io_Message
);
470 ioreq
->io_Error
= IOERR_ABORTED
;
472 if (IsListEmpty(&GPBase
->gp_PendingQueue
))
474 gpUn
->gpu_flags
&= ~GBUF_PENDING
;
488 /****************************************************************************************/
490 static VOID
mouseCallback(struct GameportBase
*GPBase
,
491 struct pHidd_Mouse_Event
*ev
)
495 D(bug("mouseCallBack(GPBase=%p, button=%d, x=%d, y=%d, type=%d)\n",
496 GPBase
, ev
->button
, ev
->x
, ev
->y
, ev
->type
));
498 /* Convert the event */
501 case vHidd_Mouse_Button1
:
502 amigacode
= IECODE_LBUTTON
;
505 case vHidd_Mouse_Button2
:
506 amigacode
= IECODE_RBUTTON
;
509 case vHidd_Mouse_Button3
:
510 amigacode
= IECODE_MBUTTON
;
516 case vHidd_Mouse_Release
:
517 amigacode
|= IECODE_UP_PREFIX
;
520 case vHidd_Mouse_Motion
:
521 amigacode
= IECODE_NOBUTTON
;
524 case vHidd_Mouse_WheelMotion
:
525 amigacode
= IECODE_DUMMY_WHEEL
;
531 GPBase
->gp_eventBuffer
[GPBase
->gp_writePos
++] = amigacode
;
532 GPBase
->gp_eventBuffer
[GPBase
->gp_writePos
++] = ev
->x
;
533 GPBase
->gp_eventBuffer
[GPBase
->gp_writePos
++] = ev
->y
;
535 D(bug("Wrote to buffer\n"));
537 if (GPBase
->gp_writePos
== GP_NUMELEMENTS
)
539 GPBase
->gp_writePos
= 0;
543 if (!IsListEmpty(&GPBase
->gp_PendingQueue
))
546 D(bug("doing software irq, node type=%d\n", GPBase
->gp_Interrupt
.is_Node
.ln_Type
));
547 Cause(&GPBase
->gp_Interrupt
);
549 AROS_UFC3(VOID
, gpSendQueuedEvents
,
550 AROS_UFCA(struct GameportBase
*, GPBase
, A1
),
551 AROS_UFCA(APTR
, NULL
, A5
),
552 AROS_UFCA(struct ExecBase
* , SysBase
, A6
));
559 /****************************************************************************************/
561 /* nlorentz: Software interrupt to be called when keys are received
562 Copied and pasted from the function above */
566 AROS_UFH3S(VOID
, gpSendQueuedEvents
,
567 AROS_UFHA(struct GameportBase
*, GPBase
, A1
),
568 AROS_UFHA(APTR
, thisfunc
, A5
),
569 AROS_UFHA(struct ExecBase
*, SysBase
, A6
))
574 struct IORequest
*ioreq
, *nextnode
;
575 struct List
*pendingList
;
577 pendingList
= (struct List
*)&GPBase
->gp_PendingQueue
;
579 D(bug("Inside software irq\n"));
581 ForeachNodeSafe(pendingList
, ioreq
, nextnode
)
583 BOOL moreevents
, trigged
;
585 D(bug("Replying msg\n"));
586 moreevents
= fillrequest(ioreq
, &trigged
, GPBase
);
590 Remove((struct Node
*)ioreq
);
591 ReplyMsg((struct Message
*)&ioreq
->io_Message
);
600 if (IsListEmpty(pendingList
))
602 gpUn
->gpu_flags
&= ~GBUF_PENDING
;
608 /****************************************************************************************/
610 /* When this function is called, there *must* be at least one event ready for
611 processing. It returns TRUE as long as there are more events to preocess */
613 static BOOL
fillrequest(struct IORequest
*ioreq
, BOOL
*trigged
,
614 struct GameportBase
*GPBase
)
616 BOOL moreevents
= TRUE
;
617 BOOL down
, up
, wheel
;
618 int i
; /* Loop variable */
619 int nEvents
; /* Number of struct InputEvent that there
620 is room for in memory pointed to by
622 struct InputEvent
*event
; /* Temporary variable */
626 nEvents
= NUM_INPUTEVENTS(ioStd(ioreq
)->io_Length
);
630 ioreq
->io_Error
= IOERR_BADLENGTH
;
631 D(bug("gpd: Bad length\n"));
636 event
= (struct InputEvent
*)(ioStd(ioreq
)->io_Data
);
640 for (i
= 0; i
< nEvents
; ) /* no i++ here, this is done if event is to report */
646 code
= GPBase
->gp_eventBuffer
[gpUn
->gpu_readPos
++];
647 x
= GPBase
->gp_eventBuffer
[gpUn
->gpu_readPos
++];
648 y
= GPBase
->gp_eventBuffer
[gpUn
->gpu_readPos
++];
650 down
= up
= wheel
= FALSE
; /* Reset states */
652 /* Take care of the qualifiers */
656 gpUn
->gpu_Qualifiers
|= IEQUALIFIER_LEFTBUTTON
;
660 case IECODE_LBUTTON
| IECODE_UP_PREFIX
:
661 gpUn
->gpu_Qualifiers
&= ~IEQUALIFIER_LEFTBUTTON
;
666 gpUn
->gpu_Qualifiers
|= IEQUALIFIER_MIDBUTTON
;
670 case IECODE_MBUTTON
| IECODE_UP_PREFIX
:
671 gpUn
->gpu_Qualifiers
&= ~IEQUALIFIER_MIDBUTTON
;
676 gpUn
->gpu_Qualifiers
|= IEQUALIFIER_RBUTTON
;
680 case IECODE_RBUTTON
| IECODE_UP_PREFIX
:
681 gpUn
->gpu_Qualifiers
&= ~IEQUALIFIER_RBUTTON
;
685 case IECODE_DUMMY_WHEEL
:
689 code
= RAWKEY_NM_WHEEL_UP
;
693 code
= RAWKEY_NM_WHEEL_DOWN
;
697 code
= RAWKEY_NM_WHEEL_LEFT
;
701 code
= RAWKEY_NM_WHEEL_RIGHT
;
711 if (gpUn
->gpu_readPos
== GP_NUMELEMENTS
)
713 gpUn
->gpu_readPos
= 0;
716 D(bug("gpd: Adding event of code %d\n", code
));
720 #warning This needs to be fixed. And needs different handling, depending on whether coords are relative (x86-native) or not (x86-linux)!!!!
723 /* Should we report this event? */
724 if((down
&& (gpUn
->gpu_trigger
.gpt_Keys
& GPTF_DOWNKEYS
)) ||
725 (up
&& (gpUn
->gpu_trigger
.gpt_Keys
& GPTF_UPKEYS
)) ||
726 (ABS(gpUn
->gpu_lastX
- x
) > gpUn
->gpu_trigger
.gpt_XDelta
) ||
727 (ABS(gpUn
->gpu_lastY
- y
) > gpUn
->gpu_trigger
.gpt_YDelta
) ||
728 (GPBase
->gp_nTicks
> gpUn
->gpu_trigger
.gpt_Timeout
) ||
735 if (*trigged
== TRUE
)
737 event
= event
->ie_NextEvent
;
744 event
->ie_Class
= (wheel
? IECLASS_NEWMOUSE
: IECLASS_RAWMOUSE
);
745 event
->ie_SubClass
= 0; /* Only port 0 for now */
746 event
->ie_Code
= code
;
747 event
->ie_Qualifier
= gpUn
->gpu_Qualifiers
;
748 if (GPBase
->gp_RelativeMouse
) event
->ie_Qualifier
|= IEQUALIFIER_RELATIVEMOUSE
;
756 event
->ie_TimeStamp
.tv_secs
= GPBase
->gp_nTicks
;
757 event
->ie_TimeStamp
.tv_micro
= 0;
759 /* Reset frame delta counter */
760 GPBase
->gp_nTicks
= 0;
763 event
->ie_NextEvent
= NEXT_INPUTEVENT(event
);
766 /* No more keys in buffer? */
767 if (gpUn
->gpu_readPos
== GPBase
->gp_writePos
)
774 event
->ie_NextEvent
= NULL
;
779 /****************************************************************************************/
781 static const char end
= 0;
783 /****************************************************************************************/