2 Copyright © 1995-2006, The AROS Development Team. All rights reserved.
5 Desc: Serial Unit hidd class implementation.
10 Right now I am assuming that there is a 1.8432 MHZ crystal connected to
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>
32 #include <intuition/preferences.h>
34 #include <devices/serial.h>
36 #include "serial_intern.h"
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
,
61 outb(value
, data
->baseaddr
+offset
);
64 static inline void serial_outp(struct HIDDSerialUnitData
* data
,
68 outb_p(value
, data
->baseaddr
+offset
);
71 static inline unsigned int serial_in(struct HIDDSerialUnitData
* data
,
74 return inb(data
->baseaddr
+offset
);
77 static inline unsigned int serial_inp(struct HIDDSerialUnitData
* data
,
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
;
97 EnterFunc(bug("SerialUnit::New()\n"));
99 tstate
= msg
->attrList
;
100 while ((tag
= NextTagItem((const struct TagItem
**)&tstate
)))
104 #define csd CSD(cl->UserData)
105 if (IS_HIDDSERIALUNIT_ATTR(tag
->ti_Tag
, idx
))
110 case aoHidd_SerialUnit_Unit
:
111 unitnum
= (ULONG
)tag
->ti_Data
;
116 } /* while (tags to process) */
118 obj
= (OOP_Object
*)OOP_DoSuperMethod(cl
, obj
, (OOP_Msg
)msg
);
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
);
134 data
->datalength
= 8;
135 data
->parity
= FALSE
;
136 data
->baudrate
= 9600; /* will be initialize in set_baudrate() */
138 data
->unitnum
= unitnum
;
141 CSD(cl
->UserData
)->units
[data
->unitnum
] = data
;
144 D(bug("Unit %d at 0x0%x\n", data
->unitnum
, data
->baseaddr
));
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
);
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
);
188 CSD(cl
->UserData
)->units
[data
->unitnum
] = NULL
;
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"));
207 data
->DataReceivedCallBack
= msg
->DataReceived
;
208 data
->DataReceivedUserData
= msg
->DataReceivedUserData
;
209 data
->DataWriteCallBack
= msg
->WriteData
;
210 data
->DataWriteUserData
= msg
->WriteDataUserData
;
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
;
224 EnterFunc(bug("SerialUnit::Write()\n"));
227 * If the output is currently stopped just don't do anything here.
229 if (TRUE
== data
->stopped
)
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
++]);
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
);
263 if (msg
->baudrate
!= data
->baudrate
)
265 valid
= set_baudrate(data
, msg
->baudrate
);
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
);
285 struct TagItem
* tags
= msg
->tags
;
287 while (TAG_END
!= tags
[i
].ti_Tag
&& TRUE
== valid
)
289 switch (tags
[i
].ti_Tag
)
292 if ((BYTE
)tags
[i
].ti_Data
>= 5 && (BYTE
)tags
[i
].ti_Data
<= 8)
293 data
->datalength
= tags
[i
].ti_Data
;
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
;
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
)
314 data
->paritytype
= tags
[i
].ti_Data
;
321 data
->parity
= FALSE
;
325 serial_outp(data
, UART_MCR
, (tags
[i
].ti_Data
& 0x0f) | 0x08);
339 serial_outp(data
, UART_LCR
, get_lcr(data
));
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
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
)
392 switch (tags
[i
].ti_Tag
)
394 case HIDDA_SerialUnit_BPSRate
:
395 tags
[i
].ti_Data
= (STACKIPTR
)valid_baudrates
;
398 case HIDDA_SerialUnit_DataLength
:
399 tags
[i
].ti_Data
= (STACKIPTR
)valid_datalengths
;
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
);
416 UBYTE msr
= serial_inp(data
, UART_MSR
);
417 UBYTE mcr
= serial_inp(data
, UART_MCR
);
419 if (msr
& UART_MSR_DCD
)
421 if (msr
& UART_MSR_DSR
)
423 if (msr
& UART_MSR_CTS
)
426 if (mcr
& UART_MCR_DTR
)
428 if (mcr
& UART_MCR_RTS
)
429 status
|= (1<<6); /* old RKMs say 'ready to send' */
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
))
445 struct HIDDSerialUnitData
* data
= iD
;
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
);
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
))
478 struct HIDDSerialUnitData
* data
= iD
;
481 * If the output is currently stopped just don't do
484 if (TRUE
== data
->stopped
)
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
);
500 /* some help routines */
502 unsigned char get_lcr(struct HIDDSerialUnitData
* data
)
505 switch (data
->datalength
)
522 switch (data
->stopbits
)
524 case 1: /* 1 stopbit */
528 case 3: /* 1.5 stopbits (is this supported ?!!!) */
529 if (data
->datalength
== 5)
533 case 2: /* 2 stopbits */
534 if (data
->datalength
>= 6 && data
->datalength
<= 8)
542 if (TRUE
== data
->parity
)
546 switch (data
->paritytype
)
557 lcr
|= (1 << 4) | (1 << 5);
563 if (TRUE
== data
->breakcontrol
)
568 unsigned char get_fcr(ULONG baudrate
)
574 Depending on the baudrate set the fifo interrupt threshold to a
584 if (baudrate
< 38400)
590 BOOL
set_baudrate(struct HIDDSerialUnitData
* data
, ULONG speed
)
594 if (!(speed
>= 50 && speed
<= 115200))
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
);
609 /* Serial interrupts */
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
,
619 UBYTE code
= UART_IIR_NO_INT
;
621 if (csd
->units
[unitnum
])
622 code
= serial_inp(csd
->units
[unitnum
], UART_IIR
) & 0x07;
627 (void)serial_inp(csd
->units
[unitnum
], UART_LSR
);
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
));
640 (void)serial_inp(csd
->units
[unitnum
], UART_MSR
);
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
);
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
)
671 switch ((prefs
->SerParShk
>> 4) & 0x0f) {
674 default: /* DEFAULT !! */
675 data
->parity
= FALSE
;
679 data
->paritytype
= PARITY_EVEN
;
683 data
->paritytype
= PARITY_ODD
;
687 data
->paritytype
= PARITY_1
;
690 data
->paritytype
= PARITY_0
;
698 switch ((prefs
->SerRWBits
& 0x0f)) {
701 data
->datalength
= 8;
705 data
->datalength
= 7;
709 data
->datalength
= 6;
713 data
->datalength
= 5;
718 * 2 stop bits ? default is '1'.
720 if (1 == (prefs
->SerStopBuf
>> 4))
726 * Handshake to be used.