Browse Source

:new: uart驱动

fslongjin 2 years ago
parent
commit
22359344e4
8 changed files with 188 additions and 26 deletions
  1. 2 1
      .gitignore
  2. 5 0
      kernel/Makefile
  3. 1 0
      kernel/common/glib.h
  4. 7 0
      kernel/common/printk.c
  5. 88 0
      kernel/driver/uart/uart.c
  6. 64 0
      kernel/driver/uart/uart.h
  7. 20 24
      kernel/main.c
  8. 1 1
      run.sh

+ 2 - 1
.gitignore

@@ -5,4 +5,5 @@ DragonOS.iso
 kernel/kernel
 
 *.o
-*.s
+*.s
+serial_opt.txt

+ 5 - 0
kernel/Makefile

@@ -125,6 +125,11 @@ HPET.o: driver/timers/HPET/HPET.c
 timer.o: driver/timers/timer.c
 	gcc $(CFLAGS) -c driver/timers/timer.c -o driver/timers/timer.o
 
+OBJ_LIST += uart.o
+LD_LIST += driver/uart/uart.o
+uart.o: driver/uart/uart.c
+	gcc $(CFLAGS) -c driver/uart/uart.c -o driver/uart/uart.o
+
 
 
 all: kernel

+ 1 - 0
kernel/common/glib.h

@@ -17,6 +17,7 @@
                                        : "memory") //关闭外部中断
 #define nop() __asm__ __volatile__("nop\n\t")
 #define hlt() __asm__ __volatile__("hlt\n\t")
+#define pause() asm volatile ("pause\n\t"); // 处理器等待一段时间
 
 //内存屏障
 #define io_mfence() __asm__ __volatile__("mfence\n\t" :: \

+ 7 - 0
kernel/common/printk.c

@@ -7,6 +7,8 @@
 #include "../mm/mm.h"
 #include "../process/spinlock.h"
 
+#include <driver/uart/uart.h>
+
 //#include "linkage.h"
 
 struct screen_info pos;
@@ -110,13 +112,16 @@ void auto_newline()
      * @brief 超过每行最大字符数,自动换行
      *
      */
+     
     if (pos.x > pos.max_x)
     {
+        uart_send(COM1, '\n'); 
         pos.x = 0;
         ++pos.y;
     }
     if (pos.y > pos.max_y)
     {
+        uart_send(COM1, '\n');
         pos.y = pos.max_y;
         int lines_to_scroll = 1;
         scroll(true, lines_to_scroll * pos.char_size_y, false);
@@ -625,6 +630,8 @@ static void putchar(uint *fb, int Xsize, int x, int y, unsigned int FRcolor, uns
      * @param BKcolor 背景颜色
      * @param font 字符的bitmap
      */
+    // 输出到串口
+    uart_send(COM1, font);
 
     unsigned char *font_ptr = font_ascii[font];
     unsigned int *addr;

+ 88 - 0
kernel/driver/uart/uart.c

@@ -0,0 +1,88 @@
+#include "uart.h"
+#include <common/kprint.h>
+
+#define UART_MAX_BITS_RATE 115200
+
+/**
+ * @brief 当前是否有数据到达
+ *
+ */
+#define serial_received(p) ((io_in8(p + 5) & 1))
+
+/**
+ * @brief 当前是否有数据正等待发送
+ *
+ */
+#define is_transmit_empty(p) ((io_in8(p + 5) & 0x20))
+
+/**
+ * @brief 初始化com口
+ *
+ * @param port com口的端口号
+ * @param bits_rate 通信的比特率
+ */
+int uart_init(uint32_t port, uint32_t bits_rate)
+{
+    // 错误的比特率
+    if (bits_rate > UART_MAX_BITS_RATE || UART_MAX_BITS_RATE % bits_rate != 0)
+        return E_UART_BITS_RATE_ERROR;
+
+    io_out8(port + 1, 0x00); // Disable all interrupts
+    io_out8(port + 3, 0x80); // Enable DLAB (set baud rate divisor)
+
+    uint16_t divisor = E_UART_BITS_RATE_ERROR / bits_rate;
+    io_out8(port + 0, divisor & 0xff);        // Set divisor  (lo byte)
+    io_out8(port + 1, (divisor >> 8) & 0xff); //                  (hi byte)
+    io_out8(port + 3, 0x03);                  // 8 bits, no parity, one stop bit
+    io_out8(port + 2, 0xC7);                  // Enable FIFO, clear them, with 14-byte threshold
+    io_out8(port + 4, 0x0B);                  // IRQs enabled, RTS/DSR set
+    io_out8(port + 4, 0x1E);                  // Set in loopback mode, test the serial chip
+    io_out8(port + 0, 0xAE);                  // Test serial chip (send byte 0xAE and check if serial returns same byte)
+
+    // Check if serial is faulty (i.e: not same byte as sent)
+    if (io_in8(port + 0) != 0xAE)
+    {
+        return E_UART_SERIAL_FAULT;
+    }
+
+    // If serial is not faulty set it in normal operation mode
+    // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
+    io_out8(port + 4, 0x0F);
+    return UART_SUCCESS;
+
+    /*
+            Notice that the initialization code above writes to [PORT + 1]
+        twice with different values. This is once to write to the Divisor
+        register along with [PORT + 0] and once to write to the Interrupt
+        register as detailed in the previous section.
+            The second write to the Line Control register [PORT + 3]
+        clears the DLAB again as well as setting various other bits.
+    */
+}
+
+/**
+ * @brief 发送数据
+ *
+ * @param port 端口号
+ * @param c 要发送的数据
+ */
+void uart_send(uint32_t port, char c)
+{
+    while (is_transmit_empty(port) == 0)
+        pause();
+    io_out8(port, c);
+}
+
+/**
+ * @brief 从uart接收数据
+ *
+ * @param port 端口号
+ * @return uchar 接收到的数据
+ */
+uchar uart_read(uint32_t port)
+{
+    while (serial_received(port) == 0)
+        pause();
+    
+    return io_in8(port);
+}

+ 64 - 0
kernel/driver/uart/uart.h

@@ -0,0 +1,64 @@
+/**
+ * @file uart.h
+ * @author longjin ([email protected])
+ * @brief uart驱动程序 RS-232驱动
+ * @version 0.1
+ * @date 2022-04-15
+ * 
+ * @copyright Copyright (c) 2022
+ * 
+ */
+#pragma once
+
+#include <common/glib.h>
+
+#define UART_SUCCESS 0
+#define E_UART_BITS_RATE_ERROR 1
+#define E_UART_SERIAL_FAULT 2
+enum uart_port_io_addr
+{
+    COM1 = 0x3f8,
+    COM2 = 0x2f8,
+    COM3 = 0x3e8,
+    COM4 = 0x2e8,
+    COM5 = 0x5f8,
+    COM6 = 0x4f8,
+    COM7 = 0x5e8,
+    COM8 = 0x4E8,
+};
+
+enum uart_register_offset
+{
+    REG_DATA = 0,
+    REG_INTERRUPT_ENABLE = 1,
+    REG_II_FIFO = 2,    // 	Interrupt Identification and FIFO control registers
+    REG_LINE_CONTROL = 3,
+    REG_MODEM_CONTROL = 4,
+    REG_LINE_STATUS = 5,
+    REG_MODEM_STATUE = 6,
+    REG_SCRATCH = 7
+};
+
+/**
+ * @brief 初始化com口
+ * 
+ * @param port com口的端口号
+ * @param bits_rate 通信的比特率
+ */
+int uart_init(uint32_t port, uint32_t bits_rate);
+
+/**
+ * @brief 发送数据
+ * 
+ * @param port 端口号
+ * @param c 要发送的数据
+ */
+void uart_send(uint32_t port, char c);
+
+/**
+ * @brief 从uart接收数据
+ * 
+ * @param port 端口号
+ * @return uchar 接收到的数据
+ */
+uchar uart_read(uint32_t port);

+ 20 - 24
kernel/main.c

@@ -27,6 +27,7 @@
 #include <driver/timers/rtc/rtc.h>
 #include <driver/timers/HPET/HPET.h>
 #include <driver/timers/timer.h>
+#include <driver/uart/uart.h>
 
 unsigned int *FR_address = (unsigned int *)0xb8000; //帧缓存区的地址
 ul bsp_idt_size, bsp_gdt_size;
@@ -57,8 +58,8 @@ struct gdtr gdtp;
 struct idtr idtp;
 void reload_gdt()
 {
-    
-    gdtp.size = bsp_gdt_size-1;
+
+    gdtp.size = bsp_gdt_size - 1;
     gdtp.gdt_vaddr = (ul)phys_2_virt((ul)&GDT_Table);
 
     asm volatile("lgdt (%0)   \n\t" ::"r"(&gdtp)
@@ -67,11 +68,11 @@ void reload_gdt()
 
 void reload_idt()
 {
-    
-    idtp.size = bsp_idt_size-1;
+
+    idtp.size = bsp_idt_size - 1;
     idtp.idt_vaddr = (ul)phys_2_virt((ul)&IDT_Table);
-    //kdebug("gdtvaddr=%#018lx", p.gdt_vaddr);
-    //kdebug("gdt size=%d", p.size);
+    // kdebug("gdtvaddr=%#018lx", p.gdt_vaddr);
+    // kdebug("gdt size=%d", p.size);
 
     asm volatile("lidt (%0)   \n\t" ::"r"(&idtp)
                  : "memory");
@@ -83,12 +84,13 @@ void system_initialize()
 
     // 初始化printk
     printk_init(8, 16);
+    uart_init(COM1, 115200);
     kinfo("Kernel Starting...");
     // 重新加载gdt和idt
-    
+
     ul tss_item_addr = (ul)phys_2_virt(0x7c00);
-    
-    _stack_start = head_stack_start;    // 保存init proc的栈基地址(由于之后取消了地址重映射,因此必须在这里重新保存)
+
+    _stack_start = head_stack_start; // 保存init proc的栈基地址(由于之后取消了地址重映射,因此必须在这里重新保存)
     kdebug("_stack_start=%#018lx", _stack_start);
 
     load_TR(10); // 加载TR寄存器
@@ -99,8 +101,7 @@ void system_initialize()
     cpu_core_info[0].tss_vaddr = (uint64_t)&initial_tss[0];
     kdebug("cpu_core_info[0].tss_vaddr=%#018lx", cpu_core_info[0].tss_vaddr);
     kdebug("cpu_core_info[0].stack_start%#018lx", cpu_core_info[0].stack_start);
-    
-    
+
     // 初始化中断描述符表
     sys_vector_init();
 
@@ -108,8 +109,8 @@ void system_initialize()
     mm_init();
 
     // =========== 重新设置initial_tss[0]的ist
-    uchar *ptr  = (uchar*)kmalloc(STACK_SIZE, 0)+STACK_SIZE;
-    ((struct process_control_block*)(ptr-STACK_SIZE))->cpu_id = 0;
+    uchar *ptr = (uchar *)kmalloc(STACK_SIZE, 0) + STACK_SIZE;
+    ((struct process_control_block *)(ptr - STACK_SIZE))->cpu_id = 0;
 
     initial_tss[0].ist1 = (ul)ptr;
     initial_tss[0].ist2 = (ul)ptr;
@@ -119,7 +120,7 @@ void system_initialize()
     initial_tss[0].ist6 = (ul)ptr;
     initial_tss[0].ist7 = (ul)ptr;
     // ===========================
-    
+
     acpi_init();
 
     // 初始化中断模块
@@ -127,12 +128,12 @@ void system_initialize()
     irq_init();
 
     softirq_init();
-    
+
     // 先初始化系统调用模块
     syscall_init();
     //  再初始化进程模块。顺序不能调转
     sched_init();
-    
+
     timer_init();
 
     smp_init();
@@ -144,21 +145,17 @@ void system_initialize()
     ahci_init();
     // test_slab();
     // test_mm();
-    
 
-    //process_init();
+    // process_init();
     current_pcb->cpu_id = 0;
     current_pcb->preempt_count = 0;
     HPET_init();
-
-
-    
 }
 
 //操作系统内核从这里开始执行
 void Start_Kernel(void)
 {
-    
+
     // 获取multiboot2的信息
     uint64_t mb2_info, mb2_magic;
     __asm__ __volatile__("movq %%r15, %0    \n\t"
@@ -168,7 +165,7 @@ void Start_Kernel(void)
                          : "=r"(mb2_info), "=r"(mb2_magic), "=r"(bsp_gdt_size), "=r"(bsp_idt_size)::"memory");
     reload_gdt();
     reload_idt();
-    
+
     // 重新设置TSS描述符
     set_tss_descriptor(10, (void *)(&initial_tss[0]));
 
@@ -177,7 +174,6 @@ void Start_Kernel(void)
     multiboot2_magic = (uint)mb2_magic;
     multiboot2_boot_info_addr = mb2_info + PAGE_OFFSET;
 
-    
     system_initialize();
 
     /*

+ 1 - 1
run.sh

@@ -93,7 +93,7 @@ if [ $flag_can_run -eq 1 ]; then
         bochs -q -f ${bochsrc} -rc ./tools/bochsinit
     else
         qemu-system-x86_64 -cdrom ${iso} -m 512M -smp 2,cores=2,threads=1,sockets=1 \
-        -monitor stdio -d cpu_reset,guest_errors,trace:check_exception,exec,cpu,out_asm,in_asm -s -S -cpu IvyBridge --enable-kvm -rtc clock=host,base=localtime \
+        -monitor stdio -d cpu_reset,guest_errors,trace:check_exception,exec,cpu,out_asm,in_asm -s -S -cpu IvyBridge --enable-kvm -rtc clock=host,base=localtime -serial file:serial_opt.txt \
         -drive id=disk,file=bin/disk.img,if=none \
         -device ahci,id=ahci \
         -device ide-hd,drive=disk,bus=ahci.0    \