serial.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2014-2018 K. Lange
  5. *
  6. * Serial communication device
  7. *
  8. */
  9. #include <kernel/system.h>
  10. #include <kernel/fs.h>
  11. #include <kernel/pipe.h>
  12. #include <kernel/logging.h>
  13. #include <kernel/args.h>
  14. #include <kernel/module.h>
  15. #include <kernel/pty.h>
  16. #include <kernel/printf.h>
  17. #define SERIAL_PORT_A 0x3F8
  18. #define SERIAL_PORT_B 0x2F8
  19. #define SERIAL_PORT_C 0x3E8
  20. #define SERIAL_PORT_D 0x2E8
  21. #define SERIAL_IRQ_AC 4
  22. #define SERIAL_IRQ_BD 3
  23. static pty_t * _serial_port_pty_a = NULL;
  24. static pty_t * _serial_port_pty_b = NULL;
  25. static pty_t * _serial_port_pty_c = NULL;
  26. static pty_t * _serial_port_pty_d = NULL;
  27. static pty_t ** pty_for_port(int port) {
  28. switch (port) {
  29. case SERIAL_PORT_A: return &_serial_port_pty_a;
  30. case SERIAL_PORT_B: return &_serial_port_pty_b;
  31. case SERIAL_PORT_C: return &_serial_port_pty_c;
  32. case SERIAL_PORT_D: return &_serial_port_pty_d;
  33. }
  34. __builtin_unreachable();
  35. }
  36. static int serial_rcvd(int device) {
  37. return inportb(device + 5) & 1;
  38. }
  39. static char serial_recv(int device) {
  40. while (serial_rcvd(device) == 0) ;
  41. return inportb(device);
  42. }
  43. static int serial_transmit_empty(int device) {
  44. return inportb(device + 5) & 0x20;
  45. }
  46. static void serial_send(int device, char out) {
  47. while (serial_transmit_empty(device) == 0);
  48. outportb(device, out);
  49. }
  50. static int serial_handler_ac(struct regs *r) {
  51. char serial;
  52. int port = 0;
  53. if (inportb(SERIAL_PORT_A+1) & 0x01) {
  54. port = SERIAL_PORT_A;
  55. } else {
  56. port = SERIAL_PORT_C;
  57. }
  58. serial = serial_recv(port);
  59. irq_ack(SERIAL_IRQ_AC);
  60. tty_input_process(*pty_for_port(port), serial);
  61. return 1;
  62. }
  63. static int serial_handler_bd(struct regs *r) {
  64. char serial;
  65. int port = 0;
  66. debug_print(NOTICE, "Received something on secondary port");
  67. if (inportb(SERIAL_PORT_B+1) & 0x01) {
  68. port = SERIAL_PORT_B;
  69. } else {
  70. port = SERIAL_PORT_D;
  71. }
  72. serial = serial_recv(port);
  73. irq_ack(SERIAL_IRQ_BD);
  74. tty_input_process(*pty_for_port(port), serial);
  75. return 1;
  76. }
  77. static void serial_enable(int port) {
  78. outportb(port + 1, 0x00); /* Disable interrupts */
  79. outportb(port + 3, 0x80); /* Enable divisor mode */
  80. outportb(port + 0, 0x01); /* Div Low: 01 Set the port to 115200 bps */
  81. outportb(port + 1, 0x00); /* Div High: 00 */
  82. outportb(port + 3, 0x03); /* Disable divisor mode, set parity */
  83. outportb(port + 2, 0xC7); /* Enable FIFO and clear */
  84. outportb(port + 4, 0x0B); /* Enable interrupts */
  85. outportb(port + 1, 0x01); /* Enable interrupts */
  86. }
  87. static int have_installed_ac = 0;
  88. static int have_installed_bd = 0;
  89. static void serial_write_out(pty_t * pty, uint8_t c) {
  90. if (pty == _serial_port_pty_a) serial_send(SERIAL_PORT_A, c);
  91. if (pty == _serial_port_pty_b) serial_send(SERIAL_PORT_B, c);
  92. if (pty == _serial_port_pty_c) serial_send(SERIAL_PORT_C, c);
  93. if (pty == _serial_port_pty_d) serial_send(SERIAL_PORT_D, c);
  94. }
  95. #define DEV_PATH "/dev/"
  96. #define TTY_A "ttyS0"
  97. #define TTY_B "ttyS1"
  98. #define TTY_C "ttyS2"
  99. #define TTY_D "ttyS3"
  100. static void serial_fill_name(pty_t * pty, char * name) {
  101. if (pty == _serial_port_pty_a) sprintf(name, DEV_PATH TTY_A);
  102. if (pty == _serial_port_pty_b) sprintf(name, DEV_PATH TTY_B);
  103. if (pty == _serial_port_pty_c) sprintf(name, DEV_PATH TTY_C);
  104. if (pty == _serial_port_pty_d) sprintf(name, DEV_PATH TTY_D);
  105. }
  106. static fs_node_t * serial_device_create(int port) {
  107. pty_t * pty = pty_new(NULL);
  108. *pty_for_port(port) = pty;
  109. pty->write_out = serial_write_out;
  110. pty->fill_name = serial_fill_name;
  111. serial_enable(port);
  112. if (port == SERIAL_PORT_A || port == SERIAL_PORT_C) {
  113. if (!have_installed_ac) {
  114. irq_install_handler(SERIAL_IRQ_AC, serial_handler_ac, "serial ac");
  115. have_installed_ac = 1;
  116. }
  117. } else {
  118. if (!have_installed_bd) {
  119. irq_install_handler(SERIAL_IRQ_BD, serial_handler_bd, "serial bd");
  120. have_installed_bd = 1;
  121. }
  122. }
  123. return pty->slave;
  124. }
  125. static int serial_mount_devices(void) {
  126. fs_node_t * ttyS0 = serial_device_create(SERIAL_PORT_A); vfs_mount(DEV_PATH TTY_A, ttyS0);
  127. fs_node_t * ttyS1 = serial_device_create(SERIAL_PORT_B); vfs_mount(DEV_PATH TTY_B, ttyS1);
  128. fs_node_t * ttyS2 = serial_device_create(SERIAL_PORT_C); vfs_mount(DEV_PATH TTY_C, ttyS2);
  129. fs_node_t * ttyS3 = serial_device_create(SERIAL_PORT_D); vfs_mount(DEV_PATH TTY_D, ttyS3);
  130. char * c;
  131. if ((c = args_value("logtoserial"))) {
  132. debug_file = ttyS0;
  133. if (!strcmp(c,"INFO") || !strcmp(c,"info")) {
  134. debug_level = INFO;
  135. } else if (!strcmp(c,"NOTICE") || !strcmp(c,"notice")) {
  136. debug_level = NOTICE;
  137. } else if (!strcmp(c,"WARNING") || !strcmp(c,"warning")) {
  138. debug_level = WARNING;
  139. } else if (!strcmp(c,"ERROR") || !strcmp(c,"error")) {
  140. debug_level = ERROR;
  141. } else if (!strcmp(c,"CRITICAL") || !strcmp(c,"critical")) {
  142. debug_level = CRITICAL;
  143. } else if (!strcmp(c,"INSANE") || !strcmp(c,"insane")) {
  144. debug_level = INSANE;
  145. } else {
  146. debug_level = atoi(c);
  147. }
  148. debug_print(NOTICE, "Serial logging enabled at level %d.", debug_level);
  149. }
  150. return 0;
  151. }
  152. static int serial_finalize(void) {
  153. return 0;
  154. }
  155. MODULE_DEF(serial, serial_mount_devices, serial_finalize);