/* * Ring buffer character device * * Copyright (C) 2016 Krzysztof Mazur * * 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 major urządzenia znakowego ringdev, * 0 dla automatycznego wyboru */ #define RINGDEV_MAJOR 0 /* * numer major urządzenia znakowego ringdev, w przypadku automatycznego * wyboru wpisywany tutaj jest otrzymany numer */ static int ringdev_major = RINGDEV_MAJOR; /* * domyślna maksymalna liczba urządzeń ringdev */ #define MAX_RINGDEV 8 static int max_ringdev = MAX_RINGDEV; module_param(max_ringdev, int, 0444); MODULE_PARM_DESC(max_ringdev, "Maximum number of ringdev devices"); /* * rozmiar bufora */ #define RINGDEV_BUF_SIZE 4096 /** * struktura opisująca pojedyncze urządzenie ringdev * @lock: mutex używany do synchronizacji dostępu do wszystkich pól * @buf: bufor * @len: liczba bajtów w buforze */ struct ringdev { struct mutex lock; void *buf; size_t len; }; /* * tablica struktur opisujących urządzenia, alokowana dynamicznie */ static struct ringdev *ringdev; static int ringdev_open(struct inode *inode, struct file *filp) { unsigned int minor = iminor(inode); struct ringdev *d = &ringdev[minor]; int ret; /* * obsługujemy tylko max_ringdev urządzeń */ if (minor >= max_ringdev) return -ENXIO; mutex_lock(&d->lock); if (!d->buf) { ret = -ENOMEM; d->buf = kmalloc(RINGDEV_BUF_SIZE, GFP_KERNEL); if (!d->buf) goto out_unlock; } d->len = snprintf(d->buf, RINGDEV_BUF_SIZE, "last opened by PID %d (%s)\n", current->pid, current->comm); filp->private_data = d; ret = 0; out_unlock: mutex_unlock(&d->lock); return ret; } static ssize_t ringdev_read(struct file *filp, char __user *buf, size_t count, loff_t *off) { struct ringdev *d = filp->private_data; 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(&d->lock); if (*off > d->len) count = 0; else if (count >= d->len - *off) count = d->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, d->buf + *off, count)) goto out_unlock; ret = count; *off += ret; out_unlock: mutex_unlock(&d->lock); return ret; } static ssize_t ringdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *off) { /* zapis obecnie nieobługiwany */ return -EINVAL; } 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 int __init ringdev_init(void) { unsigned int i; int ret; ret = -ENOMEM; ringdev = kcalloc(max_ringdev, sizeof(*ringdev), GFP_KERNEL); if (!ringdev) { pr_err("can't allocate memory for %d devices.\n", max_ringdev); goto out; } for (i = 0; i < max_ringdev; i++) mutex_init(&ringdev[i].lock); ret = register_chrdev(ringdev_major, "ringdev", &ringdev_fops); if (ret < 0) { pr_err("can't get major %d.\n", ringdev_major); goto out_free; } ringdev_major = ret; pr_info("major %d with %d minor devices\n", ringdev_major, max_ringdev); return 0; out_free: kfree(ringdev); out: return ret; } static void __exit ringdev_exit(void) { unsigned int i; unregister_chrdev(ringdev_major, "ringdev"); /* kfree(NULL) is ok */ for (i = 0; i < MAX_RINGDEV; i++) { kfree(ringdev[i].buf); mutex_destroy(&ringdev[i].lock); } kfree(ringdev); } module_init(ringdev_init); module_exit(ringdev_exit); MODULE_DESCRIPTION("Ring buffer device"); MODULE_AUTHOR("Krzysztof Mazur "); MODULE_LICENSE("GPL");