一個簡單的字元裝置模組
阿新 • • 發佈:2018-12-19
/*-------------------------------------------------- Derived from Linux Device Drivers(3rd edition) http://www.makelinux.net/ldd3/ Midas Zhou ---------------------------------------------------*/ #include <linux/module.h> #include <linux/init.h> /* __init() __exit() */ #include <linux/sched.h> #include <linux/fs.h> /* register_chrdev_region(), alloc_chrdev_region()...*/ #include <linux/cdev.h> /* cdev_init(), cdev_add(), cdev_del() */ #include <asm/errno.h> #include <linux/slab.h> /* kmalloc() kfree() */ #include <linux/uaccess.h> /* copy_to_user(), copy_from_user() */ #define HELLO_DATA_LEN 1024 /* data length for hello_cdev.data */ MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Free"); MODULE_DESCRIPTION("A LINUX Free Hello World Example"); MODULE_VERSION("0.1"); static char * whom ="This is the Free Linux World! "; // Do not necessary to allocate mem for whom, you can use long string input when load the module. static int howmany = 1; module_param(whom,charp, S_IRUGO); /* S_IRUGO = S_IRUSR | S_IRGRP | S_IROTH */ module_param(howmany,int,S_IRUGO); MODULE_PARM_DESC(whom, "The name to display"); MODULE_PARM_DESC(howmany, "How many times to print"); static dev_t hello_devnum; /* 32bits device number: major(12) + minor(20) */ struct hello_cdev { char *data; /* mem data for the device */ struct semaphore sem; /* mutual exclusion */ struct cdev cdev; /* Char device */ }; static struct hello_cdev my_hello_cdev; struct class * hello_class; struct device *hello_device; /* ----------------------- File Operation Functions: open/close/read/write/lseek ------------------- */ static int hello_open(struct inode *inode, struct file *filp) { struct hello_cdev *pdev; pdev=container_of(inode->i_cdev,struct hello_cdev, cdev); // to locate and get helloc_cdev here. printk(KERN_INFO "%s: Hello, you just open the Free Linux World!\n",__func__); // printk(KERN_INFO "%s: read data in helloc_cdev: %s\n",__func__,pdev->data); filp->private_data = pdev; /* put pdev to private data for other operation reference */ return 0; } static int hello_close(struct inode *inode, struct file *filp) { printk(KERN_INFO "%s: Hello, you just close the Free Linux World!\n",__func__); return 0; } ssize_t hello_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct hello_cdev *dev = filp->private_data; ssize_t retval = 0; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; if( *f_pos > HELLO_DATA_LEN-1 ) { printk(KERN_ALERT "%s: f_pos exceeds the end of the file.\n",__func__); goto out; } if( count > HELLO_DATA_LEN-*f_pos ) count = HELLO_DATA_LEN-*f_pos; if( copy_to_user(buf, (dev->data)+*f_pos, count)) { retval=-EFAULT; goto out; } *f_pos += count; retval = count; out: up(&dev->sem); return retval; } ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct hello_cdev *dev = filp->private_data; ssize_t retval =0; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; if ( count > HELLO_DATA_LEN - *f_pos ) count = HELLO_DATA_LEN - *f_pos; if (copy_from_user(dev->data+*f_pos, buf, count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; out: up(&dev->sem); return retval; } loff_t hello_llseek(struct file *filp, loff_t off, int whence) { // struct hello_cdev *dev = filp->private_data; loff_t newpos; switch(whence) { case 0: /*SEEK_SET*/ newpos = off; break; case 1: /*SEEK_CUR*/ newpos = filp->f_pos + off; break; default: return -EINVAL; } if (newpos < 0 || newpos > HELLO_DATA_LEN-1 ) return -EINVAL; filp->f_pos=newpos; return newpos; } static const struct file_operations hello_fops={ .owner = THIS_MODULE, .open = hello_open, .read = hello_read, .write = hello_write, .llseek = hello_llseek, .release = hello_close, }; /* ------------------------------ Module Functions ------------------------- */ static int __init hello_init(void) { int k; int ret=0; for(k=0;k<howmany;k++) { printk(KERN_ALERT "Hello, %s!\n",whom); /* No';' after KERN_ALERT !!!*/ } printk(KERN_INFO "The process is \"%s\" (pid %i)\n",current->comm, current->pid); /* --- allocate mem for hello_cdev.data --- */ my_hello_cdev.data = kmalloc(HELLO_DATA_LEN, GFP_KERNEL); if(IS_ERR(my_hello_cdev.data)) { printk(KERN_ALERT "kmalloc_fail, kmalloc for my_hello_cdev.data failed!\n"); ret = -1; goto kmalloc_fail; } memset(my_hello_cdev.data,0,HELLO_DATA_LEN); strcpy(my_hello_cdev.data,"-/-/-/-/-/-/-/-/-/-/-/"); /* --- init. semaphore for hello_cdev ---*/ sema_init(&my_hello_cdev.sem,1); /* ------------------ Char Device Registration and Creation ---------------------- */ /* --- 1. register and get dev number / name --- !!!!! ASSOCIATE name WITH major devnum */ ret=alloc_chrdev_region(&hello_devnum, 0, 1, "dev_hello"); //warning: devnum must NOT be pointer type. if(ret<0){ printk("%s: alloc_chrdev_region() error!\n",__func__); ret = -2; goto alloc_region_fail; } /* --- 2. char device init --- !!!!! ASSOCIATE file operations */ cdev_init(&my_hello_cdev.cdev, &hello_fops); // !!!! ASSOCIATE hello_cdev with inode, to use container_of() to exact helloc_cdev later. /* --- 3. add char dev to kernel ---- */ ret=cdev_add(&my_hello_cdev.cdev,hello_devnum,1); if(ret<0){ printk("%s: cdev_add() error!\n",__func__); ret = -3; goto cdev_fail; } /* --- 4. create class in sysfs */ hello_class = class_create(THIS_MODULE,"hello_class"); if( IS_ERR(hello_class)) { printk(KERN_INFO "class_create() fail!\n"); ret = -4; goto class_fail; } /* --- 5. create devices file in /dev */ hello_device = device_create(hello_class, NULL, hello_devnum, NULL, "hello_dev"); if ( IS_ERR(hello_device)) { printk(KERN_INFO "device_create() fail!\n"); ret = -5; goto device_fail; } /* --- 6. module init. success --- */ goto init_success; device_fail: /* --- 1. destroy class --- */ class_destroy(hello_class); class_fail: /* --- 2. del char dev --- */ cdev_del(&my_hello_cdev.cdev); cdev_fail: /* --- 3. unregister char dev region --- */ unregister_chrdev_region(hello_devnum,1); /* --- 4. kfree mem --- */ kfree(my_hello_cdev.data); kmalloc_fail: alloc_region_fail: return ret; /* if ret !=0; the module will NOT be inserted to the kernel. */ init_success: /* --- init. success --- */ return 0; } static void __exit hello_exit(void) /* !!!! void */ { /*--- 1. unregister device ---*/ printk(KERN_INFO "unregister device \n"); device_unregister(hello_device); /*--- 2. del class ---*/ printk(KERN_INFO "destroy class \n"); class_destroy(hello_class); /*--- 3. del char dev ---*/ printk(KERN_INFO "delete char dev.\n"); cdev_del(&my_hello_cdev.cdev); /*--- 4. unregister char dev region ---*/ printk(KERN_INFO "unregister chardev region.\n"); unregister_chrdev_region(hello_devnum,1); /*--- 5. kfree all mem ---*/ printk(KERN_INFO "kfree mem.\n"); kfree(my_hello_cdev.data); printk(KERN_INFO "The process is \"%s\" (pid %i)\n",current->comm, current->pid); printk(KERN_ALERT "Goodbye, Cruel World!\n"); } module_init(hello_init); module_exit(hello_exit);