X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=hellochar.c;h=a8473920d829a63f383554973541c290bbd54649;hb=refs%2Fheads%2Fqueue;hp=9d4e40a0b149c553b9251206fcfd9a21e31ac2a8;hpb=4b37cdbb4e22dac308907a27286e92fcd98f13eb;p=cascardo%2Fkernel%2Fsamples%2Fchar2%2F.git diff --git a/hellochar.c b/hellochar.c index 9d4e40a..a847392 100644 --- a/hellochar.c +++ b/hellochar.c @@ -20,29 +20,116 @@ #include #include #include +#include +#include +#include +#include +#include MODULE_LICENSE("GPL"); +#define MAXLEN 4096 static dev_t devnum; static struct cdev *dev; +static const char default_greeting[] = "Hello, World!\n"; + +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) { - printk(KERN_DEBUG "Hello, World!\n"); + return 0; +} + +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; + 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; + complete(&hello_wait); + return sz; +} + +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; + len = CIRC_SPACE(hello->head, hello->tail, MAXLEN); + while (len == 0) { + mutex_unlock(&hello_mtx); + 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); + } + 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; +} + +static int hello_release(struct inode *ino, struct file *fp) +{ return 0; } static const struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, + .release = hello_release, + .read = hello_read, + .write = hello_write, }; 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; @@ -52,12 +139,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; } @@ -66,6 +154,7 @@ static void __exit ch_exit(void) { cdev_del(dev); unregister_chrdev_region(devnum, 256); + kfree(hello->buf); } module_init(ch_init);