Bladeren bron

:new: 鼠标驱动(有bug,还不能用)

fslongjin 3 jaren geleden
bovenliggende
commit
b3cbd3caa2

+ 2 - 1
.vscode/settings.json

@@ -17,7 +17,8 @@
         "multiboot2.h": "c",
         "kprint.h": "c",
         "8259a.h": "c",
-        "ptrace.h": "c"
+        "ptrace.h": "c",
+        "mouse.h": "c"
     },
     "C_Cpp.errorSquiggles": "Enabled"
 }

+ 6 - 3
kernel/Makefile

@@ -17,13 +17,12 @@ ASFLAGS := --64
 
 all: kernel
 	objcopy -I elf64-x86-64 -S -R ".comment" -R ".eh_frame" -O elf64-x86-64 kernel ../bin/kernel/kernel.elf
-# cp kernel ../bin/kernel/kernel.elf
 
 
-kernel: head.o entry.o main.o printk.o trap.o mm.o slab.o irq.o pic.o process.o syscall.o multiboot2.o cpu.o acpi.o keyboard.o
+kernel: head.o entry.o main.o printk.o trap.o mm.o slab.o irq.o pic.o process.o syscall.o multiboot2.o cpu.o acpi.o keyboard.o mouse.o
 	ld -b elf64-x86-64 -z muldefs -o kernel head.o exception/entry.o main.o common/printk.o exception/trap.o exception/irq.o mm/mm.o mm/slab.o process/process.o syscall/syscall.o driver/multiboot2/multiboot2.o \
 	common/cpu.o	\
-	driver/acpi/acpi.o driver/interrupt/pic.o driver/keyboard/keyboard.o \
+	driver/acpi/acpi.o driver/interrupt/pic.o driver/keyboard/keyboard.o driver/mouse/mouse.o \
 	-T link.lds
 
 head.o: head.S
@@ -89,5 +88,9 @@ acpi.o: driver/acpi/acpi.c
 keyboard.o: driver/keyboard/keyboard.c
 	gcc $(CFLAGS) -c driver/keyboard/keyboard.c  -o driver/keyboard/keyboard.o
 
+mouse.o: driver/mouse/mouse.c
+	gcc $(CFLAGS) -c driver/mouse/mouse.c -o driver/mouse/mouse.o
+
+
 clean: 
 	rm -rf $(GARBAGE)

+ 4 - 4
kernel/driver/keyboard/keyboard.c

@@ -33,7 +33,7 @@ hardware_intr_controller keyboard_intr_controller =
 void keyboard_handler(ul irq_num, ul param, struct pt_regs *regs)
 {
     // 读取键盘输入的信息
-    unsigned x = io_in8(0x60);
+    unsigned char x = io_in8(PORT_KEYBOARD_DATA);
     // printk_color(ORANGE, BLACK, "key_pressed:%02x\n", x);
 
     // 当头指针越过界时,恢复指向数组头部
@@ -69,7 +69,7 @@ void keyboard_init()
 
     // ======== 初始化中断RTE entry ==========
 
-    entry.vector = 0x21;                // 设置中断向量号
+    entry.vector = KEYBOARD_INTR_VECTOR;                // 设置中断向量号
     entry.deliver_mode = IO_APIC_FIXED; // 投递模式:混合
     entry.dest_mode = DEST_PHYSICAL;    // 物理模式投递中断
     entry.deliver_status = IDLE;
@@ -102,7 +102,7 @@ void keyboard_init()
     alt_r = false;
 
     // 注册中断处理程序
-    irq_register(0x21, &entry, &keyboard_handler, (ul)kb_buf_ptr, &keyboard_intr_controller, "ps/2 keyboard");
+    irq_register(KEYBOARD_INTR_VECTOR, &entry, &keyboard_handler, (ul)kb_buf_ptr, &keyboard_intr_controller, "ps/2 keyboard");
 }
 
 /**
@@ -111,7 +111,7 @@ void keyboard_init()
  */
 void keyboard_exit()
 {
-    irq_unregister(0x21);
+    irq_unregister(KEYBOARD_INTR_VECTOR);
     kfree((ul *)kb_buf_ptr);
 }
 

+ 3 - 1
kernel/driver/keyboard/keyboard.h

@@ -2,6 +2,8 @@
 
 #include "../../common/glib.h"
 
+#define KEYBOARD_INTR_VECTOR 0x21   // 键盘的中断向量号
+
 // 定义键盘循环队列缓冲区大小为100bytes
 #define keyboard_buffer_size 100
 
@@ -25,7 +27,7 @@ struct keyboard_input_buffer
 #define KEYBOARD_COMMAND_READ 0x20  // 读取键盘的配置值
 #define KEYBOARD_PARAM_INIT 0x47    // 初始化键盘控制器的配置值
 
-// ========= 检测键盘输入/输出缓冲区是否已满
+// ========= 检测键盘控制器输入/输出缓冲区是否已满
 #define KEYBOARD_FLAG_OUTBUF_FULL 0x01 // 键盘的输出缓冲区已满标志位
 #define KEYBOARD_FLAG_INBUF_FULL 0x02  // 键盘的输入缓冲区已满标志位
 

+ 315 - 0
kernel/driver/mouse/mouse.c

@@ -0,0 +1,315 @@
+#include "mouse.h"
+#include "../interrupt/apic/apic.h"
+#include "../../mm/mm.h"
+#include "../../mm/slab.h"
+#include "../../common/printk.h"
+#include "../../common/kprint.h"
+
+static struct mouse_input_buffer *mouse_buf_ptr = NULL;
+static int c = 0;
+struct apic_IO_APIC_RTE_entry entry;
+static unsigned char mouse_id = 0;
+
+/**
+ * @brief 清空缓冲区
+ *
+ */
+static void mouse_clear_buf()
+{
+    mouse_buf_ptr->ptr_head = mouse_buf_ptr->buffer;
+    mouse_buf_ptr->ptr_tail = mouse_buf_ptr->buffer;
+    mouse_buf_ptr->count = 0;
+    memset(mouse_buf_ptr->buffer, 0, mouse_buffer_size);
+}
+
+/**
+ * @brief 从缓冲队列中获取鼠标数据字节
+ * @return 鼠标数据包的字节
+ * 若缓冲队列为空则返回-1024
+ */
+static int mouse_get_scancode()
+{
+    // 缓冲队列为空
+    if (mouse_buf_ptr->count == 0)
+        while (!mouse_buf_ptr->count)
+            nop();
+
+    if (mouse_buf_ptr->ptr_tail == mouse_buf_ptr->buffer + mouse_buffer_size)
+        mouse_buf_ptr->ptr_tail = mouse_buf_ptr->buffer;
+
+    int ret = (int)((char)(*(mouse_buf_ptr->ptr_tail)));
+    --(mouse_buf_ptr->count);
+    ++(mouse_buf_ptr->ptr_tail);
+    // printk("count=%d", mouse_buf_ptr->count);
+
+    return ret;
+}
+
+/**
+ * @brief 鼠标中断处理函数(中断上半部)
+ *  将数据存入缓冲区
+ * @param irq_num 中断向量号
+ * @param param 参数
+ * @param regs 寄存器信息
+ */
+void mouse_handler(ul irq_num, ul param, struct pt_regs *regs)
+{
+    // 读取鼠标输入的信息
+    unsigned char x = io_in8(PORT_KEYBOARD_DATA);
+
+    // 当头指针越过界时,恢复指向数组头部
+    if (mouse_buf_ptr->ptr_head == mouse_buf_ptr->buffer + mouse_buffer_size)
+        mouse_buf_ptr->ptr_head = mouse_buf_ptr->buffer;
+
+    if (mouse_buf_ptr->count >= mouse_buffer_size)
+    {
+        // kwarn("mouse input buffer is full.");
+        // return;
+    }
+
+    *mouse_buf_ptr->ptr_head = x;
+    ++(mouse_buf_ptr->count);
+    ++(mouse_buf_ptr->ptr_head);
+    printk("c=%d\n", ++c);
+}
+
+hardware_intr_controller mouse_intr_controller =
+    {
+        .enable = apic_ioapic_enable,
+        .disable = apic_ioapic_disable,
+        .install = apic_ioapic_install,
+        .uninstall = apic_ioapic_uninstall,
+        .ack = apic_ioapic_edge_ack,
+
+};
+
+/**
+ * @brief 从键盘控制器读取mouse id
+ *
+ * @return unsigned char 鼠标id
+ */
+static unsigned char mouse_get_mouse_ID()
+{
+    // 读取鼠标的ID
+    io_out8(PORT_KEYBOARD_CONTROL, KEYBOARD_COMMAND_SEND_TO_MOUSE);
+    wait_keyboard_write();
+    io_out8(PORT_KEYBOARD_DATA, MOUSE_GET_ID);
+    wait_keyboard_write();
+    mouse_id = io_in8(PORT_KEYBOARD_DATA);
+    return mouse_id;
+}
+
+/**
+ * @brief 设置鼠标采样率
+ *
+ * @param hz 采样率
+ */
+int mouse_set_sample_rate(unsigned int hz)
+{
+    switch (hz)
+    {
+    case 10:
+    case 20:
+    case 40:
+    case 60:
+    case 80:
+    case 100:
+    case 200:
+        wait_keyboard_write();
+        io_out8(PORT_KEYBOARD_CONTROL, KEYBOARD_COMMAND_SEND_TO_MOUSE);
+        wait_keyboard_write();
+        io_out8(PORT_KEYBOARD_DATA, MOUSE_SET_SAMPLING_RATE);
+        wait_keyboard_write();
+        io_out8(PORT_KEYBOARD_CONTROL, KEYBOARD_COMMAND_SEND_TO_MOUSE);
+        wait_keyboard_write();
+        io_out8(PORT_KEYBOARD_DATA, hz);
+        wait_keyboard_write();
+        break;
+
+    default:
+        return EINVALID_ARGUMENT;
+        break;
+    }
+    return SUCCESS;
+}
+/**
+ * @brief 使鼠标支持滚轮
+ * 该模式下,鼠标ID=3
+ */
+static int mouse_enable_scroll_wheel()
+{
+    if (mouse_id == 3)
+        return SUCCESS;
+
+    mouse_set_sample_rate(200);
+    mouse_set_sample_rate(100);
+    mouse_set_sample_rate(80);
+    if (mouse_get_mouse_ID() != 3)
+    {
+        kerror("Cannot set mouse ID to 3");
+        return EFAIL;
+    }
+    // 清空缓冲区,防止解析时产生错误
+    mouse_clear_buf();
+    return SUCCESS;
+}
+/**
+ * @brief 使鼠标支持5键
+ *  该模式下ID=4
+ */
+static int mouse_enable_5keys()
+{
+    if (mouse_id == 4)
+        return SUCCESS;
+    // 根据规范,应当先启用ID=3
+    mouse_enable_scroll_wheel();
+
+    mouse_set_sample_rate(200);
+    mouse_set_sample_rate(200);
+    mouse_set_sample_rate(80);
+    if (mouse_get_mouse_ID() != 4)
+    {
+        kerror("Cannot set mouse ID to 4");
+        return EFAIL;
+    }
+    // 清空缓冲区,防止解析时产生错误
+    mouse_clear_buf();
+
+    return SUCCESS;
+}
+/**
+ * @brief 初始化鼠标驱动程序
+ *
+ */
+void mouse_init()
+{
+    // 初始化鼠标读入队列缓冲区
+    mouse_buf_ptr = (struct mouse_input_buffer *)kmalloc(sizeof(struct mouse_input_buffer), 0);
+    mouse_buf_ptr->ptr_head = mouse_buf_ptr->buffer;
+    mouse_buf_ptr->ptr_tail = mouse_buf_ptr->buffer;
+    mouse_buf_ptr->count = 0;
+    memset(mouse_buf_ptr->buffer, 0, mouse_buffer_size);
+
+    // ======== 初始化中断RTE entry ==========
+
+    entry.vector = MOUSE_INTR_VECTOR;   // 设置中断向量号
+    entry.deliver_mode = IO_APIC_FIXED; // 投递模式:混合
+    entry.dest_mode = DEST_PHYSICAL;    // 物理模式投递中断
+    entry.deliver_status = IDLE;
+    entry.trigger_mode = EDGE_TRIGGER; // 设置边沿触发
+    entry.polarity = POLARITY_HIGH;    // 高电平触发
+    entry.remote_IRR = IRR_RESET;
+    entry.mask = MASKED;
+    entry.reserved = 0;
+
+    entry.destination.physical.reserved1 = 0;
+    entry.destination.physical.reserved2 = 0;
+    entry.destination.physical.phy_dest = 0; // 设置投递到BSP处理器
+
+    // 注册中断处理程序
+    irq_register(MOUSE_INTR_VECTOR, &entry, &mouse_handler, (ul)mouse_buf_ptr, &mouse_intr_controller, "ps/2 mouse");
+
+    wait_keyboard_write();
+    io_out8(PORT_KEYBOARD_CONTROL, KEYBOARD_COMMAND_ENABLE_MOUSE_PORT); // 开启鼠标端口
+    wait_keyboard_write();
+
+    io_out8(PORT_KEYBOARD_CONTROL, KEYBOARD_COMMAND_SEND_TO_MOUSE);
+    wait_keyboard_write();
+    io_out8(PORT_KEYBOARD_DATA, MOUSE_ENABLE); // 允许鼠标设备发送数据包
+    wait_keyboard_write();
+
+    io_out8(PORT_KEYBOARD_CONTROL, KEYBOARD_COMMAND_WRITE);
+    wait_keyboard_write();
+    io_out8(PORT_KEYBOARD_DATA, KEYBOARD_PARAM_INIT); // 设置键盘控制器
+    wait_keyboard_write();
+    //mouse_enable_5keys();
+    mouse_get_mouse_ID();
+    kdebug("mouse ID:%d", mouse_id);
+    c = 0;
+}
+
+/**
+ * @brief 卸载鼠标驱动程序
+ *
+ */
+void mouse_exit()
+{
+    irq_unregister(MOUSE_INTR_VECTOR);
+    kfree((ul *)mouse_buf_ptr);
+}
+
+/**
+ * @brief 获取鼠标数据包
+ *
+ * @param packet 数据包的返回值
+ * @return int 错误码
+ */
+int mouse_get_packet(void *packet)
+{
+    if (mouse_buf_ptr->count != 0)
+        kdebug("at  get packet: count=%d", mouse_buf_ptr->count);
+    int code = 0;
+    switch (mouse_id)
+    {
+    case 0: // 3bytes 数据包
+        if (mouse_buf_ptr->count < 3)
+            return EFAIL;
+        do
+        {
+            code = mouse_get_scancode();
+            ((struct mouse_packet_3bytes *)packet)->byte0 = (unsigned char)code;
+        } while (code == -1024);
+
+        do
+        {
+            code = mouse_get_scancode();
+            ((struct mouse_packet_3bytes *)packet)->movement_x = (char)code;
+        } while (code == -1024);
+
+        do
+        {
+            code = mouse_get_scancode();
+            ((struct mouse_packet_3bytes *)packet)->movement_y = (char)code;
+        } while (code == -1024);
+
+        return SUCCESS;
+        break;
+
+    case 3: // 4bytes数据包
+    case 4:
+        if (mouse_buf_ptr->count < 4)
+            return EFAIL;
+        do
+        {
+            code = mouse_get_scancode();
+            ((struct mouse_packet_4bytes *)packet)->byte0 = (unsigned char)code;
+        } while (code == -1024);
+
+        do
+        {
+            code = mouse_get_scancode();
+            ((struct mouse_packet_4bytes *)packet)->movement_x = (char)code;
+        } while (code == -1024);
+
+        do
+        {
+            code = mouse_get_scancode();
+            ((struct mouse_packet_4bytes *)packet)->movement_y = (char)code;
+        } while (code == -1024);
+
+        do
+        {
+            code = mouse_get_scancode();
+            ((struct mouse_packet_4bytes *)packet)->byte3 = (char)code;
+        } while (code == -1024);
+
+        return SUCCESS;
+        break;
+
+    default: // Should not reach here
+        kBUG("mouse_get_packet(): Invalid mouse_id!");
+        return EFAIL;
+        break;
+    }
+    return SUCCESS;
+}

+ 107 - 0
kernel/driver/mouse/mouse.h

@@ -0,0 +1,107 @@
+#pragma once
+
+#include "../../common/glib.h"
+
+#define MOUSE_INTR_VECTOR 0x2c // 鼠标的中断向量号
+
+#define KEYBOARD_COMMAND_SEND_TO_MOUSE 0xd4 // 键盘控制器向鼠标设备发送数据的命令
+
+#define MOUSE_GET_ID 0xf2                    // 获取鼠标的ID
+#define MOUSE_SET_SAMPLING_RATE 0xf3         // 设置鼠标的采样率
+#define MOUSE_ENABLE 0xf4                    // 允许鼠标设备发送数据包
+#define MOUSE_DISABLE 0xf5                   // 禁止鼠标设备发送数据包
+#define MOUSE_SET_DEFAULT_SAMPLING_RATE 0xf6 // 设置使用默认采样率100hz,分辨率4px/mm
+#define MOUSE_RESEND_LAST_PACKET 0xfe        // 重新发送上一条数据包
+#define MOUSE_RESET 0xff                     // 重启鼠标
+
+#define KEYBOARD_COMMAND_ENABLE_MOUSE_PORT 0xa8 // 通过键盘控制器开启鼠标端口的命令
+
+#define mouse_buffer_size 120
+
+#define PORT_KEYBOARD_DATA 0x60
+#define PORT_KEYBOARD_STATUS 0x64
+#define PORT_KEYBOARD_CONTROL 0x64
+
+#define KEYBOARD_COMMAND_WRITE 0x60 // 向键盘发送配置命令
+#define KEYBOARD_COMMAND_READ 0x20  // 读取键盘的配置值
+#define KEYBOARD_PARAM_INIT 0x47    // 初始化键盘控制器的配置值
+
+// ========= 检测键盘控制器输入/输出缓冲区是否已满
+#define KEYBOARD_FLAG_OUTBUF_FULL 0x01 // 键盘的输出缓冲区已满标志位
+#define KEYBOARD_FLAG_INBUF_FULL 0x02  // 键盘的输入缓冲区已满标志位
+
+// 等待向键盘控制器写入信息完成
+#define wait_keyboard_write() while (io_in8(PORT_KEYBOARD_STATUS) & KEYBOARD_FLAG_INBUF_FULL)
+// 等待从键盘控制器读取信息完成
+#define wait_keyboard_read() while (io_in8(PORT_KEYBOARD_STATUS) & KEYBOARD_FLAG_OUTBUF_FULL)
+
+#define SUCCESS 0
+#define EINVALID_ARGUMENT -1
+#define EFAIL -2
+
+// =========== 定义鼠标数据包 ==============
+// 其中,x、y方向的移动值用9位二进制补码表示(算上byte0中的符号位)
+// 目前只用到8位,(精度要求没那么高)
+struct mouse_packet_3bytes
+{
+
+    unsigned char byte0; // 第0字节
+                         // [y溢出,x溢出,y符号位, x符号位, 1, 鼠标中键, 鼠标右键,鼠标左键]
+
+    char movement_x;
+    char movement_y;
+};
+
+// ID = 3 或 ID = 4时,采用4bytes数据包
+struct mouse_packet_4bytes
+{
+    unsigned char byte0; // 第0字节
+                         // [y溢出,x溢出,y符号位, x符号位, 1, 鼠标中键, 鼠标右键,鼠标左键]
+
+    char movement_x;
+    char movement_y;
+
+    char byte3; // 当鼠标ID=3时,表示z移动值
+                // 当鼠标ID=4时,表示:[0, 0, 鼠标第5键, 鼠标第4键, Z3, Z2, Z1, Z0]
+                // 其中,[Z3,Z0]表示鼠标滚轮滚动方向
+                // Z3~Z0:   0:无滚动, 1:垂直向上滚动,  F:垂直向下滚动, 2:水平向右滚动, E:水平向左滚动
+};
+
+/**
+ * @brief 键盘循环队列缓冲区结构体
+ *
+ */
+struct mouse_input_buffer
+{
+    unsigned char *ptr_head;
+    unsigned char *ptr_tail;
+    int count;
+    unsigned char buffer[mouse_buffer_size];
+};
+
+/**
+ * @brief 初始化鼠标驱动程序
+ *
+ */
+void mouse_init();
+
+/**
+ * @brief 卸载鼠标驱动程序
+ *
+ */
+void mouse_exit();
+
+/**
+ * @brief 设置鼠标采样率
+ *
+ * @param hz 采样率
+ */
+int mouse_set_sample_rate(unsigned int hz);
+
+/**
+ * @brief 获取鼠标数据包
+ *
+ * @param packet 数据包的返回值
+ * @return int 错误码
+ */
+int mouse_get_packet(void *packet);

+ 1 - 1
kernel/exception/irq.h

@@ -59,7 +59,7 @@ extern void do_IRQ(struct pt_regs *regs, ul number);
 	41	Generic
 	42	Generic
 	43	HPET timer 2
-	44	HPET timer 3
+	44	HPET timer 3	/ mouse
 	45	FERR#
 	46	SATA primary
 	47	SATA secondary

+ 17 - 0
kernel/main.c

@@ -16,6 +16,7 @@
 #include "driver/multiboot2/multiboot2.h"
 #include "driver/acpi/acpi.h"
 #include "driver/keyboard/keyboard.h"
+#include "driver/mouse/mouse.h"
 
 unsigned int *FR_address = (unsigned int *)0xb8000; //帧缓存区的地址
 
@@ -162,6 +163,7 @@ void system_initialize()
 
     cpu_init();
     keyboard_init();
+    mouse_init();
     // test_slab();
     // test_mm();
 
@@ -178,8 +180,23 @@ void Start_Kernel(void)
     // show_welcome();
     // test_mm();
 
+/*
     while (1)
+    {
         keyboard_analyze_keycode();
+        struct mouse_packet_3bytes packet = {0};
+        //struct mouse_packet_4bytes packet = {0};
+        int errcode = 0;
+        errcode = mouse_get_packet(&packet);
+        if(errcode == 0)
+        {
+            printk_color(GREEN, BLACK, " (Mouse: byte0:%d, x:%3d, y:%3d)\n", packet.byte0, packet.movement_x, packet.movement_y);
+            //printk_color(GREEN, BLACK, " (Mouse: byte0:%d, x:%3d, y:%3d, byte3:%3d)\n", packet.byte0, packet.movement_x, packet.movement_y, (unsigned char)packet.byte3);
+        }
+    }
+    */
+   while(1);
+    
 }
 
 void ignore_int()