237 lines
8.4 KiB
Markdown
237 lines
8.4 KiB
Markdown
# RS232 — Serial Port Library for DJGPP
|
|
|
|
ISR-driven UART communication library supporting up to 4 simultaneous
|
|
COM ports with ring buffers and hardware/software flow control.
|
|
|
|
Ported from the DOS Serial Library 1.4 by Karl Stenerud (MIT License),
|
|
stripped to DJGPP-only codepaths and restyled.
|
|
|
|
## Features
|
|
|
|
- ISR-driven receive and transmit with 2048-byte ring buffers
|
|
- Auto-detected IRQ from BIOS data area
|
|
- UART type detection (8250, 16450, 16550, 16550A)
|
|
- 16550 FIFO detection and configurable trigger threshold
|
|
- XON/XOFF, RTS/CTS, and DTR/DSR flow control
|
|
- DPMI memory locking for ISR safety
|
|
- Speeds from 50 to 115200 bps
|
|
- 5-8 data bits, N/O/E/M/S parity, 1-2 stop bits
|
|
|
|
## API Reference
|
|
|
|
### Types
|
|
|
|
All functions take a COM port index (`int com`) as their first argument:
|
|
|
|
| Constant | Value | Description |
|
|
|--------------|-------|-------------|
|
|
| `RS232_COM1` | 0 | COM1 |
|
|
| `RS232_COM2` | 1 | COM2 |
|
|
| `RS232_COM3` | 2 | COM3 |
|
|
| `RS232_COM4` | 3 | COM4 |
|
|
|
|
### UART Types
|
|
|
|
| Constant | Value | Description |
|
|
|---------------------|-------|--------------------------------------|
|
|
| `RS232_UART_UNKNOWN`| 0 | Unknown or undetected |
|
|
| `RS232_UART_8250` | 1 | 8250 — no FIFO, no scratch register |
|
|
| `RS232_UART_16450` | 2 | 16450 — scratch register, no FIFO |
|
|
| `RS232_UART_16550` | 3 | 16550 — broken FIFO (unusable) |
|
|
| `RS232_UART_16550A` | 4 | 16550A — working 16-byte FIFO |
|
|
|
|
### Handshaking Modes
|
|
|
|
| Constant | Value | Description |
|
|
|--------------------------|-------|---------------------------|
|
|
| `RS232_HANDSHAKE_NONE` | 0 | No flow control |
|
|
| `RS232_HANDSHAKE_XONXOFF`| 1 | Software (XON/XOFF) |
|
|
| `RS232_HANDSHAKE_RTSCTS` | 2 | Hardware (RTS/CTS) |
|
|
| `RS232_HANDSHAKE_DTRDSR` | 3 | Hardware (DTR/DSR) |
|
|
|
|
### Error Codes
|
|
|
|
| Constant | Value | Description |
|
|
|-------------------------------|-------|---------------------------|
|
|
| `RS232_SUCCESS` | 0 | Success |
|
|
| `RS232_ERR_UNKNOWN` | -1 | Unknown error |
|
|
| `RS232_ERR_NOT_OPEN` | -2 | Port not open |
|
|
| `RS232_ERR_ALREADY_OPEN` | -3 | Port already open |
|
|
| `RS232_ERR_NO_UART` | -4 | No UART detected |
|
|
| `RS232_ERR_INVALID_PORT` | -5 | Bad port index |
|
|
| `RS232_ERR_INVALID_BASE` | -6 | Bad I/O base address |
|
|
| `RS232_ERR_INVALID_IRQ` | -7 | Bad IRQ number |
|
|
| `RS232_ERR_INVALID_BPS` | -8 | Unsupported baud rate |
|
|
| `RS232_ERR_INVALID_DATA` | -9 | Bad data bits (not 5-8) |
|
|
| `RS232_ERR_INVALID_PARITY` | -10 | Bad parity character |
|
|
| `RS232_ERR_INVALID_STOP` | -11 | Bad stop bits (not 1-2) |
|
|
| `RS232_ERR_INVALID_HANDSHAKE` | -12 | Bad handshaking mode |
|
|
| `RS232_ERR_INVALID_FIFO` | -13 | Bad FIFO threshold |
|
|
| `RS232_ERR_NULL_PTR` | -14 | NULL pointer argument |
|
|
| `RS232_ERR_IRQ_NOT_FOUND` | -15 | Could not detect IRQ |
|
|
| `RS232_ERR_LOCK_MEM` | -16 | DPMI memory lock failed |
|
|
|
|
### Functions
|
|
|
|
#### Open / Close
|
|
|
|
```c
|
|
int rs232Open(int com, int32_t bps, int dataBits, char parity,
|
|
int stopBits, int handshake);
|
|
```
|
|
|
|
Opens a COM port. Detects the UART base address from the BIOS data
|
|
area, auto-detects the IRQ, installs the ISR, and configures the port.
|
|
|
|
- `bps` — baud rate (50, 75, 110, 150, 300, 600, 1200, 1800, 2400,
|
|
3800, 4800, 7200, 9600, 19200, 38400, 57600, 115200)
|
|
- `dataBits` — 5, 6, 7, or 8
|
|
- `parity` — `'N'` (none), `'O'` (odd), `'E'` (even), `'M'` (mark),
|
|
`'S'` (space)
|
|
- `stopBits` — 1 or 2
|
|
- `handshake` — `RS232_HANDSHAKE_*` constant
|
|
|
|
```c
|
|
int rs232Close(int com);
|
|
```
|
|
|
|
Closes the port, removes the ISR, and restores the original interrupt
|
|
vector.
|
|
|
|
#### Read / Write
|
|
|
|
```c
|
|
int rs232Read(int com, char *data, int len);
|
|
```
|
|
|
|
Reads up to `len` bytes from the receive buffer. Returns the number of
|
|
bytes actually read (0 if the buffer is empty).
|
|
|
|
```c
|
|
int rs232Write(int com, const char *data, int len);
|
|
```
|
|
|
|
Blocking write. Sends `len` bytes, waiting for transmit buffer space
|
|
as needed. Returns `RS232_SUCCESS` or an error code.
|
|
|
|
```c
|
|
int rs232WriteBuf(int com, const char *data, int len);
|
|
```
|
|
|
|
Non-blocking write. Copies as many bytes as will fit into the transmit
|
|
buffer. Returns the number of bytes actually queued.
|
|
|
|
#### Buffer Management
|
|
|
|
```c
|
|
int rs232ClearRxBuffer(int com);
|
|
int rs232ClearTxBuffer(int com);
|
|
```
|
|
|
|
Discard all data in the receive or transmit ring buffer.
|
|
|
|
#### Getters
|
|
|
|
```c
|
|
int rs232GetBase(int com); // UART I/O base address
|
|
int32_t rs232GetBps(int com); // Current baud rate
|
|
int rs232GetCts(int com); // CTS line state (0 or 1)
|
|
int rs232GetData(int com); // Data bits setting
|
|
int rs232GetDsr(int com); // DSR line state (0 or 1)
|
|
int rs232GetDtr(int com); // DTR line state (0 or 1)
|
|
int rs232GetHandshake(int com); // Handshaking mode
|
|
int rs232GetIrq(int com); // IRQ number
|
|
int rs232GetLsr(int com); // Line status register
|
|
int rs232GetMcr(int com); // Modem control register
|
|
int rs232GetMsr(int com); // Modem status register
|
|
char rs232GetParity(int com); // Parity setting ('N','O','E','M','S')
|
|
int rs232GetRts(int com); // RTS line state (0 or 1)
|
|
int rs232GetRxBuffered(int com); // Bytes in receive buffer
|
|
int rs232GetStop(int com); // Stop bits setting
|
|
int rs232GetTxBuffered(int com); // Bytes in transmit buffer
|
|
int rs232GetUartType(int com); // UART type (RS232_UART_* constant)
|
|
```
|
|
|
|
`rs232GetUartType` probes the UART hardware to identify the chip:
|
|
|
|
1. **Scratch register test** — writes two values to register 7 and
|
|
reads them back. The 8250 lacks this register, so readback fails.
|
|
2. **FIFO test** — enables the FIFO via the FCR, then reads IIR bits
|
|
7:6. `0b11` = 16550A (working FIFO), `0b10` = 16550 (broken FIFO),
|
|
`0b00` = 16450 (no FIFO). The original FCR value is restored after
|
|
probing.
|
|
|
|
#### Setters
|
|
|
|
```c
|
|
int rs232Set(int com, int32_t bps, int dataBits, char parity,
|
|
int stopBits, int handshake);
|
|
```
|
|
|
|
Reconfigure all port parameters at once (port must be open).
|
|
|
|
```c
|
|
int rs232SetBase(int com, int base); // Override I/O base address
|
|
int rs232SetBps(int com, int32_t bps); // Change baud rate
|
|
int rs232SetData(int com, int dataBits); // Change data bits
|
|
int rs232SetDtr(int com, bool dtr); // Assert/deassert DTR
|
|
int rs232SetFifoThreshold(int com, int thr); // FIFO trigger level (1,4,8,14)
|
|
int rs232SetHandshake(int com, int handshake); // Change flow control mode
|
|
int rs232SetIrq(int com, int irq); // Override IRQ (before Open)
|
|
int rs232SetMcr(int com, int mcr); // Write modem control register
|
|
int rs232SetParity(int com, char parity); // Change parity
|
|
int rs232SetRts(int com, bool rts); // Assert/deassert RTS
|
|
int rs232SetStop(int com, int stopBits); // Change stop bits
|
|
```
|
|
|
|
## Example
|
|
|
|
```c
|
|
#include "rs232.h"
|
|
|
|
int main(void) {
|
|
// Open COM1 at 115200 8N1, no flow control
|
|
int rc = rs232Open(RS232_COM1, 115200, 8, 'N', 1, RS232_HANDSHAKE_NONE);
|
|
if (rc != RS232_SUCCESS) {
|
|
return 1;
|
|
}
|
|
|
|
// Identify UART chip
|
|
int uartType = rs232GetUartType(RS232_COM1);
|
|
// uartType == RS232_UART_16550A on most systems
|
|
|
|
// Blocking send
|
|
rs232Write(RS232_COM1, "Hello\r\n", 7);
|
|
|
|
// Non-blocking receive
|
|
char buf[128];
|
|
int n;
|
|
while ((n = rs232Read(RS232_COM1, buf, sizeof(buf))) > 0) {
|
|
// process buf[0..n-1]
|
|
}
|
|
|
|
rs232Close(RS232_COM1);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
## Implementation Notes
|
|
|
|
- The ISR handles all four COM ports from a single shared handler.
|
|
On entry it disables UART interrupts for all open ports, then
|
|
re-enables CPU interrupts so higher-priority devices are serviced.
|
|
- Ring buffers use power-of-2 sizes (2048 bytes) with bitmask indexing
|
|
for zero-branch wraparound.
|
|
- Flow control watermarks are at 80% (assert) and 20% (deassert) of
|
|
buffer capacity.
|
|
- DPMI `__dpmi_lock_linear_region` is used to pin the ISR, ring
|
|
buffers, and port state in physical memory.
|
|
|
|
## Building
|
|
|
|
```
|
|
make # builds ../lib/librs232.a
|
|
make clean # removes objects and library
|
|
```
|
|
|
|
Target: DJGPP cross-compiler, 486+ CPU.
|