#include #include #include #include #include #include #include #include MODULE_LICENSE("GPL"); #define DEV_NAME "sad_dev" static int sad_open(struct inode *, struct file *); static int sad_release(struct inode *, struct file *); static ssize_t sad_read(struct file *, char __user *, size_t, loff_t *); static ssize_t sad_write(struct file *, const char __user *, size_t, loff_t *); static dev_t dev; static struct cdev *cdev; static int device_open_count = 0; struct sad_buf { char *buf; size_t size; struct semaphore lock; } *buf; static struct file_operations sad_fops = { .owner = THIS_MODULE, .read = sad_read, .write = sad_write, .open = sad_open, .release = sad_release }; static ssize_t sad_read(struct file *flip, char __user *user_buf, size_t len, loff_t *offset) { size_t bytes_left, read_size, uncopied; if (down_interruptible(&buf->lock)) return -ERESTARTSYS; if (*offset >= buf->size) { up(&buf->lock); return 0; } bytes_left = buf->size - *offset; read_size = min(bytes_left, len); uncopied = copy_to_user(user_buf, buf->buf + *offset, read_size); *offset += read_size - uncopied; up(&buf->lock); return read_size - uncopied; } static ssize_t sad_write(struct file *flip, const char __user *user_buf, size_t len, loff_t *offset) { size_t uncopied; if (down_interruptible(&buf->lock)) return -ERESTARTSYS; if (buf->buf) kfree(buf->buf); buf->buf = kmalloc(len, GFP_KERNEL); if (!buf->buf) { up(&buf->lock); return -ENOMEM; } buf->size = len; *offset = 0; uncopied = copy_from_user(buf->buf, user_buf, len); up(&buf->lock); return len - uncopied; } static int sad_open(struct inode *inode, struct file *file) { device_open_count++; try_module_get(THIS_MODULE); return 0; } static int sad_release(struct inode *inode, struct file *file) { device_open_count--; if (device_open_count == 0) module_put(THIS_MODULE); return 0; } static int __init sad_init(void) { int rc; const unsigned int minor = 0; const unsigned int count = 1; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) return -ENOMEM; sema_init(&buf->lock, 1); rc = alloc_chrdev_region(&dev, minor, count, DEV_NAME); if (rc < 0) { printk(KERN_ALERT "Could not register device: %d\n", rc); return rc; } else { printk(KERN_INFO "Allocated maj %d\n", MAJOR(dev)); } cdev = cdev_alloc(); cdev->owner = THIS_MODULE; cdev->ops = &sad_fops; rc = cdev_add(cdev, dev, 1); if (rc) { printk(KERN_ALERT "Could not cdev_add: %d\n", rc); return rc; } return 0; } static void __exit sad_exit(void) { cdev_del(cdev); if (buf->buf) kfree(buf->buf); kfree(buf); unregister_chrdev_region(dev, 1); printk(KERN_INFO "okay, bye\n"); } module_init(sad_init); module_exit(sad_exit);