1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/device/serial.hpp" |
4 | |
5 | #include <mos/mos_global.h> |
6 | #include <mos/syslog/printk.hpp> |
7 | |
8 | bool ISerialDevice::setup() |
9 | { |
10 | SetInterrupts(INTERRUPT_NONE); |
11 | SetBaudrateDivisor(); |
12 | SetDataBits(); |
13 | SetStopBits(); |
14 | SetParity(); |
15 | |
16 | SetModemOptions(control: MODEM_DTR, enable: true); |
17 | SetModemOptions(control: MODEM_RTS, enable: true); |
18 | |
19 | // Try send a byte to the serial port. |
20 | // If it fails, then the serial port is not connected. |
21 | { |
22 | const char challenge = 'H'; |
23 | SetModemOptions(control: MODEM_LOOP, enable: true); |
24 | write_byte(byte: challenge); |
25 | const auto response = read_byte(); |
26 | SetModemOptions(control: MODEM_LOOP, enable: false); |
27 | if (response != 'H') |
28 | return false; |
29 | } |
30 | |
31 | SetModemOptions(control: MODEM_IRQ, enable: true); |
32 | SetInterrupts(INTERRUPT_DATA_AVAILABLE); |
33 | return true; |
34 | } |
35 | |
36 | int ISerialDevice::read_data(char *data, size_t length) |
37 | { |
38 | for (size_t i = 0; i < length; i++) |
39 | { |
40 | WaitReadyToRead(); |
41 | data[i] = read_byte(); |
42 | } |
43 | |
44 | return length; |
45 | } |
46 | |
47 | int ISerialDevice::write_data(const char *data, size_t length) |
48 | { |
49 | for (size_t i = 0; i < length; i++) |
50 | { |
51 | WaitReadyToWrite(); |
52 | write_byte(byte: data[i]); |
53 | } |
54 | |
55 | return length; |
56 | } |
57 | |
58 | void ISerialDevice::SetBaudrateDivisor() |
59 | { |
60 | // Set the most significant bit of the Line Control Register. This is the DLAB bit, and allows access to the divisor registers. |
61 | u8 reg = read_register(offset: OFFSET_LINE_CONTROL); |
62 | reg |= 0x80; // set DLAB bit |
63 | |
64 | write_register(offset: OFFSET_LINE_CONTROL, value: reg); |
65 | |
66 | // Send the least significant byte of the divisor value to [PORT + 0]. |
67 | write_register(offset: OFFSET_DLAB_DIVISOR_LSB, value: baudrate_divisor & 0xFF); |
68 | |
69 | // Send the most significant byte of the divisor value to [PORT + 1]. |
70 | write_register(offset: OFFSET_DLAB_DIVISOR_MSB, value: baudrate_divisor >> 8); |
71 | |
72 | // Clear the most significant bit of the Line Control Register. |
73 | reg &= ~0x80; |
74 | write_register(offset: OFFSET_LINE_CONTROL, value: reg); |
75 | } |
76 | void ISerialDevice::SetDataBits() |
77 | { |
78 | u8 control = read_register(offset: OFFSET_LINE_CONTROL); |
79 | control &= char_length; |
80 | write_register(offset: OFFSET_LINE_CONTROL, value: control); |
81 | } |
82 | void ISerialDevice::SetStopBits() |
83 | { |
84 | byte_t control = { .byte = read_register(offset: OFFSET_LINE_CONTROL) }; |
85 | control.bits.b1 = stop_bits == STOP_BITS_15_OR_2; |
86 | write_register(offset: OFFSET_LINE_CONTROL, value: control.byte); |
87 | } |
88 | void ISerialDevice::SetParity() |
89 | { |
90 | u8 byte = read_register(offset: OFFSET_LINE_CONTROL); |
91 | byte |= ((u8) parity) << 3; |
92 | write_register(offset: OFFSET_LINE_CONTROL, value: byte); |
93 | } |
94 | void ISerialDevice::SetInterrupts(int interrupts) |
95 | { |
96 | char control = read_register(offset: OFFSET_INTERRUPT_ENABLE); |
97 | control = interrupts; |
98 | write_register(offset: OFFSET_INTERRUPT_ENABLE, value: control); |
99 | } |
100 | void ISerialDevice::SetModemOptions(serial_modem_control_t control, bool enable) |
101 | { |
102 | byte_t byte = { .byte = read_register(offset: OFFSET_MODEM_CONTROL) }; |
103 | switch (control) |
104 | { |
105 | case MODEM_DTR: byte.bits.b0 = enable; break; |
106 | case MODEM_RTS: byte.bits.b1 = enable; break; |
107 | case MODEM_UNUSED_PIN1: byte.bits.b2 = enable; break; |
108 | case MODEM_IRQ: byte.bits.b3 = enable; break; |
109 | case MODEM_LOOP: byte.bits.b4 = enable; break; |
110 | } |
111 | write_register(offset: OFFSET_MODEM_CONTROL, value: byte.byte); |
112 | } |
113 | char ISerialDevice::GetLineStatus() |
114 | { |
115 | return read_register(offset: OFFSET_LINE_STATUS); |
116 | } |
117 | __maybe_unused char ISerialDevice::GetModelStatus() |
118 | { |
119 | return read_register(offset: OFFSET_MODEM_STATUS); |
120 | } |
121 | bool ISerialDevice::GetDataReady() |
122 | { |
123 | return GetLineStatus() & LINE_DATA_READY; |
124 | } |
125 | void ISerialDevice::WaitReadyToRead() |
126 | { |
127 | while (!GetDataReady()) |
128 | ; |
129 | } |
130 | void ISerialDevice::WaitReadyToWrite() |
131 | { |
132 | while (!(GetLineStatus() & LINE_TRANSMITR_BUF_EMPTY)) |
133 | ; |
134 | } |
135 | |