X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=hellochar.c;h=de99828ade010e78ce3e1cccb7a789db82addbd8;hb=91e474b7d6c004d5c0d3c5c1ff94721ad64d442d;hp=1649fe31da84274122e859591b91a169dc023851;hpb=0bd3205aa20ff03e41783a79665443c7d05ffdb1;p=cascardo%2Fkernel%2Fsamples%2Fchar2%2F.git diff --git a/hellochar.c b/hellochar.c index 1649fe3..de99828 100644 --- a/hellochar.c +++ b/hellochar.c @@ -20,64 +20,79 @@ #include #include #include +#include #include +#include MODULE_LICENSE("GPL"); -#define MAXLEN 4000 +#define MAXLEN 4096 static dev_t devnum; static struct cdev *dev; static const char default_greeting[] = "Hello, World!\n"; -struct hello_buffer { - size_t len; - char buffer[0]; -}; +static struct circ_buf *hello; +static struct circ_buf hello_instance; +static DEFINE_MUTEX(hello_mtx); static int hello_open(struct inode *ino, struct file *fp) { - struct hello_buffer *hello = kzalloc(sizeof(*hello) + MAXLEN, GFP_KERNEL); - if (!hello) - return -ENOMEM; - memcpy(hello->buffer, default_greeting, sizeof(default_greeting)); - hello->len = sizeof(default_greeting); - fp->private_data = hello; return 0; } static ssize_t hello_read(struct file *fp, char __user *buf, size_t sz, loff_t *pos) { - struct hello_buffer *hello = fp->private_data; int r; - if (sz + *pos > hello->len) - sz = hello->len - *pos; - r = copy_to_user(buf, hello->buffer + *pos, sz); + size_t fsz; + size_t ssz; + size_t len; + if (mutex_lock_interruptible(&hello_mtx)) + return -ERESTARTSYS; + len = CIRC_CNT(hello->head, hello->tail, MAXLEN); + if (sz > len) + sz = len; + /* First, copy at most until the limit of the buffer */ + fsz = min(sz, CIRC_CNT_TO_END(hello->head, hello->tail, MAXLEN)); + r = copy_to_user(buf, hello->buf + hello->tail, fsz); + ssz = sz - fsz; + if (!r && !ssz) + r = copy_to_user(buf + fsz, hello->buf, ssz); + if (!r) + hello->tail = (hello->tail + sz) & ~MAXLEN; + mutex_unlock(&hello_mtx); if (r) return -EFAULT; - *pos += sz; return sz; } static ssize_t hello_write(struct file *fp, const char __user *buf, size_t sz, loff_t *pos) { - struct hello_buffer *hello = fp->private_data; int r; - if (sz + *pos > MAXLEN) - sz = MAXLEN - *pos; - r = copy_from_user(hello->buffer + *pos, buf, sz); + size_t fsz; + size_t ssz; + size_t len; + if (mutex_lock_interruptible(&hello_mtx)) + return -ERESTARTSYS; + len = CIRC_SPACE(hello->head, hello->tail, MAXLEN); + if (sz > len) + sz = len; + fsz = min(sz, CIRC_SPACE_TO_END(hello->head, hello->tail, MAXLEN)); + r = copy_from_user(hello->buf + hello->head, buf, fsz); + ssz = sz - fsz; + if (!r && !ssz) + r = copy_from_user(hello->buf, buf + fsz, ssz); + if (!r) + hello->head = (hello->head + sz) & ~MAXLEN; + mutex_unlock(&hello_mtx); if (r) return -EFAULT; - *pos += sz; - if (hello->len < *pos) - hello->len = *pos; return sz; } static int hello_release(struct inode *ino, struct file *fp) { - kfree(fp->private_data); return 0; } @@ -92,9 +107,17 @@ static const struct file_operations hello_fops = { static int __init ch_init(void) { int r = 0; + hello = &hello_instance; + hello->buf = kzalloc(sizeof(*hello) + MAXLEN, GFP_KERNEL); + if (!hello) { + r = -ENOMEM; + goto out; + } + memcpy(hello->buf, default_greeting, sizeof(default_greeting)); + hello->head = sizeof(default_greeting); r = alloc_chrdev_region(&devnum, 0, 256, "hello"); if (r) - goto out; + goto reg_out; dev = cdev_alloc(); if (!dev) { r = -ENOMEM; @@ -104,12 +127,13 @@ static int __init ch_init(void) r = cdev_add(dev, devnum, 256); if (r) goto add_out; - printk(KERN_DEBUG "Allocate major %d\n", MAJOR(devnum)); return 0; add_out: kfree(dev); cdev_out: unregister_chrdev_region(devnum, 256); +reg_out: + kfree(hello); out: return r; } @@ -118,6 +142,7 @@ static void __exit ch_exit(void) { cdev_del(dev); unregister_chrdev_region(devnum, 256); + kfree(hello); } module_init(ch_init);