1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/device/serial.h" |
4 | |
5 | #include <mos/mos_global.h> |
6 | #include <mos/syslog/printk.h> |
7 | |
8 | typedef enum |
9 | { |
10 | MODEM_DCTS = 1 << 0, // Clear To Send input has changed since last read. |
11 | MODEM_DDSR = 1 << 1, // Data Set Ready input has changed since last read. |
12 | MODEM_TERI = 1 << 2, // Ring Indicator input has changed since last read. |
13 | MODEM_DDCD = 1 << 3, // Data Carrier Detect input has changed since last read. |
14 | MODEM_CLEAR_TO_SEND = 1 << 4, |
15 | MODEM_DATA_SET_READY = 1 << 5, |
16 | MODEM_RING_INDICATOR = 1 << 6, |
17 | MODEM_DATA_CARRIER_DETECT = 1 << 7, |
18 | } serial_modem_status_t; |
19 | |
20 | typedef enum |
21 | { |
22 | MODEM_DTR = 1 << 0, // Data Terminal Ready |
23 | MODEM_RTS = 1 << 1, // Request To Send |
24 | MODEM_UNUSED_PIN1 = 1 << 2, // Unused |
25 | MODEM_IRQ = 1 << 3, // Interrupt Request |
26 | MODEM_LOOP = 1 << 4, // Loopback |
27 | } serial_modem_control_t; |
28 | |
29 | typedef enum |
30 | { |
31 | LINE_DATA_READY = 1 << 0, // Data ready to be read. |
32 | LINE_ERR_OVERRUN = 1 << 1, // There has been data lost. |
33 | LINE_ERR_PARITY = 1 << 2, // Parity error. |
34 | LINE_ERR_FRAMING = 1 << 3, // Stop bit is missing. |
35 | LINE_ERR_BREAK = 1 << 4, // Break detected. |
36 | LINE_TRANSMITR_BUF_EMPTY = 1 << 5, // (transmitter buffer is empty) Data can be sent. |
37 | LINE_TRANSMITR_EMPTY = 1 << 6, // Transmitter is not doing anything. |
38 | LINE_ERR_IMPENDING = 1 << 7, // There is an error with a word in the input buffer |
39 | } serial_line_status_t; |
40 | |
41 | static void set_baudrate_divisor(serial_device_t *dev) |
42 | { |
43 | // Set the most significant bit of the Line Control Register. This is the DLAB bit, and allows access to the divisor registers. |
44 | u8 reg = dev->driver->read_register(dev, OFFSET_LINE_CONTROL); |
45 | reg |= 0x80; // set DLAB bit |
46 | |
47 | dev->driver->write_register(dev, OFFSET_LINE_CONTROL, reg); |
48 | |
49 | // Send the least significant byte of the divisor value to [PORT + 0]. |
50 | dev->driver->write_register(dev, OFFSET_DLAB_DIVISOR_LSB, dev->baudrate_divisor & 0xFF); |
51 | |
52 | // Send the most significant byte of the divisor value to [PORT + 1]. |
53 | dev->driver->write_register(dev, OFFSET_DLAB_DIVISOR_MSB, dev->baudrate_divisor >> 8); |
54 | |
55 | // Clear the most significant bit of the Line Control Register. |
56 | reg &= ~0x80; |
57 | dev->driver->write_register(dev, OFFSET_LINE_CONTROL, reg); |
58 | } |
59 | |
60 | static void set_data_bits(serial_device_t *dev) |
61 | { |
62 | u8 control = dev->driver->read_register(dev, OFFSET_LINE_CONTROL); |
63 | control &= dev->char_length; |
64 | dev->driver->write_register(dev, OFFSET_LINE_CONTROL, control); |
65 | } |
66 | |
67 | static void set_stop_bits(serial_device_t *dev) |
68 | { |
69 | byte_t control = { .byte = dev->driver->read_register(dev, OFFSET_LINE_CONTROL) }; |
70 | control.bits.b1 = dev->stop_bits == STOP_BITS_15_OR_2; |
71 | dev->driver->write_register(dev, OFFSET_LINE_CONTROL, control.byte); |
72 | } |
73 | |
74 | static void set_parity(serial_device_t *dev, serial_parity_t parity) |
75 | { |
76 | u8 byte = dev->driver->read_register(dev, OFFSET_LINE_CONTROL); |
77 | byte |= ((u8) parity) << 3; |
78 | dev->driver->write_register(dev, OFFSET_LINE_CONTROL, byte); |
79 | } |
80 | |
81 | static void serial_set_interrupts(serial_device_t *dev, int interrupts) |
82 | { |
83 | char control = dev->driver->read_register(dev, OFFSET_INTERRUPT_ENABLE); |
84 | control = interrupts; |
85 | dev->driver->write_register(dev, OFFSET_INTERRUPT_ENABLE, control); |
86 | } |
87 | |
88 | static void serial_set_modem_options(serial_device_t *dev, serial_modem_control_t control, bool enable) |
89 | { |
90 | byte_t byte = { .byte = dev->driver->read_register(dev, OFFSET_MODEM_CONTROL) }; |
91 | switch (control) |
92 | { |
93 | case MODEM_DTR: byte.bits.b0 = enable; break; |
94 | case MODEM_RTS: byte.bits.b1 = enable; break; |
95 | case MODEM_UNUSED_PIN1: byte.bits.b2 = enable; break; |
96 | case MODEM_IRQ: byte.bits.b3 = enable; break; |
97 | case MODEM_LOOP: byte.bits.b4 = enable; break; |
98 | } |
99 | dev->driver->write_register(dev, OFFSET_MODEM_CONTROL, byte.byte); |
100 | } |
101 | |
102 | static char serial_get_line_status(serial_device_t *dev) |
103 | { |
104 | return dev->driver->read_register(dev, OFFSET_LINE_STATUS); |
105 | } |
106 | |
107 | __maybe_unused static char serial_get_modem_status(serial_device_t *dev) |
108 | { |
109 | return dev->driver->read_register(dev, OFFSET_MODEM_STATUS); |
110 | } |
111 | |
112 | bool serial_device_setup(serial_device_t *device) |
113 | { |
114 | serial_set_interrupts(dev: device, interrupts: INTERRUPT_NONE); |
115 | set_baudrate_divisor(device); |
116 | set_data_bits(device); |
117 | set_stop_bits(device); |
118 | set_parity(dev: device, parity: device->parity); |
119 | |
120 | serial_set_modem_options(dev: device, control: MODEM_DTR, enable: true); |
121 | serial_set_modem_options(dev: device, control: MODEM_RTS, enable: true); |
122 | |
123 | // Try send a byte to the serial port. |
124 | // If it fails, then the serial port is not connected. |
125 | { |
126 | const char challenge = 'H'; |
127 | char response = { 0 }; |
128 | serial_set_modem_options(dev: device, control: MODEM_LOOP, enable: true); |
129 | serial_device_write(device, data: &challenge, length: 1); |
130 | serial_device_read(device, data: &response, length: 1); |
131 | serial_set_modem_options(dev: device, control: MODEM_LOOP, enable: false); |
132 | if (response != 'H') |
133 | return false; |
134 | } |
135 | |
136 | serial_set_modem_options(dev: device, control: MODEM_IRQ, enable: true); |
137 | serial_set_interrupts(dev: device, interrupts: INTERRUPT_DATA_AVAILABLE); |
138 | return true; |
139 | } |
140 | |
141 | bool serial_dev_get_data_ready(serial_device_t *device) |
142 | { |
143 | return serial_get_line_status(dev: device) & LINE_DATA_READY; |
144 | } |
145 | |
146 | static void serial_dev_wait_ready_to_read(serial_device_t *device) |
147 | { |
148 | while (!serial_dev_get_data_ready(device)) |
149 | ; |
150 | } |
151 | |
152 | static void serial_dev_wait_ready_to_write(serial_device_t *device) |
153 | { |
154 | while (!(serial_get_line_status(dev: device) & LINE_TRANSMITR_BUF_EMPTY)) |
155 | ; |
156 | } |
157 | |
158 | int serial_device_write(serial_device_t *device, const char *data, size_t length) |
159 | { |
160 | size_t i = 0; |
161 | for (; i < length; i++) |
162 | { |
163 | serial_dev_wait_ready_to_write(device); |
164 | device->driver->write_data(device, data[i]); |
165 | } |
166 | return i; |
167 | } |
168 | |
169 | int serial_device_read(serial_device_t *device, char *data, size_t length) |
170 | { |
171 | for (size_t i = 0; i < length; i++) |
172 | { |
173 | serial_dev_wait_ready_to_read(device); |
174 | data[i] = device->driver->read_data(device); |
175 | } |
176 | |
177 | return length; |
178 | } |
179 | |