From b3cbd3caa2bf711582af26671cba72a7cf3db095 Mon Sep 17 00:00:00 2001 From: fslongjin Date: Thu, 17 Mar 2022 20:51:14 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20=E9=BC=A0=E6=A0=87=E9=A9=B1=E5=8A=A8?= =?UTF-8?q?=EF=BC=88=E6=9C=89bug=EF=BC=8C=E8=BF=98=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E7=94=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 +- kernel/Makefile | 9 +- kernel/driver/keyboard/keyboard.c | 8 +- kernel/driver/keyboard/keyboard.h | 4 +- kernel/driver/mouse/mouse.c | 315 ++++++++++++++++++++++++++++++ kernel/driver/mouse/mouse.h | 107 ++++++++++ kernel/exception/irq.h | 2 +- kernel/main.c | 17 ++ 8 files changed, 455 insertions(+), 10 deletions(-) create mode 100644 kernel/driver/mouse/mouse.c create mode 100644 kernel/driver/mouse/mouse.h diff --git a/.vscode/settings.json b/.vscode/settings.json index f28cc72c..a0cba5fd 100644 --- a/.vscode/settings.json +++ b/.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" } \ No newline at end of file diff --git a/kernel/Makefile b/kernel/Makefile index cea48eb2..986386fe 100644 --- a/kernel/Makefile +++ b/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) \ No newline at end of file diff --git a/kernel/driver/keyboard/keyboard.c b/kernel/driver/keyboard/keyboard.c index 498bea0d..4da17a37 100644 --- a/kernel/driver/keyboard/keyboard.c +++ b/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); } diff --git a/kernel/driver/keyboard/keyboard.h b/kernel/driver/keyboard/keyboard.h index 9de3c376..9084781a 100644 --- a/kernel/driver/keyboard/keyboard.h +++ b/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 // 键盘的输入缓冲区已满标志位 diff --git a/kernel/driver/mouse/mouse.c b/kernel/driver/mouse/mouse.c new file mode 100644 index 00000000..77abe92d --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/kernel/driver/mouse/mouse.h b/kernel/driver/mouse/mouse.h new file mode 100644 index 00000000..acc149cb --- /dev/null +++ b/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); \ No newline at end of file diff --git a/kernel/exception/irq.h b/kernel/exception/irq.h index b1898a29..f37f0a12 100644 --- a/kernel/exception/irq.h +++ b/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 diff --git a/kernel/main.c b/kernel/main.c index b33fe7ae..adfb57a9 100644 --- a/kernel/main.c +++ b/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()