A bit number was mistakenly used instead of a flag when setting notification
[AROS.git] / arch / i386-pc / drivers / serial.hidd / SerialUnitClass.c
blobd81c4a83714c49ef78c7e25fb090fcc0c9b749d4
1 /*
2 Copyright © 1995-2011, 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 <intuition/preferences.h>
33 #include <devices/serial.h>
35 #include <SDI/SDI_interrupt.h>
37 #include "serial_intern.h"
39 #undef SDEBUG
40 #undef DEBUG
41 #define SDEBUG 0
42 #define DEBUG 0
43 #include <aros/debug.h>
45 /* The speed of the crystal */
46 #define CRYSTAL_SPEED 1843200
49 void serialunit_receive_data();
50 ULONG serialunit_write_more_data();
52 unsigned char get_lcr(struct HIDDSerialUnitData * data);
53 unsigned char get_fcr(ULONG baudrate);
54 BOOL set_baudrate(struct HIDDSerialUnitData * data, ULONG speed);
55 static void adapt_data(struct HIDDSerialUnitData * data,
56 struct Preferences * prefs);
58 static inline void serial_out(struct HIDDSerialUnitData * data,
59 int offset,
60 int value)
62 outb(value, data->baseaddr+offset);
65 static inline void serial_outp(struct HIDDSerialUnitData * data,
66 int offset,
67 int value)
69 outb_p(value, data->baseaddr+offset);
72 static inline unsigned int serial_in(struct HIDDSerialUnitData * data,
73 int offset)
75 return inb(data->baseaddr+offset);
78 static inline unsigned int serial_inp(struct HIDDSerialUnitData * data,
79 int offset)
81 return inb_p(data->baseaddr+offset);
86 /*************************** Classes *****************************/
88 /* IO bases for every COM port */
89 static ULONG bases[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
91 /******* SerialUnit::New() ***********************************/
92 OOP_Object *PCSerUnit__Root__New(OOP_Class *cl, OOP_Object *obj, struct pRoot_New *msg)
94 struct HIDDSerialUnitData * data;
95 struct TagItem *tag, *tstate;
96 ULONG unitnum = 0;
98 EnterFunc(bug("SerialUnit::New()\n"));
100 tstate = msg->attrList;
101 while ((tag = NextTagItem(&tstate)))
103 ULONG idx;
105 #define csd CSD(cl->UserData)
106 if (IS_HIDDSERIALUNIT_ATTR(tag->ti_Tag, idx))
107 #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)
123 struct IntuitionBase * IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
124 data = OOP_INST_DATA(cl, obj);
126 data->baseaddr = bases[unitnum];
128 if (NULL != IntuitionBase) {
129 struct Preferences prefs;
130 GetPrefs(&prefs,sizeof(prefs));
131 data->baudrate = prefs.BaudRate;
132 adapt_data(data, &prefs);
133 CloseLibrary((struct Library *)IntuitionBase);
134 } else {
135 data->datalength = 8;
136 data->parity = FALSE;
137 data->baudrate = 9600; /* will be initialize in set_baudrate() */
139 data->unitnum = unitnum;
141 Disable();
142 CSD(cl->UserData)->units[data->unitnum] = data;
143 Enable();
145 D(bug("Unit %d at 0x0%x\n", data->unitnum, data->baseaddr));
147 /* Wake up UART */
148 serial_outp(data, UART_LCR, 0xBF);
149 serial_outp(data, UART_EFR, UART_EFR_ECB);
150 serial_outp(data, UART_IER, 0);
151 serial_outp(data, UART_EFR, 0);
152 serial_outp(data, UART_LCR, 0);
154 /* clear the FIFOs */
155 serial_outp(data, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
157 /* clear the interrupt registers */
158 (void)serial_inp(data, UART_RX);
159 (void)serial_inp(data, UART_IIR);
160 (void)serial_inp(data, UART_MSR);
162 /* initilize the UART */
163 serial_outp(data, UART_LCR, get_lcr(data));
165 serial_outp(data, UART_MCR, UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS);
166 serial_outp(data, UART_IER, UART_IER_RDI | UART_IER_THRI | UART_IER_RLSI | UART_IER_MSI);
168 /* clear the interrupt registers again ... */
169 (void)serial_inp(data, UART_LSR);
170 (void)serial_inp(data, UART_RX);
171 (void)serial_inp(data, UART_IIR);
172 (void)serial_inp(data, UART_MSR);
174 set_baudrate(data, data->baudrate);
175 } /* if (obj) */
177 ReturnPtr("SerialUnit::New()", OOP_Object *, obj);
180 /******* SerialUnit::Dispose() ***********************************/
181 OOP_Object *PCSerUnit__Root__Dispose(OOP_Class *cl, OOP_Object *obj, OOP_Msg msg)
183 struct HIDDSerialUnitData * data;
184 EnterFunc(bug("SerialUnit::Dispose()\n"));
186 data = OOP_INST_DATA(cl, obj);
188 Disable();
189 CSD(cl->UserData)->units[data->unitnum] = NULL;
190 Enable();
192 /* stop all interrupts */
193 serial_outp(data, UART_IER, 0);
195 OOP_DoSuperMethod(cl, obj, (OOP_Msg)msg);
196 ReturnPtr("SerialUnit::Dispose()", OOP_Object *, obj);
201 /******* SerialUnit::Init() **********************************/
202 BOOL PCSerUnit__Hidd_SerialUnit__Init(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Init *msg)
204 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
206 EnterFunc(bug("SerialUnit::Init()\n"));
207 Disable();
208 data->DataReceivedCallBack = msg->DataReceived;
209 data->DataReceivedUserData = msg->DataReceivedUserData;
210 data->DataWriteCallBack = msg->WriteData;
211 data->DataWriteUserData = msg->WriteDataUserData;
212 Enable();
214 ReturnBool("SerialUnit::Init()", TRUE);
217 /******* SerialUnit::Write() **********************************/
218 ULONG PCSerUnit__Hidd_SerialUnit__Write(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Write *msg)
220 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
221 unsigned char status;
222 ULONG len = msg->Length;
223 ULONG count = 0;
225 EnterFunc(bug("SerialUnit::Write()\n"));
228 * If the output is currently stopped just don't do anything here.
230 if (TRUE == data->stopped)
231 return 0;
233 status = serial_inp(data, UART_LSR);
235 if (status & UART_LSR_THRE)
237 /* write data into FIFO */
240 serial_outp(data, UART_TX, msg->Outbuffer[count++]);
241 len--;
242 } while (len > 0 && serial_inp(data, UART_LSR & UART_LSR_TEMT));
245 ReturnInt("SerialUnit::Write()",ULONG, count);
248 /***************************************************************************/
250 static ULONG valid_baudrates[] =
252 2 | LIMIT_LOWER_BOUND,
253 115200 | LIMIT_UPPER_BOUND,
258 /******* SerialUnit::SetBaudrate() **********************************/
259 BOOL PCSerUnit__Hidd_SerialUnit__SetBaudrate(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SetBaudrate *msg)
261 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
262 BOOL valid = FALSE;
264 if (msg->baudrate != data->baudrate)
266 valid = set_baudrate(data, msg->baudrate);
267 } /* if */
268 return valid;
271 static UBYTE valid_datalengths[] =
280 /******* SerialUnit::SetParameters() **********************************/
281 BOOL PCSerUnit__Hidd_SerialUnit__SetParameters(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SetParameters *msg)
283 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
284 BOOL valid = TRUE;
285 int i = 0;
286 struct TagItem * tags = msg->tags;
288 while (TAG_END != tags[i].ti_Tag && TRUE == valid)
290 switch (tags[i].ti_Tag)
292 case TAG_DATALENGTH:
293 if ((BYTE)tags[i].ti_Data >= 5 && (BYTE)tags[i].ti_Data <= 8)
294 data->datalength = tags[i].ti_Data;
295 else
296 valid = FALSE;
297 break;
299 case TAG_STOP_BITS: /* 3 means 1.5 stopbits (if supported) */
300 if (1 == tags[i].ti_Data ||
301 2 == tags[i].ti_Data ||
302 3 == tags[i].ti_Data)
303 data->stopbits = tags[i].ti_Data;
304 else
305 valid = FALSE;
306 break;
308 case TAG_PARITY:
309 if (PARITY_0 == tags[i].ti_Data ||
310 PARITY_1 == tags[i].ti_Data ||
311 PARITY_EVEN == tags[i].ti_Data ||
312 PARITY_ODD == tags[i].ti_Data)
314 data->parity = TRUE;
315 data->paritytype = tags[i].ti_Data;
317 else
318 valid = FALSE;
319 break;
321 case TAG_PARITY_OFF:
322 data->parity = FALSE;
323 break;
325 case TAG_SET_MCR:
326 serial_outp(data, UART_MCR, (tags[i].ti_Data & 0x0f) | 0x08);
327 break;
329 case TAG_SKIP:
330 case TAG_IGNORE:
331 break;
333 default:
334 valid = FALSE;
336 i++;
339 if (TRUE == valid)
340 serial_outp(data, UART_LCR, get_lcr(data));
342 return valid;
345 /******* SerialUnit::SendBreak() **********************************/
346 BYTE PCSerUnit__Hidd_SerialUnit__SendBreak(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_SendBreak *msg)
348 return SerErr_LineErr;
351 /******* SerialUnit::Start() **********************************/
352 VOID PCSerUnit__Hidd_SerialUnit__Start(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Start *msg)
354 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
357 * Allow or start feeding the UART with data. Get the data
358 * from upper layer.
360 if (TRUE == data->stopped) {
361 if (NULL != data->DataWriteCallBack)
362 data->DataWriteCallBack(data->unitnum, data->DataWriteUserData);
364 * Also mark the stopped flag as FALSE.
366 data->stopped = FALSE;
370 /******* SerialUnit::Stop() **********************************/
371 VOID PCSerUnit__Hidd_SerialUnit__Stop(OOP_Class *cl, OOP_Object *o, struct pHidd_SerialUnit_Stop *msg)
373 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
376 * The next time the interrupt comes along and asks for
377 * more data we just don't do anything...
379 data->stopped = TRUE;
382 /****** SerialUnit::GetCapabilities ********************************/
383 VOID PCSerUnit__Hidd_SerialUnit__GetCapabilities(OOP_Class * cl, OOP_Object *o, struct TagItem * tags)
385 if (NULL != tags)
387 int i = 0;
388 BOOL end = FALSE;
389 while (FALSE == end)
391 switch (tags[i].ti_Tag)
393 case HIDDA_SerialUnit_BPSRate:
394 tags[i].ti_Data = (STACKIPTR)valid_baudrates;
395 break;
397 case HIDDA_SerialUnit_DataLength:
398 tags[i].ti_Data = (STACKIPTR)valid_datalengths;
399 break;
401 case TAG_DONE:
402 end = TRUE;
403 break;
405 i++;
410 /****** SerialUnit::GetStatus ********************************/
411 UWORD PCSerUnit__Hidd_SerialUnit__GetStatus(OOP_Class * cl, OOP_Object *o, struct pHidd_SerialUnit_GetStatus *msg)
413 struct HIDDSerialUnitData * data = OOP_INST_DATA(cl, o);
414 UWORD status = 0;
415 UBYTE msr = serial_inp(data, UART_MSR);
416 UBYTE mcr = serial_inp(data, UART_MCR);
418 if (msr & UART_MSR_DCD)
419 status |= (1<<5);
420 if (msr & UART_MSR_DSR)
421 status |= (1<<3);
422 if (msr & UART_MSR_CTS)
423 status |= (1<<4);
425 if (mcr & UART_MCR_DTR)
426 status |= (1<<7);
427 if (mcr & UART_MCR_RTS)
428 status |= (1<<6); /* old RKMs say 'ready to send' */
429 return status;
432 /************* The software interrupt handler that gets data from UART *****/
435 #define READBUFFER_SIZE 513
437 AROS_UFH3(void, serialunit_receive_data,
438 AROS_UFHA(APTR, iD, A1),
439 AROS_UFHA(APTR, iC, A5),
440 AROS_UFHA(struct ExecBase *, SysBase, A6))
442 AROS_USERFUNC_INIT
444 struct HIDDSerialUnitData * data = iD;
445 int len = 0;
446 UBYTE buffer[READBUFFER_SIZE];
449 ** Read the data from the port ...
453 buffer[len++] = serial_inp(data, UART_RX);
455 while (serial_inp(data, UART_LSR) & UART_LSR_DR);
458 ** ... and deliver them to whoever is interested.
461 if (NULL != data->DataReceivedCallBack)
462 data->DataReceivedCallBack(buffer, len, data->unitnum, data->DataReceivedUserData);
464 return;
466 AROS_USERFUNC_EXIT
469 AROS_UFH3(ULONG, serialunit_write_more_data,
470 AROS_UFHA(APTR, iD, A1),
471 AROS_UFHA(APTR, iC, A5),
472 AROS_UFHA(struct ExecBase *, SysBase, A6))
474 AROS_USERFUNC_INIT
476 ULONG bytes = 0;
477 struct HIDDSerialUnitData * data = iD;
480 * If the output is currently stopped just don't do
481 * anything here.
483 if (TRUE == data->stopped)
484 return 0;
487 ** Ask for more data be written to the unit
489 D(bug("Asking for more data to be written to unit %d\n",data->unitnum));
491 if (NULL != data->DataWriteCallBack)
492 bytes = data->DataWriteCallBack(data->unitnum, data->DataWriteUserData);
493 return bytes;
495 AROS_USERFUNC_EXIT
499 /* some help routines */
501 unsigned char get_lcr(struct HIDDSerialUnitData * data)
503 char lcr;
504 switch (data->datalength)
506 case 5: lcr = 0;
507 break;
509 case 6: lcr = 1;
510 break;
512 case 7: lcr = 2;
513 break;
515 case 8: lcr = 3;
516 break;
518 default: lcr = 3;
521 switch (data->stopbits)
523 case 1: /* 1 stopbit */
524 /* nothing to do */
525 break;
527 case 3: /* 1.5 stopbits (is this supported ?!!!) */
528 if (data->datalength == 5)
529 lcr |= (1 << 2);
530 break;
532 case 2: /* 2 stopbits */
533 if (data->datalength >= 6 && data->datalength <= 8)
534 lcr |= (1 << 2);
535 break;
537 default:
538 break;
541 if (TRUE == data->parity)
543 lcr |= (1 << 3);
545 switch (data->paritytype)
547 case PARITY_EVEN:
548 lcr |= (1 << 4);
549 break;
551 case PARITY_1:
552 lcr |= (1 << 5);
553 break;
555 case PARITY_0:
556 lcr |= (1 << 4) | (1 << 5);
557 break;
562 if (TRUE == data->breakcontrol)
563 lcr |= (1 << 6);
564 return lcr;
567 unsigned char get_fcr(ULONG baudrate)
569 unsigned char fcr;
570 fcr = (1 << 0);
573 Depending on the baudrate set the fifo interrupt threshold to a
574 different value.
577 if (baudrate < 1200)
578 fcr |= (3 << 6);
579 else
580 if (baudrate < 9600)
581 fcr |= (2 << 6);
582 else
583 if (baudrate < 38400)
584 fcr |= (1 << 6);
586 return fcr;
589 BOOL set_baudrate(struct HIDDSerialUnitData * data, ULONG speed)
591 int quot;
593 if (!(speed >= 50 && speed <= 115200))
594 return FALSE;
596 quot = CRYSTAL_SPEED / (speed << 4);
598 /* set the speed on the UART now */
599 serial_outp(data, UART_LCR, get_lcr(data) | UART_LCR_DLAB);
600 serial_outp(data, UART_DLL, quot & 0xff);
601 serial_outp(data, UART_DLM, quot >> 8);
602 serial_outp(data, UART_LCR, get_lcr(data));
603 serial_outp(data, UART_FCR, get_fcr(speed) | UART_FCR_ENABLE_FIFO);
605 return TRUE;
608 /* Serial interrupts */
610 static void common_serial_int_handler(struct class_static_data *csd,
611 ULONG unitnum)
613 UBYTE code = UART_IIR_NO_INT;
615 if (csd->units[unitnum])
616 code = serial_inp(csd->units[unitnum], UART_IIR) & 0x07;
618 switch (code)
620 case UART_IIR_RLSI:
621 (void)serial_inp(csd->units[unitnum], UART_LSR);
622 break;
624 case UART_IIR_RDI:
625 if (csd->units[unitnum]) {
626 AROS_UFC3(void, serialunit_receive_data,
627 AROS_UFCA(APTR , csd->units[unitnum], A1),
628 AROS_UFCA(APTR , NULL , A5),
629 AROS_UFCA(struct ExecBase * , SysBase, A6));
631 break;
633 case UART_IIR_MSI:
634 (void)serial_inp(csd->units[unitnum], UART_MSR);
635 break;
637 case UART_IIR_THRI:
638 if (csd->units[unitnum])
639 if (0 == serialunit_write_more_data(csd->units[unitnum], NULL, SysBase))
640 (void)serial_inp(csd->units[unitnum], UART_IIR);
641 break;
645 AROS_INTH1(serial_int_13, void *, data)
647 AROS_INTFUNC_INIT
649 common_serial_int_handler(data, 0);
650 common_serial_int_handler(data, 2);
652 return FALSE;
654 AROS_INTFUNC_EXIT
657 AROS_INTH1(serial_int_24, void *, data)
659 AROS_INTFUNC_INIT
661 common_serial_int_handler(data, 1);
662 common_serial_int_handler(data, 3);
664 return FALSE;
666 AROS_INTFUNC_EXIT
669 static void adapt_data(struct HIDDSerialUnitData * data,
670 struct Preferences * prefs)
673 * Parity.
675 data->parity = TRUE;
677 switch ((prefs->SerParShk >> 4) & 0x0f) {
679 case SPARITY_NONE:
680 default: /* DEFAULT !! */
681 data->parity = FALSE;
682 break;
684 case SPARITY_EVEN:
685 data->paritytype = PARITY_EVEN;
686 break;
688 case SPARITY_ODD:
689 data->paritytype = PARITY_ODD;
690 break;
692 case SPARITY_MARK:
693 data->paritytype = PARITY_1;
694 break;
695 case SPARITY_SPACE:
696 data->paritytype = PARITY_0;
697 break;
702 * Bit per character
704 switch ((prefs->SerRWBits & 0x0f)) {
705 default: /* 8 bit */
706 case 0:
707 data->datalength = 8;
708 break;
710 case 1: /* 7 bit */
711 data->datalength = 7;
712 break;
714 case 2: /* 6 bit */
715 data->datalength = 6;
716 break;
718 case 3: /* 5 bit */
719 data->datalength = 5;
720 break;
724 * 2 stop bits ? default is '1'.
726 if (1 == (prefs->SerStopBuf >> 4))
727 data->stopbits = 2;
728 else
729 data->stopbits = 1;
732 * Handshake to be used.
734 // MISSING!