#include <linux/cdev.h>
/* Needed for copying to/from user space */
#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/miscdevice.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>");
MODULE_DESCRIPTION("A hello world char device");
MODULE_VERSION("1.0.0");
+#define BUFFER_SIZE 4096
+
+struct message {
+ char *text;
+ size_t len;
+};
+
/* Message buffer we send to upstream */
-static char helloc_message[] = "hello, world\n";
+static struct message *hello_message;
+
+static int helloc_open(struct inode *ino, struct file *filp)
+{
+ struct message *msg;
+ printk(KERN_INFO "Opened file with minor %d\n", iminor(ino));
+ msg = hello_message;
+ filp->private_data = msg;
+ if (filp->f_flags & O_TRUNC) {
+ memset(msg->text, 0, msg->len);
+ msg->len = 0;
+ printk(KERN_INFO "File opened with truncate flags\n");
+ }
+ if (filp->f_flags & O_APPEND) {
+ filp->f_pos = msg->len;
+ }
+ return 0;
+}
+
+static int helloc_release(struct inode *ino, struct file *filp)
+{
+ return 0;
+}
/* our read function writes our message to the user buffer */
static ssize_t helloc_read(struct file *filp, char __user *buf, size_t len,
loff_t *pos)
{
int r;
+ struct message *msg = filp->private_data;
/* do not read pass through the size of the message */
- if (*pos >= sizeof(helloc_message))
+ if (*pos >= msg->len)
/* return end of file */
return 0;
/* if len is bigger than the rest of the message, clamp it */
- if (len > sizeof(helloc_message) - *pos)
- len = sizeof(helloc_message) - *pos;
+ if (len > msg->len - *pos)
+ len = msg->len - *pos;
/* copy message to user space and return error if it fails */
- r = copy_to_user(buf, helloc_message + *pos, len);
+ r = copy_to_user(buf, msg->text + *pos, len);
if (r)
return -EFAULT;
/* update the file position */
return len;
}
+static ssize_t helloc_write(struct file *filp, const char __user *buf,
+ size_t len, loff_t *pos)
+{
+ int r;
+ struct message *msg = filp->private_data;
+ /* do not read pass through the size of the message */
+ if (*pos >= BUFFER_SIZE)
+ /* return end of file */
+ return 0;
+ /* if len is bigger than the rest of the message, clamp it */
+ if (len > BUFFER_SIZE - *pos)
+ len = BUFFER_SIZE - *pos;
+
+ /* copy message to user space and return error if it fails */
+ r = copy_from_user(msg->text + *pos, buf, len);
+ if (r) {
+ if (*pos + len > msg->len)
+ memset(msg->text + msg->len, 0, *pos + len - msg->len);
+ return -EFAULT;
+ }
+ /* update the file position */
+ *pos += len;
+ if (msg->len < *pos) {
+ printk(KERN_DEBUG "Updating size from %d to %lld\n", msg->len, *pos);
+ msg->len = *pos;
+ }
+
+ return len;
+}
+
/* we only implement read */
static struct file_operations helloc_fops = {
.owner = THIS_MODULE,
+ .open = helloc_open,
+ .release = helloc_release,
.read = helloc_read,
+ .write = helloc_write,
+};
+
+static int hello_len_show(struct seq_file *seq, void *data)
+{
+ struct message *msg = seq->private;
+ seq_printf(seq, "%d\n", msg->len);
+ return 0;
+}
+
+static int hello_proc_open(struct inode *ino, struct file *filp)
+{
+ return single_open(filp, hello_len_show, hello_message);
+}
+
+static struct file_operations hello_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = hello_proc_open,
+ .llseek = seq_lseek,
+ .read = seq_read,
+ .release = seq_release,
};
-/* the device number and the char device struct */
-static dev_t dev;
-static struct cdev *cdev;
+static void helloc_free(void)
+{
+ if (hello_message)
+ kfree(hello_message->text);
+ kfree(hello_message);
+}
+
+static int __init helloc_alloc(void)
+{
+ hello_message = kzalloc(sizeof(struct message), GFP_KERNEL);
+ if (!hello_message)
+ return -ENOMEM;
+ hello_message->text = kzalloc(BUFFER_SIZE, GFP_KERNEL);
+ if (!hello_message->text)
+ goto out;
+ return 0;
+out:
+ kfree(hello_message);
+ return -ENOMEM;
+}
+
+static struct miscdevice hello_misc = {
+ .name = "hello",
+ .fops = &helloc_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+};
static int __init helloc_init(void)
{
+ struct proc_dir_entry *hello_pde;
int r;
- /* allocate any major number with only one minor */
- r = alloc_chrdev_region(&dev, 0, 1, "helloc");
+ r = helloc_alloc();
if (r)
- goto out_region;
- r = -ENOMEM;
- /* print the major number allocated so we can create our device node */
- printk(KERN_INFO "Allocated major number %d\n", MAJOR(dev));
- /* allocate the character device struct */
- cdev = cdev_alloc();
- if (!cdev)
goto out_alloc;
- /* set the module owner and the file operations of our chardev */
- cdev->owner = THIS_MODULE;
- cdev->ops = &helloc_fops;
- /* register the chardev to the system */
- r = cdev_add(cdev, dev, 1);
+ r = misc_register(&hello_misc);
if (r)
- goto out_add;
+ goto out_reg;
+ hello_pde = proc_create("hello_len", 0666, NULL, &hello_proc_fops);
+ if (!hello_pde)
+ goto out_pde;
return 0;
-out_add:
- /* release memory allocated to the cdev device */
- kfree(cdev);
+out_pde:
+ misc_deregister(&hello_misc);
+out_reg:
out_alloc:
- /* release the device number allocated */
- unregister_chrdev_region(dev, 1);
-out_region:
return r;
}
static void __exit helloc_exit(void)
{
- /* remove the chardev from the system */
- cdev_del(cdev);
- /* release the device number allocated */
- unregister_chrdev_region(dev, 1);
+ remove_proc_entry("hello_len", NULL);
+ misc_deregister(&hello_misc);
+ helloc_free();
}
module_init(helloc_init);