added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / rom / devs / keyboard / keyboard.c
blob4043c68b85fa63b9fbf25708132150558bbdfa39
1 /*
2 Copyright © 1995-2006, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Keyboard device
6 Lang: English
7 */
9 /* HISTORY: 12.04.98 SDuvan Began work
10 xx.06.98 SDuvan Fixes, added amigakeyboard.HIDD
13 /****************************************************************************************/
15 #include <exec/resident.h>
16 #include <exec/interrupts.h>
17 #include <exec/initializers.h>
18 #include <devices/inputevent.h>
19 #include <devices/keyboard.h>
20 #include <devices/newstyle.h>
21 #include <proto/exec.h>
22 #include <proto/dos.h>
23 #include <proto/oop.h>
24 #include <exec/memory.h>
25 #include <exec/errors.h>
26 #include <exec/lists.h>
27 #include <oop/oop.h>
28 #include <utility/utility.h>
29 #include <hidd/keyboard.h>
30 #include <aros/libcall.h>
31 #include <aros/symbolsets.h>
32 #include "abstractkeycodes.h"
33 #include "keyboard_intern.h"
34 #include "devs_private.h"
36 #ifdef __GNUC__
37 #include "keyboard_gcc.h"
38 #endif
40 #include LC_LIBDEFS_FILE
42 #define DEBUG 0
43 #include <aros/debug.h>
45 /****************************************************************************************/
47 #define NEWSTYLE_DEVICE 1
48 #define ALIGN_IS_EVIL 1
50 #define ioStd(x) ((struct IOStdReq *)x)
51 #define kbUn ((struct KBUnit *)(ioreq->io_Unit))
53 #define min(a,b) ((a) < (b)) ? (a) : (b)
54 #define ALIGN(x) ((((x) + (__AROS_STRUCTURE_ALIGNMENT - 1)) / __AROS_STRUCTURE_ALIGNMENT) * __AROS_STRUCTURE_ALIGNMENT)
56 #define isQualifier(x) ((((x) & ~KEYUPMASK) >= AKC_QUALIFIERS_FIRST) && (((x) & ~KEYUPMASK) <= AKC_QUALIFIERS_LAST))
58 /* Temporary - we should make a bit vector of this to check for numeric pad keys */
59 #define isNumericPad(x) ((x) == AKC_NUM_1 || (x) == AKC_NUM_2 || \
60 (x) == AKC_NUM_3 || (x) == AKC_NUM_4 || \
61 (x) == AKC_NUM_5 || (x) == AKC_NUM_6 || \
62 (x) == AKC_NUM_7 || (x) == AKC_NUM_8 || \
63 (x) == AKC_NUM_9 || (x) == AKC_NUM_0 || \
64 (x) == AKC_NUM_POINT || (x) == AKC_NUM_ENTER || \
65 (x) == AKC_NUM_DASH || (x) == AKC_NUM_LPAREN || \
66 (x) == AKC_NUM_RPAREN || (x) == AKC_NUM_SLASH || \
67 (x) == AKC_NUM_PLUS || (x) == AKC_NUM_TIMES)
69 #if ALIGN_IS_EVIL
71 #define NUM_INPUTEVENTS(bytesize) ((bytesize) / sizeof(struct InputEvent))
72 #define NEXT_INPUTEVENT(event) (((struct InputEvent *)(event)) + 1)
74 #else
76 /* Number of InputEvents we can store in io_Data */
77 /* be careful, the io_Length might be the size of the InputEvent structure,
78 but it can be that the ALIGN() returns a larger size and then nEvents would
79 be 0.
82 #define NUM_INPUTEVENTS(bytesize) (((bytesize) == sizeof(struct InputEvent)) ? \
83 1 : (bytesize) / ALIGN(sizeof(struct InputEvent)))
84 #define NEXT_INPUTEVENT(event) ((struct InputEvent *)((UBYTE*)(event) + \
85 ALIGN(sizeof(struct InputEvent))))
87 #endif /* ALIGN_IS_EVIL */
89 /****************************************************************************************/
91 #if NEWSTYLE_DEVICE
93 static const UWORD SupportedCommands[] =
95 CMD_CLEAR,
96 KBD_ADDRESETHANDLER,
97 KBD_REMRESETHANDLER,
98 KBD_RESETHANDLERDONE,
99 KBD_READMATRIX,
100 KBD_READEVENT,
101 CMD_HIDDINIT,
102 NSCMD_DEVICEQUERY,
106 #endif
108 /****************************************************************************************/
110 VOID keyCallback(struct KeyboardBase *KBBase, UWORD keyCode);
111 AROS_UFP3(VOID, kbdSendQueuedEvents,
112 AROS_UFPA(struct KeyboardBase *, KBBase, A1),
113 AROS_UFPA(APTR, thisfunc, A5),
114 AROS_UFPA(struct ExecBase *, SysBase, A6));
115 static BOOL writeEvents(struct IORequest *ioreq, struct KeyboardBase *KBBase);
117 /****************************************************************************************/
119 static int GM_UNIQUENAME(Init)(LIBBASETYPEPTR KBBase)
121 /* reset static data */
122 HiddKbdAB = 0;
124 InitSemaphore(&KBBase->kb_QueueLock);
125 NEWLIST(&KBBase->kb_ResetHandlerList);
126 NEWLIST(&KBBase->kb_PendingQueue);
128 return TRUE;
131 /****************************************************************************************/
133 static int GM_UNIQUENAME(Open)
135 LIBBASETYPEPTR KBBase,
136 struct IORequest *ioreq,
137 ULONG unitnum,
138 ULONG flags
141 if (ioreq->io_Message.mn_Length < sizeof(struct IOStdReq))
143 D(bug("keyport.device/open: IORequest structure passed to OpenDevice is too small!\n"));
144 ioreq->io_Error = IOERR_OPENFAIL;
145 return FALSE;
148 if(KBBase->kb_keyBuffer == NULL)
150 KBBase->kb_keyBuffer = AllocMem(sizeof(UWORD)*KB_BUFFERSIZE, MEMF_ANY);
153 /* No memory for key buffer? */
154 if(KBBase->kb_keyBuffer == NULL)
156 ioreq->io_Error = IOERR_OPENFAIL;
157 return FALSE;
160 if((ioreq->io_Unit = AllocMem(sizeof(KBUnit), MEMF_CLEAR)) == NULL)
162 ioreq->io_Error = IOERR_OPENFAIL;
163 return FALSE;
166 /* nlorentz: Some extra stuff that must be inited */
167 if (NULL == KBBase->kb_Matrix)
169 KBBase->kb_Matrix = AllocMem(KB_MATRIXSIZE, MEMF_ANY|MEMF_CLEAR);
171 if (NULL == KBBase->kb_Matrix)
173 ioreq->io_Error = IOERR_OPENFAIL;
174 return FALSE;
178 if (!HiddKbdAB)
180 HiddKbdAB = OOP_ObtainAttrBase(IID_Hidd_Kbd);
181 if (!HiddKbdAB)
183 ioreq->io_Error = IOERR_OPENFAIL;
184 D(bug("keyboard.device: Could not get attrbase\n"));
185 return FALSE;
188 D(bug("keyboard.device: Attrbase: %x\n", HiddKbdAB));
190 KBBase->kb_Interrupt.is_Node.ln_Type = NT_INTERRUPT;
191 KBBase->kb_Interrupt.is_Node.ln_Pri = 0;
192 KBBase->kb_Interrupt.is_Data = (APTR)KBBase;
193 KBBase->kb_Interrupt.is_Code = kbdSendQueuedEvents;
195 /******* nlorentz: End of stuff added by me ********/
198 /* nlorentz: No lowlevel library yet */
199 #if 0
200 if(!KBBase->kb_LowLevelBase)
202 KBBase->kb_LowLevelBase = OpenLibrary("lowlevel.library", 41);
204 /* Install our own keyboard handler if opened for the first time */
205 if(KBBase->kb_LowLevelBase)
206 if((KBBase->kb_kbIrqHandle = AddKBInt(keyCallback, KBBase)) == NULL)
208 CloseLibrary(KBBase->kb_LowLevelBase);
209 KBBase->kb_LowLevelBase = NULL; /* Do cleanup below. */
214 if(!KBBase->kb_LowLevelBase)
216 ioreq->io_Error = IOERR_OPENFAIL;
217 return FALSE;
218 /* TODO: Clean up. */
220 #endif
222 return TRUE;
225 /****************************************************************************************/
227 static int GM_UNIQUENAME(Close)
229 LIBBASETYPEPTR KBBase,
230 struct IORequest *ioreq
233 FreeMem(ioreq->io_Unit, sizeof(KBUnit));
235 return TRUE;
238 /****************************************************************************************/
240 ADD2INITLIB(GM_UNIQUENAME(Init), 0)
241 ADD2OPENDEV(GM_UNIQUENAME(Open), 0)
242 ADD2CLOSEDEV(GM_UNIQUENAME(Close), 0)
244 /****************************************************************************************/
246 AROS_LH1(void, beginio,
247 AROS_LHA(struct IORequest *, ioreq, A1),
248 struct KeyboardBase *, KBBase, 5, Keyboard)
250 AROS_LIBFUNC_INIT
252 BOOL request_queued = FALSE;
255 D(bug("kbd: beginio(ioreq=%p, cmd=%d)\n", ioreq, ioreq->io_Command));
257 /* WaitIO will look into this */
258 ioreq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
259 ioreq->io_Error = 0;
261 switch (ioreq->io_Command)
263 #if NEWSTYLE_DEVICE
264 case NSCMD_DEVICEQUERY:
265 if(ioStd(ioreq)->io_Length < ((LONG)OFFSET(NSDeviceQueryResult, SupportedCommands)) + sizeof(UWORD *))
267 ioreq->io_Error = IOERR_BADLENGTH;
269 else
271 struct NSDeviceQueryResult *d;
273 d = (struct NSDeviceQueryResult *)ioStd(ioreq)->io_Data;
275 d->DevQueryFormat = 0;
276 d->SizeAvailable = sizeof(struct NSDeviceQueryResult);
277 d->DeviceType = NSDEVTYPE_KEYBOARD;
278 d->DeviceSubType = 0;
279 d->SupportedCommands = (UWORD *)SupportedCommands;
281 ioStd(ioreq)->io_Actual = sizeof(struct NSDeviceQueryResult);
283 break;
284 #endif
286 case CMD_CLEAR:
287 kbUn->kbu_readPos = KBBase->kb_writePos;
288 break;
290 case KBD_ADDRESETHANDLER:
291 Disable();
292 Enqueue((struct List *)(&KBBase->kb_ResetHandlerList),
293 (struct Node *)(ioStd(ioreq)->io_Data));
294 KBBase->kb_nHandlers++;
295 Enable();
296 break;
298 case KBD_REMRESETHANDLER:
299 Disable();
300 Remove((struct Node *)(ioStd(ioreq)->io_Data));
301 KBBase->kb_nHandlers--;
302 Enable();
303 break;
305 case KBD_RESETHANDLERDONE:
306 /* We don't want any phony resets. */
308 if(KBBase->kb_ResetPhase == TRUE)
310 if(--(KBBase->kb_nHandlers) <= 0)
311 ColdReboot(); /* Shut down system */
313 else
315 /* There is no good (defined) IOERR to return in this situation */
316 ioreq->io_Error = IOERR_NOCMD;
318 break;
320 case KBD_READMATRIX:
321 ioStd(ioreq)->io_Actual = min(KB_MATRIXSIZE, ioStd(ioreq)->io_Length);
322 CopyMem(KBBase->kb_Matrix, ioStd(ioreq)->io_Data,
323 ioStd(ioreq)->io_Actual);
324 break;
326 case KBD_READEVENT:
328 /* TODO */
329 /* Check for reset... via keybuffer or via HIDD? */
330 /* if(bufferkey == 0x78) ... */
332 #if 0
333 if((((IPTR)ioStd(ioreq)->io_Data) & (__AROS_STRUCTURE_ALIGNMENT - 1)) != 0)
335 D(bug("kbd: Bad address\n"));
336 ioreq->io_Error = IOERR_BADADDRESS;
337 break;
339 #endif
341 Disable(); /* !! */
343 if(kbUn->kbu_readPos == KBBase->kb_writePos)
345 ioreq->io_Flags &= ~IOF_QUICK;
346 request_queued = TRUE;
347 D(bug("kbd: No keypresses, putting request in queue\n"));
349 kbUn->kbu_flags |= KBUF_PENDING;
350 AddTail((struct List *)&KBBase->kb_PendingQueue,
351 (struct Node *)ioreq);
352 } else {
353 D(bug("kbd: Events ready\n"));
355 writeEvents(ioreq, KBBase);
358 Enable();
360 break;
362 /* nlorentz: This command lets the keyboard.device initialize
363 the HIDD to use. It must be done this way, because
364 HIDDs might be loaded from disk, and keyboard.device is
365 inited before DOS is up and running.
366 The name of the HIDD class is in
367 ioStd(rew)->io_Data. Note that maybe we should
368 receive a pointer to an already created HIDD object instead.
369 Also note that the below is just a temporary hack, should
370 probably use IRQ HIDD instead to set the IRQ handler.
373 case CMD_HIDDINIT: {
374 struct TagItem tags[] =
376 { aHidd_Kbd_IrqHandler , (IPTR)keyCallback },
377 { aHidd_Kbd_IrqHandlerData , (IPTR)KBBase },
378 { TAG_DONE }
381 D(bug("keyboard.device: Received CMD_HIDDINIT, hiddname=\"%s\"\n"
382 , (STRPTR)ioStd(ioreq)->io_Data ));
384 if (KBBase->kb_Hidd != NULL)
385 OOP_DisposeObject(KBBase->kb_Hidd);
386 KBBase->kb_Hidd = OOP_NewObject(NULL, (STRPTR)ioStd(ioreq)->io_Data, tags);
387 if (!KBBase->kb_Hidd)
389 D(bug("keyboard.device: Failed to open hidd.\n"));
390 ioreq->io_Error = IOERR_OPENFAIL;
392 break; }
394 default:
395 ioreq->io_Error = IOERR_NOCMD;
396 break;
398 } /* switch (ioreq->io_Command) */
400 /* If the quick bit is not set, send the message to the port */
401 if(!(ioreq->io_Flags & IOF_QUICK) && !request_queued)
402 ReplyMsg(&ioreq->io_Message);
404 AROS_LIBFUNC_EXIT
407 /****************************************************************************************/
409 static BOOL writeEvents(struct IORequest *ioreq, struct KeyboardBase *KBBase)
411 int nEvents; /* Number of struct InputEvent:s that there is
412 room for in memory pointed to by io_Data */
413 UWORD code; /* Value of current keycode */
414 UWORD trueCode; /* Code without possible keypress addition */
415 int i; /* Loop variable */
416 struct InputEvent *event; /* Temporary variable */
417 BOOL moreevents = TRUE;
418 BOOL activate_resetphase = FALSE;
420 event = (struct InputEvent *)(ioStd(ioreq)->io_Data);
422 /* Number of InputEvents we can store in io_Data */
423 /* be careful, the io_Length might be the size of the InputEvent structure,
424 but it can be that the ALIGN() returns a larger size and then nEvents would
425 be 0.
428 nEvents = NUM_INPUTEVENTS(ioStd(ioreq)->io_Length);
430 if(nEvents == 0)
432 ioreq->io_Error = IOERR_BADLENGTH;
433 D(bug("kbd: Bad length\n"));
434 return TRUE;
437 D(bug("NEvents = %i", nEvents));
439 ioreq->io_Error = 0;
441 for(i = 0; i < nEvents; i++)
443 /* Update eventpointer -- this must be done here as I must set
444 ie_NextEvent to NULL if there are no more keys in the buffer. */
445 if(i != 0)
446 event = event->ie_NextEvent;
448 code = KBBase->kb_keyBuffer[kbUn->kbu_readPos++];
450 if(kbUn->kbu_readPos == KB_BUFFERSIZE)
451 kbUn->kbu_readPos = 0;
453 trueCode = code & AMIGAKEYMASK;
455 if(isQualifier(code) == TRUE)
458 /* Key released ? ... */
459 if(code & KEYUPMASK)
461 #if 1
462 /* stegerg: on PC keyboards caps lock also generates up events */
463 if (trueCode != AKC_CAPS_LOCK)
464 #endif
465 kbUn->kbu_Qualifiers &= ~(1 << (trueCode - AKC_QUALIFIERS_FIRST));
467 else /* ... or pressed? */
469 if (trueCode == AKC_CAPS_LOCK)
471 kbUn->kbu_Qualifiers ^= IEQUALIFIER_CAPSLOCK;
473 else
475 kbUn->kbu_Qualifiers |= 1 << (trueCode - AKC_QUALIFIERS_FIRST);
480 D(bug("kbd: Adding event of code %d\n", code));
482 event->ie_Class = IECLASS_RAWKEY;
483 event->ie_SubClass = 0;
484 event->ie_Code = code;
485 event->ie_Qualifier = kbUn->kbu_Qualifiers;
486 event->ie_Qualifier |= isNumericPad(trueCode) ? IEQUALIFIER_NUMERICPAD : 0;
487 event->ie_Prev1DownCode = (UBYTE)(kbUn->kbu_LastCode & 0xff);
488 event->ie_Prev1DownQual = kbUn->kbu_LastQuals;
489 event->ie_Prev2DownCode = (UBYTE)(kbUn->kbu_LastLastCode & 0xff);
490 event->ie_Prev2DownQual = kbUn->kbu_LastLastQuals;
491 event->ie_TimeStamp.tv_secs = 0;
492 event->ie_TimeStamp.tv_micro = 0;
494 /* Update list of previous states for dead key handling */
496 if (!(code & IECODE_UP_PREFIX) && !isQualifier(code))
498 kbUn->kbu_LastLastCode = kbUn->kbu_LastCode;
499 kbUn->kbu_LastLastQuals = kbUn->kbu_LastQuals;
500 kbUn->kbu_LastCode = code;
501 kbUn->kbu_LastQuals = (UBYTE)(kbUn->kbu_Qualifiers & 0xff);
504 if(code == 0x78) activate_resetphase = TRUE;
506 /* No more keys in buffer? */
507 if(kbUn->kbu_readPos == KBBase->kb_writePos)
509 moreevents = FALSE;
510 break;
513 event->ie_NextEvent = NEXT_INPUTEVENT(event);
517 D(bug("Done writing events!"));
518 event->ie_NextEvent = NULL;
520 if(activate_resetphase && !KBBase->kb_ResetPhase)
522 struct Interrupt *node;
524 KBBase->kb_ResetPhase = TRUE;
526 if(!IsListEmpty(&KBBase->kb_ResetHandlerList))
528 /* We may want to install a timer here so that ColdReboot()
529 will eventually be called even if a reset handler hang. */
530 ForeachNode(&KBBase->kb_ResetHandlerList, node)
532 /* We may be inside an interrupt when we come here. Maybe
533 we shall use some other technique? */
534 AROS_UFC3(VOID, node->is_Code,
535 AROS_UFCA(APTR, node->is_Data, A1),
536 AROS_UFCA(APTR, node->is_Code, A5),
537 AROS_UFCA(struct ExecBase *, SysBase, A6));
540 else
542 ColdReboot(); /* Bye bye AROS */
546 return moreevents;
549 /****************************************************************************************/
551 AROS_LH1(LONG, abortio,
552 AROS_LHA(struct IORequest *, ioreq, A1),
553 struct KeyboardBase *, KBBase, 6, Keyboard)
555 AROS_LIBFUNC_INIT
557 LONG ret = -1;
559 Disable();
560 if(kbUn->kbu_flags & KBUF_PENDING)
562 if (ioreq->io_Message.mn_Node.ln_Type == NT_MESSAGE)
564 Remove((struct Node *)ioreq);
565 ReplyMsg(&ioreq->io_Message);
567 ioreq->io_Error = IOERR_ABORTED;
569 if (IsListEmpty(&KBBase->kb_PendingQueue)) kbUn->kbu_flags &= ~KBUF_PENDING;
571 ret = 0;
574 Enable();
576 return ret;
578 AROS_LIBFUNC_EXIT
581 /****************************************************************************************/
583 #define CORRECT(x) (((x) & AMIGAKEYMASK) | (((x) & NOTAMIGAKEYMASK) >> 1))
584 #define BVBITCLEAR(x, y) ((y)[(x) / (sizeof(UBYTE)*8)] &= ~(1 << ((x) & (sizeof(UBYTE)*8 - 1))))
585 #define BVBITSET(x, y) ((y)[(x) / (sizeof(UBYTE)*8)] |= (1 << ((x) & (sizeof(UBYTE)*8 - 1))))
587 /****************************************************************************************/
589 #if 0
591 /****************************************************************************************/
594 78 Reset warning.
595 F9 Last key code bad, next key is same code retransmitted
596 FA Keyboard key buffer overflow
597 FC Keyboard self-test fail.
598 FD Initiate power-up key stream (for keys held or stuck at
599 power on)
600 FE Terminate power-up key stream.
603 #include <hardware/cia.h>
605 /****************************************************************************************/
607 BOOL HIDDM_initKeyboard(struct KeyboardHIDD *kh)
609 /* What should be done here? My guess is that we need the IRQ.hidd
610 before I can complete this function.
611 Presume that an IRQ.hidd exists, and that it has a method
612 HIDDV_addServerItem(ULONG level, BOOL (*)checkFunc) that adds an
613 interrupt server (sort of) to the real interrupt server at level
614 'level'. In the case of the keyboard.hidd, this would be level 6
615 (hardware wise) but probably something else in this context.
616 Then the code would look something like: */
619 kh->kh_irqhidd = FindHidd("IRQ.hidd");
621 if(kh->irqhidd == NULL)
622 return FALSE;
624 HIDDV_addServerItem(irqhidd_keyboard, checkKBint);
627 /****************************************************************************************/
629 #endif
631 /****************************************************************************************/
633 VOID keyCallback(struct KeyboardBase *KBBase, UWORD keyCode)
635 D(bug("keyCallBack(KBBase=%p, keyCode=%d)\n"
636 , KBBase, keyCode));
638 Disable();
640 KBBase->kb_keyBuffer[(KBBase->kb_writePos)++] = keyCode;
642 D(bug("Wrote to buffer\n"));
644 if(KBBase->kb_writePos == KB_BUFFERSIZE)
645 KBBase->kb_writePos = 0;
647 if (CORRECT(keyCode) < KB_MAXKEYS)
649 if(keyCode & KEYUPMASK)
650 BVBITCLEAR(CORRECT(keyCode), KBBase->kb_Matrix);
651 else
652 BVBITSET(CORRECT(keyCode), KBBase->kb_Matrix);
653 D(bug("Wrote to matrix\n"));
655 else
657 D(bug("Keycode value too high. Is %d. Should be < %d\n", CORRECT(keyCode), KB_MAXKEYS));
660 if(!IsListEmpty(&KBBase->kb_PendingQueue))
662 #if 0
663 D(bug("doing software irq\n"));
664 Cause(&KBBase->kb_Interrupt);
665 #else
666 AROS_UFC3(VOID, kbdSendQueuedEvents,
667 AROS_UFCA(struct KeyboardBase *, KBBase , A1),
668 AROS_UFCA(APTR , NULL, A5),
669 AROS_UFCA(struct ExecBase * , SysBase , A6));
670 #endif
673 Enable();
676 /****************************************************************************************/
678 #undef BVBITSET
679 #undef BVBITCLEAR
680 #undef CORRECT
682 /****************************************************************************************/
684 /* Software interrupt to be called when keys are received */
686 #undef SysBase
688 AROS_UFH3(VOID, kbdSendQueuedEvents,
689 AROS_UFHA(struct KeyboardBase *, KBBase, A1),
690 AROS_UFHA(APTR, thisfunc, A5),
691 AROS_UFHA(struct ExecBase *, SysBase, A6))
693 AROS_USERFUNC_INIT
695 /* Broadcast keys */
696 struct IORequest *ioreq, *nextnode;
697 struct List *pendingList = (struct List *)&KBBase->kb_PendingQueue;
699 D(bug("Inside software irq\n"));
701 ForeachNodeSafe(pendingList, ioreq, nextnode)
703 BOOL moreevents;
705 D(bug("Replying msg: R: %i W: %i\n", kbUn->kbu_readPos,
706 KBBase->kb_writePos));
708 moreevents = writeEvents(ioreq, KBBase);
710 Remove((struct Node *)ioreq);
711 ReplyMsg((struct Message *)&ioreq->io_Message);
713 if (!moreevents) break;
716 if (IsListEmpty(pendingList)) kbUn->kbu_flags &= ~KBUF_PENDING;
718 AROS_USERFUNC_EXIT
721 /****************************************************************************************/
723 static const char end = 0;
725 /****************************************************************************************/