define __KERNEL_STRICT_NAMES to avoid inclusion of kernel types on systems that carry...
[cake.git] / rom / devs / gameport / gameport.c
blob9c4283b82a3a8175bcaea0d6b001d7ab7a40186f
1 /*
2 Copyright © 1995-2006, 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"
33 #include "devs_private.h"
35 #ifdef __GNUC__
36 #include "gameport_gcc.h"
37 #endif
39 #define DEBUG 0
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)
57 #if ALIGN_IS_EVIL
59 #define NUM_INPUTEVENTS(bytesize) ((bytesize) / sizeof(struct InputEvent))
60 #define NEXT_INPUTEVENT(event) (((struct InputEvent *)(event)) + 1)
62 #else
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
67 be 0.
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 /****************************************************************************************/
81 #if NEWSTYLE_DEVICE
83 static const UWORD SupportedCommands[] =
85 CMD_CLEAR,
86 GPD_ASKCTYPE,
87 GPD_SETCTYPE,
88 GPD_ASKTRIGGER,
89 GPD_SETTRIGGER,
90 GPD_READEVENT,
91 CMD_HIDDINIT,
92 NSCMD_DEVICEQUERY,
96 #endif
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))
119 AROS_USERFUNC_INIT
121 if ((*(ULONG *)data) < ~0)
123 (*(ULONG *)data)++;
126 return 0;
128 AROS_USERFUNC_EXIT
131 static int GM_UNIQUENAME(init)(LIBBASETYPEPTR GPBase)
133 int i;
135 /* reset static data */
136 HiddMouseAB = 0;
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);
161 return TRUE;
164 /****************************************************************************************/
166 static int GM_UNIQUENAME(open)
168 LIBBASETYPEPTR GPBase,
169 struct IORequest *ioreq,
170 ULONG unitnum,
171 ULONG flags
174 /* Erroneous unit? */
175 if (unitnum > GP_MAXUNIT)
177 ioreq->io_Error = IOERR_OPENFAIL;
179 return FALSE;
182 if (ioreq->io_Message.mn_Length < sizeof(struct IOStdReq))
184 D(bug("gameport.device/open: IORequest structure passed to OpenDevice "
185 "is too small!\n"));
186 ioreq->io_Error = IOERR_OPENFAIL;
188 return FALSE;
191 if (GPBase->gp_eventBuffer == NULL)
193 GPBase->gp_eventBuffer = AllocMem(sizeof(UWORD) * GP_BUFFERSIZE,
194 MEMF_ANY);
197 /* No memory for key buffer? */
198 if (GPBase->gp_eventBuffer == NULL)
200 ioreq->io_Error = IOERR_OPENFAIL;
202 return FALSE;
205 if ((ioreq->io_Unit = AllocMem(sizeof(GPUnit), MEMF_CLEAR)) == NULL)
207 ioreq->io_Error = IOERR_OPENFAIL;
209 return FALSE;
212 gpUn->gpu_unitNum = unitnum;
214 if (!HiddMouseAB)
216 HiddMouseAB = OOP_ObtainAttrBase(IID_Hidd_Mouse);
218 if (!HiddMouseAB)
220 ioreq->io_Error = IOERR_OPENFAIL;
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 return TRUE;
234 /****************************************************************************************/
236 static int GM_UNIQUENAME(close)
238 LIBBASETYPEPTR GPBase,
239 struct IORequest *ioreq
242 FreeMem(ioreq->io_Unit, sizeof(GPUnit));
244 return TRUE;
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)
259 AROS_LIBFUNC_INIT
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;
267 ioreq->io_Error = 0;
269 switch (ioreq->io_Command)
271 #if NEWSTYLE_DEVICE
272 case NSCMD_DEVICEQUERY:
273 if(ioStd(ioreq)->io_Length < ((LONG)OFFSET(NSDeviceQueryResult, SupportedCommands)) + sizeof(UWORD *))
275 ioreq->io_Error = IOERR_BADLENGTH;
277 else
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);
291 break;
292 #endif
294 case CMD_CLEAR:
295 gpUn->gpu_readPos = GPBase->gp_writePos;
296 break;
298 case GPD_ASKCTYPE:
299 if (ioStd(ioreq)->io_Length < sizeof(UBYTE))
301 ioreq->io_Error = IOERR_BADLENGTH;
302 break;
305 ObtainSemaphoreShared(&GPBase->gp_Lock);
306 *((UBYTE *)(ioStd(ioreq)->io_Data)) = (GPBase->gp_cTypes)[gpUn->gpu_unitNum];
307 ReleaseSemaphore(&GPBase->gp_Lock);
308 break;
310 case GPD_SETCTYPE:
311 if (ioStd(ioreq)->io_Length != sizeof(UBYTE))
313 ioreq->io_Error = IOERR_BADLENGTH;
314 break;
317 ObtainSemaphore(&GPBase->gp_Lock);
318 (GPBase->gp_cTypes)[gpUn->gpu_unitNum] = *((UBYTE *)(ioStd(ioreq)->io_Data));
319 ReleaseSemaphore(&GPBase->gp_Lock);
320 break;
322 case GPD_ASKTRIGGER:
323 if (ioStd(ioreq)->io_Length != sizeof(struct GamePortTrigger))
325 ioreq->io_Error = IOERR_BADLENGTH;
326 break;
329 *((struct GamePortTrigger *)(ioStd(ioreq)->io_Data)) = gpUn->gpu_trigger;
330 break;
332 case GPD_SETTRIGGER:
333 if (ioStd(ioreq)->io_Length != sizeof(struct GamePortTrigger))
335 ioreq->io_Error = IOERR_BADLENGTH;
336 break;
339 gpUn->gpu_trigger = *((struct GamePortTrigger *)(ioStd(ioreq)->io_Data));
340 break;
342 case GPD_READEVENT:
343 #if 0
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;
348 break;
350 #endif
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. */
358 Disable(); /* !! */
360 if (gpUn->gpu_readPos == GPBase->gp_writePos)
362 request_queued = TRUE;
364 else
366 BOOL trigged;
368 fillrequest(ioreq, &trigged, GPBase);
370 if (!trigged)
372 request_queued = TRUE;
376 if (request_queued)
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);
387 Enable();
389 break;
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.
403 case CMD_HIDDINIT:
405 struct TagItem tags[] =
407 { aHidd_Mouse_IrqHandler , (IPTR)mouseCallback },
408 { aHidd_Mouse_IrqHandlerData , (IPTR)GPBase },
409 { TAG_DONE }
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);
428 if (relativecoords)
430 GPBase->gp_RelativeMouse = TRUE;
432 break;
435 default:
436 ioreq->io_Error = IOERR_NOCMD;
437 break;
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);
447 AROS_LIBFUNC_EXIT
450 /******************************************************************************/
453 AROS_LH1(LONG, abortio,
454 AROS_LHA(struct IORequest *, ioreq, A1),
455 struct GameportBase *, GPBase, 6, Gameport)
457 AROS_LIBFUNC_INIT
459 LONG ret = -1;
461 Disable();
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;
477 ret = 0;
481 Enable();
483 return ret;
485 AROS_LIBFUNC_EXIT
488 /****************************************************************************************/
490 static VOID mouseCallback(struct GameportBase *GPBase,
491 struct pHidd_Mouse_Event *ev)
493 UWORD amigacode = 0;
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 */
499 switch (ev->button)
501 case vHidd_Mouse_Button1:
502 amigacode = IECODE_LBUTTON;
503 break;
505 case vHidd_Mouse_Button2:
506 amigacode = IECODE_RBUTTON;
507 break;
509 case vHidd_Mouse_Button3:
510 amigacode = IECODE_MBUTTON;
511 break;
514 switch (ev->type)
516 case vHidd_Mouse_Release:
517 amigacode |= IECODE_UP_PREFIX;
518 break;
520 case vHidd_Mouse_Motion:
521 amigacode = IECODE_NOBUTTON;
522 break;
524 case vHidd_Mouse_WheelMotion:
525 amigacode = IECODE_DUMMY_WHEEL;
526 break;
529 Disable();
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))
545 #if 0
546 D(bug("doing software irq, node type=%d\n", GPBase->gp_Interrupt.is_Node.ln_Type));
547 Cause(&GPBase->gp_Interrupt);
548 #else
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));
553 #endif
556 Enable();
559 /****************************************************************************************/
561 /* nlorentz: Software interrupt to be called when keys are received
562 Copied and pasted from the function above */
564 #undef SysBase
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))
571 AROS_USERFUNC_INIT
573 /* Broadcast keys */
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);
588 if (trigged)
590 Remove((struct Node *)ioreq);
591 ReplyMsg((struct Message *)&ioreq->io_Message);
594 if (!moreevents)
596 break;
600 if (IsListEmpty(pendingList))
602 gpUn->gpu_flags &= ~GBUF_PENDING;
605 AROS_USERFUNC_EXIT
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
621 io_Data */
622 struct InputEvent *event; /* Temporary variable */
624 *trigged = FALSE;
626 nEvents = NUM_INPUTEVENTS(ioStd(ioreq)->io_Length);
628 if (nEvents == 0)
630 ioreq->io_Error = IOERR_BADLENGTH;
631 D(bug("gpd: Bad length\n"));
633 return TRUE;
636 event = (struct InputEvent *)(ioStd(ioreq)->io_Data);
638 ioreq->io_Error = 0;
640 for (i = 0; i < nEvents; ) /* no i++ here, this is done if event is to report */
642 UWORD code;
643 WORD x;
644 WORD y;
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 */
653 switch (code)
655 case IECODE_LBUTTON:
656 gpUn->gpu_Qualifiers |= IEQUALIFIER_LEFTBUTTON;
657 down = TRUE;
658 break;
660 case IECODE_LBUTTON | IECODE_UP_PREFIX:
661 gpUn->gpu_Qualifiers &= ~IEQUALIFIER_LEFTBUTTON;
662 up = TRUE;
663 break;
665 case IECODE_MBUTTON:
666 gpUn->gpu_Qualifiers |= IEQUALIFIER_MIDBUTTON;
667 down = TRUE;
668 break;
670 case IECODE_MBUTTON | IECODE_UP_PREFIX:
671 gpUn->gpu_Qualifiers &= ~IEQUALIFIER_MIDBUTTON;
672 up = TRUE;
673 break;
675 case IECODE_RBUTTON:
676 gpUn->gpu_Qualifiers |= IEQUALIFIER_RBUTTON;
677 down = TRUE;
678 break;
680 case IECODE_RBUTTON | IECODE_UP_PREFIX:
681 gpUn->gpu_Qualifiers &= ~IEQUALIFIER_RBUTTON;
682 up = TRUE;
683 break;
685 case IECODE_DUMMY_WHEEL:
686 wheel = TRUE;
687 if (y < 0)
689 code = RAWKEY_NM_WHEEL_UP;
691 else if (y > 0)
693 code = RAWKEY_NM_WHEEL_DOWN;
695 else if (x < 0)
697 code = RAWKEY_NM_WHEEL_LEFT;
699 else if (x > 0)
701 code = RAWKEY_NM_WHEEL_RIGHT;
703 else
705 wheel = FALSE;
707 x = y = 0;
708 break;
711 if (gpUn->gpu_readPos == GP_NUMELEMENTS)
713 gpUn->gpu_readPos = 0;
716 D(bug("gpd: Adding event of code %d\n", code));
718 #if 0
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) ||
729 (wheel))
730 #endif
733 i++;
735 if (*trigged == TRUE)
737 event = event->ie_NextEvent;
739 else
741 *trigged = TRUE;
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;
750 event->ie_X = x;
751 event->ie_Y = y;
753 gpUn->gpu_lastX = x;
754 gpUn->gpu_lastY = y;
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)
769 moreevents = FALSE;
770 break;
774 event->ie_NextEvent = NULL;
776 return moreevents;
779 /****************************************************************************************/
781 static const char end = 0;
783 /****************************************************************************************/