#include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include // define the module license mode MODULE_LICENSE("GPL"); #ifdef MODULE_ALIAS MODULE_ALIAS("ghe"); #endif #define GHE_DEVNAME "/dev/ghe" #define GHE_PROCNAME "ghe" static int devmajor = -1; static char *devbuf = NULL; static char *procbuf = NULL; static int devoffset = 0; static int devend = 0; static int devlimit = 1024; static wait_queue_head_t* aaa = NULL; static void shift_buf() { int i; int len; if (devoffset == 0) return; len = devend - devoffset; for (i = 0; i < len; i++) { devbuf[i] = devbuf[devoffset+i]; } devend = devend - devoffset; devoffset = 0; } static int ghe_open(struct inode *ino, struct file *filp) { printk("ghe_open: inode: %lu, file: %p\n", ino->i_ino, filp); return 0; } static int ghe_release(struct inode *ino, struct file *filp) { printk("ghe_release: inode: %lu, file: %p\n", ino->i_ino, filp); return ( (devend-devoffset) > 0 ? 1 : 0 ); } static ssize_t ghe_read(struct file *filp, char *buf, size_t sz, loff_t *ppos) { size_t len; int rc; printk("ghe_read: file: %p buf: %p sz: %d *ppos: %d\n", filp, buf, sz, *ppos); if (devend - devoffset <= 0) { // no data printk("No data to read, waiting ... \n"); rc = wait_event_interruptible( (*aaa), ((devend-devoffset) > 0)); printk("Wait return %d\n", rc); } if (devend - devoffset >= sz) { len = sz; } else { len = devend - devoffset; } if (copy_to_user(buf, devbuf+devoffset, len) != 0) return -EFAULT; devoffset += len; *ppos += len; return len; } static ssize_t ghe_write(struct file *filp, const char *buf, size_t sz, loff_t *ppos) { size_t len; int flag = 1; printk("ghe_write: file: %p buf: %p sz: %d *ppos: %d\n", filp, buf, sz, *ppos); if (devend - devoffset >= devlimit) { // no space printk("No space to be written, return.\n"); return 0; } do { if (devlimit - devend >= sz) { len = sz; break; } else { if (flag) { shift_buf(); flag = 0; } else { len = devlimit - devend; break; } } } while (1); if (copy_from_user(devbuf+devend, buf, len) != 0) return -EFAULT; devend += len; *ppos += len; printk("Wakeup read user ... \n"); wake_up_interruptible(aaa); printk("Wakeup read user ... end\n"); return len; } struct file_operations ghe_file_operations = { ioctl: 0, open: ghe_open, release: ghe_release, read: ghe_read, write: ghe_write, owner: THIS_MODULE, }; int ghe_init(void); void ghe_exit(void); module_init(ghe_init); module_exit(ghe_exit); static struct proc_dir_entry* proc_res = NULL; int get_proc_info() { if (!procbuf) return -1; sprintf(procbuf, "Device statistics:\n" "Device major: %d buffer limit: %d\n" "Buffer offset: %d end: %d\n", devmajor, devlimit, devoffset, devend); return strlen(procbuf); } int ghe_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int len; len = get_proc_info(); if (len < 0) { strcpy(page, "error"); len = 5; } else { strcpy(page, procbuf); } *eof = 1; return len; } int ghe_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { return count; } int ghe_init(void) { int rs = 0; aaa = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); init_waitqueue_head(aaa); spin_lock_init(&aaa->lock); if (!(devbuf = kmalloc(devlimit, GFP_KERNEL))) { printk("can not alloc memory(%d)\n", devlimit); return -1; } if (!(procbuf = kmalloc(1024, GFP_KERNEL))) { printk("can not alloc memory(%d)\n", 1024); return -1; } if ((rs = register_chrdev(0, GHE_DEVNAME, &ghe_file_operations)) < 0) { printk("%s: cannot register device\n", GHE_DEVNAME); return -1; } devmajor = rs; if (!(proc_res = create_proc_entry(GHE_PROCNAME, S_IFREG | S_IRUSR | S_IWUSR, &proc_root))) { printk("%s: cannot register proc file\n", GHE_PROCNAME); } else { proc_res->read_proc = ghe_read_proc; proc_res->write_proc = ghe_write_proc; } printk("register device %s: (%d)\n", GHE_DEVNAME, devmajor); return 0; } void ghe_exit(void) { int rs = 0; if (devbuf) kfree(devbuf); if (procbuf) kfree(procbuf); if (proc_res) remove_proc_entry(GHE_PROCNAME, &proc_root); if (devmajor >= 0) { rs = unregister_chrdev(devmajor, GHE_DEVNAME); printk("unregister device %s: (%d)\n", GHE_DEVNAME, rs); } }