completed, not tested
This commit is contained in:
61
.vscode/settings.json
vendored
61
.vscode/settings.json
vendored
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ostream": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"syncstream": "cpp",
|
||||
"csignal": "cpp",
|
||||
"sstream": "cpp",
|
||||
"fstream": "cpp"
|
||||
},
|
||||
"C_Cpp.default.compilerPath": "/usr/bin/clang++"
|
||||
}
|
||||
24
lab6/.vscode/c_cpp_properties.json
vendored
Normal file
24
lab6/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux Kernel Module",
|
||||
"includePath": [
|
||||
"/usr/lib/modules/6.14.3-arch1-1/build/include",
|
||||
"/usr/lib/modules/6.14.3-arch1-1/build/arch/x86/include",
|
||||
"/usr/lib/modules/6.14.3-arch1-1/build/include/uapi",
|
||||
"/usr/lib/modules/6.14.3-arch1-1/build/include/generated/uapi",
|
||||
"/usr/lib/modules/6.14.3-arch1-1/build/include/generated",
|
||||
"/usr/lib/modules/6.14.3-arch1-1/build/arch/x86/include/generated"
|
||||
],
|
||||
"defines": [
|
||||
"__KERNEL__",
|
||||
"MODULE"
|
||||
],
|
||||
"compilerPath": "/usr/bin/g++",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++20",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
9
lab6/.vscode/settings.json
vendored
Normal file
9
lab6/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"cdev.h": "c",
|
||||
"module.h": "c",
|
||||
"types.h": "c",
|
||||
"init.h": "c"
|
||||
},
|
||||
"C_Cpp.default.compilerPath": "/usr/bin/clang++"
|
||||
}
|
||||
13
lab6/Makefile
Normal file
13
lab6/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
obj-m += mypipe.o
|
||||
OUT_DIR := build
|
||||
|
||||
.PHONY kern_mod writer reader clean
|
||||
all: kern_mod writer reader
|
||||
|
||||
kern_mod: mypipe.c
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
rm -rf $(OUT_DIR)
|
||||
|
||||
230
lab6/mypipe.c
Normal file
230
lab6/mypipe.c
Normal file
@@ -0,0 +1,230 @@
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
// Ref:
|
||||
// https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html
|
||||
// https://olegkutkov.me/2018/03/14/simple-linux-character-device-driver/
|
||||
|
||||
#define MYPIPE_BUFFER_SIZE 4096
|
||||
#define MYPIPE_DEVICE_NAME "wendy"
|
||||
|
||||
static dev_t dev_major = 0;
|
||||
|
||||
enum MyPipeMinors {
|
||||
MYPIPE_MINOR_IN = 0,
|
||||
MYPIPE_MINOR_OUT,
|
||||
MYPIPE_MINOR_COUNT,
|
||||
};
|
||||
|
||||
struct mypipe_data {
|
||||
struct cdev cdev;
|
||||
u8 buffer[MYPIPE_BUFFER_SIZE];
|
||||
size_t write_pos;
|
||||
size_t read_pos;
|
||||
size_t data_len;
|
||||
};
|
||||
|
||||
// This could be an array if we want multiple cdev
|
||||
static struct mypipe_data pdata;
|
||||
|
||||
static struct class *mypipedev_class = NULL;
|
||||
|
||||
// Protects the buffer
|
||||
static DEFINE_MUTEX(mypipe_lock);
|
||||
// These are two wait_queue_head_t for storing tasks that are waiting for
|
||||
// writing/ reading
|
||||
static DECLARE_WAIT_QUEUE_HEAD(read_queue);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(write_queue);
|
||||
|
||||
static int mypipe_open(struct inode *inode, struct file *file) {
|
||||
printk("Mypipe opened.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mypipe_release(struct inode *inode, struct file *file) {
|
||||
printk("Mypipe released.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mypipe_read(struct file *filep, char __user *buf, size_t size,
|
||||
loff_t *offset) {
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (iminor(file_iniode(filep)) != MYPIPE_MINOR_OUT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// Acquire the lock. If not acquired and interrupted by signal, let kernel
|
||||
// to try restart the syscall
|
||||
if (mutex_lock_interruptible(&mypipe_lock)) {
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
// We currently have no data, but we can wait for possible new to write in
|
||||
while (pdata.data_len == 0) {
|
||||
mutex_unlock(&mypipe_lock);
|
||||
if (filep->f_flags & O_NONBLOCK) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (wait_event_interruptible(read_queue, pdata.data_len > 0)) {
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
// Woken, relock for checking the condition
|
||||
if (mutex_lock_interruptible(&mypipe_lock)) {
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
}
|
||||
|
||||
// data_len > 0
|
||||
size_t to_read = min(size, pdata.data_len);
|
||||
size_t until_end = min(to_read, MYPIPE_BUFFER_SIZE - pdata.read_pos);
|
||||
if (copy_to_user(buf, pdata.buffer + pdata.read_pos, until_end)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (to_read > until_end) {
|
||||
if (copy_to_user(buf + until_end, pdata.buffer, to_read - until_end)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
pdata.read_pos = (pdata.read_pos + to_read) % MYPIPE_BUFFER_SIZE;
|
||||
pdata.data_len -= to_read;
|
||||
ret = to_read;
|
||||
|
||||
wake_up_interruptible(&write_queue);
|
||||
|
||||
out:
|
||||
mutex_unlock(&mypipe_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mypipe_write(struct file *filep, char __user *buf, size_t size,
|
||||
loff_t *offset) {
|
||||
// This is very similar to read(), just write instead of read
|
||||
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (iminor(file_indo(filep)) != MYPIPE_MINOR_IN) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mutex_lock_interruptible(&mypipe_lock)) {
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
while (pdata.data_len == MYPIPE_BUFFER_SIZE) {
|
||||
mutex_unlock(&mypipe_lock);
|
||||
if (filep->f_flags & O_NONBLOCK) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (wait_event_interruptible(write_queue,
|
||||
pdata.data_len < MYPIPE_BUFFER_SIZE)) {
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
if (mutex_lock_interruptible(&mypipe_lock)) {
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
}
|
||||
|
||||
size_t space_left = MYPIPE_BUFFER_SIZE - pdata.data_len;
|
||||
size_t to_write = min(size, space_left);
|
||||
size_t until_end = min(to_write, MYPIPE_BUFFER_SIZE - pdata.write_pos);
|
||||
|
||||
if (copy_from_user(pdata.buffer + pdata.write_pos, buf, until_end)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (to_write > until_end) {
|
||||
if (copy_from_user(pdata.buffer, buf + until_end,
|
||||
to_write - until_end)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
pdata.write_pos = (pdata.write_pos + to_write) % MYPIPE_BUFFER_SIZE;
|
||||
pdata.data_len += to_write;
|
||||
ret = to_write;
|
||||
|
||||
wake_up_interruptible(&read_queue);
|
||||
|
||||
out:
|
||||
mutex_unlock(&mypipe_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct file_operations mypipe_fops = {.owner = THIS_MODULE,
|
||||
.open = mypipe_open,
|
||||
.read = mypipe_read,
|
||||
.write = mypipe_write,
|
||||
.release = mypipe_release};
|
||||
|
||||
static int __init mypipe_init(void) {
|
||||
int ret;
|
||||
dev_t dev;
|
||||
|
||||
// Acquire a region of major, minor for us to use. The name appears in
|
||||
// /proc/devices (and potentially /sys ?).
|
||||
ret = alloc_chrdev_region(&dev, 0, MYPIPE_MINOR_COUNT, MYPIPE_DEVICE_NAME);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_major = MAJOR(dev);
|
||||
|
||||
// Create the class in sysfs. The name appears in /sys/class/<name>
|
||||
mypipedev_class = class_create(MYPIPE_DEVICE_NAME);
|
||||
if (IS_ERR(mypipedev_class)) {
|
||||
ret = PTR_ERR(mypipedev_class);
|
||||
goto del_cdev;
|
||||
}
|
||||
|
||||
// Create a cdev in our pdata
|
||||
cdev_init(&pdata.cdev, &mypipe_fops);
|
||||
pdata.cdev.owner = THIS_MODULE;
|
||||
|
||||
// Register this newly created cdev to kernel. We only have one dev, so we
|
||||
// can MKDEV(dev_major, 0).
|
||||
|
||||
// Also, we want to devices that one handles write and one read, so the last
|
||||
// arg is not 1, but 2 (In this case, MYPIPE_MINOR_COUNT)
|
||||
ret = cdev_add(&pdata.cdev, MKDEV(dev_major, 0), MYPIPE_MINOR_COUNT);
|
||||
if (ret)
|
||||
goto unregister;
|
||||
|
||||
// Create device node /dev/mypipe_in and /dev/mypipe_out
|
||||
device_create(mypipedev_class, NULL, MKDEV(dev_major, MYPIPE_MINOR_IN),
|
||||
NULL, "%s_in", MYPIPE_DEVICE_NAME);
|
||||
device_create(mypipedev_class, NULL, MKDEV(dev_major, MYPIPE_MINOR_OUT),
|
||||
NULL, "%s_out", MYPIPE_DEVICE_NAME);
|
||||
|
||||
printk(KERN_INFO "Wendy: module loaded\n");
|
||||
return 0;
|
||||
|
||||
del_cdev:
|
||||
cdev_del(&pdata.cdev);
|
||||
unregister:
|
||||
unregister_chrdev_region(MKDEV(dev_major, 0), MYPIPE_MINOR_COUNT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit mypipe_destroy(void) {
|
||||
// This is basically the reverse operation of mypipe_init
|
||||
device_destroy(mypipedev_class, MKDEV(dev_major, MYPIPE_MINOR_IN));
|
||||
device_destroy(mypipedev_class, MKDEV(dev_major, MYPIPE_MINOR_OUT));
|
||||
class_unregister(mypipedev_class);
|
||||
class_destory(mypipedev_class);
|
||||
cdev_del(&pdata.cdev);
|
||||
unregister_chrdev_region(MKDEV(dev_major, 0), MYPIPE_MINOR_COUNT);
|
||||
printk(KERN_INFO "Wendy: module unloaded");
|
||||
}
|
||||
|
||||
module_init(mypipe_init);
|
||||
module_exit(mypipe_destroy);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user