serial.cc 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #include <errno.h>
  2. #include <stdio.h>
  3. #include <kernel/hw/port.hpp>
  4. #include <kernel/irq.hpp>
  5. #include <kernel/log.hpp>
  6. #include <kernel/module.hpp>
  7. #include <kernel/tty.hpp>
  8. using namespace kernel::tty;
  9. using namespace kernel::hw;
  10. constexpr int PORT0 = 0x3f8;
  11. constexpr int PORT1 = 0x2f8;
  12. using port_group = const p8[6];
  13. constexpr p8 port0[] = {
  14. p8{PORT0 + 0}, p8{PORT0 + 1}, p8{PORT0 + 2},
  15. p8{PORT0 + 3}, p8{PORT0 + 4}, p8{PORT0 + 5},
  16. };
  17. constexpr p8 port1[] = {
  18. p8{PORT1 + 0}, p8{PORT1 + 1}, p8{PORT1 + 2},
  19. p8{PORT1 + 3}, p8{PORT1 + 4}, p8{PORT1 + 5},
  20. };
  21. static void _serial0_receive_data_interrupt() {
  22. while (*port0[5] & 1)
  23. console->commit_char(*port0[0]);
  24. }
  25. static void _serial1_receive_data_interrupt() {
  26. while (*port1[5] & 1)
  27. console->commit_char(*port1[0]);
  28. }
  29. static inline int _init_port(port_group ports) {
  30. // taken from osdev.org
  31. ports[1] = 0x00; // Disable all interrupts
  32. ports[3] = 0x80; // Enable DLAB (set baud rate divisor)
  33. // TODO: set baud rate
  34. ports[0] = 0x00; // Set divisor to 0 -3- (lo byte) 115200 -38400- baud
  35. ports[1] = 0x00; // (hi byte)
  36. ports[3] = 0x03; // 8 bits, no parity, one stop bit
  37. ports[2] = 0xC7; // Enable FIFO, clear them, with 14-byte threshold
  38. // TODO: IRQ disabled
  39. ports[4] = 0x0B; // IRQs enabled, RTS/DSR set
  40. ports[4] = 0x1E; // Set in loopback mode, test the serial chip
  41. ports[0] = 0xAE; // Test serial chip (send byte 0xAE and check if serial
  42. // returns same byte)
  43. // Check if serial is faulty (i.e: not same byte as sent)
  44. if (*ports[0] != 0xAE)
  45. return -EIO;
  46. // If serial is not faulty set it in normal operation mode
  47. // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
  48. ports[4] = 0x0F;
  49. ports[1] = 0x01; // Enable interrupts #0: Received Data Available
  50. return 0;
  51. }
  52. class serial_tty : public virtual tty {
  53. const p8* ports;
  54. public:
  55. serial_tty(port_group ports, int id) : tty{"ttyS"}, ports(ports) {
  56. name += '0' + id;
  57. }
  58. virtual void putchar(char c) override {
  59. while (!(*ports[5] & 0x20))
  60. ; // nop
  61. ports[0] = c;
  62. }
  63. };
  64. class serial_module : public virtual kernel::module::module {
  65. public:
  66. serial_module() : module("serial-tty") {}
  67. virtual int init() override {
  68. if (int ret = _init_port(port0); ret == 0) {
  69. auto* dev = new serial_tty(port0, 0);
  70. kernel::irq::register_handler(4, _serial0_receive_data_interrupt);
  71. if (int ret = register_tty(dev); ret != 0)
  72. kmsg("[serial] cannot register ttyS0");
  73. }
  74. if (int ret = _init_port(port1); ret == 0) {
  75. auto* dev = new serial_tty(port1, 0);
  76. kernel::irq::register_handler(3, _serial1_receive_data_interrupt);
  77. if (int ret = register_tty(dev); ret != 0)
  78. kmsg("[serial] cannot register ttyS1");
  79. }
  80. return kernel::module::MODULE_SUCCESS;
  81. }
  82. };
  83. kernel::module::module* serial_module_init() {
  84. return new serial_module();
  85. }
  86. INTERNAL_MODULE(serial_module_loader, serial_module_init);