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