#include #include #include #include #include #include MODULE_LICENSE("GPL"); #include "assoc_buf.h" #define DEV_NAME "mod_add" #define OP_INVALID "Operation invalid\n" static int device_open(struct inode *, struct file *); static int device_release(struct inode *, struct file *); static ssize_t device_read(struct file *, char *, size_t, loff_t *); static ssize_t device_write(struct file *, const char *, size_t, loff_t *); static int major_num; static int device_open_count = 0; static struct buf_list { struct asbuf *buf; struct buf_list *next; } *buffers; struct asbuf *find_by_inode(unsigned long ino) { struct buf_list *iter = buffers->next; while (iter) { if (iter->buf->ino == ino) return iter->buf; iter = iter->next; } return NULL; } ssize_t add_buf(struct asbuf *buf) { struct buf_list *iter; if (!buffers) { buffers = kmalloc(sizeof(struct buf_list), GFP_KERNEL); if (!buffers) return -ENOMEM; buffers->buf = buf; buffers->next = NULL; return 0; } iter = buffers; while (iter->next) iter = iter->next; iter->next = kmalloc(sizeof(struct buf_list), GFP_KERNEL); if (!iter->next) return -ENOMEM; iter->buf = buf; iter->next = NULL; return 0; } void free_buf_list(void) { struct buf_list *iter = buffers, *next; while (iter) { next = iter->next; asbuf_free(iter->buf); kfree(iter->buf); kfree(iter); iter = next; } } ssize_t rm_by_inode(unsigned long ino) { struct buf_list *iter = buffers; struct buf_list *save_prev; if (!buffers) return 0; while (iter->next) { save_prev = iter; iter = iter->next; if (iter->buf->ino == ino) { save_prev->next = iter->next; asbuf_free(iter->buf); kfree(iter->buf); kfree(iter); return 0; } } return -EINVAL; } static struct file_operations file_ops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release }; static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) { struct asbuf *buf = find_by_inode(flip->f_inode->i_ino); printk(KERN_INFO "req inode %lu\n", flip->f_inode->i_ino); if (!buf) return -EINVAL; return asbuf_to_user(buf, buffer, len, offset); } static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) { if (len == 0) return 0; return asbuf_from_user(find_by_inode(flip->f_inode->i_ino), buffer, len, offset); } static int device_open(struct inode *inode, struct file *file) { struct asbuf *newbuf; if (device_open_count) return -EBUSY; newbuf = kzalloc(sizeof(*newbuf), GFP_KERNEL); if (!newbuf) return -ENOMEM; printk(KERN_INFO "opened for inode %lu", inode->i_ino); newbuf->ino = inode->i_ino; add_buf (newbuf); device_open_count++; try_module_get(THIS_MODULE); return 0; } static int device_release(struct inode *inode, struct file *file) { rm_by_inode(inode->i_ino); device_open_count--; module_put(THIS_MODULE); return 0; } static int __init mod_add_init(void) { buffers = kzalloc(sizeof(*buffers), GFP_KERNEL); major_num = register_chrdev(0, DEV_NAME, &file_ops); if (major_num < 0) { printk(KERN_ALERT "Could not register device: %d\n", major_num); return major_num; } else { printk(KERN_INFO "mod_add loaded. dev maj = %d\n", major_num); return 0; } } static void __exit mod_add_exit(void) { unregister_chrdev(major_num, DEV_NAME); free_buf_list(); printk(KERN_INFO "okay, bye\n"); } module_init(mod_add_init); module_exit(mod_add_exit);