xref: /DragonOS/kernel/src/driver/serial/serial8250/serial8250_pio.rs (revision 840045af94ea3391f29e87e968db5d9c48316981)
1 //! PIO的串口驱动
2 
3 use core::{
4     hint::spin_loop,
5     sync::atomic::{AtomicBool, Ordering},
6 };
7 
8 use alloc::sync::{Arc, Weak};
9 
10 use crate::{
11     arch::{io::PortIOArch, CurrentPortIOArch},
12     driver::serial::{AtomicBaudRate, BaudRate, DivisorFraction, UartPort},
13     libs::rwlock::RwLock,
14 };
15 use system_error::SystemError;
16 
17 use super::{Serial8250ISADevices, Serial8250ISADriver, Serial8250Manager, Serial8250Port};
18 
19 static mut PIO_PORTS: [Option<Serial8250PIOPort>; 8] =
20     [None, None, None, None, None, None, None, None];
21 
22 impl Serial8250Manager {
23     pub(super) fn bind_pio_ports(
24         &self,
25         uart_driver: &Arc<Serial8250ISADriver>,
26         devs: &Arc<Serial8250ISADevices>,
27     ) {
28         for i in 0..8 {
29             if let Some(port) = unsafe { PIO_PORTS[i].as_ref() } {
30                 port.set_device(Some(devs));
31                 self.uart_add_one_port(uart_driver, port).ok();
32             }
33         }
34     }
35 }
36 
37 macro_rules! init_port {
38     ($port_num:expr, $baudrate:expr) => {
39         unsafe {
40             let port = Serial8250PIOPort::new(
41                 match $port_num {
42                     1 => Serial8250PortBase::COM1,
43                     2 => Serial8250PortBase::COM2,
44                     3 => Serial8250PortBase::COM3,
45                     4 => Serial8250PortBase::COM4,
46                     5 => Serial8250PortBase::COM5,
47                     6 => Serial8250PortBase::COM6,
48                     7 => Serial8250PortBase::COM7,
49                     8 => Serial8250PortBase::COM8,
50                     _ => panic!("invalid port number"),
51                 },
52                 BaudRate::new($baudrate),
53             );
54             if let Ok(port) = port {
55                 if port.init().is_ok() {
56                     PIO_PORTS[$port_num - 1] = Some(port);
57                 }
58             }
59         }
60     };
61 }
62 
63 /// 在内存管理初始化之前,初始化串口设备
64 pub(super) fn serial8250_pio_port_early_init() -> Result<(), SystemError> {
65     for i in 1..=8 {
66         init_port!(i, 115200);
67     }
68     return Ok(());
69 }
70 
71 #[derive(Debug)]
72 pub struct Serial8250PIOPort {
73     iobase: Serial8250PortBase,
74     baudrate: AtomicBaudRate,
75     initialized: AtomicBool,
76     inner: RwLock<Serial8250PIOPortInner>,
77 }
78 
79 impl Serial8250PIOPort {
80     const SERIAL8250PIO_MAX_BAUD_RATE: BaudRate = BaudRate::new(115200);
81     pub fn new(iobase: Serial8250PortBase, baudrate: BaudRate) -> Result<Self, SystemError> {
82         let r = Self {
83             iobase,
84             baudrate: AtomicBaudRate::new(baudrate),
85             initialized: AtomicBool::new(false),
86             inner: RwLock::new(Serial8250PIOPortInner::new()),
87         };
88 
89         if let Err(e) = r.check_baudrate(&baudrate) {
90             return Err(e);
91         }
92 
93         return Ok(r);
94     }
95 
96     pub fn init(&self) -> Result<(), SystemError> {
97         let r = self
98             .initialized
99             .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst);
100         if r.is_err() {
101             // 已经初始化
102             return Ok(());
103         }
104 
105         let port = self.iobase as u16;
106 
107         unsafe {
108             CurrentPortIOArch::out8(port + 1, 0x00); // Disable all interrupts
109             self.set_divisor(self.baudrate.load(Ordering::SeqCst))
110                 .unwrap(); // Set baud rate
111 
112             CurrentPortIOArch::out8(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
113             CurrentPortIOArch::out8(port + 4, 0x08); // IRQs enabled, RTS/DSR clear (现代计算机上一般都不需要hardware flow control,因此不需要置位RTS/DSR)
114             CurrentPortIOArch::out8(port + 4, 0x1E); // Set in loopback mode, test the serial chip
115             CurrentPortIOArch::out8(port + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
116 
117             // Check if serial is faulty (i.e: not same byte as sent)
118             if CurrentPortIOArch::in8(port + 0) != 0xAE {
119                 self.initialized.store(false, Ordering::SeqCst);
120                 return Err(SystemError::ENODEV);
121             }
122 
123             // If serial is not faulty set it in normal operation mode
124             // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
125             CurrentPortIOArch::out8(port + 4, 0x08);
126         }
127 
128         return Ok(());
129         /*
130                 Notice that the initialization code above writes to [PORT + 1]
131             twice with different values. This is once to write to the Divisor
132             register along with [PORT + 0] and once to write to the Interrupt
133             register as detailed in the previous section.
134                 The second write to the Line Control register [PORT + 3]
135             clears the DLAB again as well as setting various other bits.
136         */
137     }
138 
139     const fn check_baudrate(&self, baudrate: &BaudRate) -> Result<(), SystemError> {
140         // 错误的比特率
141         if baudrate.data() > Self::SERIAL8250PIO_MAX_BAUD_RATE.data()
142             || Self::SERIAL8250PIO_MAX_BAUD_RATE.data() % baudrate.data() != 0
143         {
144             return Err(SystemError::EINVAL);
145         }
146 
147         return Ok(());
148     }
149 
150     #[allow(dead_code)]
151     fn serial_received(&self) -> bool {
152         if self.serial_in(5) & 1 != 0 {
153             true
154         } else {
155             false
156         }
157     }
158 
159     fn is_transmit_empty(&self) -> bool {
160         if self.serial_in(5) & 0x20 != 0 {
161             true
162         } else {
163             false
164         }
165     }
166 
167     /// 发送字节
168     ///
169     /// ## 参数
170     ///
171     /// - `s`:待发送的字节
172     fn send_bytes(&self, s: &[u8]) {
173         while self.is_transmit_empty() == false {
174             spin_loop();
175         }
176 
177         for c in s {
178             self.serial_out(0, (*c).into());
179         }
180     }
181 
182     /// 读取一个字节
183     #[allow(dead_code)]
184     fn read_one_byte(&self) -> u8 {
185         while self.serial_received() == false {
186             spin_loop();
187         }
188         return self.serial_in(0) as u8;
189     }
190 }
191 
192 impl Serial8250Port for Serial8250PIOPort {
193     fn device(&self) -> Option<Arc<Serial8250ISADevices>> {
194         self.inner.read().device()
195     }
196 
197     fn set_device(&self, device: Option<&Arc<Serial8250ISADevices>>) {
198         self.inner.write().set_device(device);
199     }
200 }
201 
202 impl UartPort for Serial8250PIOPort {
203     fn serial_in(&self, offset: u32) -> u32 {
204         unsafe { CurrentPortIOArch::in8(self.iobase as u16 + offset as u16).into() }
205     }
206 
207     fn serial_out(&self, offset: u32, value: u32) {
208         // warning: pio的串口只能写入8位,因此这里丢弃高24位
209         unsafe { CurrentPortIOArch::out8(self.iobase as u16 + offset as u16, value as u8) }
210     }
211 
212     fn divisor(&self, baud: BaudRate) -> (u32, DivisorFraction) {
213         let divisor = Self::SERIAL8250PIO_MAX_BAUD_RATE.data() / baud.data();
214         return (divisor, DivisorFraction::new(0));
215     }
216 
217     fn set_divisor(&self, baud: BaudRate) -> Result<(), SystemError> {
218         self.check_baudrate(&baud)?;
219 
220         let port = self.iobase as u16;
221         unsafe {
222             CurrentPortIOArch::out8(port + 3, 0x80); // Enable DLAB (set baud rate divisor)
223 
224             let divisor = self.divisor(baud).0;
225 
226             CurrentPortIOArch::out8(port + 0, (divisor & 0xff) as u8); // Set divisor  (lo byte)
227             CurrentPortIOArch::out8(port + 1, ((divisor >> 8) & 0xff) as u8); // (hi byte)
228             CurrentPortIOArch::out8(port + 3, 0x03); // 8 bits, no parity, one stop bit
229         }
230 
231         self.baudrate.store(baud, Ordering::SeqCst);
232 
233         return Ok(());
234     }
235 
236     fn startup(&self) -> Result<(), SystemError> {
237         todo!("serial8250_pio::startup")
238     }
239 
240     fn shutdown(&self) {
241         todo!("serial8250_pio::shutdown")
242     }
243 
244     fn baud_rate(&self) -> Option<BaudRate> {
245         Some(self.baudrate.load(Ordering::SeqCst))
246     }
247 
248     fn handle_irq(&self) -> Result<(), SystemError> {
249         todo!("serial8250_pio::handle_irq")
250     }
251 }
252 
253 #[derive(Debug)]
254 struct Serial8250PIOPortInner {
255     /// 当前端口绑定的设备
256     ///
257     /// ps: 存储weak以避免循环引用
258     device: Option<Weak<Serial8250ISADevices>>,
259 }
260 
261 impl Serial8250PIOPortInner {
262     pub const fn new() -> Self {
263         Self { device: None }
264     }
265 
266     pub fn device(&self) -> Option<Arc<Serial8250ISADevices>> {
267         if let Some(device) = self.device.as_ref() {
268             return device.upgrade();
269         }
270         return None;
271     }
272 
273     fn set_device(&mut self, device: Option<&Arc<Serial8250ISADevices>>) {
274         self.device = device.map(|d| Arc::downgrade(d));
275     }
276 }
277 
278 #[allow(dead_code)]
279 #[repr(u16)]
280 #[derive(Clone, Debug, Copy)]
281 pub enum Serial8250PortBase {
282     COM1 = 0x3f8,
283     COM2 = 0x2f8,
284     COM3 = 0x3e8,
285     COM4 = 0x2e8,
286     COM5 = 0x5f8,
287     COM6 = 0x4f8,
288     COM7 = 0x5e8,
289     COM8 = 0x4e8,
290 }
291 
292 /// 临时函数,用于向COM1发送数据
293 pub fn send_to_serial8250_pio_com1(s: &[u8]) {
294     if let Some(port) = unsafe { PIO_PORTS[0].as_ref() } {
295         port.send_bytes(s);
296     }
297 }
298