2 Copyright © 1995-2011, 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>
31 #include <intuition/preferences.h>
33 #include <devices/serial.h>
35 #include <SDI/SDI_interrupt.h>
37 #include "serial_intern.h"
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
,
62 outb(value
, data
->baseaddr
+offset
);
65 static inline void serial_outp(struct HIDDSerialUnitData
* data
,
69 outb_p(value
, data
->baseaddr
+offset
);
72 static inline unsigned int serial_in(struct HIDDSerialUnitData
* data
,
75 return inb(data
->baseaddr
+offset
);
78 static inline unsigned int serial_inp(struct HIDDSerialUnitData
* data
,
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
;
98 EnterFunc(bug("SerialUnit::New()\n"));
100 tstate
= msg
->attrList
;
101 while ((tag
= NextTagItem(&tstate
)))
105 #define csd CSD(cl->UserData)
106 if (IS_HIDDSERIALUNIT_ATTR(tag
->ti_Tag
, idx
))
111 case aoHidd_SerialUnit_Unit
:
112 unitnum
= (ULONG
)tag
->ti_Data
;
117 } /* while (tags to process) */
119 obj
= (OOP_Object
*)OOP_DoSuperMethod(cl
, obj
, (OOP_Msg
)msg
);
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
);
135 data
->datalength
= 8;
136 data
->parity
= FALSE
;
137 data
->baudrate
= 9600; /* will be initialize in set_baudrate() */
139 data
->unitnum
= unitnum
;
142 CSD(cl
->UserData
)->units
[data
->unitnum
] = data
;
145 D(bug("Unit %d at 0x0%x\n", data
->unitnum
, data
->baseaddr
));
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
);
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
);
189 CSD(cl
->UserData
)->units
[data
->unitnum
] = NULL
;
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"));
208 data
->DataReceivedCallBack
= msg
->DataReceived
;
209 data
->DataReceivedUserData
= msg
->DataReceivedUserData
;
210 data
->DataWriteCallBack
= msg
->WriteData
;
211 data
->DataWriteUserData
= msg
->WriteDataUserData
;
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
;
225 EnterFunc(bug("SerialUnit::Write()\n"));
228 * If the output is currently stopped just don't do anything here.
230 if (TRUE
== data
->stopped
)
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
++]);
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
);
264 if (msg
->baudrate
!= data
->baudrate
)
266 valid
= set_baudrate(data
, msg
->baudrate
);
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
);
286 struct TagItem
* tags
= msg
->tags
;
288 while (TAG_END
!= tags
[i
].ti_Tag
&& TRUE
== valid
)
290 switch (tags
[i
].ti_Tag
)
293 if ((BYTE
)tags
[i
].ti_Data
>= 5 && (BYTE
)tags
[i
].ti_Data
<= 8)
294 data
->datalength
= tags
[i
].ti_Data
;
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
;
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
)
315 data
->paritytype
= tags
[i
].ti_Data
;
322 data
->parity
= FALSE
;
326 serial_outp(data
, UART_MCR
, (tags
[i
].ti_Data
& 0x0f) | 0x08);
340 serial_outp(data
, UART_LCR
, get_lcr(data
));
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
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
)
391 switch (tags
[i
].ti_Tag
)
393 case HIDDA_SerialUnit_BPSRate
:
394 tags
[i
].ti_Data
= (STACKIPTR
)valid_baudrates
;
397 case HIDDA_SerialUnit_DataLength
:
398 tags
[i
].ti_Data
= (STACKIPTR
)valid_datalengths
;
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
);
415 UBYTE msr
= serial_inp(data
, UART_MSR
);
416 UBYTE mcr
= serial_inp(data
, UART_MCR
);
418 if (msr
& UART_MSR_DCD
)
420 if (msr
& UART_MSR_DSR
)
422 if (msr
& UART_MSR_CTS
)
425 if (mcr
& UART_MCR_DTR
)
427 if (mcr
& UART_MCR_RTS
)
428 status
|= (1<<6); /* old RKMs say 'ready to send' */
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
))
444 struct HIDDSerialUnitData
* data
= iD
;
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
);
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
))
477 struct HIDDSerialUnitData
* data
= iD
;
480 * If the output is currently stopped just don't do
483 if (TRUE
== data
->stopped
)
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
);
499 /* some help routines */
501 unsigned char get_lcr(struct HIDDSerialUnitData
* data
)
504 switch (data
->datalength
)
521 switch (data
->stopbits
)
523 case 1: /* 1 stopbit */
527 case 3: /* 1.5 stopbits (is this supported ?!!!) */
528 if (data
->datalength
== 5)
532 case 2: /* 2 stopbits */
533 if (data
->datalength
>= 6 && data
->datalength
<= 8)
541 if (TRUE
== data
->parity
)
545 switch (data
->paritytype
)
556 lcr
|= (1 << 4) | (1 << 5);
562 if (TRUE
== data
->breakcontrol
)
567 unsigned char get_fcr(ULONG baudrate
)
573 Depending on the baudrate set the fifo interrupt threshold to a
583 if (baudrate
< 38400)
589 BOOL
set_baudrate(struct HIDDSerialUnitData
* data
, ULONG speed
)
593 if (!(speed
>= 50 && speed
<= 115200))
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
);
608 /* Serial interrupts */
610 static void common_serial_int_handler(struct class_static_data
*csd
,
613 UBYTE code
= UART_IIR_NO_INT
;
615 if (csd
->units
[unitnum
])
616 code
= serial_inp(csd
->units
[unitnum
], UART_IIR
) & 0x07;
621 (void)serial_inp(csd
->units
[unitnum
], UART_LSR
);
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
));
634 (void)serial_inp(csd
->units
[unitnum
], UART_MSR
);
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
);
645 AROS_INTH1(serial_int_13
, void *, data
)
649 common_serial_int_handler(data
, 0);
650 common_serial_int_handler(data
, 2);
657 AROS_INTH1(serial_int_24
, void *, data
)
661 common_serial_int_handler(data
, 1);
662 common_serial_int_handler(data
, 3);
669 static void adapt_data(struct HIDDSerialUnitData
* data
,
670 struct Preferences
* prefs
)
677 switch ((prefs
->SerParShk
>> 4) & 0x0f) {
680 default: /* DEFAULT !! */
681 data
->parity
= FALSE
;
685 data
->paritytype
= PARITY_EVEN
;
689 data
->paritytype
= PARITY_ODD
;
693 data
->paritytype
= PARITY_1
;
696 data
->paritytype
= PARITY_0
;
704 switch ((prefs
->SerRWBits
& 0x0f)) {
707 data
->datalength
= 8;
711 data
->datalength
= 7;
715 data
->datalength
= 6;
719 data
->datalength
= 5;
724 * 2 stop bits ? default is '1'.
726 if (1 == (prefs
->SerStopBuf
>> 4))
732 * Handshake to be used.