/* * Ring buffer character device - example for Operating System course * * Copyright (C) 2016, 2017 Krzysztof Mazur * * Add your copyright here and change MODULE_AUTHOR. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #define pr_fmt(fmt) "ringdev: " fmt #include #include #include #include #include #include /* * pożądany numer minor urządzenia znakowego ringdev, * MISC_DYNAMIC_MINOR dla automatycznego wyboru */ #define RINGDEV_MINOR MISC_DYNAMIC_MINOR /* * mutex używany do synchronizacji dostępu do wszystkich zmiennych modułu */ static struct mutex ringdev_lock; /* bufor i liczba bajtów w buforze */ static char ringdev_buf[4096]; static size_t ringdev_len; static int ringdev_open(struct inode *inode, struct file *filp) { return 0; } static ssize_t ringdev_read(struct file *filp, char __user *buf, size_t count, loff_t *off) { ssize_t ret = 0; /* * bufor wraz z długością mogą być zmieniane przez inny proces, * by się przed tym zabezpieczyć dane muszą być czytane * trzymając blokadę */ mutex_lock(&ringdev_lock); if (*off > ringdev_len) count = 0; else if (count >= ringdev_len - *off) count = ringdev_len - *off; /* * kopiowanie danych do przestrzeni użytkownika, trzeba użyć * copy_*_user(), na niektórych architekturach pisanie/czytanie * pamięci użytkownika może być niemożliwe, na innych * wprowadza luki bezpieczeństwa */ ret = -EFAULT; if (copy_to_user(buf, ringdev_buf + *off, count)) goto out_unlock; ret = count; *off += ret; out_unlock: mutex_unlock(&ringdev_lock); return ret; } static ssize_t ringdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *off) { ssize_t ret; mutex_lock(&ringdev_lock); if (count > sizeof(ringdev_buf) - ringdev_len) count = sizeof(ringdev_buf) - ringdev_len; ret = -ENOSPC; if (!count) goto out_unlock; ret = -EFAULT; if (copy_from_user(ringdev_buf + ringdev_len, buf, count)) goto out_unlock; ringdev_len += count; ret = count; out_unlock: mutex_unlock(&ringdev_lock); return ret; } static int ringdev_release(struct inode *inode, struct file *filp) { return 0; } static const struct file_operations ringdev_fops = { .owner = THIS_MODULE, .open = ringdev_open, .read = ringdev_read, .write = ringdev_write, .release = ringdev_release, }; static struct miscdevice ringdev_miscdevice = { .minor = RINGDEV_MINOR, .name = "ringdev", .fops = &ringdev_fops }; static int __init ringdev_init(void) { int ret; mutex_init(&ringdev_lock); ringdev_len = snprintf(ringdev_buf, sizeof(ringdev_buf), "Hello world!\n"); ret = misc_register(&ringdev_miscdevice); if (ret < 0) { pr_err("can't register miscdevice.\n"); return ret; } pr_info("minor %d\n", ringdev_miscdevice.minor); return 0; } static void __exit ringdev_exit(void) { misc_deregister(&ringdev_miscdevice); mutex_destroy(&ringdev_lock); } module_init(ringdev_init); module_exit(ringdev_exit); MODULE_DESCRIPTION("Ring buffer device"); MODULE_AUTHOR("Real Name "); MODULE_LICENSE("GPL");