r4548@vps: verhaegs | 2007-04-23 10:55:24 -0400
[AROS.git] / arch / .unmaintained / m68k-pp-native / Drivers / serial.hidd / SerialUnitClass.c
blob3342f242fe6e5347691c6a8e4017f5db3c935fdd
1 /*
2 Copyright © 1995-2001, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Serial Unit hidd class implementation.
6 Lang: english
7 */
10 /* the rest are Amiga includes */
11 #include <proto/exec.h>
12 #include <proto/utility.h>
13 #include <proto/oop.h>
14 #include <proto/alib.h>
15 #include <exec/libraries.h>
16 #include <exec/ports.h>
17 #include <exec/memory.h>
18 #include <exec/interrupts.h>
19 #include <exec/lists.h>
21 #include <utility/tagitem.h>
22 #include <hidd/serial.h>
23 #include <hidd/unixio.h>
24 #include <hidd/irq.h>
26 #include <devices/serial.h>
28 #include "serial_intern.h"
29 #include <asm/registers.h>
31 #undef SDEBUG
32 #undef DEBUG
33 #define SDEBUG 1
34 #define DEBUG 1
35 #include <aros/debug.h>
37 static void serialunit_receive_data(APTR iD, UWORD data, struct ExecBase *);
38 static ULONG serialunit_write_more_data(APTR iD, APTR iC, struct ExecBase *);
40 UWORD get_ustcnt(struct HIDDSerialUnitData * data);
41 BOOL set_baudrate(struct HIDDSerialUnitData * data, ULONG speed);
44 * Min. and max. supported speed by the UART
46 #define MINSPEED 300
47 #define MAXSPEED 230400
49 #undef SysBase
51 static inline void serial_out_w(struct HIDDSerialUnitData * data,
52 ULONG offset,
53 UWORD value)
55 D(bug("poke.w 0x%x,0x%x\n",data->baseaddr+offset,value));
56 WREG_W((data->baseaddr+offset)) = value;
59 static inline UWORD serial_in_w(struct HIDDSerialUnitData * data,
60 ULONG offset)
62 UWORD value = RREG_W((data->baseaddr+offset));
63 D(bug("peek.w 0x%x = 0x%x\n",data->baseaddr+offset,value));
64 return value;
67 #if 0
68 static inline void serial_out_b(struct HIDDSerialUnitData * data,
69 ULONG offset,
70 UBYTE value)
72 D(bug("poke.b 0x%x,0x%x\n",data->baseaddr+offset,value));
73 WREG_B((data->baseaddr+offset)) = value;
75 #endif
77 static inline UBYTE serial_in_b(struct HIDDSerialUnitData * data,
78 ULONG offset)
80 UBYTE value = RREG_B(data->baseaddr+offset);
81 D(bug("peek.b 0x%x = 0x%x\n",data->baseaddr+offset,value));
82 return value;
85 #define SysBase (CSD(cl->UserData)->sysbase)
87 /*************************** Classes *****************************/
89 /* IO bases for every COM port */
90 ULONG bases[] = {USTCNT1, USTCNT2};
93 /******* SerialUnit::New() ***********************************/
94 static OOP_Object *serialunit_new(OOP_Class *cl, OOP_Object *obj, struct pRoot_New *msg)
96 struct HIDDSerialUnitData * data;
97 struct TagItem *tag, *tstate;
98 ULONG unitnum = 0;
100 EnterFunc(bug("SerialUnit::New()\n"));
102 tstate = msg->attrList;
103 while ((tag = NextTagItem((struct TagItem **)&tstate))) {
104 ULONG idx;
106 #define csd CSD(cl->UserData)
107 if (IS_HIDDSERIALUNIT_ATTR(tag->ti_Tag, idx)) {
108 #undef csd
109 switch (idx)
111 case aoHidd_SerialUnit_Unit:
112 unitnum = (ULONG)tag->ti_Data;
113 break;
117 } /* while (tags to process) */
119 obj = (OOP_Object *)OOP_DoSuperMethod(cl, obj, (OOP_Msg)msg);
121 if (obj) {
122 WORD dummy;
123 data = OOP_INST_DATA(cl, obj);
125 data->baseaddr = bases[unitnum];
127 data->datalength = 8;
128 data->parity = FALSE;
129 data->baudrate = SER_DEFAULT_BAUDRATE;
130 data->unitnum = unitnum;
132 CSD(cl->UserData)->units[data->unitnum] = data;
134 //D(bug("Unit %d at 0x0%x\n", data->unitnum, data->baseaddr));
136 /* Init UART - See 14-10 of dragonball documentation */
137 serial_out_w(data, O_USTCNT, UEN_F | RXEN_F | TXEN_F | RXRE_F);
138 dummy = RREG_W(URX1);
139 //D(bug("Setting baudrate now!"));
140 /* Now set the baudrate */
141 set_baudrate(data, data->baudrate);
143 /* Set the interrupts and the levels according to the baudrate */
144 serial_out_w(data, O_USTCNT, (get_ustcnt(data) | UEN_F | RXEN_F | TXEN_F | RXRE_F));
146 } /* if (obj) */
148 ReturnPtr("SerialUnit::New()", OOP_Object *, obj);
151 /******* SerialUnit::Dispose() ***********************************/
152 static OOP_Object *serialunit_dispose(OOP_Class *cl, OOP_Object *obj, OOP_Msg msg)
154 struct HIDDSerialUnitData * data;
155 EnterFunc(bug("SerialUnit::Dispose()\n"));
157 data = OOP_INST_DATA(cl, obj);
159 CSD(cl->UserData)->units[data->unitnum] = NULL;
161 /* stop all interrupts, disabling the UART (might save power) */
162 serial_out_w(data, O_USTCNT, 0);
164 OOP_DoSuperMethod(cl, obj, (OOP_Msg)msg);
165 ReturnPtr("SerialUnit::Dispose()", OOP_Object *, obj);
170 /******* SerialUnit::Init() **********************************/
171 BOOL serialunit_init(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Init *msg)
173 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
175 EnterFunc(bug("SerialUnit::Init()\n"));
176 data->DataReceivedCallBack = msg->DataReceived;
177 data->DataReceivedUserData = msg->DataReceivedUserData;
178 data->DataWriteCallBack = msg->WriteData;
179 data->DataWriteUserData = msg->WriteDataUserData;
181 ReturnBool("SerialUnit::Init()", TRUE);
184 /******* SerialUnit::Write() **********************************/
185 ULONG serialunit_write(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Write *msg)
187 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
188 ULONG len = msg->Length;
189 ULONG count = 0;
190 UWORD utx;
192 EnterFunc(bug("SerialUnit::Write()\n"));
195 * If the output is currently stopped just don't do anything here.
197 if (TRUE == data->stopped)
198 return 0;
200 utx = serial_in_w(data, O_UTX);
203 * I may only write something here if nothing is in the fifo right
204 * now because otherwise this might be handled through an interrupt.
206 if (utx & FIFO_EMPTY_F) {
207 /* write data into FIFO */
208 do {
209 //D(bug("%c",msg->Outbuffer[count]));
210 serial_out_w(data, O_UTX, msg->Outbuffer[count++]);
211 len--;
212 utx = serial_in_w(data, O_UTX);
213 } while (len > 0 && (utx & TX_AVAIL_F));
216 ReturnInt("SerialUnit::Write()",ULONG, count);
219 /***************************************************************************/
221 static ULONG valid_baudrates[] =
223 MINSPEED | LIMIT_LOWER_BOUND,
224 MAXSPEED | LIMIT_UPPER_BOUND,
229 /******* SerialUnit::SetBaudrate() **********************************/
230 BOOL serialunit_setbaudrate(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SetBaudrate *msg)
232 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
233 BOOL valid = FALSE;
235 if (msg->baudrate != data->baudrate) {
236 valid = set_baudrate(data, msg->baudrate);
237 } /* if */
238 return valid;
241 static UBYTE valid_datalengths[] =
248 /******* SerialUnit::SetParameters() **********************************/
249 BOOL serialunit_setparameters(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SetParameters *msg)
251 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
252 BOOL valid = TRUE;
253 int i = 0;
254 struct TagItem * tags = msg->tags;
256 while (TAG_END != tags[i].ti_Tag && TRUE == valid) {
257 switch (tags[i].ti_Tag) {
258 case TAG_DATALENGTH:
259 if (tags[i].ti_Data >= 7 && tags[i].ti_Data <= 8)
260 data->datalength = tags[i].ti_Data;
261 else
262 valid = FALSE;
263 break;
265 case TAG_STOP_BITS:
266 if (1 == tags[i].ti_Data ||
267 2 == tags[i].ti_Data)
268 data->stopbits = tags[i].ti_Data;
269 else
270 valid = FALSE;
271 break;
273 case TAG_PARITY:
274 if (PARITY_EVEN == tags[i].ti_Data ||
275 PARITY_ODD == tags[i].ti_Data) {
276 data->parity = TRUE;
277 data->paritytype = tags[i].ti_Data;
279 else
280 valid = FALSE;
281 break;
283 case TAG_PARITY_OFF:
284 data->parity = FALSE;
285 break;
287 case TAG_SET_MCR:
288 #warning MCR??
289 break;
291 case TAG_SKIP:
292 case TAG_IGNORE:
293 break;
295 default:
296 valid = FALSE;
298 i++;
301 serial_out_w(data, O_USTCNT, get_ustcnt(data));
303 return valid;
306 /******* SerialUnit::SendBreak() **********************************/
307 BYTE serialunit_sendbreak(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SendBreak *msg)
309 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
311 UWORD code = serial_in_w(data, O_UTX);
312 serial_out_w(data, O_UTX, code | SEND_BREAK_F);
314 return SerErr_LineErr;
317 /******* SerialUnit::Start() **********************************/
318 VOID serialunit_start(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Start *msg)
320 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
323 * Allow or start feeding the UART with data. Get the data
324 * from upper layer.
326 if (TRUE == data->stopped) {
327 if (NULL != data->DataWriteCallBack)
328 data->DataWriteCallBack(data->unitnum, data->DataWriteUserData);
330 * Also mark the stopped flag as FALSE.
332 data->stopped = FALSE;
336 /******* SerialUnit::Stop() **********************************/
337 VOID serialunit_stop(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Stop *msg)
339 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
342 * The next time the interrupt comes along and asks for
343 * more data we just don't do anything...
345 data->stopped = TRUE;
348 /****** SerialUnit::GetCapabilities ********************************/
349 VOID serialunit_getcapabilities(OOP_Class * cl, OOP_Object *o, struct TagItem * tags)
351 if (NULL != tags) {
352 int i = 0;
353 BOOL end = FALSE;
354 while (FALSE == end) {
355 switch (tags[i].ti_Tag) {
356 case HIDDA_SerialUnit_BPSRate:
357 tags[i].ti_Data = (STACKIPTR)valid_baudrates;
358 break;
360 case HIDDA_SerialUnit_DataLength:
361 tags[i].ti_Data = (STACKIPTR)valid_datalengths;
362 break;
364 case TAG_DONE:
365 end = TRUE;
366 break;
368 i++;
373 /****** SerialUnit::GetStatus ********************************/
374 UWORD serialunit_getstatus(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_GetStatus *msg)
376 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
377 UWORD status = 0;
379 data = NULL;
384 return status;
388 /************* The function that gets data from UART *****/
391 #undef OOPBase
392 #undef SysBase
393 #undef UtilityBase
395 #define READBUFFER_SIZE 65
397 static void serialunit_receive_data(APTR iD, UWORD urx, struct ExecBase * SysBase)
399 struct HIDDSerialUnitData * data = iD;
400 int len = 0;
401 UBYTE buffer[READBUFFER_SIZE];
404 ** Read the data from the port ...
405 ** !!! The xcopilot implementation seem rather stupid. I can only get one
406 ** byte per interrupt. I hope the real thing is a bit better... !!!
408 while (len < sizeof(buffer)) {
409 buffer[len++] = (UBYTE)urx;
410 //D(bug("Got byte form serial port: %d (%c)\n",(UBYTE)urx,(UBYTE)urx));
413 * Check for the next incoming byte - whether there is one.
414 * I have to do it this way, because xcopilot returns no
415 * DATA_READY flag once the register is read with 16 bit access.
417 urx = serial_in_w(data, O_URX);
418 if (0 == (DATA_READY_F & urx)) {
419 break;
424 ** ... and deliver them to whoever is interested.
427 if (NULL != data->DataReceivedCallBack)
428 data->DataReceivedCallBack(buffer, len, data->unitnum, data->DataReceivedUserData);
431 static ULONG serialunit_write_more_data(APTR iD, APTR iC, struct ExecBase * SysBase)
433 struct HIDDSerialUnitData * data = iD;
436 * If the output is currently stopped just don't do
437 * anything here.
439 if (TRUE == data->stopped)
440 return -1;
443 ** Ask for more data be written to the unit
445 // D(bug("Asking for more data to be written to unit %d\n",data->unitnum));
447 if (NULL != data->DataWriteCallBack)
448 data->DataWriteCallBack(data->unitnum, data->DataWriteUserData);
449 return 0;
453 /******* init_serialunitclass ********************************/
455 #define SysBase (csd->sysbase)
456 #define OOPBase (csd->oopbase)
457 #define UtilityBase (csd->utilitybase)
460 #define NUM_ROOT_METHODS 2
461 #define NUM_SERIALUNIT_METHODS moHidd_SerialUnit_NumMethods
463 OOP_Class *init_serialunitclass (struct class_static_data *csd)
465 OOP_Class *cl = NULL;
467 struct OOP_MethodDescr serialunithiddroot_descr[NUM_ROOT_METHODS + 1] =
469 {(IPTR (*)())serialunit_new, moRoot_New},
470 {(IPTR (*)())serialunit_dispose, moRoot_Dispose},
472 {(IPTR (*)())serialunit_set, moRoot_Set},
473 {(IPTR (*)())serialunit_get, moRoot_Get},
475 {NULL, 0UL}
478 struct OOP_MethodDescr serialunithidd_descr[NUM_SERIALUNIT_METHODS + 1] =
480 {(IPTR (*)())serialunit_init, moHidd_SerialUnit_Init},
481 {(IPTR (*)())serialunit_write, moHidd_SerialUnit_Write},
482 {(IPTR (*)())serialunit_setbaudrate, moHidd_SerialUnit_SetBaudrate},
483 {(IPTR (*)())serialunit_setparameters, moHidd_SerialUnit_SetParameters},
484 {(IPTR (*)())serialunit_sendbreak, moHidd_SerialUnit_SendBreak},
485 {(IPTR (*)())serialunit_start, moHidd_SerialUnit_Start},
486 {(IPTR (*)())serialunit_stop, moHidd_SerialUnit_Stop},
487 {(IPTR (*)())serialunit_getcapabilities,moHidd_SerialUnit_GetCapabilities},
488 {(IPTR (*)())serialunit_getstatus ,moHidd_SerialUnit_GetStatus},
489 {NULL, 0UL}
492 struct OOP_InterfaceDescr ifdescr[] =
494 {serialunithiddroot_descr , IID_Root , NUM_ROOT_METHODS},
495 {serialunithidd_descr , IID_Hidd_SerialUnit , NUM_SERIALUNIT_METHODS},
496 {NULL, NULL, 0}
499 OOP_AttrBase MetaAttrBase = OOP_GetAttrBase(IID_Meta);
501 struct TagItem tags[] =
503 { aMeta_SuperID, (IPTR)CLID_Root},
504 { aMeta_InterfaceDescr, (IPTR)ifdescr},
505 { aMeta_ID, (IPTR)CLID_Hidd_SerialUnit},
506 { aMeta_InstSize, (IPTR)sizeof (struct HIDDSerialUnitData) },
507 {TAG_DONE, 0UL}
511 EnterFunc(bug(" init_serialunitclass(csd=%p)\n", csd));
513 cl = OOP_NewObject(NULL, CLID_HiddMeta, tags);
514 D(bug("Class=%p\n", cl));
515 if (cl) {
516 __IHidd_SerialUnitAB = OOP_ObtainAttrBase(IID_Hidd_SerialUnit);
517 if (NULL != __IHidd_SerialUnitAB) {
518 D(bug("SerialUnit Class ok\n"));
519 cl->UserData = (APTR)csd;
521 OOP_AddClass(cl);
522 } else {
523 free_serialunitclass(csd);
524 cl = NULL;
528 ReturnPtr("init_serialunitclass", OOP_Class *, cl);
532 void free_serialunitclass(struct class_static_data *csd)
534 EnterFunc(bug("free_serialhiddclass(csd=%p)\n", csd));
536 if(csd) {
537 OOP_RemoveClass(csd->serialhiddclass);
539 if(csd->serialhiddclass) OOP_DisposeObject((OOP_Object *) csd->serialhiddclass);
540 csd->serialhiddclass = NULL;
543 ReturnVoid("free_serialhiddclass");
547 UWORD get_ustcnt(struct HIDDSerialUnitData * data)
549 UWORD ustcnt = 0;
550 switch (data->datalength) {
551 case 8: ustcnt |= EITHER8OR7_F;
552 break;
555 switch (data->stopbits) {
556 case 1: /* 1 stopbit */
557 /* nothing to do */
558 break;
560 case 2: /* 2 stopbits */
561 ustcnt |= STOP_F;
562 break;
564 default:
565 break;
568 if (TRUE == data->parity) {
569 ustcnt |= PARITY_EN_F;
571 switch (data->paritytype) {
572 case PARITY_ODD:
573 ustcnt |= ODD_F;
574 break;
578 if (data->baudrate < 1200) {
579 ustcnt |= /*RXFE_F|RXHE_F|*/ RXRE_F | /* TXEE_F|*/ TXHE_F;
580 } else if (data->baudrate < 9600) {
581 ustcnt |= /* RXHE_F|*/ RXRE_F | /* TXEE_F|*/ TXHE_F;
582 } else {
583 ustcnt |= RXRE_F|TXHE_F;
586 return ustcnt;
589 struct baud_rate_settings
591 ULONG baudrate;
592 UBYTE divider;
593 UBYTE prescaler;
597 * For a default 33.16Mhz system clock
599 static const struct baud_rate_settings brs[] =
601 {600, 7, 0x26},
602 {1200, 6, 0x26},
603 {2400, 5, 0x26},
604 {4800, 4, 0x26},
605 {9600, 3, 0x26},
606 {14400, 4, 0x38},
607 {19200, 2, 0x26},
608 {28800, 3, 0x38},
609 {38400, 1, 0x26},
610 {57600, 2, 0x38},
611 {115200,1, 0x38},
612 {230400,0, 0x38},
613 {~0, ~0, ~0}
616 BOOL set_baudrate(struct HIDDSerialUnitData * data, ULONG speed)
618 /* set the speed on the UART now */
619 #warning Which bits to set in ubaud???
620 UWORD ubaud = 0;
621 int i = 0;
622 int found = FALSE;
624 if (speed < MINSPEED || speed > MAXSPEED)
625 return FALSE;
627 while (~0 != brs[i].baudrate) {
628 if (speed == brs[i].baudrate) {
629 ubaud |= (((UWORD)brs[i].divider) << 8) | brs[i].prescaler;
630 found = TRUE;
631 break;
633 i++;
636 if (FALSE == found) {
637 UBYTE divider = 7;
638 ULONG prescaler;
639 ULONG best = 0;
640 ULONG val;
641 UBYTE _divider = 7;
642 ULONG _prescaler = 0;
644 * Try to calculate the parameters for this odd baudrate.
645 * Don't know whether this is correct...
647 while (divider > 0) {
648 prescaler = (2073600/(1<<divider))/speed;
650 * Calculate backwards and see how good this
651 * result is (due to integer rounding of the
652 * previous divisions).
654 if (0 == prescaler)
655 break;
657 if (prescaler < 65) {
658 val = prescaler*(1<<divider);
659 if (val > best) {
660 best = val;
661 _divider = divider;
662 _prescaler = prescaler;
666 divider--;
669 ubaud |= (((UWORD)_divider) << 8) | (65-_prescaler);
672 serial_out_w(data, O_UBAUD, ubaud);
673 return TRUE;
676 /* Serial interrupts */
678 #undef SysBase
679 #define SysBase (hw->sysBase)
680 #define csd ((struct class_static_data *)(irq->h_Data))
683 static void common_serial_int_handler(HIDDT_IRQ_Handler * irq,
684 HIDDT_IRQ_HwInfo * hw,
685 ULONG unitnum)
687 UWORD code = 0;
688 if (csd->units[unitnum]) {
689 code = serial_in_w(csd->units[unitnum], O_URX);
692 //D(bug("---------- IN COMMON SERIAL HANDLER!\n"));
693 //D(bug("URX: code=0x%x\n",code));
694 if (code & (FIFO_EMPTY_F|FIFO_HALF_F|DATA_READY_F)) {
695 D(bug("In %s 1\n",__FUNCTION__));
696 if (csd->units[unitnum]) {
697 serialunit_receive_data(csd->units[unitnum],
698 code,
699 SysBase);
703 code = 0;
704 if (csd->units[unitnum])
705 code = serial_in_w(csd->units[unitnum], O_UTX);
707 //D(bug("UTX: code=0x%x\n",code));
708 if (code & (FIFO_EMPTY_F|FIFO_HALF_F|TX_AVAIL_F)) {
709 D(bug("In %s 2\n",__FUNCTION__));
710 if (csd->units[unitnum]) {
711 if (0 == serialunit_write_more_data(csd->units[unitnum],
712 NULL,
713 SysBase)) {
720 void serial_int_uart1(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw)
722 common_serial_int_handler(irq, hw, 0);
725 void serial_int_uart2(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw)
727 common_serial_int_handler(irq, hw, 1);