X-Git-Url: http://git.cascardo.eti.br/?p=cascardo%2Fkernel%2Fsamples%2Fchar2%2F.git;a=blobdiff_plain;f=hellochar.c;h=940bfeb837b7ca7ddcbd43aebaa496845a871ae2;hp=1b0c9f22c23f76922b0a50df3f2d97b516c4f95b;hb=HEAD;hpb=342d263173687df5085816d9be0423023cd44cf6 diff --git a/hellochar.c b/hellochar.c index 1b0c9f2..940bfeb 100644 --- a/hellochar.c +++ b/hellochar.c @@ -22,32 +22,25 @@ #include #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 hello_buffer *hello; +static struct circ_buf *hello; +static struct circ_buf hello_instance; static DEFINE_MUTEX(hello_mtx); +static DECLARE_COMPLETION(hello_wait); static int hello_open(struct inode *ino, struct file *fp) { - if (mutex_lock_interruptible(&hello_mtx)) - return -ERESTARTSYS; - if (fp->f_flags & O_TRUNC) { - memset(hello->buffer, 0, MAXLEN); - hello->len = 0; - } - if (fp->f_flags & O_APPEND) - fp->f_pos = hello->len; - mutex_unlock(&hello_mtx); return 0; } @@ -55,15 +48,26 @@ static ssize_t hello_read(struct file *fp, char __user *buf, size_t sz, loff_t *pos) { int r; + size_t fsz; + size_t ssz; + size_t len; if (mutex_lock_interruptible(&hello_mtx)) return -ERESTARTSYS; - if (sz + *pos > hello->len) - sz = hello->len - *pos; - r = copy_to_user(buf, hello->buffer + *pos, sz); + 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; + complete(&hello_wait); return sz; } @@ -71,19 +75,32 @@ static ssize_t hello_write(struct file *fp, const char __user *buf, size_t sz, loff_t *pos) { int r; + size_t fsz; + size_t ssz; + size_t len; if (mutex_lock_interruptible(&hello_mtx)) return -ERESTARTSYS; - if (sz + *pos > MAXLEN) - sz = MAXLEN - *pos; - r = copy_from_user(hello->buffer + *pos, buf, sz); - if (r) { + len = CIRC_SPACE(hello->head, hello->tail, MAXLEN); + while (len == 0) { mutex_unlock(&hello_mtx); - return -EFAULT; + if (wait_for_completion_interruptible(&hello_wait)) + return -ERESTARTSYS; + if (mutex_lock_interruptible(&hello_mtx)) + return -ERESTARTSYS; + len = CIRC_SPACE(hello->head, hello->tail, MAXLEN); } - *pos += sz; - if (hello->len < *pos) - hello->len = *pos; + 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; return sz; } @@ -100,19 +117,48 @@ static const struct file_operations hello_fops = { .write = hello_write, }; +static ssize_t hello_len_show(struct device *dev, struct device_attribute *attr, + char *buffer) +{ + size_t len = CIRC_CNT(hello->head, hello->tail, MAXLEN); + return sprintf(buffer, "%d\n", len); +} + +static const struct device_attribute hello_len_attr = { + .attr = { .name = "len", .mode = 0444, }, + .show = hello_len_show, +}; + +static struct class *hello_class; +static struct device *hello_dev; + static int __init ch_init(void) { int r = 0; - hello = kzalloc(sizeof(*hello) + MAXLEN, GFP_KERNEL); + hello = &hello_instance; + hello->buf = kzalloc(sizeof(*hello) + MAXLEN, GFP_KERNEL); if (!hello) { r = -ENOMEM; goto out; } - memcpy(hello->buffer, default_greeting, sizeof(default_greeting)); - hello->len = sizeof(default_greeting); + hello_class = class_create(THIS_MODULE, "hello"); + if (IS_ERR(hello_class)) { + r = PTR_ERR(hello_class); + goto class_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 reg_out; + hello_dev = device_create(hello_class, NULL, devnum, NULL, "hello"); + if (!hello_dev) { + r = -ENODEV; + goto dev_out; + } + r = device_create_file(hello_dev, &hello_len_attr); + if (r) + goto file_out; dev = cdev_alloc(); if (!dev) { r = -ENOMEM; @@ -122,13 +168,18 @@ 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: + device_remove_file(hello_dev, &hello_len_attr); +file_out: + device_destroy(hello_class, devnum); +dev_out: unregister_chrdev_region(devnum, 256); reg_out: + class_destroy(hello_class); +class_out: kfree(hello); out: return r; @@ -137,8 +188,11 @@ out: static void __exit ch_exit(void) { cdev_del(dev); + device_remove_file(hello_dev, &hello_len_attr); + device_destroy(hello_class, devnum); unregister_chrdev_region(devnum, 256); - kfree(hello); + kfree(hello->buf); + class_destroy(hello_class); } module_init(ch_init);