From 636840569cd352017f0c0fb920c8fe102d5de549 Mon Sep 17 00:00:00 2001 From: Mara Date: Fri, 25 Mar 2022 09:00:24 +0200 Subject: [PATCH 1/2] lab04: Add empty skel Signed-off-by: Mara-Ioana Nicolae --- tools/labs/skels/interrupts/Kbuild | 3 + tools/labs/skels/interrupts/kbd.c | 190 +++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 tools/labs/skels/interrupts/Kbuild create mode 100644 tools/labs/skels/interrupts/kbd.c diff --git a/tools/labs/skels/interrupts/Kbuild b/tools/labs/skels/interrupts/Kbuild new file mode 100644 index 00000000000000..20b33f5d968dee --- /dev/null +++ b/tools/labs/skels/interrupts/Kbuild @@ -0,0 +1,3 @@ +ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable + +obj-m = kbd.o diff --git a/tools/labs/skels/interrupts/kbd.c b/tools/labs/skels/interrupts/kbd.c new file mode 100644 index 00000000000000..b605653d949743 --- /dev/null +++ b/tools/labs/skels/interrupts/kbd.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("KBD"); +MODULE_AUTHOR("Kernel Hacker"); +MODULE_LICENSE("GPL"); + +#define MODULE_NAME "kbd" + +#define KBD_MAJOR 42 +#define KBD_MINOR 0 +#define KBD_NR_MINORS 1 + +#define I8042_KBD_IRQ 1 +#define I8042_STATUS_REG 0x64 +#define I8042_DATA_REG 0x60 + +#define BUFFER_SIZE 1024 +#define SCANCODE_RELEASED_MASK 0x80 + +struct kbd { + struct cdev cdev; + /* TODO 3: add spinlock */ + char buf[BUFFER_SIZE]; + size_t put_idx, get_idx, count; +} devs[1]; + +/* + * Checks if scancode corresponds to key press or release. + */ +static int is_key_press(unsigned int scancode) +{ + return !(scancode & SCANCODE_RELEASED_MASK); +} + +/* + * Return the character of the given scancode. + * Only works for alphanumeric/space/enter; returns '?' for other + * characters. + */ +static int get_ascii(unsigned int scancode) +{ + static char *row1 = "1234567890"; + static char *row2 = "qwertyuiop"; + static char *row3 = "asdfghjkl"; + static char *row4 = "zxcvbnm"; + + scancode &= ~SCANCODE_RELEASED_MASK; + if (scancode >= 0x02 && scancode <= 0x0b) + return *(row1 + scancode - 0x02); + if (scancode >= 0x10 && scancode <= 0x19) + return *(row2 + scancode - 0x10); + if (scancode >= 0x1e && scancode <= 0x26) + return *(row3 + scancode - 0x1e); + if (scancode >= 0x2c && scancode <= 0x32) + return *(row4 + scancode - 0x2c); + if (scancode == 0x39) + return ' '; + if (scancode == 0x1c) + return '\n'; + return '?'; +} + +static void put_char(struct kbd *data, char c) +{ + if (data->count >= BUFFER_SIZE) + return; + + data->buf[data->put_idx] = c; + data->put_idx = (data->put_idx + 1) % BUFFER_SIZE; + data->count++; +} + +static bool get_char(char *c, struct kbd *data) +{ + /* TODO 4: get char from buffer; update count and get_idx */ + return false; +} + +static void reset_buffer(struct kbd *data) +{ + /* TODO 5: reset count, put_idx, get_idx */ +} + +/* + * Return the value of the DATA register. + */ +static inline u8 i8042_read_data(void) +{ + u8 val; + /* TODO 3: Read DATA register (8 bits). */ + return val; +} + +/* TODO 2: implement interrupt handler */ + /* TODO 3: read the scancode */ + /* TODO 3: interpret the scancode */ + /* TODO 3: display information about the keystrokes */ + /* TODO 3: store ASCII key to buffer */ + +static int kbd_open(struct inode *inode, struct file *file) +{ + struct kbd *data = container_of(inode->i_cdev, struct kbd, cdev); + + file->private_data = data; + pr_info("%s opened\n", MODULE_NAME); + return 0; +} + +static int kbd_release(struct inode *inode, struct file *file) +{ + pr_info("%s closed\n", MODULE_NAME); + return 0; +} + +/* TODO 5: add write operation and reset the buffer */ + +static ssize_t kbd_read(struct file *file, char __user *user_buffer, + size_t size, loff_t *offset) +{ + struct kbd *data = (struct kbd *) file->private_data; + size_t read = 0; + /* TODO 4: read data from buffer */ + return read; +} + +static const struct file_operations kbd_fops = { + .owner = THIS_MODULE, + .open = kbd_open, + .release = kbd_release, + .read = kbd_read, + /* TODO 5: add write operation */ +}; + +static int kbd_init(void) +{ + int err; + + err = register_chrdev_region(MKDEV(KBD_MAJOR, KBD_MINOR), + KBD_NR_MINORS, MODULE_NAME); + if (err != 0) { + pr_err("register_region failed: %d\n", err); + goto out; + } + + /* TODO 1: request the keyboard I/O ports */ + + /* TODO 3: initialize spinlock */ + + /* TODO 2: Register IRQ handler for keyboard IRQ (IRQ 1). */ + + cdev_init(&devs[0].cdev, &kbd_fops); + cdev_add(&devs[0].cdev, MKDEV(KBD_MAJOR, KBD_MINOR), 1); + + pr_notice("Driver %s loaded\n", MODULE_NAME); + return 0; + + /*TODO 2: release regions in case of error */ + +out_unregister: + unregister_chrdev_region(MKDEV(KBD_MAJOR, KBD_MINOR), + KBD_NR_MINORS); +out: + return err; +} + +static void kbd_exit(void) +{ + cdev_del(&devs[0].cdev); + + /* TODO 2: Free IRQ. */ + + /* TODO 1: release keyboard I/O ports */ + + + unregister_chrdev_region(MKDEV(KBD_MAJOR, KBD_MINOR), + KBD_NR_MINORS); + pr_notice("Driver %s unloaded\n", MODULE_NAME); +} + +module_init(kbd_init); +module_exit(kbd_exit); From 837305993b462f4b098375ee8b1354aa2c0a380d Mon Sep 17 00:00:00 2001 From: Mara Date: Wed, 30 Mar 2022 23:42:39 +0300 Subject: [PATCH 2/2] lab04: Add solution for exercises Signed-off-by: Mara --- tools/labs/skels/interrupts/kbd.c | 96 +++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/tools/labs/skels/interrupts/kbd.c b/tools/labs/skels/interrupts/kbd.c index b605653d949743..0306bef0d863d1 100644 --- a/tools/labs/skels/interrupts/kbd.c +++ b/tools/labs/skels/interrupts/kbd.c @@ -29,6 +29,7 @@ MODULE_LICENSE("GPL"); struct kbd { struct cdev cdev; /* TODO 3: add spinlock */ + spinlock_t lock; char buf[BUFFER_SIZE]; size_t put_idx, get_idx, count; } devs[1]; @@ -38,7 +39,7 @@ struct kbd { */ static int is_key_press(unsigned int scancode) { - return !(scancode & SCANCODE_RELEASED_MASK); + return scancode & SCANCODE_RELEASED_MASK; } /* @@ -82,12 +83,22 @@ static void put_char(struct kbd *data, char c) static bool get_char(char *c, struct kbd *data) { /* TODO 4: get char from buffer; update count and get_idx */ - return false; + if (!data->count) + return false; + + *c = data->buf[data->get_idx]; + data->get_idx = (data->get_idx + 1) % BUFFER_SIZE; + data->count--; + return true; } static void reset_buffer(struct kbd *data) { /* TODO 5: reset count, put_idx, get_idx */ + memset(data->buf, 0, BUFFER_SIZE); + data->count = 0; + data->put_idx = 0; + data->get_idx = 0; } /* @@ -95,12 +106,42 @@ static void reset_buffer(struct kbd *data) */ static inline u8 i8042_read_data(void) { - u8 val; + u8 val = 0xFF; /* TODO 3: Read DATA register (8 bits). */ + val = inb(I8042_DATA_REG); return val; } /* TODO 2: implement interrupt handler */ +irqreturn_t kbd_interrupt_handler(int irq_no, void *dev_id) +{ + u8 scancode = i8042_read_data(); + pr_info("IRQ:% d, scancode = 0x%x (%u,%c)\n", + irq_no, scancode, scancode, scancode); + + int is_pressed = is_key_press(scancode); + int ascii_val = 0; + + if (is_pressed == 0) { + ascii_val = get_ascii(scancode); + pr_info("IRQ %d: scancode=0x%x (%u) pressed=%d ch=%c\n", + irq_no, scancode, scancode, is_pressed, ascii_val); + + struct kbd *data = (struct kbd *)dev_id; + spin_lock(&data->lock); + put_char(data, ascii_val); + spin_unlock(&data->lock); + } + + return IRQ_NONE; + + /* if interrupt is not for this device (shared interrupts) */ + /* return IRQ_NONE;*/ + + /* clear interrupt-pending bit */ + /* read from device or write to device*/ +} + /* TODO 3: read the scancode */ /* TODO 3: interpret the scancode */ /* TODO 3: display information about the keystrokes */ @@ -122,6 +163,18 @@ static int kbd_release(struct inode *inode, struct file *file) } /* TODO 5: add write operation and reset the buffer */ +static ssize_t kbd_write(struct file *file, const char __user *user_buffer, + size_t size, loff_t *offset) +{ + struct kbd *data = (struct kbd *) file->private_data; + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + reset_buffer(data); + spin_unlock_irqrestore(&data->lock, flags); + + return size; +} static ssize_t kbd_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset) @@ -129,6 +182,24 @@ static ssize_t kbd_read(struct file *file, char __user *user_buffer, struct kbd *data = (struct kbd *) file->private_data; size_t read = 0; /* TODO 4: read data from buffer */ + unsigned long flags; + + while (size) { + spin_lock_irqsave(&data->lock, flags); + char c; + bool rc = get_char(&c, data); + spin_unlock_irqrestore(&data->lock, flags); + + if (rc) { + put_user(c, user_buffer++); + } else { + break; + } + + read++; + size--; + } + return read; } @@ -138,6 +209,7 @@ static const struct file_operations kbd_fops = { .release = kbd_release, .read = kbd_read, /* TODO 5: add write operation */ + .write = kbd_write, }; static int kbd_init(void) @@ -152,10 +224,20 @@ static int kbd_init(void) } /* TODO 1: request the keyboard I/O ports */ + if (!request_region(0x61, 1, "io port1")) { + return -ENODEV; + } + + if (!request_region(0x65, 1, "io port2")) { + return -ENODEV; + } /* TODO 3: initialize spinlock */ + spin_lock_init(&devs[0].lock); /* TODO 2: Register IRQ handler for keyboard IRQ (IRQ 1). */ + err = request_irq(I8042_KBD_IRQ, kbd_interrupt_handler, IRQF_SHARED, + MODULE_NAME, &devs[0]); cdev_init(&devs[0].cdev, &kbd_fops); cdev_add(&devs[0].cdev, MKDEV(KBD_MAJOR, KBD_MINOR), 1); @@ -164,6 +246,10 @@ static int kbd_init(void) return 0; /*TODO 2: release regions in case of error */ + if (err < 0) { + free_irq (I8042_KBD_IRQ, &devs[0]); + return err; + } out_unregister: unregister_chrdev_region(MKDEV(KBD_MAJOR, KBD_MINOR), @@ -177,9 +263,11 @@ static void kbd_exit(void) cdev_del(&devs[0].cdev); /* TODO 2: Free IRQ. */ + free_irq (I8042_KBD_IRQ, &devs[0]); /* TODO 1: release keyboard I/O ports */ - + release_region(0x61, 1); + release_region(0x65, 1); unregister_chrdev_region(MKDEV(KBD_MAJOR, KBD_MINOR), KBD_NR_MINORS);