wrong version committed
[AROS.git] / rom / devs / gameport / gameport.c
blobf2abfe6267586db709ad9f247ecea4ac553f93ae
1 /*
2 Copyright © 1995-2010, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Gameport device
6 Lang: English
7 */
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>
26 #include <oop/oop.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"
34 #ifdef __GNUC__
35 #include "gameport_gcc.h"
36 #endif
38 #define DEBUG 0
39 #include <aros/debug.h>
41 #include LC_LIBDEFS_FILE
43 /****************************************************************************************/
45 #define NEWSTYLE_DEVICE 1
46 #define ALIGN_IS_EVIL 1
48 #define ioStd(x) ((struct IOStdReq *)x)
49 #define gpUn ((struct GPUnit *)(ioreq->io_Unit))
51 #define min(a,b) ((a) < (b)) ? (a) : (b)
52 #define ABS(a) ((a) >= 0) ? (a) : (-(a))
53 #define ALIGN(x) ((((x) + (__AROS_STRUCTURE_ALIGNMENT - 1)) / \
54 __AROS_STRUCTURE_ALIGNMENT) * __AROS_STRUCTURE_ALIGNMENT)
56 #if ALIGN_IS_EVIL
58 #define NUM_INPUTEVENTS(bytesize) ((bytesize) / sizeof(struct InputEvent))
59 #define NEXT_INPUTEVENT(event) (((struct InputEvent *)(event)) + 1)
61 #else
63 /* Number of InputEvents we can store in io_Data */
64 /* be careful, the io_Length might be the size of the InputEvent structure,
65 but it can be that the ALIGN() returns a larger size and then nEvents would
66 be 0.
69 #define NUM_INPUTEVENTS(bytesize) (((bytesize) == sizeof(struct InputEvent)) ? \
70 1 : (bytesize) / ALIGN(sizeof(struct InputEvent)))
71 #define NEXT_INPUTEVENT(event) ((struct InputEvent *)((UBYTE*)(event) + \
72 ALIGN(sizeof(struct InputEvent))))
74 #endif /* ALIGN_IS_EVIL */
76 #define IECODE_DUMMY_WHEEL 0xFE
78 /****************************************************************************************/
80 #if NEWSTYLE_DEVICE
82 static const UWORD SupportedCommands[] =
84 CMD_CLEAR,
85 GPD_ASKCTYPE,
86 GPD_SETCTYPE,
87 GPD_ASKTRIGGER,
88 GPD_SETTRIGGER,
89 GPD_READEVENT,
90 NSCMD_DEVICEQUERY,
94 #endif
96 /****************************************************************************************/
98 static BOOL fillrequest(struct IORequest *ioreq, BOOL *trigged, struct GameportBase *GPBase);
99 static VOID mouseCallback(struct GameportBase *GPBase,
100 struct pHidd_Mouse_ExtEvent *ev);
101 static AROS_INTP(gpSendQueuedEvents);
104 /****************************************************************************************/
106 /* 'data' is a pointer to GPBase->gp_nTicks. */
108 AROS_INTH1(gpVBlank, LIBBASETYPEPTR, GPBase)
110 AROS_INTFUNC_INIT
112 if (GPBase->gp_nTicks < ~0)
114 GPBase->gp_nTicks++;
117 return 0;
119 AROS_INTFUNC_EXIT
122 static int GM_UNIQUENAME(init)(LIBBASETYPEPTR GPBase)
124 int i;
126 /* reset static data */
127 HiddMouseAB = 0;
129 for(i = 0; i < GP_NUNITS; i++)
131 GPBase->gp_cTypes[i] = GPCT_NOCONTROLLER;
134 InitSemaphore(&GPBase->gp_QueueLock);
135 InitSemaphore(&GPBase->gp_Lock);
136 NEWLIST(&GPBase->gp_PendingQueue);
138 GPBase->gp_Interrupt.is_Node.ln_Type = NT_INTERRUPT;
139 GPBase->gp_Interrupt.is_Node.ln_Pri = 0;
140 GPBase->gp_Interrupt.is_Data = (APTR)GPBase;
141 GPBase->gp_Interrupt.is_Code = (VOID_FUNC)gpSendQueuedEvents;
143 GPBase->gp_VBlank.is_Code = (VOID_FUNC)gpVBlank;
144 GPBase->gp_VBlank.is_Data = GPBase;
145 GPBase->gp_VBlank.is_Node.ln_Name = "Gameport VBlank server";
146 GPBase->gp_VBlank.is_Node.ln_Pri = 0;
147 GPBase->gp_VBlank.is_Node.ln_Type = NT_INTERRUPT;
149 /* Add a VBLANK server to take care of event timing. */
150 AddIntServer(INTB_VERTB, &GPBase->gp_VBlank);
152 return TRUE;
155 /****************************************************************************************/
157 static int GM_UNIQUENAME(open)
159 LIBBASETYPEPTR GPBase,
160 struct IORequest *ioreq,
161 ULONG unitnum,
162 ULONG flags
165 struct Library *OOPBase;
167 /* Erroneous unit? */
168 if (unitnum > GP_MAXUNIT)
170 ioreq->io_Error = IOERR_OPENFAIL;
172 return FALSE;
175 if (ioreq->io_Message.mn_Length < sizeof(struct IOStdReq))
177 D(bug("gameport.device/open: IORequest structure passed to OpenDevice "
178 "is too small!\n"));
179 ioreq->io_Error = IOERR_OPENFAIL;
181 return FALSE;
184 if (GPBase->gp_eventBuffer == NULL)
186 GPBase->gp_eventBuffer = AllocMem(sizeof(UWORD) * GP_BUFFERSIZE,
187 MEMF_ANY);
190 /* No memory for key buffer? */
191 if (GPBase->gp_eventBuffer == NULL)
193 ioreq->io_Error = IOERR_OPENFAIL;
195 return FALSE;
198 if ((ioreq->io_Unit = AllocMem(sizeof(GPUnit), MEMF_CLEAR)) == NULL)
200 ioreq->io_Error = IOERR_OPENFAIL;
202 return FALSE;
205 gpUn->gpu_unitNum = unitnum;
207 OOPBase = OpenLibrary("oop.library", 0);
208 if (!OOPBase) {
209 ioreq->io_Error = IOERR_OPENFAIL;
210 return FALSE;
213 if (!HiddMouseAB)
215 HiddMouseAB = OOP_ObtainAttrBase(IID_Hidd_Mouse);
217 if (!HiddMouseAB)
219 ioreq->io_Error = IOERR_OPENFAIL;
220 CloseLibrary(OOPBase);
221 D(bug("gameport.device: Could not get attrbase\n"));
223 return FALSE;
227 D(bug("gameport.device: Attrbase: %x\n", HiddMouseAB));
229 /******* nlorentz: End of stuff added by me ********/
231 if(!GPBase->gp_MouseHiddBase)
233 GPBase->gp_MouseHiddBase = OpenLibrary("mouse.hidd", 0);
235 /* Install our own keyboard handler if opened for the first time */
236 if(GPBase->gp_MouseHiddBase) {
237 struct TagItem tags[] = {
238 { aHidd_Mouse_IrqHandler , (IPTR)mouseCallback},
239 { aHidd_Mouse_IrqHandlerData, (IPTR)GPBase },
240 { TAG_DONE }
243 GPBase->gp_Hidd = OOP_NewObject(NULL, CLID_Hidd_Mouse, tags);
244 D(bug("keyboard.device: keyboard HIDD object 0x%p\n", GPBase->gp_Hidd));
245 if(!GPBase->gp_Hidd)
247 CloseLibrary(GPBase->gp_MouseHiddBase);
248 GPBase->gp_MouseHiddBase = NULL; /* Do cleanup below. */
253 CloseLibrary(OOPBase);
255 if(!GPBase->gp_MouseHiddBase)
257 ioreq->io_Error = IOERR_OPENFAIL;
258 return FALSE;
259 /* TODO: Clean up. */
262 return TRUE;
265 /****************************************************************************************/
267 static int GM_UNIQUENAME(close)
269 LIBBASETYPEPTR GPBase,
270 struct IORequest *ioreq
273 FreeMem(ioreq->io_Unit, sizeof(GPUnit));
275 return TRUE;
278 /****************************************************************************************/
280 ADD2INITLIB(GM_UNIQUENAME(init),0)
281 ADD2OPENDEV(GM_UNIQUENAME(open),0)
282 ADD2CLOSEDEV(GM_UNIQUENAME(close),0)
284 /****************************************************************************************/
286 AROS_LH1(void, beginio,
287 AROS_LHA(struct IORequest *, ioreq, A1),
288 struct GameportBase *, GPBase, 5, Gameport)
290 AROS_LIBFUNC_INIT
292 BOOL request_queued = FALSE;
294 D(bug("gpd: beginio(ioreq=%p, cmd=%d)\n", ioreq, ioreq->io_Command));
296 /* WaitIO will look into this */
297 ioreq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
298 ioreq->io_Error = 0;
300 switch (ioreq->io_Command)
302 #if NEWSTYLE_DEVICE
303 case NSCMD_DEVICEQUERY:
304 if(ioStd(ioreq)->io_Length < ((LONG)OFFSET(NSDeviceQueryResult, SupportedCommands)) + sizeof(UWORD *))
306 ioreq->io_Error = IOERR_BADLENGTH;
308 else
310 struct NSDeviceQueryResult *d;
312 d = (struct NSDeviceQueryResult *)ioStd(ioreq)->io_Data;
314 d->DevQueryFormat = 0;
315 d->SizeAvailable = sizeof(struct NSDeviceQueryResult);
316 d->DeviceType = NSDEVTYPE_GAMEPORT;
317 d->DeviceSubType = 0;
318 d->SupportedCommands = (UWORD *)SupportedCommands;
320 ioStd(ioreq)->io_Actual = sizeof(struct NSDeviceQueryResult);
322 break;
323 #endif
325 case CMD_CLEAR:
326 gpUn->gpu_readPos = GPBase->gp_writePos;
327 break;
329 case GPD_ASKCTYPE:
330 if (ioStd(ioreq)->io_Length < sizeof(UBYTE))
332 ioreq->io_Error = IOERR_BADLENGTH;
333 break;
336 ObtainSemaphoreShared(&GPBase->gp_Lock);
337 *((UBYTE *)(ioStd(ioreq)->io_Data)) = (GPBase->gp_cTypes)[gpUn->gpu_unitNum];
338 ReleaseSemaphore(&GPBase->gp_Lock);
339 break;
341 case GPD_SETCTYPE:
342 if (ioStd(ioreq)->io_Length != sizeof(UBYTE))
344 ioreq->io_Error = IOERR_BADLENGTH;
345 break;
348 ObtainSemaphore(&GPBase->gp_Lock);
349 (GPBase->gp_cTypes)[gpUn->gpu_unitNum] = *((UBYTE *)(ioStd(ioreq)->io_Data));
350 ReleaseSemaphore(&GPBase->gp_Lock);
351 break;
353 case GPD_ASKTRIGGER:
354 if (ioStd(ioreq)->io_Length != sizeof(struct GamePortTrigger))
356 ioreq->io_Error = IOERR_BADLENGTH;
357 break;
360 *((struct GamePortTrigger *)(ioStd(ioreq)->io_Data)) = gpUn->gpu_trigger;
361 break;
363 case GPD_SETTRIGGER:
364 if (ioStd(ioreq)->io_Length != sizeof(struct GamePortTrigger))
366 ioreq->io_Error = IOERR_BADLENGTH;
367 break;
370 gpUn->gpu_trigger = *((struct GamePortTrigger *)(ioStd(ioreq)->io_Data));
371 break;
373 case GPD_READEVENT:
374 #if 0
375 if(((IPTR)(&(ioStd(ioreq)->io_Data)) & (__AROS_STRUCTURE_ALIGNMENT - 1)) != 0)
377 D(bug("gpd: Bad address\n"));
378 ioreq->io_Error = IOERR_BADADDRESS;
379 break;
381 #endif
383 D(bug("gpd: Readpos: %d, Writepos: %d\n", gpUn->gpu_readPos,
384 GPBase->gp_writePos));
386 /* We queue the request if there are no events in the queue or if
387 the unit didn't trig on the events thate were in the queue. */
389 Disable(); /* !! */
391 if (gpUn->gpu_readPos == GPBase->gp_writePos)
393 request_queued = TRUE;
395 else
397 BOOL trigged;
399 fillrequest(ioreq, &trigged, GPBase);
401 if (!trigged)
403 request_queued = TRUE;
407 if (request_queued)
409 ioreq->io_Flags &= ~IOF_QUICK;
411 D(bug("gpd: No mouse events, putting request in queue\n"));
413 gpUn->gpu_flags |= GBUF_PENDING;
414 AddTail((struct List *)&GPBase->gp_PendingQueue,
415 (struct Node *)ioreq);
418 Enable();
420 break;
422 default:
423 ioreq->io_Error = IOERR_NOCMD;
424 break;
426 } /* switch (ioreq->io_Command) */
428 /* If the quick bit is not set, send the message to the port */
429 if (!(ioreq->io_Flags & IOF_QUICK) && !request_queued)
431 ReplyMsg(&ioreq->io_Message);
434 AROS_LIBFUNC_EXIT
437 /******************************************************************************/
440 AROS_LH1(LONG, abortio,
441 AROS_LHA(struct IORequest *, ioreq, A1),
442 struct GameportBase *, GPBase, 6, Gameport)
444 AROS_LIBFUNC_INIT
446 LONG ret = -1;
448 Disable();
450 if (gpUn->gpu_flags & GBUF_PENDING)
452 if (ioreq->io_Message.mn_Node.ln_Type == NT_MESSAGE)
454 Remove((struct Node *)ioreq);
455 ReplyMsg(&ioreq->io_Message);
457 ioreq->io_Error = IOERR_ABORTED;
459 if (IsListEmpty(&GPBase->gp_PendingQueue))
461 gpUn->gpu_flags &= ~GBUF_PENDING;
464 ret = 0;
468 Enable();
470 return ret;
472 AROS_LIBFUNC_EXIT
475 /****************************************************************************************/
477 static VOID mouseCallback(struct GameportBase *GPBase,
478 struct pHidd_Mouse_ExtEvent *ev)
480 UWORD amigacode = 0;
482 D(bug("mouseCallBack(GPBase=%p, button=%d, x=%d, y=%d, type=%d, flags=0x%04X)\n",
483 GPBase, ev->button, ev->x, ev->y, ev->type, ev->flags));
485 /* Convert the event */
486 switch (ev->button)
488 case vHidd_Mouse_Button1:
489 amigacode = IECODE_LBUTTON;
490 break;
492 case vHidd_Mouse_Button2:
493 amigacode = IECODE_RBUTTON;
494 break;
496 case vHidd_Mouse_Button3:
497 amigacode = IECODE_MBUTTON;
498 break;
501 switch (ev->type)
503 case vHidd_Mouse_Release:
504 amigacode |= IECODE_UP_PREFIX;
505 break;
507 case vHidd_Mouse_Motion:
508 amigacode = IECODE_NOBUTTON;
509 break;
511 case vHidd_Mouse_WheelMotion:
512 amigacode = IECODE_DUMMY_WHEEL;
513 break;
516 Disable();
518 GPBase->gp_eventBuffer[GPBase->gp_writePos++] = amigacode;
519 GPBase->gp_eventBuffer[GPBase->gp_writePos++] = ev->x;
520 GPBase->gp_eventBuffer[GPBase->gp_writePos++] = ev->y;
521 GPBase->gp_eventBuffer[GPBase->gp_writePos++] = ev->flags;
523 D(bug("Wrote to buffer\n"));
525 if (GPBase->gp_writePos == GP_NUMELEMENTS)
527 GPBase->gp_writePos = 0;
531 if (!IsListEmpty(&GPBase->gp_PendingQueue))
533 #if 0
534 D(bug("doing software irq, node type=%d\n", GPBase->gp_Interrupt.is_Node.ln_Type));
535 Cause(&GPBase->gp_Interrupt);
536 #else
537 AROS_INTC1(gpSendQueuedEvents, GPBase);
538 #endif
541 Enable();
544 /****************************************************************************************/
546 /* nlorentz: Software interrupt to be called when keys are received
547 Copied and pasted from the function above */
549 #undef SysBase
551 static AROS_INTH1(gpSendQueuedEvents, struct GameportBase *, GPBase)
553 AROS_INTFUNC_INIT
555 /* Broadcast keys */
556 struct IORequest *ioreq, *nextnode;
557 struct List *pendingList;
559 pendingList = (struct List *)&GPBase->gp_PendingQueue;
561 D(bug("Inside software irq\n"));
563 ForeachNodeSafe(pendingList, ioreq, nextnode)
565 BOOL moreevents, trigged;
567 D(bug("Replying msg\n"));
568 moreevents = fillrequest(ioreq, &trigged, GPBase);
570 if (trigged)
572 Remove((struct Node *)ioreq);
573 ReplyMsg((struct Message *)&ioreq->io_Message);
576 if (!moreevents)
578 break;
582 if (IsListEmpty(pendingList))
584 gpUn->gpu_flags &= ~GBUF_PENDING;
587 return 0;
589 AROS_INTFUNC_EXIT
592 /****************************************************************************************/
594 /* When this function is called, there *must* be at least one event ready for
595 processing. It returns TRUE as long as there are more events to preocess */
597 static BOOL fillrequest(struct IORequest *ioreq, BOOL *trigged,
598 struct GameportBase *GPBase)
600 BOOL moreevents = TRUE;
601 BOOL down, up, wheel;
602 int i; /* Loop variable */
603 int nEvents; /* Number of struct InputEvent that there
604 is room for in memory pointed to by
605 io_Data */
606 struct InputEvent *event; /* Temporary variable */
608 *trigged = FALSE;
610 nEvents = NUM_INPUTEVENTS(ioStd(ioreq)->io_Length);
612 if (nEvents == 0)
614 ioreq->io_Error = IOERR_BADLENGTH;
615 D(bug("gpd: Bad length\n"));
617 return TRUE;
620 event = (struct InputEvent *)(ioStd(ioreq)->io_Data);
622 ioreq->io_Error = 0;
624 for (i = 0; i < nEvents; ) /* no i++ here, this is done if event is to report */
626 UWORD code;
627 WORD x;
628 WORD y;
629 WORD flags;
631 code = GPBase->gp_eventBuffer[gpUn->gpu_readPos++];
632 x = GPBase->gp_eventBuffer[gpUn->gpu_readPos++];
633 y = GPBase->gp_eventBuffer[gpUn->gpu_readPos++];
634 flags = GPBase->gp_eventBuffer[gpUn->gpu_readPos++];
636 down = up = wheel = FALSE; /* Reset states */
638 /* Take care of the qualifiers */
639 switch (code)
641 case IECODE_LBUTTON:
642 gpUn->gpu_Qualifiers |= IEQUALIFIER_LEFTBUTTON;
643 down = TRUE;
644 break;
646 case IECODE_LBUTTON | IECODE_UP_PREFIX:
647 gpUn->gpu_Qualifiers &= ~IEQUALIFIER_LEFTBUTTON;
648 up = TRUE;
649 break;
651 case IECODE_MBUTTON:
652 gpUn->gpu_Qualifiers |= IEQUALIFIER_MIDBUTTON;
653 down = TRUE;
654 break;
656 case IECODE_MBUTTON | IECODE_UP_PREFIX:
657 gpUn->gpu_Qualifiers &= ~IEQUALIFIER_MIDBUTTON;
658 up = TRUE;
659 break;
661 case IECODE_RBUTTON:
662 gpUn->gpu_Qualifiers |= IEQUALIFIER_RBUTTON;
663 down = TRUE;
664 break;
666 case IECODE_RBUTTON | IECODE_UP_PREFIX:
667 gpUn->gpu_Qualifiers &= ~IEQUALIFIER_RBUTTON;
668 up = TRUE;
669 break;
671 case IECODE_DUMMY_WHEEL:
672 wheel = TRUE;
673 if (y < 0)
675 code = RAWKEY_NM_WHEEL_UP;
677 else if (y > 0)
679 code = RAWKEY_NM_WHEEL_DOWN;
681 else if (x < 0)
683 code = RAWKEY_NM_WHEEL_LEFT;
685 else if (x > 0)
687 code = RAWKEY_NM_WHEEL_RIGHT;
689 else
691 wheel = FALSE;
693 x = y = 0;
694 break;
697 if (gpUn->gpu_readPos == GP_NUMELEMENTS)
699 gpUn->gpu_readPos = 0;
702 D(bug("gpd: Adding event of code %d\n", code));
704 #if 0
706 #warning This needs to be fixed. And needs different handling, depending on whether coords are relative (x86-native) or not (x86-linux)!!!!
709 /* Should we report this event? */
710 if((down && (gpUn->gpu_trigger.gpt_Keys & GPTF_DOWNKEYS)) ||
711 (up && (gpUn->gpu_trigger.gpt_Keys & GPTF_UPKEYS)) ||
712 (ABS(gpUn->gpu_lastX - x) > gpUn->gpu_trigger.gpt_XDelta) ||
713 (ABS(gpUn->gpu_lastY - y) > gpUn->gpu_trigger.gpt_YDelta) ||
714 (GPBase->gp_nTicks > gpUn->gpu_trigger.gpt_Timeout) ||
715 (wheel))
716 #else
717 (void)(wheel); /* This is unused */
718 (void)(down); /* This is unused */
719 #endif
722 i++;
724 if (*trigged == TRUE)
726 event = event->ie_NextEvent;
728 else
730 *trigged = TRUE;
733 event->ie_Class = (wheel ? IECLASS_NEWMOUSE : IECLASS_RAWMOUSE);
734 event->ie_SubClass = 0; /* Only port 0 for now */
735 event->ie_Code = code;
736 event->ie_Qualifier = gpUn->gpu_Qualifiers;
737 if (flags & vHidd_Mouse_Relative) event->ie_Qualifier |= IEQUALIFIER_RELATIVEMOUSE;
739 event->ie_X = x;
740 event->ie_Y = y;
742 gpUn->gpu_lastX = x;
743 gpUn->gpu_lastY = y;
745 event->ie_TimeStamp.tv_secs = GPBase->gp_nTicks;
746 event->ie_TimeStamp.tv_micro = 0;
748 /* Reset frame delta counter */
749 GPBase->gp_nTicks = 0;
752 event->ie_NextEvent = NEXT_INPUTEVENT(event);
755 /* No more keys in buffer? */
756 if (gpUn->gpu_readPos == GPBase->gp_writePos)
758 moreevents = FALSE;
759 break;
763 event->ie_NextEvent = NULL;
765 return moreevents;
768 /****************************************************************************************/
770 static const char end = 0;
772 /****************************************************************************************/