/* * ssdisk device - example for Operating System course * * Copyright (C) 2016..2020 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) "ssdisk: " fmt #include #include #include #include #include #include #include #define N_SSDISK 29 /* * mutex used for access synchronization to buffer (ssdisk_buf and ssdisk_len) */ static struct mutex ssdisk_lock; /* * buffer and number of written bytes in the buffer */ static char ssdisk_buf[4096]; static size_t ssdisk_len; static int ssdisk_open(struct inode *inode, struct file *filp) { return 0; } static ssize_t ssdisk_read(struct file *filp, char __user *buf, size_t count, loff_t *off) { ssize_t ret = 0; /* * access to ssdisk_buf i ssdisk_len is protected by ssdisk_lock, * take that lock */ mutex_lock(&ssdisk_lock); if (*off > ssdisk_len) count = 0; else if (count >= ssdisk_len - *off) count = ssdisk_len - *off; /* * for access to user memory special functions must be used, * to copy to user memory copy_to_user must be used. */ ret = -EFAULT; if (copy_to_user(buf, ssdisk_buf + *off, count)) goto out_unlock; ret = count; *off += ret; out_unlock: mutex_unlock(&ssdisk_lock); return ret; } static ssize_t ssdisk_write(struct file *filp, const char __user *buf, size_t count, loff_t *off) { return -EINVAL; } static int ssdisk_release(struct inode *inode, struct file *filp) { return 0; } static const struct file_operations ssdisk_fops = { .owner = THIS_MODULE, .open = ssdisk_open, .read = ssdisk_read, .write = ssdisk_write, .release = ssdisk_release, }; static struct miscdevice ssdisk_miscdevice = { .minor = MISC_DYNAMIC_MINOR, .name = "ssdisk", .fops = &ssdisk_fops }; static int ssdisk_ldisc_open(struct tty_struct *tty) { pr_info("new ssdisk device at tty %s\n", tty->name); if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!tty->ops->write) return -EOPNOTSUPP; /* example: read 2 bytes from position 0 */ tty->ops->write(tty, ":0001000200000000\n", 18); tty->receive_room = 65536; /* flow control not supported */ return 0; } static void ssdisk_ldisc_close(struct tty_struct *tty) { } static int ssdisk_ldisc_hangup(struct tty_struct *tty) { return 0; } static void ssdisk_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { int c; while (count--) { if (fp && *fp++) { pr_info("error %02x\n", *fp); cp++; continue; } c = *cp++; pr_info("received %02x (%c)\n", c, (c >= 32 && c < 128) ? c : '?'); } } static void ssdisk_write_wakeup(struct tty_struct *tty) { } static struct tty_ldisc_ops ssdisk_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "ssdisk", .open = ssdisk_ldisc_open, .hangup = ssdisk_ldisc_hangup, .close = ssdisk_ldisc_close, .ioctl = tty_mode_ioctl, .receive_buf = ssdisk_receive_buf, .write_wakeup = ssdisk_write_wakeup, }; static int __init ssdisk_init(void) { int ret; mutex_init(&ssdisk_lock); ssdisk_len = snprintf(ssdisk_buf, sizeof(ssdisk_buf), "Hello world!\n"); ret = misc_register(&ssdisk_miscdevice); if (ret < 0) { pr_err("can't register miscdevice.\n"); return ret; } ret = tty_register_ldisc(N_SSDISK, &ssdisk_ldisc); if (ret) { pr_err("can't register line discipline (err = %d)\n", ret); return ret; } pr_info("minor %d\n", ssdisk_miscdevice.minor); return 0; } static void __exit ssdisk_exit(void) { misc_deregister(&ssdisk_miscdevice); mutex_destroy(&ssdisk_lock); } module_init(ssdisk_init); module_exit(ssdisk_exit); MODULE_DESCRIPTION("Secure Serial Disk"); MODULE_AUTHOR("Real Name"); MODULE_LICENSE("GPL");