From 1cf29b71ed8a1828b0ca380ab0303b72fb2cba16 Mon Sep 17 00:00:00 2001 From: paul-loedige Date: Mon, 24 Oct 2022 11:49:04 +0900 Subject: [PATCH] created first version --- Makefile | 9 +++ README.md | 21 +++++++ module.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 Makefile create mode 100644 module.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d8bb814 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +obj-m += module.o + +PWD := $(CURDIR) + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean diff --git a/README.md b/README.md index 02da448..6582cac 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ # Homework_Lesson_3 +## Assignment +Modify the kernel module you created in the #2 Homework to accommodate the functionality of a character device that puts the number of time the device file has been read from into the file. +Compile and run the program. +Try reading the device file through “cat /proc/devices”. +Submit the source code and the screen shot of the result of execution displayed by the dmesg command. +Deadline of homework: Oct 30, 2022 (in case that you cannot finish in the class) + +## Prerequisits + - requires the correct linux headers to be installed + - root access is needed + +## how to run the kernel module + - `make` to build the module + - `sudo insmod ` to run the module + - `sudo rmmod ` to stop the module + +## viewing the log +on systems that run systemd: +``` journalctl -f -k ``` +you can also use +``` dmesg ``` \ No newline at end of file diff --git a/module.c b/module.c new file mode 100644 index 0000000..bef32a1 --- /dev/null +++ b/module.c @@ -0,0 +1,165 @@ +/** + * @file module.c + * @author Paul Lödige (ploedige@g.ecc.u-tokyo.ac.jp) + * @studentid 37-229753 + * @brief kernel module for character device that says how many times the dev file has been read from + * @version 0.1 + * @date 2022-10-24 + * + * @copyright Copyright (c) 2022 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Prototypes - this would normally go in a .h file */ +static int device_open(struct inode *, struct file *); +static int device_release(struct inode *, struct file *); +static ssize_t device_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t device_write(struct file *, const char __user *, size_t, loff_t *); + +#define SUCCESS 0 +#define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices */ +#define BUF_LEN 80 /* Max length of the message from the device */ + +/* Global variables are declared as static, so are global within the file. */ + +static int major; /* major number assigned to our device driver */ + +enum { + CDEV_NOT_USED = 0, + CDEV_EXCLUSIVE_OPEN = 1, +}; + +/* Is device open? Used to prevent multiple access to device */ +static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED); + +static char msg[BUF_LEN + 1]; /* The msg the device will give when asked */ + +static struct class *cls; + +static struct file_operations chardev_fops = { + .read = device_read, + .write = device_write, + .open = device_open, + .release = device_release, +}; + +static int __init chardev_init(void) +{ + major = register_chrdev(0, DEVICE_NAME, &chardev_fops); + + if (major < 0) { + pr_alert("Registering char device failed with %d\n", major); + return major; + } + + pr_info("I was assigned major number %d.\n", major); + + cls = class_create(THIS_MODULE, DEVICE_NAME); + device_create(cls, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); + + pr_info("Device created on /dev/%s\n", DEVICE_NAME); + + return SUCCESS; +} + +static void __exit chardev_exit(void) +{ + device_destroy(cls, MKDEV(major, 0)); + class_destroy(cls); + + /* Unregister the device */ + unregister_chrdev(major, DEVICE_NAME); +} + +/* Methods */ + +/* Called when a process tries to open the device file, like +* "sudo cat /dev/chardev" +*/ +static int device_open(struct inode *inode, struct file *file) +{ + static int counter = 0; + + if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN)) + return -EBUSY; + + sprintf(msg, "I already told you %d times Hello world!\n", counter++); + try_module_get(THIS_MODULE); + + return SUCCESS; +} + +/* Called when a process closes the device file. */ +static int device_release(struct inode *inode, struct file *file) +{ + /* We're now ready for our next caller */ + atomic_set(&already_open, CDEV_NOT_USED); + + /* Decrement the usage count, or else once you opened the file, you will + * never get rid of the module. + */ + module_put(THIS_MODULE); + + return SUCCESS; +} + +/* Called when a process, which already opened the dev file, attempts to +* read from it. +*/ +static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ + char __user *buffer, /* buffer to fill with data */ + size_t length, /* length of the buffer */ + loff_t *offset) +{ + /* Number of bytes actually written to the buffer */ + int bytes_read = 0; + const char *msg_ptr = msg; + + if (!*(msg_ptr + *offset)) + { /* we are at the end of message */ + *offset = 0; /* reset the offset */ + return 0; /* signify end of file */ + } + + msg_ptr += *offset; + + /* Actually put the data into the buffer */ + while (length && *msg_ptr) + { + /* The buffer is in the user data segment, not the kernel + * segment so "*" assignment won't work. We have to use + * put_user which copies data from the kernel data segment to + * the user data segment. + */ + put_user(*(msg_ptr++), buffer++); + length--; + bytes_read++; + } + + *offset += bytes_read; + /* Most read functions return the number of bytes put into the buffer. */ + return bytes_read; +} + +/* Called when a process writes to dev file: echo "hi" > /dev/hello */ +static ssize_t device_write(struct file *filp, const char __user *buff, + size_t len, loff_t *off) +{ + pr_alert("Sorry, this operation is not supported.\n"); + return -EINVAL; +} + +module_init(chardev_init); +module_exit(chardev_exit); + +MODULE_LICENSE("GPL");