1use crate::cpu;
60use core::{fmt, marker::PhantomData};
61use mycelium_util::{
62 io,
63 sync::{
64 blocking::{Mutex, MutexGuard},
65 spin::Spinlock,
66 Lazy,
67 },
68};
69
70static COM1: Lazy<Option<Port>> = Lazy::new(|| Port::new(0x3F8).ok());
71static COM2: Lazy<Option<Port>> = Lazy::new(|| Port::new(0x2F8).ok());
72static COM3: Lazy<Option<Port>> = Lazy::new(|| Port::new(0x3E8).ok());
73static COM4: Lazy<Option<Port>> = Lazy::new(|| Port::new(0x2E8).ok());
74
75pub fn com1() -> Option<&'static Port> {
76 COM1.as_ref()
77}
78
79pub fn com2() -> Option<&'static Port> {
80 COM2.as_ref()
81}
82
83pub fn com3() -> Option<&'static Port> {
84 COM3.as_ref()
85}
86
87pub fn com4() -> Option<&'static Port> {
88 COM4.as_ref()
89}
90
91#[derive(Debug)]
94pub enum Pc16550dInterrupt {
95 ModemStatus = 0b0000,
96 TransmitterHoldingRegEmpty = 0b0010,
97 ReceivedDataAvailable = 0b0100,
98 ReceiverLineStatus = 0b110,
99 CharacterTimeout = 0b1100,
100}
101
102pub struct Port {
103 read_inner: Mutex<ReadRegisters, Spinlock>,
104 write_inner: Mutex<WriteRegisters, Spinlock>,
105}
106
107pub struct ReadLock<'a, B = Blocking> {
110 inner: MutexGuard<'a, ReadRegisters, Spinlock>,
112 _is_blocking: PhantomData<B>,
113}
114
115pub struct WriteLock<'a, B = Blocking> {
118 inner: WriteLockInner<'a>,
120 _is_blocking: PhantomData<B>,
121}
122
123struct WriteLockInner<'a> {
124 inner: MutexGuard<'a, WriteRegisters, Spinlock>,
125 prev_divisor: Option<u16>,
126}
127
128struct ReadRegisters {
136 data: cpu::Port,
137 iir: cpu::Port,
141 status: cpu::Port,
142}
143
144struct WriteRegisters {
146 data: cpu::Port,
147 irq_enable: cpu::Port,
148 line_ctrl: cpu::Port,
149 modem_ctrl: cpu::Port,
150 status: cpu::Port,
151 baud_rate_divisor: u16,
152}
153
154#[derive(Debug, Copy, Clone, Eq, PartialEq)]
155pub struct Blocking {
156 _p: (),
157}
158
159#[derive(Debug, Copy, Clone, Eq, PartialEq)]
160pub struct Nonblocking {
161 _p: (),
162}
163
164impl Port {
165 pub const MAX_BAUD_RATE: usize = 115_200;
166
167 pub fn new(port: u16) -> io::Result<Self> {
168 let scratch_test = unsafe {
169 const TEST_BYTE: u8 = 69;
170 let scratch_port = cpu::Port::at(port + 7);
171 scratch_port.writeb(TEST_BYTE);
172 scratch_port.readb() == TEST_BYTE
173 };
174
175 if !scratch_test {
176 return Err(io::Error::new(
177 io::ErrorKind::InvalidInput,
178 "scrach port was not writeable, is there a serial port at this address?",
179 ));
180 }
181
182 let read_registers = ReadRegisters {
183 data: cpu::Port::at(port),
184 iir: cpu::Port::at(port + 2),
185 status: cpu::Port::at(port + 5),
186 };
187 let mut write_registers = WriteRegisters {
188 data: cpu::Port::at(port),
189 irq_enable: cpu::Port::at(port + 1),
190 line_ctrl: cpu::Port::at(port + 3),
191 modem_ctrl: cpu::Port::at(port + 4),
192 status: cpu::Port::at(port + 5),
193 baud_rate_divisor: 3,
194 };
195 let fifo_ctrl = cpu::Port::at(port + 2);
196
197 write_registers.without_irqs(|registers| unsafe {
199 registers.set_baud_rate_divisor(3)?;
201
202 registers.line_ctrl.writeb(0x03);
204
205 fifo_ctrl.writeb(0xC7);
207
208 registers.modem_ctrl.writeb(0x0B);
210
211 Ok::<(), io::Error>(())
212 })?;
213
214 Ok(Self {
215 read_inner: Mutex::new_with_raw_mutex(read_registers, Spinlock::new()),
216 write_inner: Mutex::new_with_raw_mutex(write_registers, Spinlock::new()),
217 })
218 }
219
220 pub fn read_lock(&self) -> ReadLock<'_> {
221 ReadLock {
222 inner: self.read_inner.lock(),
223 _is_blocking: PhantomData,
224 }
225 }
226
227 pub fn write_lock(&self) -> WriteLock<'_> {
228 WriteLock {
229 inner: WriteLockInner {
230 inner: self.write_inner.lock(),
231 prev_divisor: None,
232 },
233 _is_blocking: PhantomData,
234 }
235 }
236
237 pub unsafe fn force_unlock(&self) {
244 self.read_inner.force_unlock();
245 self.write_inner.force_unlock();
246 }
247}
248
249impl ReadRegisters {
250 #[inline]
251 fn iir(&self) -> u8 {
252 unsafe { self.iir.readb() }
253 }
254
255 #[inline]
256 fn line_status(&self) -> u8 {
257 unsafe { self.status.readb() }
258 }
259
260 #[inline]
261 fn is_read_ready(&self) -> bool {
262 self.line_status() & 1 != 0
263 }
264
265 #[inline]
266 fn read_blocking(&mut self) -> u8 {
267 while !self.is_read_ready() {}
268 unsafe { self.data.readb() }
269 }
270
271 #[inline]
272 fn read_nonblocking(&mut self) -> io::Result<u8> {
273 if self.is_read_ready() {
274 Ok(unsafe { self.data.readb() })
275 } else {
276 Err(io::Error::from(io::ErrorKind::WouldBlock))
277 }
278 }
279}
280
281impl WriteRegisters {
282 const DLAB_BIT: u8 = 0b1000_0000;
283
284 fn without_irqs<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
285 unsafe {
286 self.irq_enable.writeb(0x00);
287 }
288 let res = f(self);
289 unsafe {
290 self.irq_enable.writeb(0x01);
291 }
292 res
293 }
294
295 fn set_baud_rate_divisor(&mut self, divisor: u16) -> io::Result<u16> {
296 let prev = self.baud_rate_divisor;
297 if divisor == 0 {
298 return Err(io::Error::new(
299 io::ErrorKind::InvalidInput,
300 "baud rate divisor must be greater than 0",
301 ));
302 }
303
304 let lcr_state = unsafe { self.line_ctrl.readb() };
305 if lcr_state & Self::DLAB_BIT != 0 {
306 return Err(io::Error::new(
307 io::ErrorKind::Other,
308 "DLAB bit already set, what the heck!",
309 ));
310 }
311
312 unsafe {
313 self.line_ctrl.writeb(lcr_state | Self::DLAB_BIT);
317
318 self.data.writeb((divisor & 0x00FF) as u8);
320 self.irq_enable.writeb((divisor >> 8) as u8);
322
323 self.line_ctrl.writeb(lcr_state);
324 }
325
326 self.baud_rate_divisor = divisor;
327
328 Ok(prev)
329 }
330
331 #[inline]
332 fn line_status(&self) -> u8 {
333 unsafe { self.status.readb() }
334 }
335
336 #[inline]
337 fn is_write_ready(&self) -> bool {
338 self.line_status() & 0x20 != 0
339 }
340
341 #[inline]
342 fn write_blocking(&mut self, byte: u8) {
343 while !self.is_write_ready() {}
344 unsafe { self.data.writeb(byte) }
345 }
346
347 #[inline]
348 fn write_nonblocking(&mut self, byte: u8) -> io::Result<()> {
349 if self.is_write_ready() {
350 unsafe {
351 self.data.writeb(byte);
352 }
353 Ok(())
354 } else {
355 Err(io::Error::from(io::ErrorKind::WouldBlock))
356 }
357 }
358}
359
360impl<'a> ReadLock<'a> {
361 pub fn set_non_blocking(self) -> ReadLock<'a, Nonblocking> {
362 ReadLock {
363 inner: self.inner,
364 _is_blocking: PhantomData,
365 }
366 }
367}
368
369impl<'a> WriteLock<'a> {
370 pub fn set_non_blocking(self) -> WriteLock<'a, Nonblocking> {
371 WriteLock {
372 inner: self.inner,
373 _is_blocking: PhantomData,
374 }
375 }
376}
377
378impl<B> ReadLock<'_, B> {
379 pub fn check_interrupt_type(&mut self) -> io::Result<Option<Pc16550dInterrupt>> {
380 let iir = self.inner.iir();
383
384 if iir & 0b0011_0000 != 0b0000_0000 {
385 return Err(io::Error::new(
386 io::ErrorKind::Other,
387 "IIR indicates bogus interrupt bits",
388 ));
389 }
390
391 let interrupt = match iir & 0b1111 {
395 0b0000 => Pc16550dInterrupt::ModemStatus,
396 0b0001 => {
397 return Ok(None);
398 }
399 0b0010 => Pc16550dInterrupt::TransmitterHoldingRegEmpty,
400 0b0100 => Pc16550dInterrupt::ReceivedDataAvailable,
401 0b0110 => Pc16550dInterrupt::ReceiverLineStatus,
402 0b1100 => Pc16550dInterrupt::CharacterTimeout,
403 _ => {
404 return Err(io::Error::new(
405 io::ErrorKind::Other,
406 "IIR indicates unrecognized status",
407 ));
408 }
409 };
410
411 Ok(Some(interrupt))
412 }
413}
414
415impl<B> WriteLock<'_, B> {
416 pub fn set_baud_rate(&mut self, baud: usize) -> io::Result<()> {
427 if baud > Port::MAX_BAUD_RATE {
428 return Err(io::Error::new(
429 io::ErrorKind::InvalidInput,
430 "cannot exceed max baud rate (115200)",
431 ));
432 }
433
434 if Port::MAX_BAUD_RATE % baud != 0 {
435 return Err(io::Error::new(
436 io::ErrorKind::InvalidInput,
437 "max baud rate not divisible by target",
438 ));
439 }
440
441 let divisor = Port::MAX_BAUD_RATE / baud;
442 if divisor > (u16::MAX as usize) {
443 return Err(io::Error::new(
444 io::ErrorKind::InvalidInput,
445 "divisor for target baud rate is too high!",
446 ));
447 }
448
449 self.inner.set_baud_rate_divisor(divisor as u16)
450 }
451}
452
453impl io::Read for ReadLock<'_, Blocking> {
454 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
455 for byte in buf.iter_mut() {
456 *byte = self.inner.read_blocking();
457 }
458 Ok(buf.len())
459 }
460}
461
462impl io::Read for ReadLock<'_, Nonblocking> {
463 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
464 for byte in buf.iter_mut() {
465 *byte = self.inner.read_nonblocking()?;
467 }
468 Ok(buf.len())
469 }
470}
471
472impl io::Write for WriteLock<'_, Blocking> {
473 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
474 for &byte in buf.iter() {
475 self.inner.write_blocking(byte)
476 }
477 Ok(buf.len())
478 }
479
480 fn flush(&mut self) -> io::Result<()> {
481 while !self.inner.is_write_ready() {}
482 Ok(())
483 }
484}
485
486impl fmt::Write for WriteLock<'_, Blocking> {
487 fn write_str(&mut self, s: &str) -> fmt::Result {
488 for byte in s.bytes() {
489 self.inner.write_blocking(byte)
490 }
491 Ok(())
492 }
493}
494
495impl io::Write for WriteLock<'_, Nonblocking> {
496 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
497 for &byte in buf.iter() {
498 self.inner.write_nonblocking(byte)?;
499 }
500 Ok(buf.len())
501 }
502
503 fn flush(&mut self) -> io::Result<()> {
504 while !self.inner.is_write_ready() {}
505 Ok(())
506 }
507}
508
509impl WriteLockInner<'_> {
510 #[inline(always)]
511 fn is_write_ready(&self) -> bool {
512 self.inner.is_write_ready()
513 }
514
515 #[inline(always)]
516 fn write_nonblocking(&mut self, byte: u8) -> io::Result<()> {
517 self.inner.write_nonblocking(byte)
518 }
519
520 #[inline(always)]
521 fn write_blocking(&mut self, byte: u8) {
522 self.inner.write_blocking(byte)
523 }
524
525 #[inline(always)]
526 fn set_baud_rate_divisor(&mut self, divisor: u16) -> io::Result<()> {
527 let prev: u16 = self
528 .inner
529 .without_irqs(|inner| inner.set_baud_rate_divisor(divisor))?;
530 self.prev_divisor = Some(prev);
531
532 Ok(())
533 }
534}
535
536impl Drop for WriteLockInner<'_> {
537 fn drop(&mut self) {
538 if let Some(divisor) = self.prev_divisor {
539 self.inner.without_irqs(|inner| {
541 let _ = inner.set_baud_rate_divisor(divisor);
543 });
544 }
545 }
546}
547
548impl<'a> mycelium_trace::writer::MakeWriter<'a> for &Port {
549 type Writer = WriteLock<'a, Blocking>;
550 fn make_writer(&'a self) -> Self::Writer {
551 self.write_lock()
552 }
553
554 fn line_len(&self) -> usize {
555 120
556 }
557}