added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / arch / i386-pc / Drivers / serial.hidd / SerialUnitClass.c
blob3551bbccea6f361e9ff4f7a5f1e20099969f1fe1
1 /*
2 Copyright © 1995-2006, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Serial Unit hidd class implementation.
6 Lang: english
7 */
9 /*
10 Right now I am assuming that there is a 1.8432 MHZ crystal connected to
11 the 16550 UART.
15 #include <asm/io.h>
16 /* the rest are Amiga includes */
17 #include <proto/exec.h>
18 #include <proto/utility.h>
19 #include <proto/oop.h>
20 #include <proto/alib.h>
21 #include <proto/intuition.h>
22 #include <exec/libraries.h>
23 #include <exec/ports.h>
24 #include <exec/memory.h>
25 #include <exec/interrupts.h>
26 #include <exec/lists.h>
28 #include <utility/tagitem.h>
29 #include <hidd/serial.h>
30 #include <hidd/unixio.h>
31 #include <hidd/irq.h>
32 #include <intuition/preferences.h>
34 #include <devices/serial.h>
36 #include "serial_intern.h"
38 #undef SDEBUG
39 #undef DEBUG
40 #define SDEBUG 0
41 #define DEBUG 0
42 #include <aros/debug.h>
44 /* The speed of the crystal */
45 #define CRYSTAL_SPEED 1843200
48 void serialunit_receive_data();
49 ULONG serialunit_write_more_data();
51 unsigned char get_lcr(struct HIDDSerialUnitData * data);
52 unsigned char get_fcr(ULONG baudrate);
53 BOOL set_baudrate(struct HIDDSerialUnitData * data, ULONG speed);
54 static void adapt_data(struct HIDDSerialUnitData * data,
55 struct Preferences * prefs);
57 static inline void serial_out(struct HIDDSerialUnitData * data,
58 int offset,
59 int value)
61 outb(value, data->baseaddr+offset);
64 static inline void serial_outp(struct HIDDSerialUnitData * data,
65 int offset,
66 int value)
68 outb_p(value, data->baseaddr+offset);
71 static inline unsigned int serial_in(struct HIDDSerialUnitData * data,
72 int offset)
74 return inb(data->baseaddr+offset);
77 static inline unsigned int serial_inp(struct HIDDSerialUnitData * data,
78 int offset)
80 return inb_p(data->baseaddr+offset);
85 /*************************** Classes *****************************/
87 /* IO bases for every COM port */
88 ULONG bases[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
90 /******* SerialUnit::New() ***********************************/
91 OOP_Object *PCSerUnit__Root__New(OOP_Class *cl, OOP_Object *obj, struct pRoot_New *msg)
93 struct HIDDSerialUnitData * data;
94 struct TagItem *tag, *tstate;
95 ULONG unitnum = 0;
97 EnterFunc(bug("SerialUnit::New()\n"));
99 tstate = msg->attrList;
100 while ((tag = NextTagItem((const struct TagItem **)&tstate)))
102 ULONG idx;
104 #define csd CSD(cl->UserData)
105 if (IS_HIDDSERIALUNIT_ATTR(tag->ti_Tag, idx))
106 #undef csd
108 switch (idx)
110 case aoHidd_SerialUnit_Unit:
111 unitnum = (ULONG)tag->ti_Data;
112 break;
116 } /* while (tags to process) */
118 obj = (OOP_Object *)OOP_DoSuperMethod(cl, obj, (OOP_Msg)msg);
120 if (obj)
122 struct IntuitionBase * IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
123 data = OOP_INST_DATA(cl, obj);
125 data->baseaddr = bases[unitnum];
127 if (NULL != IntuitionBase) {
128 struct Preferences prefs;
129 GetPrefs(&prefs,sizeof(prefs));
130 data->baudrate = prefs.BaudRate;
131 adapt_data(data, &prefs);
132 CloseLibrary((struct Library *)IntuitionBase);
133 } else {
134 data->datalength = 8;
135 data->parity = FALSE;
136 data->baudrate = 9600; /* will be initialize in set_baudrate() */
138 data->unitnum = unitnum;
140 Disable();
141 CSD(cl->UserData)->units[data->unitnum] = data;
142 Enable();
144 D(bug("Unit %d at 0x0%x\n", data->unitnum, data->baseaddr));
146 /* Wake up UART */
147 serial_outp(data, UART_LCR, 0xBF);
148 serial_outp(data, UART_EFR, UART_EFR_ECB);
149 serial_outp(data, UART_IER, 0);
150 serial_outp(data, UART_EFR, 0);
151 serial_outp(data, UART_LCR, 0);
153 /* clear the FIFOs */
154 serial_outp(data, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
156 /* clear the interrupt registers */
157 (void)serial_inp(data, UART_RX);
158 (void)serial_inp(data, UART_IIR);
159 (void)serial_inp(data, UART_MSR);
161 /* initilize the UART */
162 serial_outp(data, UART_LCR, get_lcr(data));
164 serial_outp(data, UART_MCR, UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS);
165 serial_outp(data, UART_IER, UART_IER_RDI | UART_IER_THRI | UART_IER_RLSI | UART_IER_MSI);
167 /* clear the interrupt registers again ... */
168 (void)serial_inp(data, UART_LSR);
169 (void)serial_inp(data, UART_RX);
170 (void)serial_inp(data, UART_IIR);
171 (void)serial_inp(data, UART_MSR);
173 set_baudrate(data, data->baudrate);
174 } /* if (obj) */
176 ReturnPtr("SerialUnit::New()", OOP_Object *, obj);
179 /******* SerialUnit::Dispose() ***********************************/
180 OOP_Object *PCSerUnit__Root__Dispose(OOP_Class *cl, OOP_Object *obj, OOP_Msg msg)
182 struct HIDDSerialUnitData * data;
183 EnterFunc(bug("SerialUnit::Dispose()\n"));
185 data = OOP_INST_DATA(cl, obj);
187 Disable();
188 CSD(cl->UserData)->units[data->unitnum] = NULL;
189 Enable();
191 /* stop all interrupts */
192 serial_outp(data, UART_IER, 0);
194 OOP_DoSuperMethod(cl, obj, (OOP_Msg)msg);
195 ReturnPtr("SerialUnit::Dispose()", OOP_Object *, obj);
200 /******* SerialUnit::Init() **********************************/
201 BOOL PCSerUnit__Hidd_SerialUnit__Init(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Init *msg)
203 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
205 EnterFunc(bug("SerialUnit::Init()\n"));
206 Disable();
207 data->DataReceivedCallBack = msg->DataReceived;
208 data->DataReceivedUserData = msg->DataReceivedUserData;
209 data->DataWriteCallBack = msg->WriteData;
210 data->DataWriteUserData = msg->WriteDataUserData;
211 Enable();
213 ReturnBool("SerialUnit::Init()", TRUE);
216 /******* SerialUnit::Write() **********************************/
217 ULONG PCSerUnit__Hidd_SerialUnit__Write(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Write *msg)
219 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
220 unsigned char status;
221 ULONG len = msg->Length;
222 ULONG count = 0;
224 EnterFunc(bug("SerialUnit::Write()\n"));
227 * If the output is currently stopped just don't do anything here.
229 if (TRUE == data->stopped)
230 return 0;
232 status = serial_inp(data, UART_LSR);
234 if (status & UART_LSR_THRE)
236 /* write data into FIFO */
239 serial_outp(data, UART_TX, msg->Outbuffer[count++]);
240 len--;
241 } while (len > 0 && serial_inp(data, UART_LSR & UART_LSR_TEMT));
244 ReturnInt("SerialUnit::Write()",ULONG, count);
247 /***************************************************************************/
249 static ULONG valid_baudrates[] =
251 2 | LIMIT_LOWER_BOUND,
252 115200 | LIMIT_UPPER_BOUND,
257 /******* SerialUnit::SetBaudrate() **********************************/
258 BOOL PCSerUnit__Hidd_SerialUnit__SetBaudrate(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SetBaudrate *msg)
260 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
261 BOOL valid = FALSE;
263 if (msg->baudrate != data->baudrate)
265 valid = set_baudrate(data, msg->baudrate);
266 } /* if */
267 return valid;
270 static UBYTE valid_datalengths[] =
279 /******* SerialUnit::SetParameters() **********************************/
280 BOOL PCSerUnit__Hidd_SerialUnit__SetParameters(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SetParameters *msg)
282 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
283 BOOL valid = TRUE;
284 int i = 0;
285 struct TagItem * tags = msg->tags;
287 while (TAG_END != tags[i].ti_Tag && TRUE == valid)
289 switch (tags[i].ti_Tag)
291 case TAG_DATALENGTH:
292 if ((BYTE)tags[i].ti_Data >= 5 && (BYTE)tags[i].ti_Data <= 8)
293 data->datalength = tags[i].ti_Data;
294 else
295 valid = FALSE;
296 break;
298 case TAG_STOP_BITS: /* 3 means 1.5 stopbits (if supported) */
299 if (1 == tags[i].ti_Data ||
300 2 == tags[i].ti_Data ||
301 3 == tags[i].ti_Data)
302 data->stopbits = tags[i].ti_Data;
303 else
304 valid = FALSE;
305 break;
307 case TAG_PARITY:
308 if (PARITY_0 == tags[i].ti_Data ||
309 PARITY_1 == tags[i].ti_Data ||
310 PARITY_EVEN == tags[i].ti_Data ||
311 PARITY_ODD == tags[i].ti_Data)
313 data->parity = TRUE;
314 data->paritytype = tags[i].ti_Data;
316 else
317 valid = FALSE;
318 break;
320 case TAG_PARITY_OFF:
321 data->parity = FALSE;
322 break;
324 case TAG_SET_MCR:
325 serial_outp(data, UART_MCR, (tags[i].ti_Data & 0x0f) | 0x08);
326 break;
328 case TAG_SKIP:
329 case TAG_IGNORE:
330 break;
332 default:
333 valid = FALSE;
335 i++;
338 if (TRUE == valid)
339 serial_outp(data, UART_LCR, get_lcr(data));
341 return valid;
344 /******* SerialUnit::SendBreak() **********************************/
345 BYTE PCSerUnit__Hidd_SerialUnit__SendBreak(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SendBreak *msg)
347 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
349 return SerErr_LineErr;
352 /******* SerialUnit::Start() **********************************/
353 VOID PCSerUnit__Hidd_SerialUnit__Start(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Start *msg)
355 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
358 * Allow or start feeding the UART with data. Get the data
359 * from upper layer.
361 if (TRUE == data->stopped) {
362 if (NULL != data->DataWriteCallBack)
363 data->DataWriteCallBack(data->unitnum, data->DataWriteUserData);
365 * Also mark the stopped flag as FALSE.
367 data->stopped = FALSE;
371 /******* SerialUnit::Stop() **********************************/
372 VOID PCSerUnit__Hidd_SerialUnit__Stop(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Stop *msg)
374 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
377 * The next time the interrupt comes along and asks for
378 * more data we just don't do anything...
380 data->stopped = TRUE;
383 /****** SerialUnit::GetCapabilities ********************************/
384 VOID PCSerUnit__Hidd_SerialUnit__GetCapabilities(OOP_Class * cl, OOP_Object *o, struct TagItem * tags)
386 if (NULL != tags)
388 int i = 0;
389 BOOL end = FALSE;
390 while (FALSE == end)
392 switch (tags[i].ti_Tag)
394 case HIDDA_SerialUnit_BPSRate:
395 tags[i].ti_Data = (STACKIPTR)valid_baudrates;
396 break;
398 case HIDDA_SerialUnit_DataLength:
399 tags[i].ti_Data = (STACKIPTR)valid_datalengths;
400 break;
402 case TAG_DONE:
403 end = TRUE;
404 break;
406 i++;
411 /****** SerialUnit::GetStatus ********************************/
412 UWORD PCSerUnit__Hidd_SerialUnit__GetStatus(OOP_Class * cl, OOP_Object *o, struct pHidd_SerialUnit_GetStatus *msg)
414 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
415 UWORD status = 0;
416 UBYTE msr = serial_inp(data, UART_MSR);
417 UBYTE mcr = serial_inp(data, UART_MCR);
419 if (msr & UART_MSR_DCD)
420 status |= (1<<5);
421 if (msr & UART_MSR_DSR)
422 status |= (1<<3);
423 if (msr & UART_MSR_CTS)
424 status |= (1<<4);
426 if (mcr & UART_MCR_DTR)
427 status |= (1<<7);
428 if (mcr & UART_MCR_RTS)
429 status |= (1<<6); /* old RKMs say 'ready to send' */
430 return status;
433 /************* The software interrupt handler that gets data from UART *****/
436 #define READBUFFER_SIZE 513
438 AROS_UFH3(void, serialunit_receive_data,
439 AROS_UFHA(APTR, iD, A1),
440 AROS_UFHA(APTR, iC, A5),
441 AROS_UFHA(struct ExecBase *, SysBase, A6))
443 AROS_USERFUNC_INIT
445 struct HIDDSerialUnitData * data = iD;
446 int len = 0;
447 UBYTE buffer[READBUFFER_SIZE];
450 ** Read the data from the port ...
454 buffer[len++] = serial_inp(data, UART_RX);
456 while (serial_inp(data, UART_LSR) & UART_LSR_DR);
459 ** ... and deliver them to whoever is interested.
462 if (NULL != data->DataReceivedCallBack)
463 data->DataReceivedCallBack(buffer, len, data->unitnum, data->DataReceivedUserData);
465 return;
467 AROS_USERFUNC_EXIT
470 AROS_UFH3(ULONG, serialunit_write_more_data,
471 AROS_UFHA(APTR, iD, A1),
472 AROS_UFHA(APTR, iC, A5),
473 AROS_UFHA(struct ExecBase *, SysBase, A6))
475 AROS_USERFUNC_INIT
477 ULONG bytes = 0;
478 struct HIDDSerialUnitData * data = iD;
481 * If the output is currently stopped just don't do
482 * anything here.
484 if (TRUE == data->stopped)
485 return 0;
488 ** Ask for more data be written to the unit
490 D(bug("Asking for more data to be written to unit %d\n",data->unitnum));
492 if (NULL != data->DataWriteCallBack)
493 bytes = data->DataWriteCallBack(data->unitnum, data->DataWriteUserData);
494 return bytes;
496 AROS_USERFUNC_EXIT
500 /* some help routines */
502 unsigned char get_lcr(struct HIDDSerialUnitData * data)
504 char lcr;
505 switch (data->datalength)
507 case 5: lcr = 0;
508 break;
510 case 6: lcr = 1;
511 break;
513 case 7: lcr = 2;
514 break;
516 case 8: lcr = 3;
517 break;
519 default: lcr = 3;
522 switch (data->stopbits)
524 case 1: /* 1 stopbit */
525 /* nothing to do */
526 break;
528 case 3: /* 1.5 stopbits (is this supported ?!!!) */
529 if (data->datalength == 5)
530 lcr |= (1 << 2);
531 break;
533 case 2: /* 2 stopbits */
534 if (data->datalength >= 6 && data->datalength <= 8)
535 lcr |= (1 << 2);
536 break;
538 default:
539 break;
542 if (TRUE == data->parity)
544 lcr |= (1 << 3);
546 switch (data->paritytype)
548 case PARITY_EVEN:
549 lcr |= (1 << 4);
550 break;
552 case PARITY_1:
553 lcr |= (1 << 5);
554 break;
556 case PARITY_0:
557 lcr |= (1 << 4) | (1 << 5);
558 break;
563 if (TRUE == data->breakcontrol)
564 lcr |= (1 << 6);
565 return lcr;
568 unsigned char get_fcr(ULONG baudrate)
570 unsigned char fcr;
571 fcr = (1 << 0);
574 Depending on the baudrate set the fifo interrupt threshold to a
575 different value.
578 if (baudrate < 1200)
579 fcr |= (3 << 6);
580 else
581 if (baudrate < 9600)
582 fcr |= (2 << 6);
583 else
584 if (baudrate < 38400)
585 fcr |= (1 << 6);
587 return fcr;
590 BOOL set_baudrate(struct HIDDSerialUnitData * data, ULONG speed)
592 int quot;
594 if (!(speed >= 50 && speed <= 115200))
595 return FALSE;
597 quot = CRYSTAL_SPEED / (speed << 4);
599 /* set the speed on the UART now */
600 serial_outp(data, UART_LCR, get_lcr(data) | UART_LCR_DLAB);
601 serial_outp(data, UART_DLL, quot & 0xff);
602 serial_outp(data, UART_DLM, quot >> 8);
603 serial_outp(data, UART_LCR, get_lcr(data));
604 serial_outp(data, UART_FCR, get_fcr(speed) | UART_FCR_ENABLE_FIFO);
606 return TRUE;
609 /* Serial interrupts */
611 #undef SysBase
612 #define SysBase (hw->sysBase)
613 #define csd ((struct class_static_data *)(irq->h_Data))
615 static void common_serial_int_handler(HIDDT_IRQ_Handler *irq,
616 HIDDT_IRQ_HwInfo *hw,
617 ULONG unitnum)
619 UBYTE code = UART_IIR_NO_INT;
621 if (csd->units[unitnum])
622 code = serial_inp(csd->units[unitnum], UART_IIR) & 0x07;
624 switch (code)
626 case UART_IIR_RLSI:
627 (void)serial_inp(csd->units[unitnum], UART_LSR);
628 break;
630 case UART_IIR_RDI:
631 if (csd->units[unitnum]) {
632 AROS_UFC3(void, serialunit_receive_data,
633 AROS_UFCA(APTR , csd->units[unitnum], A1),
634 AROS_UFCA(APTR , NULL , A5),
635 AROS_UFCA(struct ExecBase * , SysBase, A6));
637 break;
639 case UART_IIR_MSI:
640 (void)serial_inp(csd->units[unitnum], UART_MSR);
641 break;
643 case UART_IIR_THRI:
644 if (csd->units[unitnum])
645 if (0 == serialunit_write_more_data(csd->units[unitnum], NULL, SysBase))
646 (void)serial_inp(csd->units[unitnum], UART_IIR);
647 break;
651 void serial_int_13(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw)
653 common_serial_int_handler(irq, hw, 0);
654 common_serial_int_handler(irq, hw, 2);
657 void serial_int_24(HIDDT_IRQ_Handler * irq, HIDDT_IRQ_HwInfo *hw)
659 common_serial_int_handler(irq, hw, 1);
660 common_serial_int_handler(irq, hw, 3);
663 static void adapt_data(struct HIDDSerialUnitData * data,
664 struct Preferences * prefs)
667 * Parity.
669 data->parity = TRUE;
671 switch ((prefs->SerParShk >> 4) & 0x0f) {
673 case SPARITY_NONE:
674 default: /* DEFAULT !! */
675 data->parity = FALSE;
676 break;
678 case SPARITY_EVEN:
679 data->paritytype = PARITY_EVEN;
680 break;
682 case SPARITY_ODD:
683 data->paritytype = PARITY_ODD;
684 break;
686 case SPARITY_MARK:
687 data->paritytype = PARITY_1;
688 break;
689 case SPARITY_SPACE:
690 data->paritytype = PARITY_0;
691 break;
696 * Bit per character
698 switch ((prefs->SerRWBits & 0x0f)) {
699 default: /* 8 bit */
700 case 0:
701 data->datalength = 8;
702 break;
704 case 1: /* 7 bit */
705 data->datalength = 7;
706 break;
708 case 2: /* 6 bit */
709 data->datalength = 6;
710 break;
712 case 3: /* 5 bit */
713 data->datalength = 5;
714 break;
718 * 2 stop bits ? default is '1'.
720 if (1 == (prefs->SerStopBuf >> 4))
721 data->stopbits = 2;
722 else
723 data->stopbits = 1;
726 * Handshake to be used.
728 // MISSING!