Updated PCI IDs to latest snapshot.
[tangerine.git] / arch / .unmaintained / generic / hidd / serial / SerialUnitClass.c
blobb4d39f8962ec504d8e035fb397efc6a8f47465d0
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 0
34 #define DEBUG 0
35 #include <aros/debug.h>
37 void serialunit_receive_data();
38 void serialunit_write_more_data();
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 static inline void serial_out_w(struct HIDDSerialUnitData * data,
50 int offset,
51 int value)
53 WREG_W((data->baseaddr+offset)) = value;
56 static inline unsigned int serial_in_w(struct HIDDSerialUnitData * data,
57 int offset)
59 return RREG_W(data->baseaddr+offset);
62 /*************************** Classes *****************************/
64 /* IO bases for every COM port */
65 ULONG bases[] = {USTCNT1, USTCNT2};
68 /******* SerialUnit::New() ***********************************/
69 static OOP_Object *serialunit_new(OOP_Class *cl, OOP_Object *obj, struct pRoot_New *msg)
71 struct HIDDSerialUnitData * data;
72 struct TagItem *tag, *tstate;
73 ULONG unitnum = 0;
75 EnterFunc(bug("SerialUnit::New()\n"));
77 tstate = msg->attrList;
78 while ((tag = NextTagItem((const struct TagItem **)&tstate))) {
79 ULONG idx;
81 #define csd CSD(cl->UserData)
82 if (IS_HIDDSERIALUNIT_ATTR(tag->ti_Tag, idx)) {
83 #undef csd
84 switch (idx)
86 case aoHidd_SerialUnit_Unit:
87 unitnum = (ULONG)tag->ti_Data;
88 break;
92 } /* while (tags to process) */
94 obj = (OOP_Object *)OOP_DoSuperMethod(cl, obj, (OOP_Msg)msg);
96 if (obj) {
97 WORD dummy;
98 data = OOP_INST_DATA(cl, obj);
100 data->baseaddr = bases[unitnum];
102 data->datalength = 8;
103 data->parity = FALSE;
104 data->baudrate = 0; /* will be initialize in set_baudrate() */
105 data->unitnum = unitnum;
107 CSD(cl->UserData)->units[data->unitnum] = data;
109 D(bug("Unit %d at 0x0%x\n", data->unitnum, data->baseaddr));
111 /* Init UART - See 14-10 of dragonball documentation */
112 serial_out_w(data,USTCNT, UEN | RXEN);
113 dummy = RREG_W(URX1);
114 serial_out_w(data, USTCNT, (get_ustcnt(data) | UEN | RXEN | TXEN));
116 set_baudrate(data, SER_DEFAULT_BAUDRATE);
117 } /* if (obj) */
119 ReturnPtr("SerialUnit::New()", OOP_Object *, obj);
122 /******* SerialUnit::Dispose() ***********************************/
123 static OOP_Object *serialunit_dispose(OOP_Class *cl, OOP_Object *obj, OOP_Msg msg)
125 struct HIDDSerialUnitData * data;
126 EnterFunc(bug("SerialUnit::Dispose()\n"));
128 data = OOP_INST_DATA(cl, obj);
130 CSD(cl->UserData)->units[data->unitnum] = NULL;
132 /* stop all interrupts, disabling the UART (might save power) */
133 serial_out_w(data, USTCNT, 0);
135 OOP_DoSuperMethod(cl, obj, (OOP_Msg)msg);
136 ReturnPtr("SerialUnit::Dispose()", OOP_Object *, obj);
141 /******* SerialUnit::Init() **********************************/
142 BOOL serialunit_init(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Init *msg)
144 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
146 EnterFunc(bug("SerialUnit::Init()\n"));
147 data->DataReceivedCallBack = msg->DataReceived;
148 data->DataReceivedUserData = msg->DataReceivedUserData;
149 data->DataWriteCallBack = msg->WriteData;
150 data->DataWriteUserData = msg->WriteDataUserData;
152 ReturnBool("SerialUnit::Init()", TRUE);
155 /******* SerialUnit::Write() **********************************/
156 ULONG serialunit_write(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Write *msg)
158 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
159 ULONG len = msg->Length;
160 ULONG count = 0;
161 UWORD utx;
163 EnterFunc(bug("SerialUnit::Write()\n"));
166 * If the output is currently stopped just don't do anything here.
168 if (TRUE == data->stopped)
169 return 0;
171 utx = serial_in_w(data, UTX);
174 * I may only write something here if nothing is in the fifo right
175 * now because otherwise this might be handled through an interrupt.
177 if (utx & FIFO_EMPTY) {
178 /* write data into FIFO */
179 do {
180 serial_out_w(data, UTX, msg->Outbuffer[count++]);
181 len--;
182 utx = serial_in_w(data, UTX);
183 } while (len > 0 && (utx & TX_AVAIL));
186 ReturnInt("SerialUnit::Write()",ULONG, count);
189 /***************************************************************************/
191 static ULONG valid_baudrates[] =
193 MINSPEED | LIMIT_LOWER_BOUND,
194 MAXSPEED | LIMIT_UPPER_BOUND,
199 /******* SerialUnit::SetBaudrate() **********************************/
200 BOOL serialunit_setbaudrate(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SetBaudrate *msg)
202 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
203 BOOL valid = FALSE;
205 if (msg->baudrate != data->baudrate) {
206 valid = set_baudrate(data, msg->baudrate);
207 } /* if */
208 return valid;
211 static UBYTE valid_datalengths[] =
218 /******* SerialUnit::SetParameters() **********************************/
219 BOOL serialunit_setparameters(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SetParameters *msg)
221 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
222 BOOL valid = TRUE;
223 int i = 0;
224 struct TagItem * tags = msg->tags;
226 while (TAG_END != tags[i].ti_Tag && TRUE == valid) {
227 switch (tags[i].ti_Tag) {
228 case TAG_DATALENGTH:
229 if (tags[i].ti_Data >= 7 && tags[i].ti_Data <= 8)
230 data->datalength = tags[i].ti_Data;
231 else
232 valid = FALSE;
233 break;
235 case TAG_STOP_BITS:
236 if (1 == tags[i].ti_Data ||
237 2 == tags[i].ti_Data)
238 data->stopbits = tags[i].ti_Data;
239 else
240 valid = FALSE;
241 break;
243 case TAG_PARITY:
244 if (PARITY_EVEN == tags[i].ti_Data ||
245 PARITY_ODD == tags[i].ti_Data) {
246 data->parity = TRUE;
247 data->paritytype = tags[i].ti_Data;
249 else
250 valid = FALSE;
251 break;
253 case TAG_PARITY_OFF:
254 data->parity = FALSE;
255 break;
257 case TAG_SET_MCR:
258 #warning MCR??
259 // serial_out_w(data, UART_MCR, (tags[i].ti_Data & 0x0f) | 0x08);
260 break;
262 case TAG_SKIP:
263 case TAG_IGNORE:
264 break;
266 default:
267 valid = FALSE;
269 i++;
272 serial_out_w(data, USTCNT, get_ustcnt(data));
274 return valid;
277 /******* SerialUnit::SendBreak() **********************************/
278 BYTE serialunit_sendbreak(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SendBreak *msg)
280 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
282 return SerErr_LineErr;
285 /******* SerialUnit::Start() **********************************/
286 VOID serialunit_start(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Start *msg)
288 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
291 * Allow or start feeding the UART with data. Get the data
292 * from upper layer.
294 if (TRUE == data->stopped) {
295 if (NULL != data->DataWriteCallBack)
296 data->DataWriteCallBack(data->unitnum, data->DataWriteUserData);
298 * Also mark the stopped flag as FALSE.
300 data->stopped = FALSE;
304 /******* SerialUnit::Stop() **********************************/
305 VOID serialunit_stop(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Stop *msg)
307 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
310 * The next time the interrupt comes along and asks for
311 * more data we just don't do anything...
313 data->stopped = TRUE;
316 /****** SerialUnit::GetCapabilities ********************************/
317 VOID serialunit_getcapabilities(OOP_Class * cl, OOP_Object *o, struct TagItem * tags)
319 if (NULL != tags) {
320 int i = 0;
321 BOOL end = FALSE;
322 while (FALSE == end) {
323 switch (tags[i].ti_Tag) {
324 case HIDDA_SerialUnit_BPSRate:
325 tags[i].ti_Data = (STACKIPTR)valid_baudrates;
326 break;
328 case HIDDA_SerialUnit_DataLength:
329 tags[i].ti_Data = (STACKIPTR)valid_datalengths;
330 break;
332 case TAG_DONE:
333 end = TRUE;
334 break;
336 i++;
341 /****** SerialUnit::GetStatus ********************************/
342 UWORD serialunit_getstatus(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_GetStatus *msg)
344 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
345 UWORD status = 0;
351 return status;
355 /************* The software interrupt handler that gets data from UART *****/
358 #undef OOPBase
359 #undef SysBase
360 #undef UtilityBase
362 #define READBUFFER_SIZE 65
364 AROS_UFH3(void, serialunit_receive_data,
365 AROS_UFHA(APTR, iD, A1),
366 AROS_UFHA(APTR, iC, A5),
367 AROS_UFHA(struct ExecBase *, SysBase, A6))
369 struct HIDDSerialUnitData * data = iD;
370 int len = 0;
371 UWORD urx;
372 UBYTE buffer[READBUFFER_SIZE];
375 ** Read the data from the port ...
377 while (1) {
378 urx = serial_in_w(data, URX);
379 if (urx & DATA_READY)
380 buffer[len++] = (UBYTE)urx;
381 else
382 break;
386 ** ... and deliver them to whoever is interested.
389 if (NULL != data->DataReceivedCallBack)
390 data->DataReceivedCallBack(buffer, len, data->unitnum, data->DataReceivedUserData);
393 AROS_UFH3(void, serialunit_write_more_data,
394 AROS_UFHA(APTR, iD, A1),
395 AROS_UFHA(APTR, iC, A5),
396 AROS_UFHA(struct ExecBase *, SysBase, A6))
398 struct HIDDSerialUnitData * data = iD;
401 * If the output is currently stopped just don't do
402 * anything here.
404 if (TRUE == data->stopped)
405 return;
408 ** Ask for more data be written to the unit
410 D(bug("Asking for more data to be written to unit %d\n",data->unitnum));
412 if (NULL != data->DataWriteCallBack)
413 data->DataWriteCallBack(data->unitnum, data->DataWriteUserData);
417 /******* init_serialunitclass ********************************/
419 #define SysBase (csd->sysbase)
420 #define OOPBase (csd->oopbase)
421 #define UtilityBase (csd->utilitybase)
424 #define NUM_ROOT_METHODS 2
425 #define NUM_SERIALUNIT_METHODS moHidd_SerialUnit_NumMethods
427 OOP_Class *init_serialunitclass (struct class_static_data *csd)
429 OOP_Class *cl = NULL;
431 struct OOP_MethodDescr serialunithiddroot_descr[NUM_ROOT_METHODS + 1] =
433 {(IPTR (*)())serialunit_new, moRoot_New},
434 {(IPTR (*)())serialunit_dispose, moRoot_Dispose},
436 {(IPTR (*)())serialunit_set, moRoot_Set},
437 {(IPTR (*)())serialunit_get, moRoot_Get},
439 {NULL, 0UL}
442 struct OOP_MethodDescr serialunithidd_descr[NUM_SERIALUNIT_METHODS + 1] =
444 {(IPTR (*)())serialunit_init, moHidd_SerialUnit_Init},
445 {(IPTR (*)())serialunit_write, moHidd_SerialUnit_Write},
446 {(IPTR (*)())serialunit_setbaudrate, moHidd_SerialUnit_SetBaudrate},
447 {(IPTR (*)())serialunit_setparameters, moHidd_SerialUnit_SetParameters},
448 {(IPTR (*)())serialunit_sendbreak, moHidd_SerialUnit_SendBreak},
449 {(IPTR (*)())serialunit_start, moHidd_SerialUnit_Start},
450 {(IPTR (*)())serialunit_stop, moHidd_SerialUnit_Stop},
451 {(IPTR (*)())serialunit_getcapabilities,moHidd_SerialUnit_GetCapabilities},
452 {(IPTR (*)())serialunit_getstatus ,moHidd_SerialUnit_GetStatus},
453 {NULL, 0UL}
456 struct OOP_InterfaceDescr ifdescr[] =
458 {serialunithiddroot_descr , IID_Root , NUM_ROOT_METHODS},
459 {serialunithidd_descr , IID_Hidd_SerialUnit , NUM_SERIALUNIT_METHODS},
460 {NULL, NULL, 0}
463 OOP_AttrBase MetaAttrBase = OOP_GetAttrBase(IID_Meta);
465 struct TagItem tags[] =
467 { aMeta_SuperID, (IPTR)CLID_Root},
468 { aMeta_InterfaceDescr, (IPTR)ifdescr},
469 { aMeta_ID, (IPTR)CLID_Hidd_SerialUnit},
470 { aMeta_InstSize, (IPTR)sizeof (struct HIDDSerialUnitData) },
471 {TAG_DONE, 0UL}
475 EnterFunc(bug(" init_serialunitclass(csd=%p)\n", csd));
477 cl = OOP_NewObject(NULL, CLID_HiddMeta, tags);
478 D(bug("Class=%p\n", cl));
479 if (cl) {
480 __IHidd_SerialUnitAB = OOP_ObtainAttrBase(IID_Hidd_SerialUnit);
481 if (NULL != __IHidd_SerialUnitAB) {
482 D(bug("SerialUnit Class ok\n"));
483 cl->UserData = (APTR)csd;
485 OOP_AddClass(cl);
486 } else {
487 free_serialunitclass(csd);
488 cl = NULL;
492 ReturnPtr("init_serialunitclass", OOP_Class *, cl);
496 void free_serialunitclass(struct class_static_data *csd)
498 EnterFunc(bug("free_serialhiddclass(csd=%p)\n", csd));
500 if(csd) {
501 OOP_RemoveClass(csd->serialhiddclass);
503 if(csd->serialhiddclass) OOP_DisposeObject((OOP_Object *) csd->serialhiddclass);
504 csd->serialhiddclass = NULL;
507 ReturnVoid("free_serialhiddclass");
511 UWORD get_ustcnt(struct HIDDSerialUnitData * data)
513 UWORD ustcnt = 0;
514 switch (data->datalength) {
515 case 8: ustcnt |= EITHER8OR7;
516 break;
519 switch (data->stopbits) {
520 case 1: /* 1 stopbit */
521 /* nothing to do */
522 break;
524 case 2: /* 2 stopbits */
525 ustcnt |= STOP;
526 break;
528 default:
531 if (TRUE == data->parity) {
532 ustcnt |= PEN;
534 switch (data->paritytype) {
535 case PARITY_ODD:
536 ustcnt |= ODD;
537 break;
541 if (data->baudrate < 1200) {
542 ustcnt |= /*RXFE|RXHE|*/ RXRE | /* TXEE|*/ TXHE;
543 } else if (data->baudrate < 9600) {
544 ustcnt |= /* RXHE|*/ RXRE | /* TXEE|*/ TXHE;
545 } else {
546 ustcnt |= RXRE|TXHE;
549 return ustcnt;
552 struct baud_rate_settings
554 ULONG baudrate;
555 UBYTE divider;
556 UBYTE prescaler;
560 * For a default 33.16Mhz system clock
562 static const struct baud_rate_settings brs[] =
564 {600, 7, 0x26},
565 {1200, 6, 0x26},
566 {2400, 5, 0x26},
567 {4800, 4, 0x26},
568 {9600, 3, 0x26},
569 {14400, 4, 0x38},
570 {19200, 2, 0x26},
571 {28800, 3, 0x38},
572 {38400, 1, 0x26},
573 {57600, 2, 0x38},
574 {115200,1, 0x38},
575 {230400,0, 0x38},
576 {~0, ~0, ~0}
579 BOOL set_baudrate(struct HIDDSerialUnitData * data, ULONG speed)
581 /* set the speed on the UART now */
582 #warning Which bits to set in ubaud???
583 UWORD ubaud = 0;
584 int i = 0;
585 int found = FALSE;
587 if (speed < MINSPEED || speed > MAXSPEED)
588 return FALSE;
590 while (~0 != brs[i].baudrate) {
591 if (speed == brs[i].baudrate) {
592 ubaud |= (((UWORD)brs[i].divider) << 8) | brs[i].prescaler;
593 found = TRUE;
594 break;
598 if (FALSE == found) {
599 UBYTE divider = 7;
600 ULONG prescaler;
601 ULONG best = 0;
602 ULONG val;
603 UBYTE _divider = 7;
604 ULONG _prescaler = 0;
606 * Try to calculate the parameters for this odd baudrate.
607 * Don't know whether this is correct...
609 while (divider > 0) {
610 prescaler = (2073600/(1<<divider))/speed;
612 * Calculate backwards and see how good this
613 * result is (due to integer rounding of the
614 * previous divisions).
616 if (0 == prescaler)
617 break;
619 if (prescaler < 65) {
620 val = prescaler*(1<<divider);
621 if (val > best) {
622 best = val;
623 _divider = divider;
624 _prescaler = prescaler;
628 divider--;
631 ubaud |= (((UWORD)_divider) << 8) | (65-_prescaler);
634 serial_out_w(data, UBAUD, ubaud);
635 return TRUE;
638 /* Serial interrupts */
640 #if 0
642 #undef SysBase
643 #define SysBase (hw->sysBase)
644 #define csd ((struct class_static_data *)(irq->h_Data))
646 void serial_int_13(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw)
651 #endif